@tinyweb_dev/oe-exam-sdk 1.0.9 → 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.
Files changed (104) hide show
  1. package/dist/components/exams/take/components/QuestionRenderer.js +1 -5
  2. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js +24 -10
  3. package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.js +1 -1
  4. package/dist/components/exams/take/components/question-renderers/MoversFillInBlankGroupRenderer.js +2 -1
  5. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.js +12 -2
  6. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.js +2 -1
  7. package/dist/components/exams/take/utils/answer-transformers.js +16 -1
  8. package/dist/components/exams/take/utils/cross-out-word-group.transformer.js +4 -6
  9. package/dist/components/exams/take/utils/question-transformers.d.ts +5 -0
  10. package/dist/components/exams/take/utils/question-transformers.js +6 -0
  11. package/dist/components/questions/_shared/components/compact/KidChoiceRow.d.ts +12 -0
  12. package/dist/components/questions/_shared/components/compact/KidChoiceRow.js +33 -0
  13. package/dist/components/questions/_shared/components/compact/KidInstruction.d.ts +5 -0
  14. package/dist/components/questions/_shared/components/compact/KidInstruction.js +9 -0
  15. package/dist/components/questions/_shared/components/compact/KidPrompt.d.ts +5 -0
  16. package/dist/components/questions/_shared/components/compact/KidPrompt.js +9 -0
  17. package/dist/components/questions/_shared/components/compact/KidQuestionCard.d.ts +12 -0
  18. package/dist/components/questions/_shared/components/compact/KidQuestionCard.js +7 -0
  19. package/dist/components/questions/_shared/components/compact/index.d.ts +4 -0
  20. package/dist/components/questions/_shared/components/compact/index.js +4 -0
  21. package/dist/components/questions/_shared/types/crossword-puzzle.type.d.ts +5 -1
  22. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +17 -0
  23. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +18 -19
  24. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +8 -7
  25. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +5 -4
  26. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +17 -11
  27. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +15 -42
  28. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +15 -36
  29. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -30
  30. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +8 -7
  31. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +50 -32
  32. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +24 -44
  33. package/dist/components/questions/types/cross-out-word-group/index.d.ts +1 -0
  34. package/dist/components/questions/types/cross-out-word-group/index.js +1 -0
  35. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +9 -9
  36. package/dist/components/questions/types/cross-out-word-group/tokenize.d.ts +2 -0
  37. package/dist/components/questions/types/cross-out-word-group/tokenize.js +11 -0
  38. package/dist/components/questions/types/cross-out-word-group/transform.js +2 -4
  39. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.d.ts +2 -1
  40. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +8 -6
  41. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +68 -11
  42. package/dist/components/questions/types/crossword-puzzle/transform.js +59 -30
  43. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +26 -31
  44. package/dist/components/questions/types/fill-in-blank-group/FillInBlankAnswerKey.d.ts +6 -0
  45. package/dist/components/questions/types/fill-in-blank-group/FillInBlankAnswerKey.js +17 -0
  46. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupClient.js +32 -28
  47. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +109 -26
  48. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupMediaField.js +21 -11
  49. package/dist/components/questions/types/fill-in-blank-group/blank-utils.d.ts +1 -0
  50. package/dist/components/questions/types/fill-in-blank-group/blank-utils.js +4 -1
  51. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +32 -31
  52. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +71 -28
  53. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +6 -2
  54. package/dist/components/questions/types/find-words-in-matrix/transform.js +5 -0
  55. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +4 -3
  56. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +11 -30
  57. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +7 -6
  58. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +2 -1
  59. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +14 -13
  60. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +4 -3
  61. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +4 -3
  62. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +11 -10
  63. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +4 -3
  64. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +13 -11
  65. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +17 -14
  66. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +16 -12
  67. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureGroupClient.js +14 -14
  68. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +13 -13
  69. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupClient.js +7 -6
  70. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +8 -26
  71. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +2 -1
  72. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +6 -5
  73. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +2 -1
  74. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +5 -4
  75. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +1 -1
  76. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +54 -11
  77. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +11 -7
  78. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +8 -2
  79. package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +1 -0
  80. package/dist/components/questions/types/spontaneous-qa-group/flatten.js +4 -2
  81. package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +5 -3
  82. package/dist/components/questions/types/spontaneous-qa-group/register.js +5 -2
  83. package/dist/components/questions/types/spontaneous-qa-group/transform.js +6 -2
  84. package/dist/components/questions/types/true-false/TrueFalseClient.js +5 -54
  85. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +21 -23
  86. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +8 -17
  87. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +76 -81
  88. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +17 -16
  89. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +31 -36
  90. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +15 -14
  91. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +9 -8
  92. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +16 -13
  93. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +1 -4
  94. package/dist/components/questions/viewer/FillInBlankGroupViewer.d.ts +6 -2
  95. package/dist/components/questions/viewer/FillInBlankGroupViewer.js +13 -7
  96. package/dist/components/questions/viewer/QuestionViewer.js +2 -2
  97. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +7 -1
  98. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +14 -2
  99. package/dist/components/results/renderers/review-question-body-movers.js +1 -1
  100. package/dist/components/results/review-data.js +8 -1
  101. package/dist/shared/lib/utils/question-reverse-transform.js +31 -5
  102. package/dist/shared/types/questions/crossword-puzzle.d.ts +1 -0
  103. package/dist/shared/types/questions/find-words-in-matrix.d.ts +1 -0
  104. package/package.json +1 -1
@@ -10,7 +10,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '../../../../components
10
10
  import { Switch } from '../../../../components/ui/switch';
11
11
  import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
12
12
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
13
- import { MessageSquare, Plus, Star, Trash2 } from 'lucide-react';
13
+ import { BookOpen, MessageSquare, Plus, Star, Trash2 } from 'lucide-react';
14
14
  import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
15
15
  import { AnswerTheQuestionGroupClient } from './AnswerTheQuestionGroupClient';
16
16
  import { toAnswerTheQuestionGroupImageUrls } from '../../_shared/types/answer-the-question-group.type';
@@ -57,6 +57,7 @@ export function AnswerTheQuestionGroupCreator({ initialData, onChange, externalE
57
57
  const [items, setItems] = useState(() => normalizeItems(initialData?.items));
58
58
  const [explanation, setExplanation] = useState(initialData?.explanation || '');
59
59
  const [isClientMode, setIsClientMode] = useState(false);
60
+ const [showPassage, setShowPassage] = useState(false);
60
61
  const [errors, setErrors] = useState([]);
61
62
  const onChangeRef = useRef(onChange);
62
63
  const previousPayloadRef = useRef('');
@@ -103,7 +104,7 @@ export function AnswerTheQuestionGroupCreator({ initialData, onChange, externalE
103
104
  .map((answer) => answer.trim())
104
105
  .filter((answer) => answer !== '');
105
106
  if (!item.isExample && filledAnswers.length === 0) {
106
- nextErrors.push(`Câu ${index + 1}: cần ít nhất một đáp án mẫu.`);
107
+ nextErrors.push(`Câu ${index + 1}: cần ít nhất một đáp án.`);
107
108
  }
108
109
  });
109
110
  return nextErrors;
@@ -185,8 +186,8 @@ export function AnswerTheQuestionGroupCreator({ initialData, onChange, externalE
185
186
  if (isClientMode) {
186
187
  return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(AnswerTheQuestionGroupClient, { questionData: buildPayload(), isReviewMode: true })] }));
187
188
  }
188
- return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: displayErrors.map((error) => (_jsx("p", { children: error }, error))) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(MessageSquare, { className: "h-3.5 w-3.5" }), title: "\u0110\u1ECDc v\u00E0 tr\u1EA3 l\u1EDDi c\u00E2u h\u1ECFi (AI)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "group-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "group-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: DEFAULT_INSTRUCTION, className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsxs("div", { className: "rounded-md border border-gray-200 bg-gray-50 px-3 py-2.5", children: [_jsx(PointsInput, { value: sumPoints(items), allowZero: true, readOnly: true }), _jsx("p", { className: "mt-1.5 text-xs text-gray-500", children: "T\u1ED5ng \u0111i\u1EC3m \u0111\u01B0\u1EE3c c\u1ED9ng t\u1EEB c\u00E1c c\u00E2u kh\u00F4ng ph\u1EA3i example." })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["Danh s\u00E1ch c\u00E2u h\u1ECFi (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u00E2u h\u1ECFi"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, itemIndex) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", itemIndex + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: item.isExample === true, onCheckedChange: (checked) => updateItem(itemIndex, {
189
+ return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: displayErrors.map((error) => (_jsx("p", { children: error }, error))) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(MessageSquare, { className: "h-3.5 w-3.5" }), title: "\u0110\u1ECDc v\u00E0 tr\u1EA3 l\u1EDDi c\u00E2u h\u1ECFi (AI)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "group-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "group-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: DEFAULT_INSTRUCTION, className: "h-10 border-gray-200 bg-white" })] }), showPassage ? (_jsxs("div", { className: "relative space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setShowPassage(false), className: "h-7 px-2 text-xs text-gray-500 hover:text-gray-700", children: "\u1EA8n" })] }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] })) : (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setShowPassage(true), className: "h-8 gap-1.5 border-indigo-200 px-2.5 text-xs text-indigo-700 hover:bg-indigo-50 hover:text-indigo-800", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5" }), isContentEmpty(passage) ? 'Thêm bài đọc' : 'Hiện bài đọc'] })), _jsxs("div", { className: "rounded-md border border-gray-200 bg-gray-50 px-3 py-2.5", children: [_jsx(PointsInput, { value: sumPoints(items), allowZero: true, readOnly: true }), _jsx("p", { className: "mt-1.5 text-xs text-gray-500", children: "T\u1ED5ng \u0111i\u1EC3m \u0111\u01B0\u1EE3c c\u1ED9ng t\u1EEB c\u00E1c c\u00E2u kh\u00F4ng ph\u1EA3i example." })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["Danh s\u00E1ch c\u00E2u h\u1ECFi (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u00E2u h\u1ECFi"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, itemIndex) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", itemIndex + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: item.isExample === true, onCheckedChange: (checked) => updateItem(itemIndex, {
189
190
  isExample: checked,
190
191
  points: checked ? 0 : (item.points || 1),
191
- }) }), _jsxs(Label, { className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "C\u00E2u v\u00ED d\u1EE5"] })] }), !item.isExample && (_jsx(PointsInput, { value: item.points, allowZero: true, onChange: (e) => updateItem(itemIndex, { points: Number(e.target.value) || 0 }) })), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(itemIndex), disabled: items.length <= 1, className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "C\u00E2u h\u1ECFi" }), _jsx(Textarea, { value: item.question, onChange: (event) => updateItem(itemIndex, { question: event.target.value }), rows: 2, placeholder: "What is autism?", className: "min-h-[56px] border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "\u0110\u00E1p \u00E1n m\u1EABu cho AI" }), item.expectedAnswers.map((answer, answerIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: answer, onChange: (event) => updateExpectedAnswer(itemIndex, answerIndex, event.target.value), placeholder: `Đáp án mẫu ${answerIndex + 1}`, className: "h-10 border-gray-200 bg-white" }), item.expectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeExpectedAnswer(itemIndex, answerIndex), className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, `expected-${itemIndex}-${answerIndex}`))), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addExpectedAnswer(itemIndex), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm \u0111\u00E1p \u00E1n m\u1EABu"] })] })] }, `item-${itemIndex}`))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
192
+ }) }), _jsxs(Label, { className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "C\u00E2u v\u00ED d\u1EE5"] })] }), !item.isExample && (_jsx(PointsInput, { value: item.points, allowZero: true, onChange: (e) => updateItem(itemIndex, { points: Number(e.target.value) || 0 }) })), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(itemIndex), disabled: items.length <= 1, className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "C\u00E2u h\u1ECFi" }), _jsx(Textarea, { value: item.question, onChange: (event) => updateItem(itemIndex, { question: event.target.value }), rows: 2, placeholder: "What is autism?", className: "min-h-[56px] border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "\u0110\u00E1p \u00E1n" }), item.expectedAnswers.map((answer, answerIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: answer, onChange: (event) => updateExpectedAnswer(itemIndex, answerIndex, event.target.value), placeholder: `Đáp án ${answerIndex + 1}`, className: "h-10 border-gray-200 bg-white" }), item.expectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeExpectedAnswer(itemIndex, answerIndex), className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, `expected-${itemIndex}-${answerIndex}`))), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addExpectedAnswer(itemIndex), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] })] }, `item-${itemIndex}`))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
192
193
  }
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useRef } from 'react';
4
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
5
4
  import { Check, X } from 'lucide-react';
5
+ import { KidQuestionCard } from '../../_shared/components/compact';
6
6
  // Fisher-Yates shuffle algorithm
7
7
  function shuffleArray(array) {
8
8
  const shuffled = [...array];
@@ -128,14 +128,20 @@ export function ArrangeLettersIntoWordsClient({ questionData, onSubmit, isReview
128
128
  const available = availableLetters[subQuestion.id] || [];
129
129
  const arranged = arrangedLetters[subQuestion.id] || [];
130
130
  const imagePreviewUrl = getImagePreviewUrl(subQuestion);
131
- return (_jsxs(Card, { children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs(CardTitle, { className: "text-sm", children: ["C\u00E2u ", sqIndex + 1] }) }), _jsxs(CardContent, { className: "space-y-3 p-3", children: [imagePreviewUrl && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "relative h-48 w-full max-w-md overflow-hidden rounded-lg", children: _jsx("img", { src: imagePreviewUrl, alt: `Hint for question ${sqIndex + 1}`, className: "h-full w-full object-contain", onError: () => {
132
- console.error('Failed to load image:', subQuestion.imageUrl);
133
- } }) }) })), _jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "Ch\u1ECDn c\u00E1c ch\u1EEF c\u00E1i:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: available.map((letter, index) => (_jsx("button", { onClick: () => handleLetterClick(subQuestion.id, index), disabled: isReviewMode, className: "flex h-12 w-12 items-center justify-center rounded-full border-2 border-blue-500 bg-white text-xl font-bold text-blue-600 transition-all hover:bg-blue-50 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50", children: letter }, `${letter}-${index}`))) })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "S\u1EAFp x\u1EBFp th\u00E0nh t\u1EEB:" }), _jsx("div", { className: `min-h-[60px] rounded-lg border-2 p-4 ${isReviewMode
134
- ? isCorrect
135
- ? 'border-green-500 bg-green-50'
136
- : userAnswerValue.trim() !== ''
137
- ? 'border-red-500 bg-red-50'
138
- : 'border-gray-300 bg-gray-50'
139
- : 'border-gray-300 bg-gray-50'}`, children: _jsx("div", { className: "flex flex-wrap items-center gap-2", children: arranged.length === 0 ? (_jsx("span", { className: "text-gray-400", children: "G\u1EA1ch ngang \u0111\u1EC3 s\u1EAFp x\u1EBFp..." })) : (arranged.map((letter, index) => (_jsx("button", { onClick: () => handleArrangedLetterClick(subQuestion.id, index), disabled: isReviewMode, className: "flex h-12 w-12 items-center justify-center rounded-full border-2 border-gray-400 bg-white text-xl font-bold text-gray-700 transition-all hover:bg-gray-100 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50", children: letter }, `arranged-${letter}-${index}`)))) }) })] }), isReviewMode && (_jsx("div", { className: "flex items-center gap-2", children: isCorrect ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-5 w-5 text-green-600" }), _jsx("span", { className: "text-sm text-green-600", children: "\u0110\u00FAng!" })] })) : userAnswerValue.trim() !== '' ? (_jsxs(_Fragment, { children: [_jsx(X, { className: "h-5 w-5 text-red-600" }), _jsxs("span", { className: "text-sm text-red-600", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: subQuestion.correctWord })] })] })) : null }))] })] }, subQuestion.id));
131
+ return (_jsxs(KidQuestionCard, { title: `Câu ${sqIndex + 1}`, review: isReviewMode
132
+ ? isCorrect
133
+ ? 'correct'
134
+ : userAnswerValue.trim() !== ''
135
+ ? 'wrong'
136
+ : null
137
+ : null, children: [imagePreviewUrl && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "relative h-48 w-full max-w-md overflow-hidden rounded-lg", children: _jsx("img", { src: imagePreviewUrl, alt: `Hint for question ${sqIndex + 1}`, className: "h-full w-full object-contain", onError: () => {
138
+ console.error('Failed to load image:', subQuestion.imageUrl);
139
+ } }) }) })), _jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "Ch\u1ECDn c\u00E1c ch\u1EEF c\u00E1i:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: available.map((letter, index) => (_jsx("button", { onClick: () => handleLetterClick(subQuestion.id, index), disabled: isReviewMode, className: "flex h-12 w-12 items-center justify-center rounded-full border-2 border-blue-500 bg-white text-xl font-bold text-blue-600 transition-all hover:bg-blue-50 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50", children: letter }, `${letter}-${index}`))) })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "S\u1EAFp x\u1EBFp th\u00E0nh t\u1EEB:" }), _jsx("div", { className: `min-h-[60px] rounded-lg border-2 p-4 ${isReviewMode
140
+ ? isCorrect
141
+ ? 'border-green-500 bg-green-50'
142
+ : userAnswerValue.trim() !== ''
143
+ ? 'border-red-500 bg-red-50'
144
+ : 'border-gray-300 bg-gray-50'
145
+ : 'border-gray-300 bg-gray-50'}`, children: _jsx("div", { className: "flex flex-wrap items-center gap-2", children: arranged.length === 0 ? (_jsx("span", { className: "text-gray-400", children: "G\u1EA1ch ngang \u0111\u1EC3 s\u1EAFp x\u1EBFp..." })) : (arranged.map((letter, index) => (_jsx("button", { onClick: () => handleArrangedLetterClick(subQuestion.id, index), disabled: isReviewMode, className: "flex h-12 w-12 items-center justify-center rounded-full border-2 border-gray-400 bg-white text-xl font-bold text-gray-700 transition-all hover:bg-gray-100 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50", children: letter }, `arranged-${letter}-${index}`)))) }) })] }), isReviewMode && (_jsx("div", { className: "flex items-center gap-2", children: isCorrect ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-5 w-5 text-green-600" }), _jsx("span", { className: "text-sm text-green-600", children: "\u0110\u00FAng!" })] })) : userAnswerValue.trim() !== '' ? (_jsxs(_Fragment, { children: [_jsx(X, { className: "h-5 w-5 text-red-600" }), _jsxs("span", { className: "text-sm text-red-600", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: subQuestion.correctWord })] })] })) : null }))] }, subQuestion.id));
140
146
  }) }));
141
147
  }
@@ -2,7 +2,8 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from 'react';
4
4
  import { cn } from '../../../../shared/lib/utils';
5
- import { Check, X, ListChecks, Lightbulb } from 'lucide-react';
5
+ import { Check, X, Lightbulb } from 'lucide-react';
6
+ import { KidChoiceRow, KidQuestionCard, optionLetter } from '../../_shared/components/compact';
6
7
  export function ChooseCorrectAdjectiveClient({ questionData, onSubmit, isReviewMode = false, userAnswer, }) {
7
8
  const [selectedAnswer, setSelectedAnswer] = useState(userAnswer);
8
9
  const handleAnswerChange = (index) => {
@@ -30,45 +31,17 @@ export function ChooseCorrectAdjectiveClient({ questionData, onSubmit, isReviewM
30
31
  };
31
32
  // Get the correct answer text
32
33
  const correctAnswerText = getOptionText(questionData.options[questionData.correctAnswer]);
33
- return (_jsx("div", { className: "space-y-3", children: _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: cn('flex items-center justify-between border-b px-3 py-2', isReviewMode && userAnswerCorrect && 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50', isReviewMode && hasAnswered && !userAnswerCorrect && 'border-red-200 bg-gradient-to-r from-red-50 to-rose-50', (!isReviewMode || !hasAnswered) && 'border-gray-100 bg-gradient-to-r from-gray-50 to-white'), children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: cn('flex h-8 w-8 items-center justify-center rounded-lg text-sm font-bold text-white shadow-sm', isReviewMode && userAnswerCorrect && 'bg-gradient-to-br from-green-500 to-emerald-600', isReviewMode && hasAnswered && !userAnswerCorrect && 'bg-gradient-to-br from-red-500 to-rose-600', (!isReviewMode || !hasAnswered) && 'bg-gradient-to-br from-teal-500 to-cyan-600'), children: _jsx(ListChecks, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Ch\u1ECDn t\u00EDnh t\u1EEB \u0111\u00FAng" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', userAnswerCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [userAnswerCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), userAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-3 space-y-3", children: [_jsxs("div", { className: "rounded-xl bg-gradient-to-br from-slate-50 to-gray-50 border border-gray-100 p-5", children: [_jsx("div", { className: "text-lg leading-relaxed flex flex-wrap items-center gap-1", children: questionData.sentence.split('{0}').map((part, index, arr) => (_jsxs("span", { children: [_jsx("span", { className: "text-gray-800", children: part }), index < arr.length - 1 && (_jsx("span", { className: "inline-flex items-center mx-1", children: _jsx("span", { className: cn('inline-block min-w-[100px] px-3 py-1 text-center font-bold rounded-lg border-2 border-dashed', hasAnswered
34
- ? isReviewMode
35
- ? userAnswerCorrect
36
- ? 'border-green-400 bg-green-100 text-green-800'
37
- : 'border-red-400 bg-red-100 text-red-800'
38
- : 'border-indigo-400 bg-indigo-100 text-indigo-800'
39
- : 'border-gray-300 bg-gray-100 text-gray-400'), children: hasAnswered ? getOptionText(questionData.options[selectedAnswer]) : '_______' }) }))] }, index))) }), _jsxs("div", { className: "mt-3 flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-gray-500", children: "L\u1EF1a ch\u1ECDn:" }), _jsxs("span", { className: "text-sm font-medium text-gray-600", children: ["(", questionData.options.map(getOptionText).join(' / '), ")"] })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium text-gray-600 mb-3", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n:" }), _jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-4", children: questionData.options.map((option, index) => {
40
- const selected = isSelected(index);
41
- const correct = isCorrect(index);
42
- const letter = String.fromCharCode(65 + index);
43
- const optText = getOptionText(option);
44
- return (_jsxs("button", { type: "button", onClick: () => handleAnswerChange(index), disabled: isReviewMode, className: cn('group flex items-center gap-2 rounded-xl border-2 p-3 text-left transition-all', isReviewMode
45
- ? correct
46
- ? 'border-green-400 bg-green-50'
47
- : selected
48
- ? 'border-red-400 bg-red-50'
49
- : 'border-gray-200 bg-gray-50'
50
- : selected
51
- ? 'border-indigo-500 bg-indigo-50 shadow-sm ring-2 ring-indigo-100'
52
- : 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm'), children: [_jsx("div", { className: cn('flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold transition-all', isReviewMode
53
- ? correct
54
- ? 'bg-green-500 text-white'
55
- : selected
56
- ? 'bg-red-500 text-white'
57
- : 'bg-gray-200 text-gray-600'
58
- : selected
59
- ? 'bg-gradient-to-br from-indigo-500 to-purple-600 text-white shadow-sm'
60
- : 'bg-gray-100 text-gray-600 group-hover:bg-gray-200'), children: letter }), _jsx("span", { className: cn('flex-1 text-sm font-semibold', isReviewMode && correct
61
- ? 'text-green-800'
62
- : isReviewMode && selected && !correct
63
- ? 'text-red-800'
64
- : 'text-gray-700'), children: optText }), isReviewMode && (correct || selected) && (_jsx("div", { className: cn('flex h-5 w-5 items-center justify-center rounded-full', correct ? 'bg-green-500' : 'bg-red-500'), children: correct
65
- ? _jsx(Check, { className: "h-3 w-3 text-white" })
66
- : _jsx(X, { className: "h-3 w-3 text-white" }) }))] }, index));
67
- }) })] }), isReviewMode && (_jsxs("div", { className: "space-y-4 pt-2", children: [_jsxs("div", { className: "rounded-xl border border-green-200 bg-gradient-to-r from-green-50 to-emerald-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-green-200", children: _jsx(Check, { className: "h-3.5 w-3.5 text-green-700" }) }), _jsx("span", { className: "text-sm font-medium text-green-800", children: "C\u00E2u ho\u00E0n ch\u1EC9nh:" })] }), _jsx("p", { className: "text-base font-medium text-green-900 pl-8", children: questionData.sentence.replace('{0}', correctAnswerText) })] }), _jsx("div", { className: cn('rounded-xl p-4', userAnswerCorrect
68
- ? 'bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200'
69
- : 'bg-gradient-to-r from-red-50 to-rose-50 border border-red-200'), children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: cn('flex h-10 w-10 items-center justify-center rounded-xl text-white shadow-sm', userAnswerCorrect
70
- ? 'bg-gradient-to-br from-green-500 to-emerald-600'
71
- : 'bg-gradient-to-br from-red-500 to-rose-600'), children: userAnswerCorrect ? _jsx(Check, { className: "h-5 w-5" }) : _jsx(X, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("p", { className: cn('text-sm font-semibold', userAnswerCorrect ? 'text-green-800' : 'text-red-800'), children: userAnswerCorrect ? 'Chính xác!' : 'Chưa đúng' }), _jsx("p", { className: cn('text-xs', userAnswerCorrect ? 'text-green-600' : 'text-red-600'), children: userAnswerCorrect
72
- ? 'Bạn đã chọn đúng tính từ.'
73
- : `Đáp án đúng là "${correctAnswerText}".` })] })] }) }), questionData.explanation && (_jsx("div", { className: "rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(Lightbulb, { className: "h-4 w-4" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-semibold text-blue-800", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700 mt-1", children: questionData.explanation })] })] }) }))] })), !isReviewMode && !hasAnswered && (_jsx("div", { className: "rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm", children: _jsx("span", { className: "text-sm font-bold", children: "!" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-amber-800", children: "Ch\u1ECDn t\u00EDnh t\u1EEB ph\u00F9 h\u1EE3p" }), _jsx("p", { className: "text-xs text-amber-600", children: "Nh\u1EA5n v\u00E0o m\u1ED9t trong c\u00E1c l\u1EF1a ch\u1ECDn b\u00EAn tr\u00EAn." })] })] }) }))] })] }) }));
34
+ return (_jsx("div", { className: "space-y-3", children: _jsxs(KidQuestionCard, { title: "Ch\u1ECDn t\u00EDnh t\u1EEB \u0111\u00FAng", review: isReviewMode && hasAnswered ? (userAnswerCorrect ? 'correct' : 'wrong') : null, children: [_jsxs("div", { className: "rounded-xl bg-gradient-to-br from-slate-50 to-gray-50 border border-gray-100 p-5", children: [_jsx("div", { className: "text-lg leading-relaxed flex flex-wrap items-center gap-1", children: questionData.sentence.split('{0}').map((part, index, arr) => (_jsxs("span", { children: [_jsx("span", { className: "text-gray-800", children: part }), index < arr.length - 1 && (_jsx("span", { className: "inline-flex items-center mx-1", children: _jsx("span", { className: cn('inline-block min-w-[100px] px-3 py-1 text-center font-bold rounded-lg border-2 border-dashed', hasAnswered
35
+ ? isReviewMode
36
+ ? userAnswerCorrect
37
+ ? 'border-green-400 bg-green-100 text-green-800'
38
+ : 'border-red-400 bg-red-100 text-red-800'
39
+ : 'border-indigo-400 bg-indigo-100 text-indigo-800'
40
+ : 'border-gray-300 bg-gray-100 text-gray-400'), children: hasAnswered ? getOptionText(questionData.options[selectedAnswer]) : '_______' }) }))] }, index))) }), _jsxs("div", { className: "mt-3 flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-gray-500", children: "L\u1EF1a ch\u1ECDn:" }), _jsxs("span", { className: "text-sm font-medium text-gray-600", children: ["(", questionData.options.map(getOptionText).join(' / '), ")"] })] })] }), _jsx("div", { className: "space-y-1.5", children: questionData.options.map((option, index) => (_jsx(KidChoiceRow, { letter: optionLetter(index), selected: isSelected(index), isReviewMode: isReviewMode, isCorrect: isReviewMode && isCorrect(index), onClick: () => handleAnswerChange(index), children: getOptionText(option) }, index))) }), isReviewMode && (_jsxs("div", { className: "space-y-4 pt-2", children: [_jsxs("div", { className: "rounded-xl border border-green-200 bg-gradient-to-r from-green-50 to-emerald-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-green-200", children: _jsx(Check, { className: "h-3.5 w-3.5 text-green-700" }) }), _jsx("span", { className: "text-sm font-medium text-green-800", children: "C\u00E2u ho\u00E0n ch\u1EC9nh:" })] }), _jsx("p", { className: "text-base font-medium text-green-900 pl-8", children: questionData.sentence.replace('{0}', correctAnswerText) })] }), _jsx("div", { className: cn('rounded-xl p-4', userAnswerCorrect
41
+ ? 'bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200'
42
+ : 'bg-gradient-to-r from-red-50 to-rose-50 border border-red-200'), children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: cn('flex h-10 w-10 items-center justify-center rounded-xl text-white shadow-sm', userAnswerCorrect
43
+ ? 'bg-gradient-to-br from-green-500 to-emerald-600'
44
+ : 'bg-gradient-to-br from-red-500 to-rose-600'), children: userAnswerCorrect ? _jsx(Check, { className: "h-5 w-5" }) : _jsx(X, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("p", { className: cn('text-sm font-semibold', userAnswerCorrect ? 'text-green-800' : 'text-red-800'), children: userAnswerCorrect ? 'Chính xác!' : 'Chưa đúng' }), _jsx("p", { className: cn('text-xs', userAnswerCorrect ? 'text-green-600' : 'text-red-600'), children: userAnswerCorrect
45
+ ? 'Bạn đã chọn đúng tính từ.'
46
+ : `Đáp án đúng là "${correctAnswerText}".` })] })] }) }), questionData.explanation && (_jsx("div", { className: "rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(Lightbulb, { className: "h-4 w-4" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-semibold text-blue-800", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700 mt-1", children: questionData.explanation })] })] }) }))] })), !isReviewMode && !hasAnswered && (_jsx("div", { className: "rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm", children: _jsx("span", { className: "text-sm font-bold", children: "!" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-amber-800", children: "Ch\u1ECDn t\u00EDnh t\u1EEB ph\u00F9 h\u1EE3p" }), _jsx("p", { className: "text-xs text-amber-600", children: "Nh\u1EA5n v\u00E0o m\u1ED9t trong c\u00E1c l\u1EF1a ch\u1ECDn b\u00EAn tr\u00EAn." })] })] }) }))] }) }));
74
47
  }
@@ -1,12 +1,13 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useMemo } from 'react';
4
- import { Check, X, CircleHelp, Lightbulb, ClipboardList, Users, BookOpen } from 'lucide-react';
4
+ import { Check, X, Lightbulb, ClipboardList, Users, BookOpen } from 'lucide-react';
5
5
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
6
6
  import { cn } from '../../../../shared/lib/utils';
7
7
  import { RichTextHtml } from '../../../../components/shared/RichTextHtml';
8
8
  import { isRichTextEmpty, richTextToPlainText } from '../../../../shared/lib/rich-text';
9
9
  import { normalizeChooseAnswerCorrectAnswer, normalizeChooseAnswerOptions, optionToString } from './normalize';
10
+ import { KidChoiceRow, KidPrompt, KidQuestionCard, optionLetter } from '../../_shared/components/compact';
10
11
  // Helper component to resolve presigned URL for each image option
11
12
  function OptionImage({ src, alt, className }) {
12
13
  const { previewUrl, isLoading } = usePresignedFileUrl(src);
@@ -110,41 +111,19 @@ export function ChooseTheCorrectAnswerClient({ questionData, onSubmit, isReviewM
110
111
  .split('\n')
111
112
  .map((line) => `<p class="last:mb-0">${line}</p>`)
112
113
  .join(''),
113
- } })), questionData.groupImageUrl && (_jsxs("div", { className: "mt-5 relative overflow-hidden rounded-xl border border-slate-200/60 bg-slate-50 shadow-sm transition-all hover:shadow-md", children: [_jsx("div", { className: "flex items-center justify-between border-b border-slate-200/60 bg-slate-100/50 px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("span", { className: "ml-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500", children: "T\u00E0i li\u1EC7u \u0111\u00EDnh k\u00E8m" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4 min-h-[160px]", children: _jsx(OptionImage, { src: questionData.groupImageUrl, alt: "Group illustration", className: "max-h-64 w-auto rounded-lg object-contain" }) })] }))] }))] })] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xs", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: isMultipleAnswers ? 'Chọn các đáp án đúng' : 'Chọn đáp án đúng' })] }), questionData.points !== undefined && (_jsx("div", { className: "flex items-center gap-1.5 rounded-full bg-blue-50 px-2.5 py-0.5 text-xs font-semibold text-blue-700", children: _jsxs("span", { children: [questionData.points, " \u0111i\u1EC3m"] }) }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [(questionData.askerName || questionData.answererName) && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-gray-50 p-2.5 text-xs text-gray-600", children: [_jsx(Users, { className: "h-4 w-4 text-gray-400" }), questionData.askerName && (_jsxs("span", { children: [_jsx("strong", { children: "Ng\u01B0\u1EDDi h\u1ECFi:" }), " ", questionData.askerName] })), questionData.askerName && questionData.answererName && _jsx("span", { children: "\u2022" }), questionData.answererName && (_jsxs("span", { children: [_jsx("strong", { children: "Ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi:" }), " ", questionData.answererName] }))] })), showQuestion && (_jsx("div", { className: "rounded-md border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 px-3 py-2", children: isRichTextEmpty(questionData.question) ? (_jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi ch\u01B0a \u0111\u01B0\u1EE3c nh\u1EADp" })) : (_jsx(RichTextHtml, { html: questionData.question, className: "text-sm font-semibold text-gray-800" })) })), showImage && (imagePreviewUrl || cleanImageUrl) && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2 w-2 rounded-full bg-red-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-green-400" }), _jsx("span", { className: "ml-2 text-xs font-medium text-gray-500", children: "H\u00ECnh \u1EA3nh" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4", children: isLoading ? (_jsx("div", { className: "flex h-48 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500" }) })) : (
114
- /* eslint-disable-next-line @next/next/no-img-element */
115
- _jsx("img", { src: imagePreviewUrl || cleanImageUrl, alt: "Question illustration", className: "max-h-56 w-auto object-contain rounded-md", onError: (e) => {
116
- const target = e.target;
117
- target.onerror = null;
118
- target.src = '/images/placeholder-image.svg';
119
- } })) })] })), showOptions && (_jsxs("div", { className: "space-y-1.5", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: isMultipleAnswers ? 'Chọn một hoặc nhiều đáp án' : 'Chọn đáp án' }), displayOptions.map((option, index) => {
120
- const isSelected = selectedAnswer.includes(index);
121
- const isCorrectAnswer = isCorrect(index);
122
- const letter = String.fromCharCode(65 + index);
123
- const optionText = optionToString(option);
124
- return (_jsxs("button", { type: "button", onClick: () => handleAnswerChange(index), disabled: isReviewMode, className: cn('group flex w-full items-center gap-2 rounded-md border px-3 py-1.5 text-left transition-all', isReviewMode
125
- ? isCorrectAnswer
126
- ? 'border-green-400 bg-green-50'
127
- : isSelected
128
- ? 'border-red-400 bg-red-50'
129
- : 'border-gray-200 bg-gray-50'
130
- : isSelected
131
- ? 'border-blue-500 bg-blue-50 shadow-sm ring-2 ring-blue-100'
132
- : 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm'), children: [_jsx("div", { className: cn('flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-bold transition-all', isReviewMode
133
- ? isCorrectAnswer
134
- ? 'bg-green-500 text-white'
135
- : isSelected
136
- ? 'bg-red-500 text-white'
137
- : 'bg-gray-200 text-gray-600'
138
- : isSelected
139
- ? 'bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm'
140
- : 'bg-gray-100 text-gray-600 group-hover:bg-gray-200'), children: letter }), questionData.optionType === 'image' ? (_jsx("div", { className: "flex-1 flex items-center justify-center py-1", children: _jsx(OptionImage, { src: optionText, alt: `Đáp án ${letter}` }) })) : (_jsx(RichTextHtml, { html: optionText, className: cn('flex-1 text-sm font-medium', isReviewMode && isCorrectAnswer
141
- ? 'text-green-800'
142
- : isReviewMode && isSelected && !isCorrectAnswer
143
- ? 'text-red-800'
144
- : 'text-gray-700') })), isReviewMode && (isCorrectAnswer || isSelected) && (_jsx("div", { className: cn('flex h-6 w-6 items-center justify-center rounded-full', isCorrectAnswer ? 'bg-green-500' : 'bg-red-500'), children: isCorrectAnswer
145
- ? _jsx(Check, { className: "h-4 w-4 text-white" })
146
- : _jsx(X, { className: "h-4 w-4 text-white" }) }))] }, index));
147
- })] })), !isReviewMode && !hasAnswered && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-2.5 py-1.5", children: [_jsx("div", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white", children: _jsx("span", { className: "text-[10px] font-bold", children: "!" }) }), _jsx("span", { className: "text-xs text-amber-800", children: isMultipleAnswers ? 'Vui lòng chọn ít nhất một đáp án' : 'Vui lòng chọn một đáp án' })] }))] })] }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2", children: [_jsx(Lightbulb, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }), _jsxs("p", { className: "text-xs text-gray-700", children: [_jsx("span", { className: "font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch: " }), questionData.explanation] })] })), isReviewMode && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-slate-500 to-gray-600 text-white", children: _jsx(ClipboardList, { className: "h-3.5 w-3.5" }) }), _jsx("h4", { className: "text-sm font-semibold text-gray-800", children: "T\u1ED5ng k\u1EBFt" })] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("div", { className: "flex items-center justify-between rounded-lg bg-gray-50 p-3", children: [_jsx("span", { className: "text-sm text-gray-600", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n:" }), _jsx("span", { className: cn('text-sm font-semibold', isAnswerCorrect ? 'text-green-600' : hasAnswered ? 'text-red-600' : 'text-gray-500'), children: formatAnswerLabel(selectedAnswer) })] }), _jsxs("div", { className: "flex items-center justify-between rounded-lg bg-green-50 p-3 border border-green-100", children: [_jsx("span", { className: "text-sm text-gray-600", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng:" }), _jsx("span", { className: "text-sm font-semibold text-green-600", children: formatAnswerLabel(normalizedCorrectAnswers) })] }), _jsxs("div", { className: cn('flex items-center justify-between rounded-lg p-3 border', isAnswerCorrect
114
+ } })), questionData.groupImageUrl && (_jsxs("div", { className: "mt-5 relative overflow-hidden rounded-xl border border-slate-200/60 bg-slate-50 shadow-sm transition-all hover:shadow-md", children: [_jsx("div", { className: "flex items-center justify-between border-b border-slate-200/60 bg-slate-100/50 px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("span", { className: "ml-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500", children: "T\u00E0i li\u1EC7u \u0111\u00EDnh k\u00E8m" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4 min-h-[160px]", children: _jsx(OptionImage, { src: questionData.groupImageUrl, alt: "Group illustration", className: "max-h-64 w-auto rounded-lg object-contain" }) })] }))] }))] })] })), _jsxs(KidQuestionCard, { title: isMultipleAnswers ? 'Chọn các đáp án đúng' : 'Chọn đáp án đúng', points: questionData.points, review: isReviewMode && hasAnswered ? (isAnswerCorrect ? 'correct' : 'wrong') : null, children: [(questionData.askerName || questionData.answererName) && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-gray-50 p-2.5 text-xs text-gray-600", children: [_jsx(Users, { className: "h-4 w-4 text-gray-400" }), questionData.askerName && (_jsxs("span", { children: [_jsx("strong", { children: "Ng\u01B0\u1EDDi h\u1ECFi:" }), " ", questionData.askerName] })), questionData.askerName && questionData.answererName && _jsx("span", { children: "\u2022" }), questionData.answererName && (_jsxs("span", { children: [_jsx("strong", { children: "Ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi:" }), " ", questionData.answererName] }))] })), showQuestion && (_jsx(KidPrompt, { children: isRichTextEmpty(questionData.question) ? (_jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi ch\u01B0a \u0111\u01B0\u1EE3c nh\u1EADp" })) : (_jsx(RichTextHtml, { html: questionData.question, className: "text-sm font-semibold text-gray-800" })) })), showImage && (imagePreviewUrl || cleanImageUrl) && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2 w-2 rounded-full bg-red-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-green-400" }), _jsx("span", { className: "ml-2 text-xs font-medium text-gray-500", children: "H\u00ECnh \u1EA3nh" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4", children: isLoading ? (_jsx("div", { className: "flex h-48 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500" }) })) : (
115
+ /* eslint-disable-next-line @next/next/no-img-element */
116
+ _jsx("img", { src: imagePreviewUrl || cleanImageUrl, alt: "Question illustration", className: "max-h-56 w-auto object-contain rounded-md", onError: (e) => {
117
+ const target = e.target;
118
+ target.onerror = null;
119
+ target.src = '/images/placeholder-image.svg';
120
+ } })) })] })), showOptions && (_jsxs("div", { className: "space-y-1.5", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: isMultipleAnswers ? 'Chọn một hoặc nhiều đáp án' : 'Chọn đáp án' }), displayOptions.map((option, index) => {
121
+ const isSelected = selectedAnswer.includes(index);
122
+ const isCorrectAnswer = isCorrect(index);
123
+ const letter = optionLetter(index);
124
+ const optionText = optionToString(option);
125
+ return (_jsx(KidChoiceRow, { letter: letter, selected: isSelected, isReviewMode: isReviewMode, isCorrect: isCorrectAnswer, onClick: () => handleAnswerChange(index), children: questionData.optionType === 'image' ? (_jsx("div", { className: "flex items-center justify-center py-1", children: _jsx(OptionImage, { src: optionText, alt: `Đáp án ${letter}` }) })) : (_jsx(RichTextHtml, { html: optionText })) }, index));
126
+ })] })), !isReviewMode && !hasAnswered && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-2.5 py-1.5", children: [_jsx("div", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white", children: _jsx("span", { className: "text-[10px] font-bold", children: "!" }) }), _jsx("span", { className: "text-xs text-amber-800", children: isMultipleAnswers ? 'Vui lòng chọn ít nhất một đáp án' : 'Vui lòng chọn một đáp án' })] }))] }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2", children: [_jsx(Lightbulb, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }), _jsxs("p", { className: "text-xs text-gray-700", children: [_jsx("span", { className: "font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch: " }), questionData.explanation] })] })), isReviewMode && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-slate-500 to-gray-600 text-white", children: _jsx(ClipboardList, { className: "h-3.5 w-3.5" }) }), _jsx("h4", { className: "text-sm font-semibold text-gray-800", children: "T\u1ED5ng k\u1EBFt" })] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("div", { className: "flex items-center justify-between rounded-lg bg-gray-50 p-3", children: [_jsx("span", { className: "text-sm text-gray-600", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n:" }), _jsx("span", { className: cn('text-sm font-semibold', isAnswerCorrect ? 'text-green-600' : hasAnswered ? 'text-red-600' : 'text-gray-500'), children: formatAnswerLabel(selectedAnswer) })] }), _jsxs("div", { className: "flex items-center justify-between rounded-lg bg-green-50 p-3 border border-green-100", children: [_jsx("span", { className: "text-sm text-gray-600", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng:" }), _jsx("span", { className: "text-sm font-semibold text-green-600", children: formatAnswerLabel(normalizedCorrectAnswers) })] }), _jsxs("div", { className: cn('flex items-center justify-between rounded-lg p-3 border', isAnswerCorrect
148
127
  ? 'bg-green-50 border-green-200'
149
128
  : 'bg-red-50 border-red-200'), children: [_jsx("span", { className: "text-sm text-gray-600", children: "K\u1EBFt qu\u1EA3:" }), _jsx("div", { className: cn('flex items-center gap-1.5 text-sm font-semibold', isAnswerCorrect ? 'text-green-600' : 'text-red-600'), children: isAnswerCorrect ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-4 w-4" }), _jsx("span", { children: "Ch\u00EDnh x\u00E1c" })] })) : (_jsxs(_Fragment, { children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { children: "Sai" })] })) })] })] })] }))] }));
150
129
  }
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { BookOpen, Check, CircleHelp, Lightbulb, Star, Volume2, X } from 'lucide-react';
4
- import { cn } from '../../../../shared/lib/utils';
3
+ import { BookOpen, Lightbulb, Volume2 } from 'lucide-react';
5
4
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
6
5
  import { RichTextHtml } from '../../../../components/shared/RichTextHtml';
7
6
  import { isRichTextEmpty } from '../../../../shared/lib/rich-text';
8
7
  import { getChooseAnswerGroupOptionImageSrc, toChooseAnswerGroupImageUrls, } from '../../_shared/types/choose-the-correct-answer-group.type';
8
+ import { KidChoiceRow, KidInstruction, KidPrompt, KidQuestionCard, optionLetter } from '../../_shared/components/compact';
9
9
  function ClientImageItem({ url, alt }) {
10
10
  const { previewUrl, isLoading } = usePresignedFileUrl(url);
11
11
  const src = previewUrl ||
@@ -24,21 +24,6 @@ function ClientAudio({ url }) {
24
24
  }
25
25
  return (_jsxs("div", { className: "flex items-center gap-3 rounded-xl border border-indigo-100 bg-indigo-50/70 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 shrink-0 text-indigo-600" }), isLoading ? (_jsx("div", { className: "h-8 flex-1 animate-pulse rounded-md bg-indigo-100" })) : (_jsx("audio", { controls: true, className: "h-9 w-full", src: src, preload: "metadata", children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." }))] }));
26
26
  }
27
- function optionLetter(index) {
28
- return String.fromCharCode(65 + index);
29
- }
30
- function reviewOptionTone(isReviewMode, isCorrect, isSelected) {
31
- if (isReviewMode && isCorrect) {
32
- return { border: 'border-green-400 bg-green-50', badge: 'bg-green-500 text-white' };
33
- }
34
- if (isReviewMode && isSelected) {
35
- return { border: 'border-red-400 bg-red-50', badge: 'bg-red-500 text-white' };
36
- }
37
- if (isSelected) {
38
- return { border: 'border-blue-400 bg-blue-50', badge: 'bg-blue-500 text-white' };
39
- }
40
- return { border: 'border-gray-200 bg-white', badge: 'bg-gray-100 text-gray-600' };
41
- }
42
27
  export function ChooseTheCorrectAnswerGroupClient({ questionData, isReviewMode = false, userAnswers = [], onAnswerChange, }) {
43
28
  const items = questionData.items || [];
44
29
  const handleSelect = (itemIndex, optionId) => {
@@ -50,20 +35,14 @@ export function ChooseTheCorrectAnswerGroupClient({ questionData, isReviewMode =
50
35
  next[itemIndex] = optionId;
51
36
  onAnswerChange(next);
52
37
  };
53
- return (_jsxs("div", { className: "space-y-4", children: [questionData.instruction && (_jsx("div", { className: "rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800", children: questionData.instruction })), toChooseAnswerGroupImageUrls(questionData.audioUrl).map((url, audioIndex) => (_jsx(ClientAudio, { url: url }, `group-audio-${audioIndex}`))), toChooseAnswerGroupImageUrls(questionData.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toChooseAnswerGroupImageUrls(questionData.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh nhóm (${imageIndex + 1})` }, `group-image-${imageIndex}`))) })), questionData.passage && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white p-5 shadow-xs", children: [_jsxs("div", { className: "mb-2.5 flex items-center gap-2 text-xs font-bold uppercase tracking-wider text-indigo-700", children: [_jsx(BookOpen, { className: "h-4 w-4" }), _jsx("span", { children: "B\u00E0i \u0111\u1ECDc (Reading Passage)" })] }), _jsx("div", { className: "overflow-x-auto", children: _jsx(RichTextHtml, { html: questionData.passage, className: "text-gray-800 leading-relaxed" }) })] })), items.map((item, itemIndex) => {
38
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(KidInstruction, { children: questionData.instruction }), toChooseAnswerGroupImageUrls(questionData.audioUrl).map((url, audioIndex) => (_jsx(ClientAudio, { url: url }, `group-audio-${audioIndex}`))), toChooseAnswerGroupImageUrls(questionData.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toChooseAnswerGroupImageUrls(questionData.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh nhóm (${imageIndex + 1})` }, `group-image-${imageIndex}`))) })), questionData.passage && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white p-5 shadow-xs", children: [_jsxs("div", { className: "mb-2.5 flex items-center gap-2 text-xs font-bold uppercase tracking-wider text-indigo-700", children: [_jsx(BookOpen, { className: "h-4 w-4" }), _jsx("span", { children: "B\u00E0i \u0111\u1ECDc (Reading Passage)" })] }), _jsx("div", { className: "overflow-x-auto", children: _jsx(RichTextHtml, { html: questionData.passage, className: "text-gray-800 leading-relaxed" }) })] })), items.map((item, itemIndex) => {
54
39
  const selectedId = userAnswers[itemIndex];
55
40
  const canSelect = !isReviewMode && !item.isExample && Boolean(onAnswerChange);
56
- return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-3 p-3", children: [!isRichTextEmpty(item.question) && (_jsx(RichTextHtml, { html: item.question, className: "text-sm font-semibold leading-relaxed text-gray-800" })), toChooseAnswerGroupImageUrls(item.audioUrl).map((url, audioIndex) => (_jsx(ClientAudio, { url: url }, `${item.questionNumber}-audio-${audioIndex}`))), toChooseAnswerGroupImageUrls(item.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toChooseAnswerGroupImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1} (${imageIndex + 1})` }, `${item.questionNumber}-${imageIndex}`))) })), item.optionType === 'image' ? (_jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: item.options.map((option, optionIndex) => {
57
- const isCorrect = option.id === item.correctAnswer;
58
- const isSelected = option.id === selectedId;
59
- const tone = reviewOptionTone(isReviewMode, isCorrect, isSelected);
60
- const optionImageSrc = getChooseAnswerGroupOptionImageSrc(option);
61
- return (_jsxs("button", { type: "button", disabled: !canSelect, onClick: () => handleSelect(itemIndex, option.id), className: cn('flex flex-col items-center gap-2 rounded-lg border-2 px-3 py-2.5 text-sm text-left', tone.border, canSelect && 'cursor-pointer hover:border-blue-300', !canSelect && 'cursor-default'), children: [_jsx("span", { className: cn('flex h-7 w-7 items-center justify-center rounded-md text-xs font-bold', tone.badge), children: optionLetter(optionIndex) }), optionImageSrc ? (_jsx(ClientImageItem, { url: optionImageSrc, alt: `Đáp án ${optionLetter(optionIndex)}` })) : (_jsx("span", { className: "text-xs text-gray-500", children: `Đáp án ${optionLetter(optionIndex)}` })), isReviewMode && isCorrect ? (_jsx(Check, { className: "h-4 w-4 text-green-600" })) : isReviewMode && isSelected ? (_jsx(X, { className: "h-4 w-4 text-red-600" })) : null] }, option.id || optionIndex));
62
- }) })) : (_jsx("div", { className: "space-y-2", children: item.options.map((option, optionIndex) => {
63
- const isCorrect = option.id === item.correctAnswer;
64
- const isSelected = option.id === selectedId;
65
- const tone = reviewOptionTone(isReviewMode, isCorrect, isSelected);
66
- return (_jsxs("button", { type: "button", disabled: !canSelect, onClick: () => handleSelect(itemIndex, option.id), className: cn('flex w-full items-center gap-3 rounded-lg border-2 px-3 py-2.5 text-sm text-left', tone.border, canSelect && 'cursor-pointer hover:border-blue-300', !canSelect && 'cursor-default'), children: [_jsx("span", { className: cn('flex h-7 w-7 items-center justify-center rounded-md text-xs font-bold', tone.badge), children: optionLetter(optionIndex) }), isRichTextEmpty(option.text) ? (_jsx("span", { className: "flex-1 text-gray-800", children: `Đáp án ${optionLetter(optionIndex)}` })) : (_jsx(RichTextHtml, { html: option.text, className: "flex-1 text-gray-800" })), isReviewMode && isCorrect ? (_jsx(Check, { className: "h-4 w-4 text-green-600" })) : isReviewMode && isSelected ? (_jsx(X, { className: "h-4 w-4 text-red-600" })) : null] }, option.id || optionIndex));
67
- }) }))] })] }, `${item.questionNumber}-${itemIndex}`));
41
+ return (_jsxs(KidQuestionCard, { title: `Câu ${item.questionNumber || itemIndex + 1}`, points: item.points || 0, isExample: item.isExample, children: [!isRichTextEmpty(item.question) && (_jsx(KidPrompt, { children: _jsx(RichTextHtml, { html: item.question, className: "text-sm font-semibold leading-relaxed text-gray-800" }) })), toChooseAnswerGroupImageUrls(item.audioUrl).map((url, audioIndex) => (_jsx(ClientAudio, { url: url }, `${item.questionNumber}-audio-${audioIndex}`))), toChooseAnswerGroupImageUrls(item.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toChooseAnswerGroupImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1} (${imageIndex + 1})` }, `${item.questionNumber}-${imageIndex}`))) })), _jsx("div", { className: "space-y-1.5", children: item.options.map((option, optionIndex) => {
42
+ const isCorrect = option.id === item.correctAnswer;
43
+ const isSelected = option.id === selectedId;
44
+ const optionImageSrc = getChooseAnswerGroupOptionImageSrc(option);
45
+ return (_jsx(KidChoiceRow, { letter: optionLetter(optionIndex), selected: isSelected, isReviewMode: isReviewMode, isCorrect: isReviewMode && isCorrect, disabled: !canSelect, onClick: canSelect ? () => handleSelect(itemIndex, option.id) : undefined, children: item.optionType === 'image' ? (optionImageSrc ? (_jsx(ClientImageItem, { url: optionImageSrc, alt: `Đáp án ${optionLetter(optionIndex)}` })) : (`Đáp án ${optionLetter(optionIndex)}`)) : isRichTextEmpty(option.text) ? (`Đáp án ${optionLetter(optionIndex)}`) : (_jsx(RichTextHtml, { html: option.text })) }, option.id || optionIndex));
46
+ }) })] }, `${item.questionNumber}-${itemIndex}`));
68
47
  }), 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] }))] }));
69
48
  }
@@ -1,7 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Check, Lightbulb, Star, X } from 'lucide-react';
3
+ import { Check, Lightbulb, X } from 'lucide-react';
4
4
  import { cn } from '../../../../shared/lib/utils';
5
+ import { KidInstruction, KidQuestionCard } from '../../_shared/components/compact';
5
6
  function emptyAnswer() {
6
7
  return { optionId: '', text: '' };
7
8
  }
@@ -35,7 +36,7 @@ export function ChooseThenAnswerGroupClient({ questionData, isReviewMode = false
35
36
  const items = questionData.items || [];
36
37
  const hasAttempt = Boolean(itemResults?.length) ||
37
38
  userAnswers.some((answer) => Boolean(answer?.optionId || answer?.text?.trim()));
38
- return (_jsxs("div", { className: "space-y-2", children: [(questionData.title || questionData.instruction) && (_jsxs("div", { className: "rounded-lg border border-indigo-100 bg-indigo-50/80 px-3 py-2", children: [questionData.title && (_jsx("p", { className: "text-[11px] font-semibold text-indigo-500", children: questionData.title })), questionData.instruction && (_jsx("p", { className: "text-sm font-medium text-indigo-900", children: questionData.instruction }))] })), items.map((item, itemIndex) => {
39
+ return (_jsxs("div", { className: "space-y-2", children: [(questionData.title || questionData.instruction) && (_jsxs(KidInstruction, { children: [questionData.title ? (_jsx("p", { className: "mb-1 text-[11px] font-semibold text-indigo-500", children: questionData.title })) : null, questionData.instruction] })), items.map((item, itemIndex) => {
39
40
  const stored = userAnswers[itemIndex] || emptyAnswer();
40
41
  const current = item.isExample
41
42
  ? { optionId: item.optionId, text: stored.text }
@@ -61,7 +62,7 @@ export function ChooseThenAnswerGroupClient({ questionData, isReviewMode = false
61
62
  text: next.text ?? current.text,
62
63
  });
63
64
  };
64
- return (_jsx("div", { className: "rounded-xl border border-slate-200 bg-white p-3", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: cn('mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-bold text-white', badgeClass({
65
+ return (_jsx(KidQuestionCard, { title: `Câu ${item.questionNumber || itemIndex + 1}`, points: points, isExample: item.isExample, children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: cn('mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-bold text-white', badgeClass({
65
66
  isExample: item.isExample,
66
67
  isReviewMode,
67
68
  hasAttempt,
@@ -69,7 +70,7 @@ export function ChooseThenAnswerGroupClient({ questionData, isReviewMode = false
69
70
  partialScore: result?.partialScore,
70
71
  mappingCorrect,
71
72
  hasText: hasStudentText,
72
- })), children: item.questionNumber || itemIndex + 1 }), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold uppercase text-amber-800", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] })), _jsxs("p", { className: "flex flex-wrap items-center gap-x-1.5 gap-y-1.5 text-[15px] leading-7 text-slate-800", children: [_jsx("span", { children: parts[0] || '' }), _jsx("span", { className: "inline-flex items-center gap-1", children: item.options.map((option, optionIndex) => {
73
+ })), children: item.questionNumber || itemIndex + 1 }), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("p", { className: "flex flex-wrap items-center gap-x-1.5 gap-y-1.5 text-sm font-semibold leading-7 text-slate-800", children: [_jsx("span", { children: parts[0] || '' }), _jsx("span", { className: "inline-flex items-center gap-1", children: item.options.map((option, optionIndex) => {
73
74
  const selected = current.optionId === option.id;
74
75
  const isKey = option.id === expectedId;
75
76
  const pickedCorrect = showGradedReview && selected && isKey;
@@ -78,9 +79,9 @@ export function ChooseThenAnswerGroupClient({ questionData, isReviewMode = false
78
79
  const officialCorrect = showAnswerKey && isKey;
79
80
  return (_jsxs("span", { className: "inline-flex items-center gap-1", children: [optionIndex > 0 && (_jsx("span", { className: "text-sm font-medium text-slate-300", children: "|" })), _jsxs("button", { type: "button", disabled: !canEdit || item.isExample, onClick: () => setAnswer({ optionId: option.id }), className: cn('inline-flex min-w-[3.5rem] items-center justify-center gap-1 rounded-full border px-2.5 py-1 text-sm font-semibold transition-colors', !showReview &&
80
81
  selected &&
81
- 'border-indigo-600 bg-indigo-600 text-white shadow-sm', !showReview &&
82
+ 'border-blue-500 bg-blue-500 text-white shadow-sm ring-2 ring-blue-100', !showReview &&
82
83
  !selected &&
83
- 'border-slate-200 bg-white text-slate-800 hover:border-indigo-300 hover:bg-indigo-50', showAnswerKey &&
84
+ 'border-gray-200 bg-white text-slate-800 hover:border-blue-300 hover:bg-blue-50', showAnswerKey &&
84
85
  !isKey &&
85
86
  'border-slate-200 bg-white text-slate-800', officialCorrect &&
86
87
  'border-2 border-emerald-500 bg-white text-slate-800', pickedCorrect && 'border-emerald-600 bg-emerald-600 text-white', pickedWrong && 'border-red-500 bg-red-500 text-white', keyOnly && 'border-emerald-500 bg-white text-emerald-800', showGradedReview &&
@@ -93,6 +94,6 @@ export function ChooseThenAnswerGroupClient({ questionData, isReviewMode = false
93
94
  ? 'bg-emerald-50 text-emerald-700'
94
95
  : 'bg-red-50 text-red-700'), children: ["Ch\u1ECDn ", mappingCorrect ? halfLabel : '0'] }), result?.aiCorrect !== undefined ? (_jsxs("span", { className: cn('rounded-full px-2 py-0.5 text-[11px] font-semibold', result.aiCorrect
95
96
  ? 'bg-emerald-50 text-emerald-700'
96
- : 'bg-red-50 text-red-700'), children: ["Vi\u1EBFt ", result.aiCorrect ? halfLabel : '0'] })) : null] })) : null, canEdit || hasStudentText || (isReviewMode && hasAttempt) ? (_jsxs("label", { className: "block space-y-1", children: [_jsx("span", { className: "text-xs font-medium text-slate-500", children: "Your answer" }), isReviewMode && !hasStudentText ? (_jsx("p", { className: "rounded-lg border border-dashed border-slate-200 bg-slate-50 px-3 py-2 text-sm italic text-slate-400", children: "Ch\u01B0a vi\u1EBFt" })) : (_jsx("textarea", { value: current.text, disabled: !canEdit || item.isExample, onChange: (event) => setAnswer({ text: event.target.value }), placeholder: "Write your answer here...", rows: 2, className: "min-h-0 w-full resize-none rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm leading-relaxed text-slate-800 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 disabled:bg-slate-50" }))] })) : null, isReviewMode && sampleAnswers.length > 0 ? (_jsxs("p", { className: "text-xs text-slate-500", children: [_jsx("span", { className: "font-semibold", children: "Sample:" }), " ", sampleAnswers.join(' · ')] })) : null] })] }) }, `${item.questionNumber}-${itemIndex}`));
97
+ : 'bg-red-50 text-red-700'), children: ["Vi\u1EBFt ", result.aiCorrect ? halfLabel : '0'] })) : null] })) : null, canEdit || hasStudentText || (isReviewMode && hasAttempt) ? (_jsxs("label", { className: "block space-y-1", children: [_jsx("span", { className: "text-xs font-medium text-slate-500", children: "C\u00E2u tr\u1EA3 l\u1EDDi c\u1EE7a b\u1EA1n" }), isReviewMode && !hasStudentText ? (_jsx("p", { className: "rounded-lg border border-dashed border-slate-200 bg-slate-50 px-3 py-2 text-sm italic text-slate-400", children: "Ch\u01B0a vi\u1EBFt" })) : (_jsx("textarea", { value: current.text, disabled: !canEdit || item.isExample, onChange: (event) => setAnswer({ text: event.target.value }), placeholder: "Vi\u1EBFt c\u00E2u tr\u1EA3 l\u1EDDi t\u1EA1i \u0111\u00E2y...", rows: 2, className: "min-h-0 w-full resize-none rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm leading-relaxed text-slate-800 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-100 disabled:bg-slate-50" }))] })) : null, isReviewMode && sampleAnswers.length > 0 ? (_jsxs("p", { className: "text-xs text-slate-500", children: [_jsx("span", { className: "font-semibold", children: "M\u1EABu:" }), " ", sampleAnswers.join(' · ')] })) : null] })] }) }, `${item.questionNumber}-${itemIndex}`));
97
98
  }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-100 bg-amber-50 px-3 py-2 text-sm text-amber-800", children: [_jsx(Lightbulb, { className: "mt-0.5 h-4 w-4 flex-shrink-0" }), questionData.explanation] }))] }));
98
99
  }