@tinyweb_dev/oe-exam-sdk 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/components/exams/take/ExamTakingPageContainer.js +6 -0
  2. package/dist/components/exams/take/components/QuestionRenderer.js +7 -7
  3. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js +24 -10
  4. package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.js +1 -1
  5. package/dist/components/exams/take/components/question-renderers/MoversFillInBlankGroupRenderer.js +2 -1
  6. package/dist/components/exams/take/components/question-renderers/MoversMatchWordToPictureGroupRenderer.d.ts +14 -0
  7. package/dist/components/exams/take/components/question-renderers/MoversMatchWordToPictureGroupRenderer.js +36 -0
  8. package/dist/components/exams/take/components/question-renderers/MoversMatchingWithLinesGroupRenderer.js +4 -0
  9. package/dist/components/exams/take/components/question-renderers/index.d.ts +1 -0
  10. package/dist/components/exams/take/components/question-renderers/index.js +1 -0
  11. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.js +12 -2
  12. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.js +2 -1
  13. package/dist/components/exams/take/utils/answer-transformers.js +17 -1
  14. package/dist/components/exams/take/utils/cross-out-word-group.transformer.js +4 -6
  15. package/dist/components/exams/take/utils/question-transformers.d.ts +32 -3
  16. package/dist/components/exams/take/utils/question-transformers.js +51 -2
  17. package/dist/components/questions/_shared/components/compact/KidChoiceRow.d.ts +12 -0
  18. package/dist/components/questions/_shared/components/compact/KidChoiceRow.js +33 -0
  19. package/dist/components/questions/_shared/components/compact/KidInstruction.d.ts +5 -0
  20. package/dist/components/questions/_shared/components/compact/KidInstruction.js +9 -0
  21. package/dist/components/questions/_shared/components/compact/KidPrompt.d.ts +5 -0
  22. package/dist/components/questions/_shared/components/compact/KidPrompt.js +9 -0
  23. package/dist/components/questions/_shared/components/compact/KidQuestionCard.d.ts +12 -0
  24. package/dist/components/questions/_shared/components/compact/KidQuestionCard.js +7 -0
  25. package/dist/components/questions/_shared/components/compact/index.d.ts +4 -0
  26. package/dist/components/questions/_shared/components/compact/index.js +4 -0
  27. package/dist/components/questions/_shared/types/crossword-puzzle.type.d.ts +5 -1
  28. package/dist/components/questions/_shared/types/match-word-to-picture-group.type.d.ts +1 -0
  29. package/dist/components/questions/_shared/types/match-word-to-picture-group.type.js +14 -0
  30. package/dist/components/questions/_shared/types/matching-with-lines-group.type.d.ts +2 -2
  31. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +17 -0
  32. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -0
  33. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +18 -19
  34. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +8 -7
  35. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +5 -4
  36. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +17 -11
  37. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +15 -42
  38. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +15 -36
  39. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -30
  40. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +8 -7
  41. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +50 -32
  42. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +24 -44
  43. package/dist/components/questions/types/cross-out-word-group/index.d.ts +1 -0
  44. package/dist/components/questions/types/cross-out-word-group/index.js +1 -0
  45. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +9 -9
  46. package/dist/components/questions/types/cross-out-word-group/tokenize.d.ts +2 -0
  47. package/dist/components/questions/types/cross-out-word-group/tokenize.js +11 -0
  48. package/dist/components/questions/types/cross-out-word-group/transform.js +2 -4
  49. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.d.ts +2 -1
  50. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +8 -6
  51. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +68 -11
  52. package/dist/components/questions/types/crossword-puzzle/transform.js +59 -30
  53. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +26 -31
  54. package/dist/components/questions/types/fill-in-blank-group/FillInBlankAnswerKey.d.ts +6 -0
  55. package/dist/components/questions/types/fill-in-blank-group/FillInBlankAnswerKey.js +17 -0
  56. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupClient.js +32 -28
  57. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +109 -26
  58. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupMediaField.js +21 -11
  59. package/dist/components/questions/types/fill-in-blank-group/blank-utils.d.ts +1 -0
  60. package/dist/components/questions/types/fill-in-blank-group/blank-utils.js +4 -1
  61. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +32 -31
  62. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +71 -28
  63. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +6 -2
  64. package/dist/components/questions/types/find-words-in-matrix/transform.js +5 -0
  65. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +4 -3
  66. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +11 -30
  67. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +7 -6
  68. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +2 -1
  69. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +14 -13
  70. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +4 -3
  71. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +4 -3
  72. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +11 -10
  73. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +4 -3
  74. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +13 -11
  75. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +17 -14
  76. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +16 -12
  77. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureGroupClient.js +14 -14
  78. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +24 -35
  79. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +16 -3
  80. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupImageField.d.ts +2 -1
  81. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupImageField.js +25 -6
  82. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesBoard.js +1 -1
  83. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupClient.js +22 -10
  84. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +60 -18
  85. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesImageField.d.ts +2 -1
  86. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesImageField.js +24 -5
  87. package/dist/components/questions/types/matching-with-lines-group/map-matching-with-lines-group-data.js +8 -4
  88. package/dist/components/questions/types/matching-with-lines-group/transform.js +5 -4
  89. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +8 -26
  90. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +2 -1
  91. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +6 -5
  92. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +2 -1
  93. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +5 -4
  94. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +1 -1
  95. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +54 -11
  96. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +11 -7
  97. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +8 -2
  98. package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +1 -0
  99. package/dist/components/questions/types/spontaneous-qa-group/flatten.js +4 -2
  100. package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +5 -3
  101. package/dist/components/questions/types/spontaneous-qa-group/register.js +5 -2
  102. package/dist/components/questions/types/spontaneous-qa-group/transform.js +6 -2
  103. package/dist/components/questions/types/true-false/TrueFalseClient.js +5 -54
  104. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +21 -23
  105. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +8 -17
  106. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +76 -81
  107. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +17 -16
  108. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +31 -36
  109. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +15 -14
  110. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +9 -8
  111. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +16 -13
  112. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +1 -4
  113. package/dist/components/questions/viewer/FillInBlankGroupViewer.d.ts +6 -2
  114. package/dist/components/questions/viewer/FillInBlankGroupViewer.js +13 -7
  115. package/dist/components/questions/viewer/QuestionViewer.js +2 -2
  116. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +7 -1
  117. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +14 -2
  118. package/dist/components/results/renderers/review-question-body-movers.js +7 -3
  119. package/dist/components/results/review-data.js +20 -1
  120. package/dist/shared/lib/utils/question-reverse-transform.js +48 -7
  121. package/dist/shared/types/questions/crossword-puzzle.d.ts +1 -0
  122. package/dist/shared/types/questions/find-words-in-matrix.d.ts +1 -0
  123. package/dist/shared/types/questions/matching-with-lines-group.d.ts +2 -2
  124. package/package.json +1 -1
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare function KidInstruction({ children, className, }: {
3
+ children: ReactNode;
4
+ className?: string;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from '../../../../../shared/lib/utils';
4
+ export function KidInstruction({ children, className, }) {
5
+ if (!children) {
6
+ return null;
7
+ }
8
+ return (_jsx("div", { className: cn('rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800', className), children: children }));
9
+ }
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare function KidPrompt({ children, className, }: {
3
+ children: ReactNode;
4
+ className?: string;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from '../../../../../shared/lib/utils';
4
+ export function KidPrompt({ children, className, }) {
5
+ if (!children) {
6
+ return null;
7
+ }
8
+ return (_jsx("div", { className: cn('rounded-md border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 px-3 py-2 text-sm font-semibold text-gray-800', className), children: children }));
9
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare function KidQuestionCard({ icon, title, points, isExample, review, headerExtra, children, className, contentClassName, }: {
3
+ icon?: ReactNode;
4
+ title: string;
5
+ points?: number;
6
+ isExample?: boolean;
7
+ review?: 'correct' | 'wrong' | null;
8
+ headerExtra?: ReactNode;
9
+ children: ReactNode;
10
+ className?: string;
11
+ contentClassName?: string;
12
+ }): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Check, CircleHelp, Star, X } from 'lucide-react';
4
+ import { cn } from '../../../../../shared/lib/utils';
5
+ export function KidQuestionCard({ icon, title, points, isExample = false, review, headerExtra, children, className, contentClassName, }) {
6
+ return (_jsxs("div", { className: cn('overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm', className), children: [_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: icon ?? _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "truncate text-sm font-medium text-gray-700", children: title }), isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "C\u00E2u v\u00ED d\u1EE5"] }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [headerExtra, typeof points === 'number' && (_jsxs("span", { className: "text-xs text-gray-500", children: [points, " \u0111i\u1EC3m"] })), review === 'correct' && (_jsxs("div", { className: "flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-semibold text-green-700", children: [_jsx(Check, { className: "h-3 w-3" }), "\u0110\u00FAng"] })), review === 'wrong' && (_jsxs("div", { className: "flex items-center gap-1 rounded-full bg-red-100 px-2 py-0.5 text-xs font-semibold text-red-700", children: [_jsx(X, { className: "h-3 w-3" }), "Sai"] }))] })] }), _jsx("div", { className: cn('space-y-3 p-3', contentClassName), children: children })] }));
7
+ }
@@ -4,3 +4,7 @@ export { CompactPreviewButton } from './CompactPreviewButton';
4
4
  export { CompactCreatorHeader } from './CompactCreatorHeader';
5
5
  export { CompactFieldCard } from './CompactFieldCard';
6
6
  export { CompactExplanationToggle } from './CompactExplanationToggle';
7
+ export { KidInstruction } from './KidInstruction';
8
+ export { KidPrompt } from './KidPrompt';
9
+ export { KidQuestionCard } from './KidQuestionCard';
10
+ export { KidChoiceRow, optionLetter } from './KidChoiceRow';
@@ -4,3 +4,7 @@ export { CompactPreviewButton } from './CompactPreviewButton';
4
4
  export { CompactCreatorHeader } from './CompactCreatorHeader';
5
5
  export { CompactFieldCard } from './CompactFieldCard';
6
6
  export { CompactExplanationToggle } from './CompactExplanationToggle';
7
+ export { KidInstruction } from './KidInstruction';
8
+ export { KidPrompt } from './KidPrompt';
9
+ export { KidQuestionCard } from './KidQuestionCard';
10
+ export { KidChoiceRow, optionLetter } from './KidChoiceRow';
@@ -1,13 +1,17 @@
1
- import type { CrosswordClue, CrosswordPuzzleContent, CrosswordPuzzleCorrectAnswer } from '../../../../shared/types/questions/crossword-puzzle';
1
+ import type { CrosswordClue, CrosswordPuzzleContent, CrosswordPuzzleCorrectAnswer, CrosswordSubtype } from '../../../../shared/types/questions/crossword-puzzle';
2
2
  export interface CrosswordClueFormItem extends CrosswordClue {
3
3
  id: string;
4
4
  answer: string;
5
5
  direction: 'across' | 'down';
6
6
  }
7
7
  export interface CrosswordPuzzleFormState {
8
+ type?: CrosswordSubtype;
8
9
  title?: string;
10
+ instruction?: string;
9
11
  rows: number;
10
12
  cols: number;
13
+ markedColumn?: number;
14
+ hiddenWord?: string;
11
15
  clues: CrosswordClueFormItem[];
12
16
  caseSensitive: boolean;
13
17
  points: number;
@@ -3,6 +3,7 @@ export declare const MATCH_WORD_TO_PICTURE_GROUP_DEFAULT_INSTRUCTION = "Look and
3
3
  export declare const MATCH_WORD_TO_PICTURE_ITEM_TYPE: "MATCH_WORD_TO_PICTURE";
4
4
  export declare function toMatchWordToPictureImageUrls(value?: unknown): string[];
5
5
  export declare function fromMatchWordToPictureImageUrls(value?: unknown): string | string[] | undefined;
6
+ export declare function toMatchWordToPictureStorageKey(value: string): string;
6
7
  export interface MatchWordToPictureGroupItemData {
7
8
  imageUrl?: string | string[];
8
9
  correctAnswer: string;
@@ -19,3 +19,17 @@ export function fromMatchWordToPictureImageUrls(value) {
19
19
  }
20
20
  return urls.length === 1 ? urls[0] : urls;
21
21
  }
22
+ export function toMatchWordToPictureStorageKey(value) {
23
+ const trimmed = value.trim();
24
+ if (!trimmed)
25
+ return '';
26
+ if (trimmed.startsWith('http://')
27
+ || trimmed.startsWith('https://')
28
+ || trimmed.startsWith('blob:')
29
+ || trimmed.startsWith('data:')
30
+ || trimmed.startsWith('/images/')
31
+ || trimmed.startsWith('/_next/')) {
32
+ return trimmed;
33
+ }
34
+ return trimmed.replace(/^\/+/, '');
35
+ }
@@ -18,9 +18,9 @@ export interface MatchingWithLinesGroupData {
18
18
  instruction: string;
19
19
  /** Shared reading passage (HTML) above the matching board. */
20
20
  passage?: string;
21
- audioUrl?: string;
21
+ audioUrl?: string | string[];
22
22
  /** Shared cover / portrait image (not a left/right match item). */
23
- imageUrl?: string;
23
+ imageUrl?: string | string[];
24
24
  rightItems: MatchingWithLinesRightItem[];
25
25
  items: MatchingWithLinesGroupItemData[];
26
26
  explanation?: string;
@@ -6,6 +6,7 @@ export interface SpontaneousQaGroupQuestionData {
6
6
  expectedAnswers: string[];
7
7
  points: number;
8
8
  questionNumber: number;
9
+ isExample?: boolean;
9
10
  }
10
11
  export interface SpontaneousQaGroupPartData {
11
12
  partNumber: number;
@@ -31,7 +32,23 @@ export interface SpontaneousQaGroupCreatorProps {
31
32
  getFormData?: () => SpontaneousQaGroupData;
32
33
  } | null>;
33
34
  }
35
+ export interface SpontaneousQaGroupStudentAnswer {
36
+ audioKeys: Array<string | null>;
37
+ }
38
+ export interface SpontaneousQaGroupGradeDetail {
39
+ itemId: string;
40
+ isCorrect?: boolean;
41
+ userAnswer?: unknown;
42
+ correctAnswer?: unknown;
43
+ partialScore?: number;
44
+ feedback?: string;
45
+ }
34
46
  export interface SpontaneousQaGroupClientProps {
35
47
  questionData: SpontaneousQaGroupData;
36
48
  isReviewMode?: boolean;
49
+ userAnswer?: SpontaneousQaGroupStudentAnswer;
50
+ onItemRecorded?: (flattenIndex: number, blob: Blob) => void;
51
+ uploadingIndex?: number | null;
52
+ gradeDetails?: SpontaneousQaGroupGradeDetail[];
53
+ disabled?: boolean;
37
54
  }
@@ -26,6 +26,7 @@ const IMAGE_BASED_TYPES = [
26
26
  'IMAGE_OBJECT_MATCHING',
27
27
  'FILL_IN_BLANK',
28
28
  'MATCH_WORD_TO_PICTURE',
29
+ 'MATCH_WORD_TO_PICTURE_GROUP',
29
30
  'MATCHING_WITH_LINES_GROUP',
30
31
  ];
31
32
  // Passage-based question types
@@ -1,11 +1,10 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Card, CardContent } from '../../../../components/ui/card';
4
3
  import { Input } from '../../../../components/ui/input';
5
- import { Label } from '../../../../components/ui/label';
6
- import { CheckCircle2, XCircle, BookOpen } from 'lucide-react';
4
+ import { CheckCircle2, XCircle, PenLine } from 'lucide-react';
7
5
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
8
6
  import { toAnswerTheQuestionImageUrls, } from '../../_shared/types/answer-the-question.type';
7
+ import { KidPrompt, KidQuestionCard } from '../../_shared/components/compact';
9
8
  import { useState } from 'react';
10
9
  function isDisplayableMediaUrl(value) {
11
10
  return (value.startsWith('http://')
@@ -33,7 +32,7 @@ export function AnswerTheQuestionClient({ questionData, onSubmit, isReviewMode =
33
32
  const [answer, setAnswer] = useState(userAnswer || '');
34
33
  const imageUrls = toAnswerTheQuestionImageUrls(questionData.imageUrl);
35
34
  if (!questionData.question && imageUrls.length === 0) {
36
- return (_jsx(Card, { children: _jsx(CardContent, { className: "p-3", children: _jsx("p", { className: "text-gray-500", children: "Kh\u00F4ng c\u00F3 d\u1EEF li\u1EC7u c\u00E2u h\u1ECFi" }) }) }));
35
+ return (_jsx(KidQuestionCard, { icon: _jsx(PenLine, { className: "h-3.5 w-3.5" }), title: "Tr\u1EA3 l\u1EDDi c\u00E2u h\u1ECFi", children: _jsx("p", { className: "text-sm text-gray-500", children: "Kh\u00F4ng c\u00F3 d\u1EEF li\u1EC7u c\u00E2u h\u1ECFi" }) }));
37
36
  }
38
37
  const isExample = questionData.isExample || false;
39
38
  const correctAnswer = questionData.correctAnswer || '';
@@ -43,19 +42,19 @@ export function AnswerTheQuestionClient({ questionData, onSubmit, isReviewMode =
43
42
  const isCorrect = isReviewMode && userAnswer !== undefined
44
43
  ? userAnswer.trim().toLowerCase() === correctAnswer.trim().toLowerCase()
45
44
  : undefined;
46
- return (_jsx(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: _jsxs(CardContent, { className: "p-3 space-y-3", children: [(questionData.title || questionData.groupContent) && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-gradient-to-r from-indigo-50 to-blue-50 p-4 space-y-2", children: [questionData.title && (_jsx("h3", { className: "text-base font-bold text-indigo-900", children: questionData.title })), questionData.groupContent && (_jsx("p", { className: "text-sm text-indigo-700 leading-relaxed whitespace-pre-wrap", children: questionData.groupContent }))] })), imageUrls.length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: imageUrls.map((url, imageIndex) => (_jsx("div", { className: "relative max-w-lg overflow-hidden rounded-xl border border-gray-200 bg-gray-50 shadow-sm", children: _jsx(MediaPreview, { src: url, alt: `Question image ${imageIndex + 1}` }) }, `atq-image-${imageIndex}`))) })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold text-amber-700 uppercase tracking-wide", children: "Example" })] })), questionData.question ? (_jsx(Label, { className: "text-base font-semibold text-gray-800 mb-3 block", children: questionData.question })) : null, questionData.suggestedAnswers && questionData.suggestedAnswers.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mt-2 mb-1", children: questionData.suggestedAnswers.map((suggestion, index) => (_jsx("button", { type: "button", disabled: isReviewMode || isExample, onClick: () => {
47
- if (!isReviewMode && !isExample) {
48
- setAnswer(suggestion);
49
- onSubmit?.(suggestion);
50
- }
51
- }, className: `inline-flex items-center rounded-full border px-3 py-1 text-sm font-medium transition-colors ${answer === suggestion
52
- ? 'border-teal-400 bg-teal-50 text-teal-700 ring-1 ring-teal-200'
53
- : 'border-gray-200 bg-white text-gray-600 hover:border-teal-300 hover:bg-teal-50/50 hover:text-teal-600'} ${isReviewMode || isExample ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'}`, children: suggestion }, index))) })), _jsxs("div", { className: "flex items-center gap-3 mt-3", children: [questionData.prefixText && (_jsx("span", { className: "text-base font-medium text-gray-700", children: questionData.prefixText })), _jsx(Input, { value: displayedAnswer, onChange: (e) => {
54
- setAnswer(e.target.value);
55
- onSubmit?.(e.target.value);
56
- }, placeholder: "Vi\u1EBFt \u0111\u00E1p \u00E1n...", disabled: isReviewMode || isExample, className: `max-w-xs border-2 text-lg font-medium ${isReviewMode
57
- ? isCorrect
58
- ? 'border-green-400 bg-green-50 text-green-700'
59
- : 'border-red-400 bg-red-50 text-red-700'
60
- : 'border-teal-300 focus:border-teal-500 focus:ring-teal-200'}` }), questionData.suffixText && (_jsx("span", { className: "text-base font-medium text-gray-700", children: questionData.suffixText })), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: correctAnswer })] })] })), autoFillCorrectAnswers && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-teal-50 border border-teal-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-teal-600" }), _jsxs("span", { className: "text-sm text-teal-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "text-sm font-semibold text-blue-800 mb-1", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] }) }));
45
+ return (_jsxs("div", { className: "space-y-3", children: [(questionData.title || questionData.groupContent) && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-gradient-to-r from-indigo-50 to-blue-50 p-4 space-y-2", children: [questionData.title && (_jsx("h3", { className: "text-base font-bold text-indigo-900", children: questionData.title })), questionData.groupContent && (_jsx("p", { className: "text-sm text-indigo-700 leading-relaxed whitespace-pre-wrap", children: questionData.groupContent }))] })), _jsxs(KidQuestionCard, { icon: _jsx(PenLine, { className: "h-3.5 w-3.5" }), title: "Tr\u1EA3 l\u1EDDi c\u00E2u h\u1ECFi", points: questionData.points, isExample: isExample, review: isReviewMode && isCorrect !== undefined ? (isCorrect ? 'correct' : 'wrong') : null, children: [imageUrls.length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: imageUrls.map((url, imageIndex) => (_jsx("div", { className: "relative max-w-lg overflow-hidden rounded-md border border-gray-200 bg-gray-50", children: _jsx(MediaPreview, { src: url, alt: `Question image ${imageIndex + 1}` }) }, `atq-image-${imageIndex}`))) })), questionData.question ? (_jsx(KidPrompt, { children: questionData.question })) : null, questionData.suggestedAnswers && questionData.suggestedAnswers.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mt-2 mb-1", children: questionData.suggestedAnswers.map((suggestion, index) => (_jsx("button", { type: "button", disabled: isReviewMode || isExample, onClick: () => {
46
+ if (!isReviewMode && !isExample) {
47
+ setAnswer(suggestion);
48
+ onSubmit?.(suggestion);
49
+ }
50
+ }, className: `inline-flex items-center rounded-full border px-3 py-1 text-sm font-medium transition-colors ${answer === suggestion
51
+ ? 'border-teal-400 bg-teal-50 text-teal-700 ring-1 ring-teal-200'
52
+ : 'border-gray-200 bg-white text-gray-600 hover:border-teal-300 hover:bg-teal-50/50 hover:text-teal-600'} ${isReviewMode || isExample ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'}`, children: suggestion }, index))) })), _jsxs("div", { className: "flex items-center gap-3 mt-1", children: [questionData.prefixText && (_jsx("span", { className: "text-sm font-medium text-gray-700", children: questionData.prefixText })), _jsx(Input, { value: displayedAnswer, onChange: (e) => {
53
+ setAnswer(e.target.value);
54
+ onSubmit?.(e.target.value);
55
+ }, placeholder: "Vi\u1EBFt \u0111\u00E1p \u00E1n...", disabled: isReviewMode || isExample, className: `max-w-xs border text-sm px-3 py-1.5 rounded-md font-medium ${isReviewMode
56
+ ? isCorrect
57
+ ? 'border-green-400 bg-green-50 text-green-800'
58
+ : 'border-red-400 bg-red-50 text-red-800'
59
+ : 'border-teal-300 focus:border-teal-500 focus:ring-teal-200'}` }), questionData.suffixText && (_jsx("span", { className: "text-sm font-medium text-gray-700", children: questionData.suffixText })), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: correctAnswer })] })] })), autoFillCorrectAnswers && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-teal-50 border border-teal-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-teal-600" }), _jsxs("span", { className: "text-sm text-teal-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: correctAnswer })] })] })), explanation && isReviewMode && (_jsxs("div", { className: "rounded-md border border-blue-200 bg-blue-50 px-3 py-2", children: [_jsx("p", { className: "text-sm font-semibold text-blue-800 mb-1", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] })] }));
61
60
  }
@@ -1,8 +1,9 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { BookOpen, CircleHelp, Lightbulb, Sparkles, Star, Volume2 } from 'lucide-react';
3
+ import { BookOpen, CircleHelp, Lightbulb, Sparkles, Volume2 } from 'lucide-react';
4
4
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
5
5
  import { toAnswerTheQuestionGroupImageUrls } from '../../_shared/types/answer-the-question-group.type';
6
+ import { KidInstruction, KidPrompt, KidQuestionCard } from '../../_shared/components/compact';
6
7
  function isAbsoluteMediaUrl(value) {
7
8
  return (value.startsWith('http://')
8
9
  || value.startsWith('https://')
@@ -41,15 +42,15 @@ function MediaPreview({ src, kind = 'image', alt }) {
41
42
  }
42
43
  export function AnswerTheQuestionGroupClient({ questionData, isReviewMode = false, userAnswers = [], onAnswerChange, }) {
43
44
  const items = questionData.items || [];
44
- 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 })), questionData.audioUrl && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-3 py-2", children: [_jsx(Volume2, { className: "h-4 w-4 text-slate-500" }), _jsx(MediaPreview, { src: questionData.audioUrl, kind: "audio", alt: "Group audio" })] })), toAnswerTheQuestionGroupImageUrls(questionData.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toAnswerTheQuestionGroupImageUrls(questionData.imageUrl).map((url, imageIndex) => (_jsx(MediaPreview, { src: url, alt: `Hình ảnh (${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: "prose prose-sm max-w-none leading-relaxed text-gray-800", dangerouslySetInnerHTML: { __html: questionData.passage } })] })), items.map((item, itemIndex) => {
45
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(KidInstruction, { children: questionData.instruction }), questionData.audioUrl && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-3 py-2", children: [_jsx(Volume2, { className: "h-4 w-4 text-slate-500" }), _jsx(MediaPreview, { src: questionData.audioUrl, kind: "audio", alt: "Group audio" })] })), toAnswerTheQuestionGroupImageUrls(questionData.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toAnswerTheQuestionGroupImageUrls(questionData.imageUrl).map((url, imageIndex) => (_jsx(MediaPreview, { src: url, alt: `Hình ảnh (${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: "prose prose-sm max-w-none leading-relaxed text-gray-800", dangerouslySetInnerHTML: { __html: questionData.passage } })] })), items.map((item, itemIndex) => {
45
46
  const userValue = userAnswers[itemIndex] || '';
46
47
  const expectedAnswers = Array.isArray(item.expectedAnswers)
47
48
  ? item.expectedAnswers.filter((answer) => answer.trim() !== '')
48
49
  : [];
49
- 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: "inline-flex items-center gap-1 rounded-full bg-violet-100 px-2 py-0.5 text-xs font-semibold text-violet-700", children: [_jsx(Sparkles, { className: "h-3 w-3" }), "AI"] })] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-3 p-3", children: [toAnswerTheQuestionGroupImageUrls(item.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toAnswerTheQuestionGroupImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(MediaPreview, { src: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1} (${imageIndex + 1})` }, `${item.questionNumber}-${imageIndex}`))) })), _jsx("p", { className: "text-sm font-semibold leading-relaxed text-gray-800", children: item.question || 'Câu hỏi chưa được nhập' }), isReviewMode || item.isExample || !onAnswerChange ? (userValue ? (_jsx("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-700", children: userValue })) : null) : (_jsx("textarea", { value: userValue, onChange: (event) => {
50
- const next = items.map((_, index) => userAnswers[index] || '');
51
- next[itemIndex] = event.target.value;
52
- onAnswerChange(next);
53
- }, placeholder: "Nh\u1EADp c\u00E2u tr\u1EA3 l\u1EDDi...", rows: 2, className: "w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-800 outline-none ring-blue-200 focus:border-blue-400 focus:ring-2" })), isReviewMode && expectedAnswers.length > 0 ? (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-gray-500", children: "\u0110\u00E1p \u00E1n" }), _jsx("ul", { className: "list-disc space-y-1 pl-5 text-sm text-gray-700", children: expectedAnswers.map((answer) => (_jsx("li", { children: answer }, answer))) })] })) : isReviewMode && !userValue ? (_jsx("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-500", children: "Ch\u01B0a c\u00F3 \u0111\u00E1p \u00E1n m\u1EABu" })) : null] })] }, `${item.questionNumber}-${itemIndex}`));
50
+ return (_jsxs(KidQuestionCard, { icon: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }), title: `Câu ${item.questionNumber || itemIndex + 1}`, points: item.points || 0, isExample: item.isExample, headerExtra: _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-violet-100 px-2 py-0.5 text-xs font-semibold text-violet-700", children: [_jsx(Sparkles, { className: "h-3 w-3" }), "AI"] }), children: [toAnswerTheQuestionGroupImageUrls(item.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toAnswerTheQuestionGroupImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(MediaPreview, { src: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1} (${imageIndex + 1})` }, `${item.questionNumber}-${imageIndex}`))) })), _jsx(KidPrompt, { children: item.question || 'Câu hỏi chưa được nhập' }), isReviewMode || item.isExample || !onAnswerChange ? (userValue ? (_jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 px-3 py-1.5 text-sm text-gray-700", children: userValue })) : null) : (_jsx("textarea", { value: userValue, onChange: (event) => {
51
+ const next = items.map((_, index) => userAnswers[index] || '');
52
+ next[itemIndex] = event.target.value;
53
+ onAnswerChange(next);
54
+ }, placeholder: "Nh\u1EADp c\u00E2u tr\u1EA3 l\u1EDDi...", rows: 2, className: "w-full rounded-md border border-slate-200 px-3 py-1.5 text-sm text-slate-800 outline-none ring-blue-200 focus:border-blue-400 focus:ring-2" })), isReviewMode && expectedAnswers.length > 0 ? (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-gray-500", children: "\u0110\u00E1p \u00E1n" }), _jsx("ul", { className: "list-disc space-y-1 pl-5 text-sm text-gray-700", children: expectedAnswers.map((answer) => (_jsx("li", { children: answer }, answer))) })] })) : isReviewMode && !userValue ? (_jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 px-3 py-1.5 text-sm text-gray-500", children: "Ch\u01B0a c\u00F3 \u0111\u00E1p \u00E1n" })) : null] }, `${item.questionNumber}-${itemIndex}`));
54
55
  }), 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] }))] }));
55
56
  }
@@ -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
  }