@tinyweb_dev/oe-exam-sdk 1.0.9 → 1.0.11
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/ExamTakingPageContainer.js +6 -0
- package/dist/components/exams/take/components/QuestionRenderer.js +7 -7
- 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/question-renderers/MoversMatchWordToPictureGroupRenderer.d.ts +14 -0
- package/dist/components/exams/take/components/question-renderers/MoversMatchWordToPictureGroupRenderer.js +36 -0
- package/dist/components/exams/take/components/question-renderers/MoversMatchingWithLinesGroupRenderer.js +4 -0
- package/dist/components/exams/take/components/question-renderers/index.d.ts +1 -0
- package/dist/components/exams/take/components/question-renderers/index.js +1 -0
- 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 +17 -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 +32 -3
- package/dist/components/exams/take/utils/question-transformers.js +51 -2
- 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/crossword-puzzle.type.d.ts +5 -1
- package/dist/components/questions/_shared/types/match-word-to-picture-group.type.d.ts +1 -0
- package/dist/components/questions/_shared/types/match-word-to-picture-group.type.js +14 -0
- package/dist/components/questions/_shared/types/matching-with-lines-group.type.d.ts +2 -2
- package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +17 -0
- package/dist/components/questions/components/QuestionSearchDropdown.js +1 -0
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +18 -19
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +8 -7
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +5 -4
- 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 -30
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +8 -7
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +50 -32
- 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 +8 -6
- 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.js +71 -28
- 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 +24 -35
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +16 -3
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupImageField.d.ts +2 -1
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupImageField.js +25 -6
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesBoard.js +1 -1
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupClient.js +22 -10
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +60 -18
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesImageField.d.ts +2 -1
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesImageField.js +24 -5
- package/dist/components/questions/types/matching-with-lines-group/map-matching-with-lines-group-data.js +8 -4
- package/dist/components/questions/types/matching-with-lines-group/transform.js +5 -4
- 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.js +21 -23
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +8 -17
- 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/CrossOutWordGroupViewer.js +1 -4
- 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.js +2 -2
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +7 -1
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +14 -2
- package/dist/components/results/renderers/review-question-body-movers.js +7 -3
- package/dist/components/results/review-data.js +20 -1
- package/dist/shared/lib/utils/question-reverse-transform.js +48 -7
- 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/dist/shared/types/questions/matching-with-lines-group.d.ts +2 -2
- package/package.json +1 -1
|
@@ -632,6 +632,12 @@ function ExamTakingPageContainerInner({ roomId, api, onNavigate, resultPath = (a
|
|
|
632
632
|
map.set(`${question.id}-item-${index}`, question.id);
|
|
633
633
|
});
|
|
634
634
|
}
|
|
635
|
+
if (part.questionType === 'MATCH_WORD_TO_PICTURE_GROUP' &&
|
|
636
|
+
Array.isArray(content?.items)) {
|
|
637
|
+
content.items.forEach((_, index) => {
|
|
638
|
+
map.set(`${question.id}-item-${index}`, question.id);
|
|
639
|
+
});
|
|
640
|
+
}
|
|
635
641
|
if (part.questionType === 'CHOOSE_THEN_ANSWER_GROUP' &&
|
|
636
642
|
Array.isArray(content?.items)) {
|
|
637
643
|
content.items.forEach((_, index) => {
|
|
@@ -8,9 +8,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
* 2. Transforms data to renderer-specific props
|
|
9
9
|
* 3. Renders the correct component for each question type
|
|
10
10
|
*/
|
|
11
|
-
import { MoversListenAndWriteRenderer, MoversChooseBestAnswerRenderer, MoversChooseAdjectiveRenderer, MoversWritingRenderer, MoversColoringRenderer, MoversReadingPassageRenderer, MoversLabelThePictureRenderer, MoversGridFillRenderer, MoversTrueFalseRenderer, MoversTrueFalseCorrectGroupRenderer, MoversFillInBlankGroupRenderer, MoversCrossOutWordGroupRenderer, MoversAnswerTheQuestionRenderer, MoversWriteSentencesRenderer, MoversWordOrderingRenderer, MoversWordOrderingGroupRenderer, MoversWordFillParagraphRenderer, MoversMatchByWritingAnswerRenderer, MoversMatchWordToPictureRenderer, MoversWordFillStructuredFormRenderer, MoversWordOrderAndMatchGroupRenderer, MoversMatchingWithLinesGroupRenderer, MoversCrosswordPuzzleRenderer, FindWordsInMatrixRenderer, SortWordsIntoCategoriesRenderer, ChooseThenAnswerGroupRenderer, } from './question-renderers';
|
|
11
|
+
import { MoversListenAndWriteRenderer, MoversChooseBestAnswerRenderer, MoversChooseAdjectiveRenderer, MoversWritingRenderer, MoversColoringRenderer, MoversReadingPassageRenderer, MoversLabelThePictureRenderer, MoversGridFillRenderer, MoversTrueFalseRenderer, MoversTrueFalseCorrectGroupRenderer, MoversFillInBlankGroupRenderer, MoversCrossOutWordGroupRenderer, MoversAnswerTheQuestionRenderer, MoversWriteSentencesRenderer, MoversWordOrderingRenderer, MoversWordOrderingGroupRenderer, MoversWordFillParagraphRenderer, MoversMatchByWritingAnswerRenderer, MoversMatchWordToPictureRenderer, MoversMatchWordToPictureGroupRenderer, MoversWordFillStructuredFormRenderer, MoversWordOrderAndMatchGroupRenderer, MoversMatchingWithLinesGroupRenderer, MoversCrosswordPuzzleRenderer, FindWordsInMatrixRenderer, SortWordsIntoCategoriesRenderer, ChooseThenAnswerGroupRenderer, } from './question-renderers';
|
|
12
12
|
import { SpeakingQuestionRenderer } from './speaking/renderers';
|
|
13
|
-
import { transformFillInBlank, transformWriteCorrectVerbForm, transformChooseCorrectAnswer, transformChooseTheCorrectAnswerGroup, transformChooseCorrectAdjective, transformWriteShortLetter, transformColorObjects, transformReadingPassage, transformLabelPicture, transformGridFill, transformAnswerTheQuestion, transformAnswerTheQuestionGroup, transformTrueFalse, transformTrueFalseGroup, transformTrueFalseCorrectGroup, transformFillInBlankGroup, transformWordOrdering, transformWordOrderingGroupTake, transformWordFillParagraph, transformMatchByWritingAnswer, transformMatchWordToPicture, transformWriteSentences, transformWordFillStructuredForm, transformWordOrderAndMatchGroup, transformMatchingWithLinesGroup, transformCrosswordPuzzle, transformFindWordsInMatrix, transformSortWordsIntoCategories, transformChooseThenAnswerGroup, } from '../utils/question-transformers';
|
|
13
|
+
import { transformFillInBlank, transformWriteCorrectVerbForm, transformChooseCorrectAnswer, transformChooseTheCorrectAnswerGroup, transformChooseCorrectAdjective, transformWriteShortLetter, transformColorObjects, transformReadingPassage, transformLabelPicture, transformGridFill, transformAnswerTheQuestion, transformAnswerTheQuestionGroup, transformTrueFalse, transformTrueFalseGroup, transformTrueFalseCorrectGroup, transformFillInBlankGroup, transformWordOrdering, transformWordOrderingGroupTake, transformWordFillParagraph, transformMatchByWritingAnswer, transformMatchWordToPicture, transformMatchWordToPictureGroup, transformWriteSentences, transformWordFillStructuredForm, transformWordOrderAndMatchGroup, transformMatchingWithLinesGroup, transformCrosswordPuzzle, transformFindWordsInMatrix, transformSortWordsIntoCategories, transformChooseThenAnswerGroup, } from '../utils/question-transformers';
|
|
14
14
|
import { transformCrossOutWordGroup } from '../utils/cross-out-word-group.transformer';
|
|
15
15
|
import { normalizeColoringAssignments } from '../utils/coloring-assignments';
|
|
16
16
|
export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode = false, onExit, onPartComplete, onPartBack, speakingTheme, initialQuestionIndex, skipTeacherVideo, onTeacherIntroPlayed, currentQuestionIndex, compactLayout = false, }) {
|
|
@@ -102,7 +102,7 @@ export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode =
|
|
|
102
102
|
}
|
|
103
103
|
case 'CROSSWORD_PUZZLE': {
|
|
104
104
|
const data = transformCrosswordPuzzle(questions);
|
|
105
|
-
return (_jsx(MoversCrosswordPuzzleRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, title: data.title, gridSize: data.gridSize, clues: data.clues, answers: answers, correctAnswers: data.correctAnswers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
105
|
+
return (_jsx(MoversCrosswordPuzzleRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, title: data.title, gridSize: data.gridSize, markedColumn: data.markedColumn, hiddenWord: data.hiddenWord, clues: data.clues, answers: answers, correctAnswers: data.correctAnswers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode, caseSensitive: data.caseSensitive }));
|
|
106
106
|
}
|
|
107
107
|
case 'IMAGE_OBJECT_MATCHING': {
|
|
108
108
|
// Reuse coloring renderer — same content structure as READ_AND_COLOR_OBJECTS
|
|
@@ -125,10 +125,6 @@ export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode =
|
|
|
125
125
|
onAnswerChange(regionId, hintItemId);
|
|
126
126
|
}, isReviewMode: isReviewMode }));
|
|
127
127
|
}
|
|
128
|
-
case 'CROSSWORD_PUZZLE': {
|
|
129
|
-
const data = transformCrosswordPuzzle(questions);
|
|
130
|
-
return (_jsx(MoversCrosswordPuzzleRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, title: data.title, gridSize: data.gridSize, clues: data.clues, answers: answers, correctAnswers: data.correctAnswers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
131
|
-
}
|
|
132
128
|
case 'SORT_WORDS_INTO_CATEGORIES': {
|
|
133
129
|
const data = transformSortWordsIntoCategories(questions);
|
|
134
130
|
const gradableCount = data.words.filter((word) => !word.isExample).length;
|
|
@@ -204,6 +200,10 @@ export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode =
|
|
|
204
200
|
const data = transformMatchByWritingAnswer(questions);
|
|
205
201
|
return (_jsx(MoversMatchByWritingAnswerRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, audioUrl: part.audioUrl, options: data.options, questions: getRenderQuestions(data.questions), answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode, viewMode: data.viewMode, groupImageUrl: data.groupImageUrl }));
|
|
206
202
|
}
|
|
203
|
+
case 'MATCH_WORD_TO_PICTURE_GROUP': {
|
|
204
|
+
const data = transformMatchWordToPictureGroup(questions);
|
|
205
|
+
return (_jsx(MoversMatchWordToPictureGroupRenderer, { partNumber: partNo, partName: part.name, questionCount: data.questions.filter((q) => !q.isExample).length, instruction: part.instructions || data.instruction, audioUrl: part.audioUrl, data: data, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
206
|
+
}
|
|
207
207
|
case 'MATCH_WORD_TO_PICTURE': {
|
|
208
208
|
const data = transformMatchWordToPicture(questions);
|
|
209
209
|
return (_jsx(MoversMatchWordToPictureRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, wordBank: data.wordBank, questions: getRenderQuestions(data.questions), answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js
CHANGED
|
@@ -8,9 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
import { useMemo } from 'react';
|
|
9
9
|
import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
|
|
10
10
|
import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
|
|
11
|
-
|
|
12
|
-
return sentence.trim().split(/\s+/).filter(Boolean);
|
|
13
|
-
}
|
|
11
|
+
import { tokenizeCrossOutQuestion } from '../../../../questions/types/cross-out-word-group/tokenize';
|
|
14
12
|
function normalizeWord(word) {
|
|
15
13
|
return word.replace(/[^\p{L}\p{N}'-]/gu, '').toLowerCase();
|
|
16
14
|
}
|
|
@@ -27,12 +25,20 @@ function formatAnswerLabel(answer) {
|
|
|
27
25
|
return 'câu đúng (không gạch)';
|
|
28
26
|
return `gạch "${answer.crossOut}"${posInfo}`;
|
|
29
27
|
}
|
|
28
|
+
function isCorrectToken(correct, tokenIndex, plain) {
|
|
29
|
+
if (!correct)
|
|
30
|
+
return false;
|
|
31
|
+
if (typeof correct.wordIndex === 'number' && correct.wordIndex >= 0) {
|
|
32
|
+
return correct.wordIndex === tokenIndex;
|
|
33
|
+
}
|
|
34
|
+
return Boolean(correct.crossOut) && normalizeWord(correct.crossOut) === plain && plain !== '';
|
|
35
|
+
}
|
|
30
36
|
function QuestionItem({ question, answer, onAnswerChange, isReviewMode, correctAnswer, id, }) {
|
|
31
37
|
const isDisabled = isReviewMode || question.isExample;
|
|
32
38
|
const currentAnswer = question.isExample
|
|
33
39
|
? question.exampleCorrectAnswer
|
|
34
40
|
: answer;
|
|
35
|
-
const tokens = useMemo(() =>
|
|
41
|
+
const tokens = useMemo(() => tokenizeCrossOutQuestion(question.question), [question.question]);
|
|
36
42
|
const selectedWord = currentAnswer?.crossOut || '';
|
|
37
43
|
const selectedIndex = currentAnswer?.wordIndex;
|
|
38
44
|
const replaceWith = currentAnswer?.replaceWith || '';
|
|
@@ -76,11 +82,10 @@ function QuestionItem({ question, answer, onAnswerChange, isReviewMode, correctA
|
|
|
76
82
|
});
|
|
77
83
|
}
|
|
78
84
|
else {
|
|
79
|
-
const cleanWord = rawToken.replace(/[^\p{L}\p{N}'-]/gu, '') || rawToken;
|
|
80
85
|
onAnswerChange(question.id, {
|
|
81
86
|
type: mode,
|
|
82
87
|
wordIndex: tokenIndex,
|
|
83
|
-
crossOut:
|
|
88
|
+
crossOut: rawToken,
|
|
84
89
|
...(mode === 'CROSS_OUT_AND_REPLACE' ? { replaceWith } : {}),
|
|
85
90
|
});
|
|
86
91
|
}
|
|
@@ -109,9 +114,9 @@ function QuestionItem({ question, answer, onAnswerChange, isReviewMode, correctA
|
|
|
109
114
|
? 'bg-emerald-500'
|
|
110
115
|
: isWrong
|
|
111
116
|
? 'bg-rose-500'
|
|
112
|
-
: 'bg-blue-600'}`, children: question.questionNumber }), question.isExample && (_jsx("span", { className: "rounded-full border border-amber-300 bg-amber-100/90 px-2 py-0.5 text-[10.5px] font-extrabold uppercase tracking-wider text-amber-800", children: "EXAMPLE" })), _jsx("span", { className: `rounded-full px-2.5 py-0.5 text-[11px] font-bold ${mode === 'CROSS_OUT_AND_REPLACE'
|
|
117
|
+
: 'bg-blue-600'}`, children: question.questionNumber }), question.isExample && (_jsx("span", { className: "rounded-full border border-amber-300 bg-amber-100/90 px-2 py-0.5 text-[10.5px] font-extrabold uppercase tracking-wider text-amber-800", children: "EXAMPLE" })), isReviewMode ? (_jsx("span", { className: `rounded-full px-2.5 py-0.5 text-[11px] font-bold ${mode === 'CROSS_OUT_AND_REPLACE'
|
|
113
118
|
? 'bg-violet-100/80 text-violet-700 border border-violet-200/60'
|
|
114
|
-
: 'bg-sky-100/80 text-sky-700 border border-sky-200/60'}`, children: mode === 'CROSS_OUT_AND_REPLACE' ? 'Gạch + Thay thế' : 'Chỉ gạch' }), !question.isExample && selectedWord && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-emerald-100 px-2 py-0.5 text-[10.5px] font-bold text-emerald-700", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500" }), "\u0110\u00E3 g\u1EA1ch: \"", selectedWord, "\"", typeof selectedIndex === 'number' && (_jsxs("span", { className: "text-[10px] text-emerald-600 font-normal", children: ["(t\u1EEB #", selectedIndex + 1, ")"] }))] }))] }), mode === 'CROSS_OUT_AND_REPLACE' && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("label", { className: "text-xs font-bold text-slate-600 whitespace-nowrap", children: "T\u1EEB \u0111\u00FAng:" }), _jsx("input", { type: "text", value: replaceWith, disabled: isDisabled, onChange: (e) => setReplaceWith(e.target.value), placeholder: "Nh\u1EADp t\u1EEB thay th\u1EBF...", className: "h-8 w-40 sm:w-52 rounded-lg border border-slate-200 bg-white px-2.5 text-sm font-semibold text-slate-800 outline-none transition-all focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-100 disabled:cursor-not-allowed disabled:bg-slate-50" })] }))] }), _jsx("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-2 text-[15.5px] sm:text-[16.5px] font-medium leading-loose text-slate-800 px-1", children: tokens.map((token, tokenIndex) => {
|
|
119
|
+
: 'bg-sky-100/80 text-sky-700 border border-sky-200/60'}`, children: mode === 'CROSS_OUT_AND_REPLACE' ? 'Gạch + Thay thế' : 'Chỉ gạch' })) : null, !question.isExample && selectedWord && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-emerald-100 px-2 py-0.5 text-[10.5px] font-bold text-emerald-700", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500" }), "\u0110\u00E3 g\u1EA1ch: \"", selectedWord, "\"", typeof selectedIndex === 'number' && (_jsxs("span", { className: "text-[10px] text-emerald-600 font-normal", children: ["(t\u1EEB #", selectedIndex + 1, ")"] }))] }))] }), mode === 'CROSS_OUT_AND_REPLACE' && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("label", { className: "text-xs font-bold text-slate-600 whitespace-nowrap", children: "T\u1EEB \u0111\u00FAng:" }), _jsx("input", { type: "text", value: replaceWith, disabled: isDisabled, onChange: (e) => setReplaceWith(e.target.value), placeholder: "Nh\u1EADp t\u1EEB thay th\u1EBF...", className: "h-8 w-40 sm:w-52 rounded-lg border border-slate-200 bg-white px-2.5 text-sm font-semibold text-slate-800 outline-none transition-all focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-100 disabled:cursor-not-allowed disabled:bg-slate-50" })] }))] }), _jsx("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-2 text-[15.5px] sm:text-[16.5px] font-medium leading-loose text-slate-800 px-1", children: tokens.map((token, tokenIndex) => {
|
|
115
120
|
const plain = normalizeWord(token);
|
|
116
121
|
const isSelected = selectedIndex !== undefined && selectedIndex !== null
|
|
117
122
|
? selectedIndex === tokenIndex
|
|
@@ -119,12 +124,21 @@ function QuestionItem({ question, answer, onAnswerChange, isReviewMode, correctA
|
|
|
119
124
|
const isCorrectTargetInReview = isReviewMode &&
|
|
120
125
|
!question.isExample &&
|
|
121
126
|
correctAnswer?.wordIndex === tokenIndex;
|
|
127
|
+
const isSelectedCorrect = isReviewMode &&
|
|
128
|
+
!question.isExample &&
|
|
129
|
+
isSelected &&
|
|
130
|
+
isCorrectToken(correctAnswer, tokenIndex, plain);
|
|
131
|
+
const isSelectedWrong = isReviewMode &&
|
|
132
|
+
!question.isExample &&
|
|
133
|
+
isSelected &&
|
|
134
|
+
!!correctAnswer &&
|
|
135
|
+
!isSelectedCorrect;
|
|
122
136
|
return (_jsx("button", { type: "button", disabled: isDisabled || plain === '', onClick: () => setCrossOut(tokenIndex, token), className: `rounded-lg px-2 py-1 transition-all duration-150 select-none relative ${isSelected
|
|
123
137
|
? question.isExample
|
|
124
138
|
? 'bg-amber-200/90 text-amber-900 font-bold line-through decoration-amber-700 decoration-2 shadow-sm'
|
|
125
|
-
:
|
|
139
|
+
: isSelectedCorrect
|
|
126
140
|
? 'bg-emerald-100 text-emerald-800 font-bold line-through decoration-emerald-600 decoration-2 shadow-sm border border-emerald-300'
|
|
127
|
-
:
|
|
141
|
+
: isSelectedWrong
|
|
128
142
|
? 'bg-rose-100 text-rose-700 font-bold line-through decoration-rose-600 decoration-2 shadow-sm border border-rose-300 scale-[0.98]'
|
|
129
143
|
: 'bg-blue-100 text-blue-700 font-bold line-through decoration-blue-600 decoration-2 shadow-sm border border-blue-200 scale-[0.98]'
|
|
130
144
|
: isCorrectTargetInReview
|
package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.js
CHANGED
|
@@ -307,5 +307,5 @@ export function MoversCrosswordPuzzleRenderer({ partNumber, partName, questionCo
|
|
|
307
307
|
inputRefs.current.delete(key);
|
|
308
308
|
}, type: "text", maxLength: 1, disabled: Boolean(isExample), value: cellValue, onChange: (e) => handleInputChange(e.target.value, r, c), onKeyDown: (e) => handleKeyDown(e, r, c), onFocus: () => handleCellClick(r, c), className: `w-full h-full text-center bg-transparent uppercase font-bold outline-none ${isExample ? 'cursor-default' : 'cursor-pointer'}` })) : (_jsxs("div", { className: "flex flex-col items-center justify-center", children: [_jsx("span", { children: cellValue || '—' }), reviewStatus && !reviewStatus.isCorrect && (_jsx("span", { className: "absolute -bottom-1 -right-1 text-[9px] bg-green-600 text-white font-bold px-1 rounded-full shadow", children: reviewStatus.expected }))] })), isReviewMode && reviewStatus && (_jsx("span", { className: `absolute -top-1 -right-1 rounded-full p-0.5 text-white ${reviewStatus.isCorrect ? 'bg-green-500' : 'bg-red-500'}`, children: reviewStatus.isCorrect ? (_jsx(Check, { className: "h-2.5 w-2.5" })) : (_jsx(X, { className: "h-2.5 w-2.5" })) }))] }, key));
|
|
309
309
|
});
|
|
310
|
-
}) }) })] })] })] }));
|
|
310
|
+
}) }) }), (markedColumn || hiddenWord) && (_jsxs("div", { className: "w-full max-w-md rounded-xl border-2 border-amber-200 bg-amber-50 p-4", children: [_jsx("label", { className: "block text-sm font-bold text-amber-900 mb-2", children: "What's the hidden word?" }), !isReviewMode ? (_jsx("input", { type: "text", value: answers['hidden-word'] || '', onChange: (event) => onAnswerChange('hidden-word', event.target.value.toUpperCase()), placeholder: "Hidden word", className: "w-full rounded-lg border border-amber-300 bg-white px-3 py-2 text-sm font-semibold uppercase tracking-wider outline-none focus:border-amber-500" })) : (_jsxs("div", { className: "text-sm", children: [answers['hidden-word'] && (_jsx("span", { className: "font-semibold text-gray-800", children: answers['hidden-word'] })), hiddenWord && (_jsxs("div", { className: "mt-1 text-xs", children: [_jsx("span", { className: "text-gray-500", children: "\u0110\u00E1p \u00E1n: " }), _jsx("span", { className: "font-bold text-green-700", children: hiddenWord })] }))] }))] }))] })] })] }));
|
|
311
311
|
}
|
package/dist/components/exams/take/components/question-renderers/MoversFillInBlankGroupRenderer.js
CHANGED
|
@@ -8,6 +8,7 @@ import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
|
|
|
8
8
|
import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
|
|
9
9
|
import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
|
|
10
10
|
import CambridgeYleAudioPlayer from '../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer';
|
|
11
|
+
import { FillInBlankAnswerKey } from '../../../../questions/types/fill-in-blank-group/FillInBlankAnswerKey';
|
|
11
12
|
import { extractPlaceholderIndices, isBlankMatch, padStudentAnswers, splitStem, toFillInBlankImageUrls, } from '../../../../questions/types/fill-in-blank-group/blank-utils';
|
|
12
13
|
function emptyAnswer(blankCount) {
|
|
13
14
|
return { answers: Array.from({ length: blankCount }, () => '') };
|
|
@@ -53,7 +54,7 @@ function ItemCard({ question, answer, onAnswerChange, isReviewMode, correctAnswe
|
|
|
53
54
|
: blankWrong
|
|
54
55
|
? 'border-rose-400 bg-rose-50 text-rose-800'
|
|
55
56
|
: 'border-blue-200 bg-blue-50/40 text-blue-900 focus:border-blue-500 focus:bg-white'}` }, partIndex));
|
|
56
|
-
}) })] }), isReviewMode && !question.isExample && expected.length > 0 && (
|
|
57
|
+
}) })] }), isReviewMode && !question.isExample && expected.length > 0 && (_jsx("div", { className: "mt-2 rounded-lg border border-emerald-200 bg-emerald-50 px-2.5 py-2", children: _jsx(FillInBlankAnswerKey, { answersByBlank: expected, blankIndices: blankIndices }) }))] }));
|
|
57
58
|
}
|
|
58
59
|
export function MoversFillInBlankGroupRenderer({ partNumber, partName, questionCount, instruction, audioUrl, imageUrl, showHints = false, hints = [], questions, answers, onAnswerChange, isReviewMode = false, correctAnswerMap, }) {
|
|
59
60
|
return (_jsxs("div", { className: "relative flex h-full flex-col gap-4 overflow-y-auto custom-scrollbar sm:gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2 p-3 sm:gap-3 sm:px-6 sm:pt-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode })] }), (audioUrl || toFillInBlankImageUrls(imageUrl).length > 0) && (_jsxs("div", { className: "sticky top-0 z-20 flex flex-col gap-3 border-b border-gray-100 bg-white/95 p-3 pb-2 shadow-sm backdrop-blur sm:px-6", children: [audioUrl && _jsx(CambridgeYleAudioPlayer, { audioSrc: audioUrl, compact: true }), toFillInBlankImageUrls(imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: toFillInBlankImageUrls(imageUrl).map((url, imageIndex) => (_jsx(ResolvedImage, { src: url, alt: `Shared illustration ${imageIndex + 1}`, className: "max-h-[180px] rounded-lg object-contain" }, `group-image-${imageIndex}`))) }))] })), showHints && hints.length > 0 && !isReviewMode && (_jsx("div", { className: "flex flex-wrap gap-2 px-3 sm:px-6", children: hints.map((hint) => (_jsx("span", { className: "rounded-full border border-blue-200 bg-blue-50 px-3 py-1 text-xs font-semibold text-blue-700", children: hint }, hint))) })), _jsx("div", { className: "grid grid-cols-1 gap-3 p-3 sm:grid-cols-2 sm:px-6 lg:grid-cols-3", children: questions.map((question) => (_jsx(ItemCard, { question: question, answer: answers[question.id], onAnswerChange: onAnswerChange, isReviewMode: isReviewMode, correctAnswer: correctAnswerMap?.[question.id] }, question.id))) })] }));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MatchWordToPictureGroupPartData } from '../../utils/question-transformers';
|
|
2
|
+
interface MoversMatchWordToPictureGroupRendererProps {
|
|
3
|
+
partNumber: number;
|
|
4
|
+
partName?: string;
|
|
5
|
+
questionCount: number;
|
|
6
|
+
instruction: string;
|
|
7
|
+
audioUrl?: string;
|
|
8
|
+
data: MatchWordToPictureGroupPartData;
|
|
9
|
+
answers: Record<string, string | undefined>;
|
|
10
|
+
onAnswerChange: (questionId: string, value: string) => void;
|
|
11
|
+
isReviewMode?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function MoversMatchWordToPictureGroupRenderer({ partNumber, partName, questionCount, instruction, audioUrl, data, answers, onAnswerChange, isReviewMode, }: MoversMatchWordToPictureGroupRendererProps): import("react").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
|
|
4
|
+
import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
|
|
5
|
+
import { MatchWordToPictureGroupClient } from '../../../../questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient';
|
|
6
|
+
export function MoversMatchWordToPictureGroupRenderer({ partNumber, partName, questionCount, instruction, audioUrl, data, answers, onAnswerChange, isReviewMode = false, }) {
|
|
7
|
+
const userAnswers = data.questions.map((question) => {
|
|
8
|
+
if (question.isExample)
|
|
9
|
+
return question.correctAnswer;
|
|
10
|
+
return typeof answers[question.id] === 'string' ? answers[question.id] || '' : '';
|
|
11
|
+
});
|
|
12
|
+
const resolvedAudio = data.audioUrl || audioUrl || '';
|
|
13
|
+
return (_jsxs("div", { className: "w-full space-y-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, partName: partName, questionCount: questionCount }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction }), _jsx("div", { className: "mt-4", children: _jsx(MatchWordToPictureGroupClient, { questionData: {
|
|
14
|
+
title: data.title,
|
|
15
|
+
instruction: data.instruction,
|
|
16
|
+
...(data.passage ? { passage: data.passage } : {}),
|
|
17
|
+
...(resolvedAudio ? { audioUrl: resolvedAudio } : {}),
|
|
18
|
+
...(data.imageUrl ? { imageUrl: data.imageUrl } : {}),
|
|
19
|
+
wordBank: data.wordBank,
|
|
20
|
+
items: data.questions.map((question) => ({
|
|
21
|
+
imageUrl: question.imageUrl,
|
|
22
|
+
correctAnswer: question.correctAnswer,
|
|
23
|
+
isExample: question.isExample,
|
|
24
|
+
points: question.points,
|
|
25
|
+
questionNumber: question.questionNumber,
|
|
26
|
+
})),
|
|
27
|
+
explanation: data.explanation,
|
|
28
|
+
}, isReviewMode: isReviewMode, userAnswers: userAnswers, onAnswerChange: (next) => {
|
|
29
|
+
next.forEach((value, index) => {
|
|
30
|
+
const target = data.questions[index];
|
|
31
|
+
if (target && !target.isExample) {
|
|
32
|
+
onAnswerChange(target.id, value);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
} }) })] }));
|
|
36
|
+
}
|
|
@@ -13,9 +13,13 @@ export function MoversMatchingWithLinesGroupRenderer({ partNumber, partName, que
|
|
|
13
13
|
return (_jsxs("div", { className: "w-full space-y-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, partName: partName, questionCount: questionCount }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction }), _jsx("div", { className: "mt-4", children: _jsx(MatchingWithLinesGroupClient, { questionData: {
|
|
14
14
|
title: data.title,
|
|
15
15
|
instruction: data.instruction,
|
|
16
|
+
...(data.passage ? { passage: data.passage } : {}),
|
|
17
|
+
...(data.audioUrl ? { audioUrl: data.audioUrl } : {}),
|
|
18
|
+
...(data.imageUrl ? { imageUrl: data.imageUrl } : {}),
|
|
16
19
|
rightItems: data.rightItems,
|
|
17
20
|
items: data.questions.map((question) => ({
|
|
18
21
|
imageUrl: question.imageUrl,
|
|
22
|
+
text: question.text,
|
|
19
23
|
correctAnswer: question.correctRightId,
|
|
20
24
|
isExample: question.isExample,
|
|
21
25
|
points: question.points,
|
|
@@ -17,6 +17,7 @@ export { MoversWordOrderingGroupRenderer } from './MoversWordOrderingGroupRender
|
|
|
17
17
|
export { MoversWordFillParagraphRenderer } from './MoversWordFillParagraphRenderer';
|
|
18
18
|
export { MoversMatchByWritingAnswerRenderer } from './MoversMatchByWritingAnswerRenderer';
|
|
19
19
|
export { MoversMatchWordToPictureRenderer } from './MoversMatchWordToPictureRenderer';
|
|
20
|
+
export { MoversMatchWordToPictureGroupRenderer } from './MoversMatchWordToPictureGroupRenderer';
|
|
20
21
|
export { MoversWordFillStructuredFormRenderer } from './MoversWordFillStructuredFormRenderer';
|
|
21
22
|
export { MoversWordOrderAndMatchGroupRenderer } from './MoversWordOrderAndMatchGroupRenderer';
|
|
22
23
|
export { MoversMatchingWithLinesGroupRenderer } from './MoversMatchingWithLinesGroupRenderer';
|
|
@@ -21,6 +21,7 @@ export { MoversWordOrderingGroupRenderer } from './MoversWordOrderingGroupRender
|
|
|
21
21
|
export { MoversWordFillParagraphRenderer } from './MoversWordFillParagraphRenderer';
|
|
22
22
|
export { MoversMatchByWritingAnswerRenderer } from './MoversMatchByWritingAnswerRenderer';
|
|
23
23
|
export { MoversMatchWordToPictureRenderer } from './MoversMatchWordToPictureRenderer';
|
|
24
|
+
export { MoversMatchWordToPictureGroupRenderer } from './MoversMatchWordToPictureGroupRenderer';
|
|
24
25
|
export { MoversWordFillStructuredFormRenderer } from './MoversWordFillStructuredFormRenderer';
|
|
25
26
|
export { MoversWordOrderAndMatchGroupRenderer } from './MoversWordOrderAndMatchGroupRenderer';
|
|
26
27
|
export { MoversMatchingWithLinesGroupRenderer } from './MoversMatchingWithLinesGroupRenderer';
|
|
@@ -37,14 +37,24 @@ export function SpeakingSpontaneousQaGroupRenderer(props) {
|
|
|
37
37
|
useEffect(() => {
|
|
38
38
|
setSequenceStep(getInitialSequenceStep());
|
|
39
39
|
}, [getInitialSequenceStep, setup.nav.currentIndex]);
|
|
40
|
+
const hasQuestionAudio = Boolean(content?.audioUrl?.trim());
|
|
40
41
|
useEffect(() => {
|
|
41
42
|
if (sequenceStep !== SPONTANEOUS_QA_SEQUENCE_STEP.DELAYING || shouldResumeCompletedState)
|
|
42
43
|
return;
|
|
43
44
|
const timer = setTimeout(() => {
|
|
44
|
-
setSequenceStep(
|
|
45
|
+
setSequenceStep(hasQuestionAudio
|
|
46
|
+
? SPONTANEOUS_QA_SEQUENCE_STEP.PLAYING_QUESTION
|
|
47
|
+
: SPONTANEOUS_QA_SEQUENCE_STEP.ALLOW_RECORDING);
|
|
45
48
|
}, 3000);
|
|
46
49
|
return () => clearTimeout(timer);
|
|
47
|
-
}, [sequenceStep, shouldResumeCompletedState]);
|
|
50
|
+
}, [hasQuestionAudio, sequenceStep, shouldResumeCompletedState]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (sequenceStep === SPONTANEOUS_QA_SEQUENCE_STEP.PLAYING_QUESTION &&
|
|
53
|
+
!hasQuestionAudio &&
|
|
54
|
+
!shouldResumeCompletedState) {
|
|
55
|
+
setSequenceStep(SPONTANEOUS_QA_SEQUENCE_STEP.ALLOW_RECORDING);
|
|
56
|
+
}
|
|
57
|
+
}, [hasQuestionAudio, sequenceStep, shouldResumeCompletedState]);
|
|
48
58
|
const handleTeacherVideoEnded = useCallback(() => {
|
|
49
59
|
setSequenceStep(SPONTANEOUS_QA_SEQUENCE_STEP.DELAYING);
|
|
50
60
|
props.onTeacherIntroPlayed?.();
|
|
@@ -27,8 +27,9 @@ export function useSpontaneousQaGroupRendererSetup({ part, answers, onAnswerChan
|
|
|
27
27
|
flattenIndex: child.flattenIndex,
|
|
28
28
|
expectedAnswers: child.expectedAnswers,
|
|
29
29
|
points: child.points,
|
|
30
|
+
isExample: child.isExample,
|
|
30
31
|
},
|
|
31
|
-
is_example:
|
|
32
|
+
is_example: child.isExample,
|
|
32
33
|
correct_answer: { expectedAnswers: child.expectedAnswers },
|
|
33
34
|
total_points: String(child.points),
|
|
34
35
|
question_type: 'SPONTANEOUS_QA_GROUP',
|
|
@@ -90,6 +90,7 @@ function findParentQuestion(subQuestionId, parts) {
|
|
|
90
90
|
part.questionType === 'TRUE_FALSE_CORRECT_GROUP' ||
|
|
91
91
|
part.questionType === 'FILL_IN_BLANK_GROUP' ||
|
|
92
92
|
part.questionType === 'MATCHING_WITH_LINES_GROUP' ||
|
|
93
|
+
part.questionType === 'MATCH_WORD_TO_PICTURE_GROUP' ||
|
|
93
94
|
part.questionType === 'WORD_ORDER_AND_MATCH_GROUP' ||
|
|
94
95
|
part.questionType === 'SPONTANEOUS_QA_GROUP' ||
|
|
95
96
|
part.questionType === 'ANSWER_THE_QUESTION_GROUP' ||
|
|
@@ -263,6 +264,16 @@ export function transformAnswersForSubmission(answers, parts) {
|
|
|
263
264
|
if (speakingQuestionIds.has(questionId) && typeof answer === 'string') {
|
|
264
265
|
continue;
|
|
265
266
|
}
|
|
267
|
+
if (questionId === 'hidden-word') {
|
|
268
|
+
const parent = findGridParentQuestion(parts);
|
|
269
|
+
if (parent?.questionType === 'CROSSWORD_PUZZLE') {
|
|
270
|
+
if (!gridCellAnswers.has(parent.parentId)) {
|
|
271
|
+
gridCellAnswers.set(parent.parentId, {});
|
|
272
|
+
}
|
|
273
|
+
gridCellAnswers.get(parent.parentId)['hidden-word'] = String(answer);
|
|
274
|
+
}
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
266
277
|
// Handle grid cell keys (from FILL_MISSING_WORDS_IN_GRID)
|
|
267
278
|
if (isGridCellKey(questionId)) {
|
|
268
279
|
const parent = findGridParentQuestion(parts);
|
|
@@ -454,9 +465,14 @@ export function transformAnswersForSubmission(answers, parts) {
|
|
|
454
465
|
words[`down-${c.number}`] = word;
|
|
455
466
|
}
|
|
456
467
|
}
|
|
468
|
+
const hiddenWord = cellAnswers['hidden-word'];
|
|
469
|
+
const cells = Object.fromEntries(Object.entries(cellAnswers).filter(([key]) => key !== 'hidden-word'));
|
|
470
|
+
if (hiddenWord) {
|
|
471
|
+
words['hidden-word'] = hiddenWord;
|
|
472
|
+
}
|
|
457
473
|
result.push({
|
|
458
474
|
questionId: parentId,
|
|
459
|
-
answer: { answers: words, cells
|
|
475
|
+
answer: { answers: words, cells },
|
|
460
476
|
});
|
|
461
477
|
}
|
|
462
478
|
else {
|
|
@@ -63,21 +63,19 @@ export function normalizeCrossOutWordGroupAnswer(raw) {
|
|
|
63
63
|
const rawIndex = obj.wordIndex;
|
|
64
64
|
const wordIndex = typeof rawIndex === 'number' && Number.isInteger(rawIndex) && rawIndex >= 0
|
|
65
65
|
? rawIndex
|
|
66
|
-
:
|
|
67
|
-
? null
|
|
68
|
-
: undefined;
|
|
66
|
+
: null;
|
|
69
67
|
return type === 'CROSS_OUT_AND_REPLACE'
|
|
70
68
|
? {
|
|
71
69
|
type,
|
|
72
|
-
|
|
70
|
+
wordIndex,
|
|
73
71
|
crossOut: typeof obj.crossOut === 'string' ? obj.crossOut : '',
|
|
74
72
|
replaceWith: typeof obj.replaceWith === 'string' ? obj.replaceWith : '',
|
|
75
73
|
}
|
|
76
74
|
: {
|
|
77
75
|
type: 'CROSS_OUT',
|
|
78
|
-
|
|
76
|
+
wordIndex,
|
|
79
77
|
crossOut: typeof obj.crossOut === 'string' ? obj.crossOut : '',
|
|
80
78
|
};
|
|
81
79
|
}
|
|
82
|
-
return { type: 'CROSS_OUT', crossOut: '' };
|
|
80
|
+
return { type: 'CROSS_OUT', wordIndex: null, crossOut: '' };
|
|
83
81
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Each transformer handles specific question type data structure.
|
|
6
6
|
*/
|
|
7
7
|
import type { FillBlankQuestion, ChooseBestAnswerQuestion, ReadingPassageQuestion, LabelThePictureLabel, ColoringHintItem, ColoringRegion } from '../types';
|
|
8
|
-
export type QuestionType = 'FILL_IN_BLANK' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'CHOOSE_CORRECT_ADJECTIVE' | 'WRITE_A_SHORT_LETTER' | 'READ_AND_COLOR_OBJECTS' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'LABEL_THE_PICTURE' | 'FILL_MISSING_WORDS_IN_GRID' | 'IMAGE_OBJECT_MATCHING' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'TRUE_FALSE' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'WORD_ORDERING' | 'WORD_ORDERING_GROUP' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'WRITE_SENTENCES' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX' | 'SORT_WORDS_INTO_CATEGORIES' | 'CHOOSE_THEN_ANSWER_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPONTANEOUS_QA_GROUP' | 'SPEAKING_CONVERSATION' | 'SPEAKING_CUE_CARD';
|
|
8
|
+
export type QuestionType = 'FILL_IN_BLANK' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'CHOOSE_CORRECT_ADJECTIVE' | 'WRITE_A_SHORT_LETTER' | 'READ_AND_COLOR_OBJECTS' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'LABEL_THE_PICTURE' | 'FILL_MISSING_WORDS_IN_GRID' | 'IMAGE_OBJECT_MATCHING' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'TRUE_FALSE' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'WORD_ORDERING' | 'WORD_ORDERING_GROUP' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'MATCH_WORD_TO_PICTURE_GROUP' | 'WRITE_SENTENCES' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX' | 'SORT_WORDS_INTO_CATEGORIES' | 'CHOOSE_THEN_ANSWER_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPONTANEOUS_QA_GROUP' | 'SPEAKING_CONVERSATION' | 'SPEAKING_CUE_CARD';
|
|
9
9
|
export interface ApiQuestion {
|
|
10
10
|
id: string;
|
|
11
11
|
content: any;
|
|
@@ -372,6 +372,25 @@ export declare function transformMatchWordToPicture(questions: ApiQuestion[]): {
|
|
|
372
372
|
wordBank: string[];
|
|
373
373
|
instruction: string;
|
|
374
374
|
};
|
|
375
|
+
export interface MatchWordToPictureGroupQuestionItem {
|
|
376
|
+
id: string;
|
|
377
|
+
questionNumber: number;
|
|
378
|
+
imageUrl?: string | string[];
|
|
379
|
+
correctAnswer: string;
|
|
380
|
+
isExample: boolean;
|
|
381
|
+
points: number;
|
|
382
|
+
}
|
|
383
|
+
export interface MatchWordToPictureGroupPartData {
|
|
384
|
+
questions: MatchWordToPictureGroupQuestionItem[];
|
|
385
|
+
wordBank: string[];
|
|
386
|
+
title?: string;
|
|
387
|
+
instruction: string;
|
|
388
|
+
passage?: string;
|
|
389
|
+
audioUrl?: string;
|
|
390
|
+
imageUrl?: string | string[];
|
|
391
|
+
explanation?: string;
|
|
392
|
+
}
|
|
393
|
+
export declare function transformMatchWordToPictureGroup(questions: ApiQuestion[]): MatchWordToPictureGroupPartData;
|
|
375
394
|
export declare function transformWriteSentences(questions: ApiQuestion[]): {
|
|
376
395
|
questions: Array<{
|
|
377
396
|
id: string;
|
|
@@ -472,14 +491,19 @@ export interface CrosswordClueItem {
|
|
|
472
491
|
startRow: number;
|
|
473
492
|
startCol: number;
|
|
474
493
|
length: number;
|
|
494
|
+
isExample?: boolean;
|
|
475
495
|
}
|
|
476
496
|
export interface CrosswordPuzzlePartData {
|
|
497
|
+
type?: 'STANDARD' | 'HIDDEN_WORD';
|
|
477
498
|
title?: string;
|
|
478
499
|
instruction: string;
|
|
479
500
|
gridSize: {
|
|
480
501
|
rows: number;
|
|
481
502
|
cols: number;
|
|
482
503
|
};
|
|
504
|
+
markedColumn?: number;
|
|
505
|
+
hiddenWord?: string;
|
|
506
|
+
caseSensitive?: boolean;
|
|
483
507
|
clues: {
|
|
484
508
|
across: CrosswordClueItem[];
|
|
485
509
|
down: CrosswordClueItem[];
|
|
@@ -569,7 +593,8 @@ export declare function transformChooseThenAnswerGroup(questions: ApiQuestion[])
|
|
|
569
593
|
export interface MatchingWithLinesGroupQuestionItem {
|
|
570
594
|
id: string;
|
|
571
595
|
questionNumber: number;
|
|
572
|
-
imageUrl
|
|
596
|
+
imageUrl?: string;
|
|
597
|
+
text?: string;
|
|
573
598
|
correctRightId: string;
|
|
574
599
|
isExample: boolean;
|
|
575
600
|
points: number;
|
|
@@ -578,10 +603,14 @@ export interface MatchingWithLinesGroupPartData {
|
|
|
578
603
|
questions: MatchingWithLinesGroupQuestionItem[];
|
|
579
604
|
rightItems: Array<{
|
|
580
605
|
id: string;
|
|
581
|
-
text
|
|
606
|
+
text?: string;
|
|
607
|
+
imageUrl?: string;
|
|
582
608
|
}>;
|
|
583
609
|
title?: string;
|
|
584
610
|
instruction: string;
|
|
611
|
+
passage?: string;
|
|
612
|
+
audioUrl?: string | string[];
|
|
613
|
+
imageUrl?: string | string[];
|
|
585
614
|
explanation?: string;
|
|
586
615
|
}
|
|
587
616
|
export declare function transformMatchingWithLinesGroup(questions: ApiQuestion[]): MatchingWithLinesGroupPartData;
|
|
@@ -1045,6 +1045,39 @@ export function transformMatchWordToPicture(questions) {
|
|
|
1045
1045
|
instruction,
|
|
1046
1046
|
};
|
|
1047
1047
|
}
|
|
1048
|
+
export function transformMatchWordToPictureGroup(questions) {
|
|
1049
|
+
const parent = questions[0];
|
|
1050
|
+
const content = (parent?.content || {});
|
|
1051
|
+
const items = Array.isArray(content.items) ? content.items : [];
|
|
1052
|
+
const parentCorrect = Array.isArray(parent?.correct_answer?.answer)
|
|
1053
|
+
? parent.correct_answer.answer
|
|
1054
|
+
: [];
|
|
1055
|
+
const title = typeof content.meta?.title === 'string' ? content.meta.title.trim() : '';
|
|
1056
|
+
const passage = typeof content.meta?.passage === 'string' ? content.meta.passage.trim() : '';
|
|
1057
|
+
const audioUrl = typeof content.meta?.audioUrl === 'string' ? content.meta.audioUrl.trim() : '';
|
|
1058
|
+
return {
|
|
1059
|
+
questions: items.map((item, index) => {
|
|
1060
|
+
const isExample = item.isExample === true;
|
|
1061
|
+
const fromItem = typeof item.correctAnswer?.answer === 'string' ? item.correctAnswer.answer : '';
|
|
1062
|
+
const fromParent = typeof parentCorrect[index] === 'string' ? parentCorrect[index] : '';
|
|
1063
|
+
return {
|
|
1064
|
+
id: `${parent?.id || 'q'}-item-${index}`,
|
|
1065
|
+
questionNumber: item.questionNumber || index + 1,
|
|
1066
|
+
imageUrl: item.content?.imageUrl,
|
|
1067
|
+
correctAnswer: fromItem || fromParent,
|
|
1068
|
+
isExample,
|
|
1069
|
+
points: isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
|
|
1070
|
+
};
|
|
1071
|
+
}),
|
|
1072
|
+
wordBank: Array.isArray(content.meta?.wordBank) ? content.meta.wordBank : [],
|
|
1073
|
+
...(title ? { title } : {}),
|
|
1074
|
+
instruction: content.meta?.instruction || 'Look and complete.',
|
|
1075
|
+
...(passage ? { passage } : {}),
|
|
1076
|
+
...(audioUrl ? { audioUrl } : {}),
|
|
1077
|
+
...(content.meta?.imageUrl ? { imageUrl: content.meta.imageUrl } : {}),
|
|
1078
|
+
explanation: parent?.explanation,
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1048
1081
|
export function transformWriteSentences(questions) {
|
|
1049
1082
|
const firstQuestion = questions[0];
|
|
1050
1083
|
const content = firstQuestion?.content;
|
|
@@ -1258,6 +1291,7 @@ export function transformCrosswordPuzzle(questions) {
|
|
|
1258
1291
|
startRow: Number(c.startRow) || 1,
|
|
1259
1292
|
startCol: Number(c.startCol) || 1,
|
|
1260
1293
|
length: Number(c.length) || 1,
|
|
1294
|
+
...(c.isExample === true ? { isExample: true } : {}),
|
|
1261
1295
|
}))
|
|
1262
1296
|
: [];
|
|
1263
1297
|
const down = Array.isArray(content?.clues?.down)
|
|
@@ -1267,15 +1301,20 @@ export function transformCrosswordPuzzle(questions) {
|
|
|
1267
1301
|
startRow: Number(c.startRow) || 1,
|
|
1268
1302
|
startCol: Number(c.startCol) || 1,
|
|
1269
1303
|
length: Number(c.length) || 1,
|
|
1304
|
+
...(c.isExample === true ? { isExample: true } : {}),
|
|
1270
1305
|
}))
|
|
1271
1306
|
: [];
|
|
1272
1307
|
const correctAnswers = correctAnswer?.answers && typeof correctAnswer.answers === 'object'
|
|
1273
1308
|
? correctAnswer.answers
|
|
1274
1309
|
: {};
|
|
1275
1310
|
return {
|
|
1311
|
+
type: content?.type === 'HIDDEN_WORD' ? 'HIDDEN_WORD' : 'STANDARD',
|
|
1276
1312
|
title: content?.title || '',
|
|
1277
1313
|
instruction: question?.instruction || content?.instruction || 'Complete the crossword.',
|
|
1278
1314
|
gridSize: { rows, cols },
|
|
1315
|
+
...(typeof content?.markedColumn === 'number' ? { markedColumn: content.markedColumn } : {}),
|
|
1316
|
+
hiddenWord: correctAnswers['hidden-word'] || '',
|
|
1317
|
+
caseSensitive: correctAnswer?.caseSensitive === true,
|
|
1279
1318
|
clues: { across, down },
|
|
1280
1319
|
correctAnswers,
|
|
1281
1320
|
explanation: question?.explanation,
|
|
@@ -1382,18 +1421,25 @@ export function transformMatchingWithLinesGroup(questions) {
|
|
|
1382
1421
|
const rightItems = Array.isArray(content.rightItems)
|
|
1383
1422
|
? content.rightItems.map((item, index) => ({
|
|
1384
1423
|
id: typeof item?.id === 'string' ? item.id : `r${index + 1}`,
|
|
1385
|
-
|
|
1424
|
+
...(typeof item?.text === 'string' && item.text.trim() ? { text: item.text } : {}),
|
|
1425
|
+
...(typeof item?.imageUrl === 'string' && item.imageUrl.trim() ? { imageUrl: item.imageUrl } : {}),
|
|
1386
1426
|
}))
|
|
1387
1427
|
: [];
|
|
1428
|
+
const passage = content.meta?.passage?.trim() || '';
|
|
1429
|
+
const audioUrl = content.meta?.audioUrl;
|
|
1430
|
+
const imageUrl = content.meta?.imageUrl;
|
|
1388
1431
|
return {
|
|
1389
1432
|
questions: items.map((item, index) => {
|
|
1390
1433
|
const isExample = item.isExample === true;
|
|
1391
1434
|
const fromItem = typeof item.correctAnswer?.answer === 'string' ? item.correctAnswer.answer : '';
|
|
1392
1435
|
const fromParent = typeof parentCorrect[index] === 'string' ? parentCorrect[index] : '';
|
|
1436
|
+
const itemImage = item.content?.imageUrl?.trim() || '';
|
|
1437
|
+
const itemText = item.content?.text?.trim() || '';
|
|
1393
1438
|
return {
|
|
1394
1439
|
id: `${parent?.id || 'q'}-item-${index}`,
|
|
1395
1440
|
questionNumber: item.questionNumber || index + 1,
|
|
1396
|
-
imageUrl:
|
|
1441
|
+
...(itemImage ? { imageUrl: itemImage } : {}),
|
|
1442
|
+
...(itemText ? { text: itemText } : {}),
|
|
1397
1443
|
correctRightId: fromItem || fromParent,
|
|
1398
1444
|
isExample,
|
|
1399
1445
|
points: isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
|
|
@@ -1402,6 +1448,9 @@ export function transformMatchingWithLinesGroup(questions) {
|
|
|
1402
1448
|
rightItems,
|
|
1403
1449
|
title: content.meta?.title || '',
|
|
1404
1450
|
instruction: content.meta?.instruction || 'Look and match.',
|
|
1451
|
+
...(passage ? { passage } : {}),
|
|
1452
|
+
...(audioUrl ? { audioUrl } : {}),
|
|
1453
|
+
...(imageUrl ? { imageUrl } : {}),
|
|
1405
1454
|
explanation: parent?.explanation,
|
|
1406
1455
|
};
|
|
1407
1456
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export declare function optionLetter(index: number): string;
|
|
3
|
+
export declare function KidChoiceRow({ letter, selected, isReviewMode, isCorrect, disabled, onClick, children, className, }: {
|
|
4
|
+
letter: string;
|
|
5
|
+
selected?: boolean;
|
|
6
|
+
isReviewMode?: boolean;
|
|
7
|
+
isCorrect?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onClick?: () => void;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Check, X } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../../../../shared/lib/utils';
|
|
5
|
+
export function optionLetter(index) {
|
|
6
|
+
return String.fromCharCode(65 + index);
|
|
7
|
+
}
|
|
8
|
+
export function KidChoiceRow({ letter, selected = false, isReviewMode = false, isCorrect = false, disabled = false, onClick, children, className, }) {
|
|
9
|
+
const showStatus = isReviewMode && (isCorrect || selected);
|
|
10
|
+
const rowTone = isReviewMode
|
|
11
|
+
? isCorrect
|
|
12
|
+
? 'border-green-400 bg-green-50'
|
|
13
|
+
: selected
|
|
14
|
+
? 'border-red-400 bg-red-50'
|
|
15
|
+
: 'border-gray-200 bg-gray-50'
|
|
16
|
+
: selected
|
|
17
|
+
? 'border-blue-500 bg-blue-50 shadow-sm ring-2 ring-blue-100'
|
|
18
|
+
: 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm';
|
|
19
|
+
const badgeTone = isReviewMode
|
|
20
|
+
? isCorrect
|
|
21
|
+
? 'bg-green-500 text-white'
|
|
22
|
+
: selected
|
|
23
|
+
? 'bg-red-500 text-white'
|
|
24
|
+
: 'bg-gray-200 text-gray-600'
|
|
25
|
+
: selected
|
|
26
|
+
? 'bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm'
|
|
27
|
+
: 'bg-gray-100 text-gray-600 group-hover:bg-gray-200';
|
|
28
|
+
return (_jsxs("button", { type: "button", onClick: onClick, disabled: disabled || isReviewMode || !onClick, className: cn('group flex w-full items-center gap-2 rounded-md border px-3 py-1.5 text-left transition-all', rowTone, (disabled || isReviewMode || !onClick) && 'cursor-default', className), children: [_jsx("div", { className: cn('flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-bold transition-all', badgeTone), children: letter }), _jsx("div", { className: cn('min-w-0 flex-1 text-sm font-medium', isReviewMode && isCorrect
|
|
29
|
+
? 'text-green-800'
|
|
30
|
+
: isReviewMode && selected && !isCorrect
|
|
31
|
+
? 'text-red-800'
|
|
32
|
+
: 'text-gray-700'), children: children }), showStatus && (_jsx("div", { className: cn('flex h-5 w-5 shrink-0 items-center justify-center rounded-full', isCorrect ? 'bg-green-500' : 'bg-red-500'), children: isCorrect ? _jsx(Check, { className: "h-3 w-3 text-white" }) : _jsx(X, { className: "h-3 w-3 text-white" }) }))] }));
|
|
33
|
+
}
|