@tinyweb_dev/oe-exam-sdk 0.2.10 → 0.2.12
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/question-renderers/MoversWordOrderingGroupRenderer.js +1 -0
- package/dist/components/exams/take/utils/question-transformers.d.ts +1 -0
- package/dist/components/exams/take/utils/question-transformers.js +2 -0
- package/dist/components/questions/_shared/types/word-ordering-group.type.d.ts +1 -0
- package/dist/components/questions/types/fill-in-blank-group/transform.js +21 -2
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +1 -0
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +26 -10
- package/dist/components/questions/types/word-ordering-group/map-word-ordering-group-data.js +2 -0
- package/dist/components/questions/types/word-ordering-group/transform.js +4 -1
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +5 -5
- package/dist/shared/lib/utils/question-reverse-transform.js +2 -0
- package/dist/shared/types/questions/word-ordering-group.d.ts +1 -0
- package/package.json +1 -1
package/dist/components/exams/take/components/question-renderers/MoversWordOrderingGroupRenderer.js
CHANGED
|
@@ -14,6 +14,7 @@ export function MoversWordOrderingGroupRenderer({ partNumber, partName, question
|
|
|
14
14
|
items: data.questions.map((question) => ({
|
|
15
15
|
words: question.words,
|
|
16
16
|
answer: question.answer,
|
|
17
|
+
imageUrl: question.imageUrl,
|
|
17
18
|
isExample: question.isExample,
|
|
18
19
|
points: question.points,
|
|
19
20
|
questionNumber: question.questionNumber,
|
|
@@ -1424,11 +1424,13 @@ export function transformWordOrderingGroupTake(questions) {
|
|
|
1424
1424
|
const isExample = item.isExample === true;
|
|
1425
1425
|
const fromItem = Array.isArray(item.correctAnswer?.answer) ? item.correctAnswer.answer : [];
|
|
1426
1426
|
const fromParent = Array.isArray(parentCorrect[index]) ? parentCorrect[index] : [];
|
|
1427
|
+
const imageUrl = typeof item.content?.imageUrl === 'string' ? item.content.imageUrl.trim() : '';
|
|
1427
1428
|
return {
|
|
1428
1429
|
id: `${parent?.id || 'q'}-item-${index}`,
|
|
1429
1430
|
questionNumber: item.questionNumber || index + 1,
|
|
1430
1431
|
words: Array.isArray(item.content?.words) ? item.content.words : [],
|
|
1431
1432
|
answer: fromItem.length ? fromItem : fromParent,
|
|
1433
|
+
...(imageUrl ? { imageUrl } : {}),
|
|
1432
1434
|
isExample,
|
|
1433
1435
|
points: isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
|
|
1434
1436
|
};
|
|
@@ -20,9 +20,28 @@ function toItemAnswer(item) {
|
|
|
20
20
|
caseSensitive: item.caseSensitive === true,
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* wrapOnSave nests creator fields under `answer`. getFormData() (Quiz Playground
|
|
25
|
+
* Update save) returns the same fields at the root. Prefer nested items, then root.
|
|
26
|
+
*/
|
|
27
|
+
function resolveFillInBlankGroupSource(question) {
|
|
24
28
|
const answerData = question.answer;
|
|
25
|
-
const
|
|
29
|
+
const rootData = question;
|
|
30
|
+
const answerItems = Array.isArray(answerData?.items) ? answerData.items : null;
|
|
31
|
+
const rootItems = Array.isArray(rootData.items) ? rootData.items : null;
|
|
32
|
+
if (answerItems && answerItems.length > 0) {
|
|
33
|
+
return { items: answerItems, source: answerData ?? {} };
|
|
34
|
+
}
|
|
35
|
+
if (rootItems && rootItems.length > 0) {
|
|
36
|
+
return { items: rootItems, source: rootData };
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
items: answerItems ?? rootItems ?? [],
|
|
40
|
+
source: answerData ?? {},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export const transformFillInBlankGroup = (question) => {
|
|
44
|
+
const { items, source: answerData } = resolveFillInBlankGroupSource(question);
|
|
26
45
|
if (items.length === 0) {
|
|
27
46
|
return { apiContent: { items: [] }, apiCorrectAnswer: { answer: [] } };
|
|
28
47
|
}
|
|
@@ -21,5 +21,6 @@ export function WordOrderingGroupClient({ questionData, isReviewMode = false, us
|
|
|
21
21
|
answer: item.answer,
|
|
22
22
|
isExample: item.isExample,
|
|
23
23
|
points: item.points,
|
|
24
|
+
imageUrl: item.imageUrl,
|
|
24
25
|
}, isReviewMode: isReviewMode || item.isExample, userAnswer: item.isExample ? item.answer : userAnswers[itemIndex], onSubmit: (ordered) => handleItemSubmit(itemIndex, ordered) })] }, `${item.questionNumber}-${itemIndex}`))), 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] }))] }));
|
|
25
26
|
}
|
|
@@ -18,18 +18,26 @@ function parsePointsValue(raw, allowZero = false) {
|
|
|
18
18
|
const min = allowZero ? 0 : 1;
|
|
19
19
|
return Math.max(min, parsed);
|
|
20
20
|
}
|
|
21
|
-
import { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';
|
|
21
|
+
import { Eye, ImageIcon, Pencil, Plus, Star, Trash2 } from 'lucide-react';
|
|
22
|
+
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
22
23
|
import { WordOrderingGroupClient } from './WordOrderingGroupClient';
|
|
23
24
|
import { WORD_ORDERING_GROUP_DEFAULT_INSTRUCTION } from '../../_shared/types/word-ordering-group.type';
|
|
24
25
|
import { createEmptyWordOrderingGroupItem, mapQuestionToWordOrderingGroupData, } from './map-word-ordering-group-data';
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
function withMinTokens(tokens, min = 2) {
|
|
27
|
+
if (tokens.length >= min) {
|
|
28
|
+
return tokens;
|
|
29
|
+
}
|
|
30
|
+
return [...tokens, ...Array.from({ length: min - tokens.length }, () => '')];
|
|
30
31
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
32
|
+
function setTokenAt(tokens, index, value) {
|
|
33
|
+
const next = tokens.length > index
|
|
34
|
+
? [...tokens]
|
|
35
|
+
: [...tokens, ...Array.from({ length: index - tokens.length + 1 }, () => '')];
|
|
36
|
+
next[index] = value;
|
|
37
|
+
return next;
|
|
38
|
+
}
|
|
39
|
+
function TokenBoxRow({ tokens, onChange, onAdd, onRemove, }) {
|
|
40
|
+
return (_jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2", children: [tokens.map((token, index) => (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gray-100 text-xs font-semibold text-gray-600", children: index + 1 }), _jsx(Input, { className: "h-9 w-28", value: token, onChange: (event) => onChange(index, event.target.value), placeholder: `Token ${index + 1}` }), tokens.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8 text-red-500 hover:bg-red-50 hover:text-red-700", onClick: () => onRemove(index), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, index))), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onAdd, children: [_jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), "Th\u00EAm"] })] }));
|
|
33
41
|
}
|
|
34
42
|
function sumPoints(items) {
|
|
35
43
|
return items.reduce((total, item) => (item.isExample ? total : total + (typeof item.points === 'number' ? item.points : 1)), 0);
|
|
@@ -100,7 +108,15 @@ export function WordOrderingGroupCreator({ initialData, onChange, externalErrors
|
|
|
100
108
|
};
|
|
101
109
|
const currentPayload = buildPayload();
|
|
102
110
|
const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
|
|
103
|
-
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg font-medium text-gray-900", children: "T\u1EA1o c\u00E2u h\u1ECFi S\u1EAFp x\u1EBFp t\u1EEB (nh\u00F3m)" }), _jsx("p", { className: "text-sm text-gray-500", children: "M\u1ED7i d\u00F2ng m\u1ED9t c\u00E2u x\u00E1o tr\u1ED9n.
|
|
111
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg font-medium text-gray-900", children: "T\u1EA1o c\u00E2u h\u1ECFi S\u1EAFp x\u1EBFp t\u1EEB (nh\u00F3m)" }), _jsx("p", { className: "text-sm text-gray-500", children: "M\u1ED7i d\u00F2ng m\u1ED9t c\u00E2u x\u00E1o tr\u1ED9n. M\u1ED7i token \u0111i\u1EC1n m\u1ED9t \u00F4." })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsClientMode((prev) => !prev), className: "gap-2", children: [isClientMode ? _jsx(Pencil, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }), isClientMode ? 'Chế độ chỉnh sửa' : 'Xem trước học sinh'] })] }), displayErrors.length > 0 && (_jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-5", children: displayErrors.map((err, i) => _jsx("li", { children: err }, i)) }) })), isClientMode ? (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "Xem tr\u01B0\u1EDBc hi\u1EC3n th\u1ECB h\u1ECDc sinh" }) }), _jsx(CardContent, { children: _jsx(WordOrderingGroupClient, { questionData: currentPayload }) })] })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "H\u01B0\u1EDBng d\u1EABn" }) }), _jsx(CardContent, { children: _jsx(Input, { value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: WORD_ORDERING_GROUP_DEFAULT_INSTRUCTION }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "text-base font-semibold", children: ["C\u00E2u h\u1ECFi (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setItems((prev) => [...prev, createEmptyWordOrderingGroupItem(prev.length + 1)]), children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), "Th\u00EAm c\u00E2u"] })] }), _jsx(CardContent, { className: "space-y-4", children: items.map((item, index) => (_jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-medium", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { className: "flex items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
|
|
104
112
|
points: parsePointsValue(event.target.value, true),
|
|
105
|
-
}) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setItems((prev) => prev.filter((_, i) => i !== index).map((entry, i) => ({ ...entry, questionNumber: i + 1 }))), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] })] }), _jsxs("div", { children: [
|
|
113
|
+
}) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setItems((prev) => prev.filter((_, i) => i !== index).map((entry, i) => ({ ...entry, questionNumber: i + 1 }))), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-amber-500" }), "H\u00ECnh \u1EA3nh (tu\u1EF3 ch\u1ECDn)"] }), _jsx(FileUpload, { id: `word-ordering-group-image-${index}`, label: "", accept: "image/*", value: item.imageUrl || '', onChange: (url) => updateItem(index, { imageUrl: url || undefined }), maxSize: 5, placeholder: "Upload h\u00ECnh \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" })] }), _jsxs("div", { children: [_jsx(Label, { children: "Token x\u00E1o tr\u1ED9n" }), _jsx(TokenBoxRow, { tokens: withMinTokens(item.words), onChange: (tokenIndex, value) => updateItem(index, {
|
|
114
|
+
words: setTokenAt(item.words, tokenIndex, value),
|
|
115
|
+
}), onAdd: () => updateItem(index, { words: [...item.words, ''] }), onRemove: (tokenIndex) => updateItem(index, {
|
|
116
|
+
words: item.words.filter((_, current) => current !== tokenIndex),
|
|
117
|
+
}) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Th\u1EE9 t\u1EF1 \u0111\u00FAng" }), _jsx(TokenBoxRow, { tokens: withMinTokens(item.answer), onChange: (tokenIndex, value) => updateItem(index, {
|
|
118
|
+
answer: setTokenAt(item.answer, tokenIndex, value),
|
|
119
|
+
}), onAdd: () => updateItem(index, { answer: [...item.answer, ''] }), onRemove: (tokenIndex) => updateItem(index, {
|
|
120
|
+
answer: item.answer.filter((_, current) => current !== tokenIndex),
|
|
121
|
+
}) })] })] }, index))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { value: explanation, onChange: (event) => setExplanation(event.target.value), rows: 3 }) })] })] }))] }));
|
|
106
122
|
}
|
|
@@ -37,9 +37,11 @@ export function mapWordOrderingGroupItem(item, index, fallbackAnswer) {
|
|
|
37
37
|
: asStringList(record.answer).length
|
|
38
38
|
? asStringList(record.answer)
|
|
39
39
|
: asStringList(fallbackAnswer);
|
|
40
|
+
const imageUrl = asString(content.imageUrl).trim() || asString(record.imageUrl).trim();
|
|
40
41
|
return {
|
|
41
42
|
words,
|
|
42
43
|
answer,
|
|
44
|
+
...(imageUrl ? { imageUrl } : {}),
|
|
43
45
|
isExample,
|
|
44
46
|
points: isExample ? 0 : (typeof record.points === 'number' ? record.points : 1),
|
|
45
47
|
questionNumber: typeof record.questionNumber === 'number' ? record.questionNumber : index + 1,
|
|
@@ -13,7 +13,10 @@ export const transformWordOrderingGroup = (question) => {
|
|
|
13
13
|
const apiItems = items.map((item, index) => ({
|
|
14
14
|
questionType: WORD_ORDERING_ITEM_TYPE,
|
|
15
15
|
...(item.isExample ? { isExample: true } : {}),
|
|
16
|
-
content: {
|
|
16
|
+
content: {
|
|
17
|
+
words: tokens(item.words),
|
|
18
|
+
...(item.imageUrl?.trim() ? { imageUrl: item.imageUrl.trim() } : {}),
|
|
19
|
+
},
|
|
17
20
|
correctAnswer: { answer: tokens(item.answer) },
|
|
18
21
|
points: item.isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
|
|
19
22
|
questionNumber: item.questionNumber || index + 1,
|
|
@@ -4,10 +4,10 @@ import { useState, useRef, useEffect } from 'react';
|
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
5
|
import { Label } from '../../../../components/ui/label';
|
|
6
6
|
import { Input } from '../../../../components/ui/input';
|
|
7
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
8
7
|
import { PointsInput } from '../../../../components/ui/points-input';
|
|
9
8
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
10
9
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
|
+
import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
|
|
11
11
|
import { Pencil, Eye, Plus, Trash2, Lightbulb, Mail, ImageIcon, X } from 'lucide-react';
|
|
12
12
|
import { WriteAShortLetterClient } from './WriteAShortLetterClient';
|
|
13
13
|
import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
|
|
@@ -316,10 +316,10 @@ function WriteAShortLetterCreatorContent({ initialData, onChange, externalErrors
|
|
|
316
316
|
setMaxWords(numValue < 1 ? 1 : numValue);
|
|
317
317
|
}
|
|
318
318
|
form.clearErrors('maxWords');
|
|
319
|
-
}, placeholder: "\u221E", className: `mt-1 ${errors.maxWords ? 'border-red-500 focus:ring-red-500' : ''}` }), errors.maxWords && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: errors.maxWords }))] })] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-medium", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
319
|
+
}, placeholder: "\u221E", className: `mt-1 ${errors.maxWords ? 'border-red-500 focus:ring-red-500' : ''}` }), errors.maxWords && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: errors.maxWords }))] })] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-medium", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx("div", { className: `mt-1.5 ${errors.instruction ? 'rounded-md ring-1 ring-red-500' : ''}`, children: _jsx(RichTextEditor, { value: instruction, onChange: (html) => {
|
|
320
|
+
setInstruction(html);
|
|
321
|
+
form.clearErrors('instruction');
|
|
322
|
+
}, minHeightClassName: "min-h-[120px]" }) }), errors.instruction && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: errors.instruction }))] }), _jsxs("div", { className: "space-y-3 rounded-lg border border-blue-100 bg-blue-50/30 p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-100", children: _jsx("span", { className: "text-xs font-semibold text-blue-600", children: "i" }) }), _jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "Th\u00F4ng tin c\u1EA7n bao g\u1ED3m" })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addInformation, className: "h-8 gap-1.5 border-blue-200 bg-white text-blue-600 hover:bg-blue-50 hover:text-blue-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm"] })] }), errors.informations && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-3", children: _jsx("p", { className: "text-sm text-red-600", children: errors.informations }) })), _jsx("div", { className: "space-y-2", children: informations.map((info, index) => (_jsx("div", { className: "space-y-1", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600", children: index + 1 }), _jsx(Input, { value: info, onChange: (e) => updateInformation(index, e.target.value), placeholder: `Thông tin ${index + 1}...`, className: "flex-1 border-gray-200" }), informations.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeInformation(index), className: "h-8 w-8 flex-shrink-0 p-0 text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }) }, index))) }), _jsx("p", { className: "text-xs text-gray-500 italic", children: "\uD83D\uDCA1 Li\u1EC7t k\u00EA c\u00E1c th\u00F4ng tin h\u1ECDc sinh c\u1EA7n \u0111\u1EC1 c\u1EADp trong b\u00E0i vi\u1EBFt" })] }), _jsxs("div", { className: "space-y-3 rounded-lg border border-violet-100 bg-violet-50/30 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-violet-100", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5 text-violet-600" }) }), _jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "\u1EA2nh h\u01B0\u1EDBng d\u1EABn" }), _jsx("span", { className: "text-xs text-gray-400" })] }), guideImages.length > 0 && (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4", children: guideImages.map((imageKey, index) => (_jsx(GuideImageItem, { imageKey: imageKey, index: index, localPreviewUrl: guideImagePreviewUrls[imageKey], onRemove: removeGuideImage }, `${imageKey}-${index}`))) })), _jsx(FileUpload, { label: "Th\u00EAm \u1EA3nh h\u01B0\u1EDBng d\u1EABn", accept: "image/*", value: "", onChange: handleGuideImageUpload, onPresignedUrlChange: handleGuideImagePresignedUrl, autoUpload: true, prefix: "write-short-letter/guide", placeholder: "Ch\u1ECDn ho\u1EB7c k\u00E9o th\u1EA3 \u1EA3nh v\u00E0o \u0111\u00E2y", icon: _jsx(ImageIcon, { className: "h-4 w-4" }) }), _jsx("p", { className: "text-xs text-gray-500 italic", children: "\uD83D\uDCF7 Upload nhi\u1EC1u \u1EA3nh \u0111\u1EC3 h\u01B0\u1EDBng d\u1EABn h\u1ECDc sinh vi\u1EBFt b\u00E0i (v\u00ED d\u1EE5: m\u1EABu th\u01B0, h\u00ECnh \u1EA3nh minh h\u1ECDa)" })] })] })] })] }));
|
|
323
323
|
}
|
|
324
324
|
export function WriteAShortLetterCreator(props) {
|
|
325
325
|
return _jsx(WriteAShortLetterCreatorContent, { ...props });
|
|
@@ -627,9 +627,11 @@ const reverseWordOrderingGroup = (apiQuestion) => {
|
|
|
627
627
|
const words = asArray(itemContent.words).map((token) => asString(token)).filter(Boolean);
|
|
628
628
|
const itemTokens = asArray(itemAnswer.answer).map((token) => asString(token)).filter(Boolean);
|
|
629
629
|
const fallbackTokens = asArray(fallbackAnswers[index]).map((token) => asString(token)).filter(Boolean);
|
|
630
|
+
const imageUrl = asString(itemContent.imageUrl).trim();
|
|
630
631
|
return {
|
|
631
632
|
words,
|
|
632
633
|
answer: itemTokens.length ? itemTokens : fallbackTokens,
|
|
634
|
+
...(imageUrl ? { imageUrl } : {}),
|
|
633
635
|
isExample,
|
|
634
636
|
points: isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
|
|
635
637
|
questionNumber: typeof item.questionNumber === 'number' ? item.questionNumber : index + 1,
|