@tinyweb_dev/oe-exam-sdk 0.1.19 → 0.1.23

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 (87) hide show
  1. package/README.md +5 -4
  2. package/dist/api/result-review.types.d.ts +1 -0
  3. package/dist/components/exams/ExamPreviewDialog.d.ts +6 -7
  4. package/dist/components/exams/ExamPreviewDialog.js +1 -1
  5. package/dist/components/exams/ExamPreviewPartContent.d.ts +18 -0
  6. package/dist/components/exams/ExamPreviewPartContent.js +1 -0
  7. package/dist/components/exams/ExamPreviewSidebar.d.ts +17 -0
  8. package/dist/components/exams/ExamPreviewSidebar.js +1 -0
  9. package/dist/components/exams/exam-preview.types.d.ts +34 -0
  10. package/dist/components/exams/exam-preview.types.js +1 -0
  11. package/dist/components/exams/index.d.ts +1 -0
  12. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +1 -1
  13. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -1
  14. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -1
  15. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +1 -1
  16. package/dist/components/exams/take/exam-taking.utils.d.ts +13 -0
  17. package/dist/components/exams/take/exam-taking.utils.js +1 -1
  18. package/dist/components/exams/take/types.d.ts +3 -0
  19. package/dist/components/exams/take/utils/question-transformers.d.ts +13 -0
  20. package/dist/components/exams/take/utils/question-transformers.js +1 -1
  21. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +25 -0
  22. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +30 -0
  23. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +1 -0
  24. package/dist/components/questions/index.d.ts +1 -0
  25. package/dist/components/questions/index.js +1 -1
  26. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +2 -0
  27. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +1 -0
  28. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +23 -0
  29. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -0
  30. package/dist/components/questions/question-bank/difficulty.d.ts +15 -0
  31. package/dist/components/questions/question-bank/difficulty.js +1 -0
  32. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +5 -0
  33. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +1 -0
  34. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +5 -0
  35. package/dist/components/questions/question-bank/editor-adapters/index.js +1 -0
  36. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +6 -0
  37. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +1 -0
  38. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +1 -0
  39. package/dist/components/questions/question-bank/editor-adapters/types.js +1 -0
  40. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +8 -0
  41. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +1 -0
  42. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +5 -0
  43. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +1 -0
  44. package/dist/components/questions/question-bank/index.d.ts +4 -0
  45. package/dist/components/questions/question-bank/index.js +1 -0
  46. package/dist/components/questions/question-bank/types.d.ts +59 -0
  47. package/dist/components/questions/question-bank/types.js +1 -0
  48. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -1
  49. package/dist/components/questions/types/choose-the-correct-answer/index.d.ts +1 -0
  50. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -1
  51. package/dist/components/questions/types/choose-the-correct-answer/normalize.d.ts +27 -0
  52. package/dist/components/questions/types/choose-the-correct-answer/normalize.js +1 -0
  53. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -1
  54. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -1
  55. package/dist/components/questions/types/fill-in-blank/register.js +1 -1
  56. package/dist/components/questions/types/fill-in-blank/transform.js +1 -1
  57. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.d.ts +1 -1
  58. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  59. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -1
  60. package/dist/components/questions/viewer/QuestionViewer.js +1 -1
  61. package/dist/components/results/review-data.js +1 -1
  62. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.d.ts +2 -1
  63. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -1
  64. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -1
  65. package/dist/index.d.ts +3 -0
  66. package/dist/index.js +1 -1
  67. package/dist/mcp/config.d.ts +12 -0
  68. package/dist/mcp/config.js +1 -0
  69. package/dist/mcp/http-client.d.ts +6 -0
  70. package/dist/mcp/http-client.js +1 -0
  71. package/dist/mcp/index.d.ts +6 -0
  72. package/dist/mcp/index.js +2 -0
  73. package/dist/mcp/tools/_helpers.d.ts +4 -0
  74. package/dist/mcp/tools/_helpers.js +1 -0
  75. package/dist/mcp/tools/exams.d.ts +2 -0
  76. package/dist/mcp/tools/exams.js +1 -0
  77. package/dist/mcp/tools/index.d.ts +2 -0
  78. package/dist/mcp/tools/index.js +1 -0
  79. package/dist/mcp/tools/results.d.ts +2 -0
  80. package/dist/mcp/tools/results.js +1 -0
  81. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -1
  82. package/dist/shared/lib/i18n/messages/en/admin.js +1 -1
  83. package/dist/shared/lib/i18n/messages/en.d.ts +11 -1
  84. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -1
  85. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -1
  86. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -1
  87. package/package.json +163 -155
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Normalizers for CHOOSE_THE_CORRECT_ANSWER data coming from the API.
3
+ *
4
+ * The API stores options in two shapes:
5
+ * - legacy: string[] (["A", "B", "C"])
6
+ * - current: Array<{ id, text?, imageUrl? }> ([{ id: 'opt-1', text: 'A' }, ...])
7
+ *
8
+ * and correct answers as either a 0-based index (number) or an option ID
9
+ * ("opt-2"). The Creator form works with string[] + index, so everything is
10
+ * normalized on read. Saving keeps emitting string[] + index - the backend
11
+ * handler re-generates positional option IDs in sanitizeContent().
12
+ */
13
+ /**
14
+ * Normalize API options to the string[] the form expects.
15
+ * Falls back to `fallbackCount` empty options when there is nothing to show.
16
+ */
17
+ export declare function normalizeChooseAnswerOptions(rawOptions: any, fallbackCount?: number): string[];
18
+ /**
19
+ * Normalize an API correct answer (index, option ID, or array of either) into
20
+ * the index-based value the form expects. Returns -1 / [] when unresolvable.
21
+ */
22
+ export declare function normalizeChooseAnswerCorrectAnswer(rawAnswer: any, rawOptions: any): number | number[];
23
+ /**
24
+ * Pick the raw answer value out of an API correct_answer object, supporting the
25
+ * `optionId` / `optionIds` aliases the backend handler also accepts.
26
+ */
27
+ export declare function pickRawChooseAnswer(correctAnswer: any): any;
@@ -0,0 +1 @@
1
+ function r(r){return"string"==typeof r?r:"object"==typeof r&&null!==r?r.text??r.imageUrl??r.content??"":String(r??"")}export function normalizeChooseAnswerOptions(n,t=0){return Array.isArray(n)&&0!==n.length?n.map(r):Array.from({length:t},()=>"")}function n(r,n){if(null==r)return-1;const t=r=>r>=0&&(0===n.length||r<n.length);if("number"==typeof r)return t(r)?r:-1;const e=String(r),o=n.findIndex(r=>"object"==typeof r&&null!==r&&r.id===e);if(o>=0)return o;const i=e.match(/-opt-(\d+)$/);if(i){const r=parseInt(i[1],10);return t(r)?r:-1}const s=e.match(/^opt-(\d+)$/);if(s){const r=parseInt(s[1],10)-1;return t(r)?r:-1}const a=parseInt(e,10);return!isNaN(a)&&t(a)?a:-1}export function normalizeChooseAnswerCorrectAnswer(r,t){const e=Array.isArray(t)?t:[];return Array.isArray(r)?r.map(r=>n(r,e)).filter(r=>r>=0):n(r,e)}export function pickRawChooseAnswer(r){if(null!=r)return"object"!=typeof r||Array.isArray(r)?r:r.answer??r.optionIds??r.optionId}
@@ -1 +1 @@
1
- import{ChooseTheCorrectAnswerCreator as e}from"./ChooseTheCorrectAnswerCreator";export const chooseTheCorrectAnswerRegistration={component:e,transformInitialData:e=>{let t=e?.answer;const o=e?.content,n=e?.answer,r=e?.total_points?parseFloat(e.total_points):e?.points||o?.points||n?.points||1,i=e?.explanation||o?.explanation||n?.explanation||"",s=e?.questionGroup||{},a=s?.payload||{};if(o&&"object"==typeof o&&(void 0!==o.question||o.options)){const p=o.options||[],l=n?.answer,u=Array.isArray(l)||"number"==typeof l?l:-1,g=Boolean(o?.multipleAnswers??n?.multipleAnswers??e?.multipleAnswers??a?.multipleAnswers??Array.isArray(u));t={question:o.question||"",optionType:o.optionType||"text",options:p,correctAnswer:u,multipleAnswers:g,imageUrl:o.imageUrl||"",askerName:o.askerName||"",answererName:o.answererName||"",isExample:e?.isExample||n?.isExample||!1,explanation:i,points:r,groupTitle:a?.title||n?.groupTitle||"",groupContent:a?.content||n?.groupContent||"",groupImageUrl:a?.imageUrl||n?.groupImageUrl||o?.groupImageUrl||"",groupNumber:s.groupNumber||n?.groupNumber||1,articles:a?.articles||n?.articles||void 0}}else n&&"object"==typeof n&&(void 0!==n.question||n.options)&&(t={...n,multipleAnswers:Boolean(n?.multipleAnswers??e?.multipleAnswers??Array.isArray(n?.correctAnswer)),optionType:n.optionType||o?.optionType||"text",imageUrl:n.imageUrl||o?.imageUrl||"",isExample:e?.isExample||n?.isExample||!1,explanation:i,points:r,groupTitle:a?.title||n?.groupTitle||"",groupContent:a?.content||n?.groupContent||"",groupImageUrl:a?.imageUrl||n?.groupImageUrl||o?.groupImageUrl||"",groupNumber:s.groupNumber||n?.groupNumber||1,articles:a?.articles||n?.articles||void 0});return t},getExtraProps:e=>({optionCount:e.optionCount||4,optionMode:e.optionMode,defaultValue:e.defaultValue,defaultQuestion:e.defaultQuestion,viewMode:e.viewMode,onUnsavedChangesChange:e.onUnsavedChangesChange,validationRef:e.validationRef,groups:e.groups,groupType:e.groupType,articleCount:e.articleCount,questionIndexInPart:e.questionIndexInPart,partId:e.partId,questionConfig:e.questionConfig,multipleAnswers:e.multipleAnswers}),wrapOnSave:(e,t)=>({type:t.questionType,content:t.state.content,points:e?.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,answer:e}),wrapOnChange:(e,t)=>({type:t.questionType,content:t.state.content,points:e?.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,answer:e})};
1
+ import{ChooseTheCorrectAnswerCreator as e}from"./ChooseTheCorrectAnswerCreator";import{normalizeChooseAnswerCorrectAnswer as t,normalizeChooseAnswerOptions as o,pickRawChooseAnswer as n}from"./normalize";export const chooseTheCorrectAnswerRegistration={component:e,transformInitialData:e=>{let i=e?.answer;const r=e?.content,s=e?.answer,a=e?.total_points?parseFloat(e.total_points):e?.points||r?.points||s?.points||1,p=e?.explanation||r?.explanation||s?.explanation||"",l=e?.questionGroup||{},u=l?.payload||{};if(r&&"object"==typeof r&&(void 0!==r.question||r.options)){const g=o(r.options),m=n(s),c=t(m,r.options),d=Boolean(r?.multipleAnswers??s?.multipleAnswers??e?.multipleAnswers??u?.multipleAnswers??Array.isArray(c));i={question:r.question||"",optionType:r.optionType||"text",options:g,correctAnswer:c,multipleAnswers:d,imageUrl:r.imageUrl||"",askerName:r.askerName||"",answererName:r.answererName||"",isExample:e?.isExample||s?.isExample||!1,explanation:p,points:a,groupTitle:u?.title||s?.groupTitle||"",groupContent:u?.content||s?.groupContent||"",groupImageUrl:u?.imageUrl||s?.groupImageUrl||r?.groupImageUrl||"",groupNumber:l.groupNumber||s?.groupNumber||1,articles:u?.articles||s?.articles||void 0}}else s&&"object"==typeof s&&(void 0!==s.question||s.options)&&(i={...s,multipleAnswers:Boolean(s?.multipleAnswers??e?.multipleAnswers??Array.isArray(s?.correctAnswer)),optionType:s.optionType||r?.optionType||"text",imageUrl:s.imageUrl||r?.imageUrl||"",isExample:e?.isExample||s?.isExample||!1,explanation:p,points:a,groupTitle:u?.title||s?.groupTitle||"",groupContent:u?.content||s?.groupContent||"",groupImageUrl:u?.imageUrl||s?.groupImageUrl||r?.groupImageUrl||"",groupNumber:l.groupNumber||s?.groupNumber||1,articles:u?.articles||s?.articles||void 0});return i},getExtraProps:e=>({optionCount:e.optionCount||4,optionMode:e.optionMode,defaultValue:e.defaultValue,defaultQuestion:e.defaultQuestion,viewMode:e.viewMode,onUnsavedChangesChange:e.onUnsavedChangesChange,validationRef:e.validationRef,groups:e.groups,groupType:e.groupType,articleCount:e.articleCount,questionIndexInPart:e.questionIndexInPart,partId:e.partId,questionConfig:e.questionConfig,multipleAnswers:e.multipleAnswers}),wrapOnSave:(e,t)=>({type:t.questionType,content:t.state.content,points:e?.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,answer:e}),wrapOnChange:(e,t)=>({type:t.questionType,content:t.state.content,points:e?.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,answer:e})};
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ImagePreview as n}from"../../../../components/ui/image-preview";import{useState as r,useRef as s,useEffect as i,useMemo as a}from"react";import{Button as l}from"../../../../components/ui/button";import{Label as o}from"../../../../components/ui/label";import{Input as c}from"../../../../components/ui/input";import{Textarea as d}from"../../../../components/ui/textarea";import{PointsInput as m}from"../../../../components/ui/points-input";import{Card as h,CardContent as g,CardHeader as u,CardTitle as p}from"../../../../components/ui/card";import{Switch as x}from"../../../../components/ui/switch";import{Pencil as b,Eye as f,Lightbulb as N,Type as w,MessageSquare as v,ToggleLeft as y,Plus as C,Trash2 as k,ListChecks as q,ImageIcon as U,BookOpen as S}from"lucide-react";import{FillInBlankClient as j}from"./FillInBlankClient";import{useDebouncedCallback as A,usePresignedFileUrl as E}from"../../../../shared/lib/hooks";import{FileUpload as H}from"../../../../components/ui/file-upload";import{BasicQuestionGroupCard as G}from"../../groups/BasicQuestionGroupCard";import{useBasicQuestionGroup as I}from"../../_shared/hooks/useBasicQuestionGroup";function T(e){const t=/\{(\d+)\}/g,n=[];let r;for(;null!==(r=t.exec(e));){const e=parseInt(r[1],10);n.includes(e)||n.push(e)}return n.sort((e,t)=>e-t)}function F({initialData:F,onSave:B,onCancel:D,onChange:P,externalErrors:V,onUnsavedChangesChange:O,validationRef:$,groups:_,questionIndexInPart:z,partId:J,headerExtra:M}){const{isGrouped:Q,currentGroupNumber:R,groupTitle:K,groupSubtitle:L,groupContent:X,imageUrl:W,setGroupTitle:Y,setGroupSubtitle:Z,setGroupContent:ee,setImageUrl:te}=I({partId:J,groups:_,questionIndexInPart:z,initialData:{title:(()=>{if(F?.content?.passageTitle)return F.content.passageTitle;const e=F?.questionGroup?.payload;return e?.title?e.title:F?.title?F.title:""})(),subtitle:(()=>{if(F?.content?.passageSubtitle)return F.content.passageSubtitle;const e=F?.questionGroup?.payload;return e?.subtitle?e.subtitle:""})(),content:(()=>{if(F?.content?.passage)return F.content.passage;const e=F?.questionGroup?.payload;return e?.content?e.content:F?.groupContent?F.groupContent:""})(),imageUrl:(()=>{if(F?.content?.imageUrl)return F.content.imageUrl;const e=F?.questionGroup?.payload;return e?.imageUrl?e.imageUrl:F?.imageUrl?F.imageUrl:""})()}}),ne=()=>F?.content?.question?F.content.question:F?.question?F.question:F?.blanks?.[0]?.text?F.blanks[0].text:"",re=()=>F?.correctAnswer?.answers&&Array.isArray(F.correctAnswer.answers)?F.correctAnswer.answers:F?.answers&&F.answers.length>0?F.answers:F?.blanks?.[0]?.correctAnswer?[F.blanks[0].correctAnswer]:[],se=()=>void 0!==F?.correctAnswer?.caseSensitive?F.correctAnswer.caseSensitive:F?.caseSensitive||!1,ie=()=>void 0!==F?.content?.showHints?Boolean(F.content.showHints):void 0!==F?.showHints&&Boolean(F.showHints),ae=()=>Array.isArray(F?.content?.hints)?F.content.hints:Array.isArray(F?.hints)?F.hints:[],[le,oe]=r(ne()),[ce,de]=r(re()),[me,he]=r(se()),[ge,ue]=r(F?.explanation||""),[pe,xe]=r(F?.points||1),[be,fe]=r(ie()),[Ne,we]=r(ae()),[ve,ye]=r(""),[Ce,ke]=r(F?.isExample||!1),[qe,Ue]=r(!1),[Se,je]=r(""),{previewUrl:Ae}=E(W),Ee=Se||Ae||"",[He,Ge]=r({}),Ie=s(F),Te=s(!1),Fe=s(P),Be=s("");i(()=>{Fe.current=P},[P]);const De=A(e=>{Fe.current?.(e)},300),Pe=a(()=>T(le),[le]),Ve=a(()=>function(e){const t=T(e);return t.length>0?Math.max(...t):-1}(le),[le]);i(()=>{if(Ve>=0){const e=Ve+1;if(ce.length<e){const t=[...ce];for(;t.length<e;)t.push("");de(t)}}},[Ve,ce.length]),i(()=>{$&&($.current={triggerValidation:async()=>Oe(),getFormData:()=>({question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:W,showHints:be,hints:be?Ne:[],isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_})})},[$,le,ce,me,ge,pe,W,be,Ne,K,L,X,R,_]),i(()=>{JSON.stringify(Ie.current)!==JSON.stringify(F)&&(Te.current=!0,Ie.current=F,F?(oe(ne()),de(re()),he(se()),ue(F.explanation||""),xe(F.points||1),fe(ie()),we(ae()),ke(F.isExample||!1)):(oe(""),de([]),he(!1),ue(""),xe(1),fe(!1),we([]),ke(!1)),setTimeout(()=>{Te.current=!1},150))},[F]),i(()=>{if(Te.current){const e=JSON.stringify({question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:W,showHints:be,hints:Ne,isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_});return void(Be.current=e)}const e={question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:W,showHints:be,hints:be?Ne:[],isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_},t=JSON.stringify(e);Be.current!==t&&(Be.current=t,De(e))},[le,ce,me,ge,pe,W,be,Ne,Ce,K,L,X,R,_]);const Oe=()=>{const e={};le.trim()?0===Pe.length&&(e.question="Câu hỏi cần có ít nhất một chỗ trống {0}!"):e.question="Vui lòng nhập câu hỏi!";const t=Array.from({length:Pe.length},(e,t)=>t);!Pe.every((e,n)=>e===t[n])&&Pe.length>0&&(e.question="Các chỗ trống phải đánh số liên tục từ {0}!"),e.answers={};return(Pe.length>0?Pe:[0]).forEach(t=>{ce[t]&&ce[t].trim()||(e.answers[t]="Vui lòng nhập đáp án!")}),0===Object.keys(e.answers).length&&delete e.answers,Ge(e),!e.question&&!e.answers};if(qe){const n={question:le,answers:ce,caseSensitive:me,explanation:ge,imageUrl:Q?"":Ee||W,showHints:be,hints:be?Ne:[],isExample:Ce,isGroup:Q,groupTitle:Q?K:"",groupSubtitle:Q?L:"",groupContent:Q?X:"",groupImageUrl:Q?Ee||W:"",groupNumber:Q?R:void 0};return t("div",{className:"space-y-4",children:[e(h,{children:e(u,{children:t("div",{className:"flex items-center justify-between",children:[e(p,{children:"Fill In Blank (Preview)"}),t(l,{variant:"outline",size:"sm",onClick:()=>Ue(!1),children:[e(b,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(j,{questionData:n,autoFillCorrectAnswers:!0,isReviewMode:!1,explanation:ge})]})}return t("div",{className:"space-y-4",children:[Q&&e(G,{groupNumber:R,title:K,subtitle:L,content:X,imageUrl:W,onTitleChange:Y,onSubtitleChange:Z,onContentChange:ee,onImageChange:te,uploadIdPrefix:`fill-in-blank-${J??"group"}`}),t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e(u,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 shadow-sm",children:e(w,{className:"h-4 w-4 text-white"})}),t("div",{children:[e(p,{className:"text-base font-bold text-gray-900",children:"Điền vào chỗ trống"}),e("p",{className:"text-xs text-gray-500",children:"Hỗ trợ nhiều chỗ trống trong một câu hỏi"})]})]}),t("div",{className:"flex items-center gap-2",children:[M,t(l,{variant:"outline",size:"sm",onClick:()=>Ue(!0),disabled:!le?.trim()||0===Pe.length,className:"gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700",children:[e(f,{className:"h-4 w-4"}),"Xem trước"]})]})]})}),t(g,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"flex flex-wrap items-center gap-6",children:[e(m,{defaultValue:pe,onChange:e=>xe(Number(e.target.value)||1)}),t("div",{className:"flex items-center gap-2",children:[e(x,{id:"show-suggested-words",checked:be,onCheckedChange:fe}),e(o,{htmlFor:"show-suggested-words",className:"cursor-pointer text-sm font-medium text-gray-700",children:"Hiển thị từ gợi ý"})]}),t("div",{className:"flex items-center gap-2",children:[e(x,{id:"isExample",checked:Ce,onCheckedChange:ke}),e(o,{htmlFor:"isExample",className:"cursor-pointer text-sm font-medium text-gray-700",children:"Đánh dấu là câu hỏi Example"})]})]}),e("div",{className:"rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm",children:t("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100",children:e(N,{className:"h-4 w-4 text-blue-600"})}),t("div",{className:"text-sm text-blue-900",children:[e("p",{className:"font-semibold text-blue-800",children:"Hướng dẫn tạo câu hỏi"}),t("ul",{className:"mt-2 space-y-1.5 text-blue-700",children:[t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Sử dụng ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{0}"}),", ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{1}"}),", ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{2}"}),"... để đánh dấu các chỗ trống"]}),t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Hệ thống sẽ tự động tạo ô nhập đáp án cho mỗi chỗ trống"]}),t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Ví dụ: “He ","{0}"," 10 years old and she ","{1}"," 12.”"]})]})]})]})}),!Q&&t("div",{className:"space-y-2",children:[t(o,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(U,{className:"h-4 w-4 text-indigo-500"}),"Hình ảnh ",e("span",{className:"text-sm font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(H,{id:"fill-in-blank-image",label:"",accept:"image/*",value:W||"",onChange:e=>{te(e)},onPresignedUrlChange:je,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),Ee&&e(n,{src:Ee,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),t("div",{className:"space-y-2",children:[t(o,{htmlFor:"question",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-indigo-500"}),"Câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(d,{id:"question",value:le,onChange:e=>{oe(e.target.value),He.question&&Ge(e=>({...e,question:void 0}))},placeholder:"Ví dụ: He {0} 10 years old and she {1} 12.",rows:3,className:"min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(He.question?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),He.question&&e("p",{className:"text-sm text-red-600",children:He.question}),Pe.length>0&&e("div",{className:"flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2",children:t("span",{className:"text-sm text-green-700",children:["✓ Đã phát hiện ",e("strong",{children:Pe.length})," chỗ trống: ",Pe.map(t=>e("code",{className:"mx-0.5 rounded bg-green-100 px-1.5 py-0.5 font-mono text-green-800",children:`{${t}}`},t))]})})]}),t("div",{className:"space-y-4",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100",children:e(w,{className:"h-3.5 w-3.5 text-indigo-600"})}),t(o,{className:"text-base font-semibold text-gray-800",children:["Đáp án cho các chỗ trống ",e("span",{className:"text-red-500",children:"*"})]})]}),t("div",{className:"grid gap-6 lg:grid-cols-2",children:[e("div",{className:"space-y-3",children:(Pe.length>0?Pe:[0]).map(n=>t("div",{className:"rounded-xl border-2 p-4 transition-all "+(ce[n]?"border-green-200 bg-gradient-to-r from-green-50 to-emerald-50":"border-gray-200 bg-white"),children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl font-mono font-bold transition-all "+(ce[n]?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-indigo-100 text-indigo-700"),children:`{${n}}`}),e(c,{value:ce[n]||"",onChange:e=>((e,t)=>{const n=[...ce];for(;n.length<=e;)n.push("");n[e]=t,de(n),He.answers?.[e]&&Ge(t=>{const n={...t.answers};return delete n[e],{...t,answers:Object.keys(n).length>0?n:void 0}})})(n,e.target.value),placeholder:`Đáp án cho {${n}}`,className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(He.answers?.[n]?"border-red-400 focus:border-red-500 focus:ring-red-200":"")})]}),He.answers?.[n]&&e("p",{className:"mt-2 ml-13 text-sm text-red-600",children:He.answers[n]})]},n))}),be&&t("div",{className:"space-y-4 rounded-xl border-2 border-purple-200 bg-gradient-to-r from-purple-50 to-indigo-50 p-4",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-purple-100",children:e(q,{className:"h-3.5 w-3.5 text-purple-600"})}),t(o,{className:"text-base font-semibold text-gray-800",children:["Danh sách từ gợi ý ",t("span",{className:"text-sm font-normal text-gray-500",children:["(",Ne.length," từ)"]})]})]}),t("div",{className:"flex gap-2",children:[e(c,{value:ve,onChange:e=>ye(e.target.value),placeholder:"Nhập từ gợi ý mới...",className:"flex-1 border-gray-200 bg-white",onKeyDown:e=>{"Enter"===e.key&&ve.trim()&&(e.preventDefault(),Ne.includes(ve.trim())||we([...Ne,ve.trim()]),ye(""))}}),t(l,{type:"button",variant:"outline",size:"sm",onClick:()=>{ve.trim()&&!Ne.includes(ve.trim())&&(we([...Ne,ve.trim()]),ye(""))},disabled:!ve.trim(),className:"gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700",children:[e(C,{className:"h-4 w-4"}),"Thêm"]})]}),Ne.length>0?e("div",{className:"flex flex-wrap gap-2",children:Ne.map((n,r)=>t("div",{className:"flex items-center gap-2 rounded-lg border border-purple-200 bg-white px-3 py-2 shadow-sm transition-all hover:border-purple-300 hover:shadow",children:[e("span",{className:"text-sm font-medium text-gray-700",children:n}),e("button",{type:"button",onClick:()=>{we(Ne.filter((e,t)=>t!==r))},className:"rounded-full p-0.5 text-gray-400 transition-colors hover:bg-red-100 hover:text-red-500",children:e(k,{className:"h-3.5 w-3.5"})})]},r))}):e("p",{className:"text-center text-sm text-gray-500 italic",children:"Chưa có từ gợi ý nào. Thêm từ để hiển thị cho học sinh."})]})]})]}),le&&t("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[t("div",{className:"mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600",children:[e(f,{className:"h-3.5 w-3.5"}),"Xem trước câu hoàn chỉnh"]}),e("p",{className:"text-sm text-gray-700",children:(()=>{let e=le;return Pe.forEach(t=>{const n=ce[t]||"_____";e=e.replace(`{${t}}`,`[${n}]`)}),e})()})]}),t("div",{className:"flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300",children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(y,{className:"h-4 w-4 text-gray-600"})}),t("div",{className:"space-y-0.5",children:[e(o,{htmlFor:"case-sensitive",className:"font-semibold text-gray-800",children:"Phân biệt hoa/thường"}),e("p",{className:"text-sm text-gray-500",children:"Bật nếu đáp án cần phân biệt chữ hoa/thường"})]})]}),e(x,{id:"case-sensitive",checked:me,onCheckedChange:he})]}),V&&V.length>0&&t("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),t("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:V.map((t,n)=>e("li",{children:t},n))})]})]})]})]}),t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(u,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(S,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e(p,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"text-xs text-gray-500 mt-0.5",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(g,{className:"px-4 pb-4",children:e(d,{id:"explanation",value:ge,onChange:e=>ue(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function FillInBlankCreator(t){return e(F,{...t})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ImagePreview as r}from"../../../../components/ui/image-preview";import{useState as n,useRef as s,useEffect as i,useMemo as a}from"react";import{Button as l}from"../../../../components/ui/button";import{Label as o}from"../../../../components/ui/label";import{Input as c}from"../../../../components/ui/input";import{Textarea as d}from"../../../../components/ui/textarea";import{PointsInput as m}from"../../../../components/ui/points-input";import{Card as h,CardContent as g,CardHeader as u,CardTitle as p}from"../../../../components/ui/card";import{Switch as x}from"../../../../components/ui/switch";import{Pencil as b,Eye as f,Lightbulb as N,Type as w,MessageSquare as v,ToggleLeft as y,Plus as C,Trash2 as k,ListChecks as q,ImageIcon as U,BookOpen as S}from"lucide-react";import{FillInBlankClient as I}from"./FillInBlankClient";import{useDebouncedCallback as j,usePresignedFileUrl as A}from"../../../../shared/lib/hooks";import{FileUpload as E}from"../../../../components/ui/file-upload";import{BasicQuestionGroupCard as H}from"../../groups/BasicQuestionGroupCard";import{useBasicQuestionGroup as G}from"../../_shared/hooks/useBasicQuestionGroup";function T(e){const t=/\{(\d+)\}/g,r=[];let n;for(;null!==(n=t.exec(e));){const e=parseInt(n[1],10);r.includes(e)||r.push(e)}return r.sort((e,t)=>e-t)}function F({initialData:F,onSave:B,onCancel:D,onChange:P,externalErrors:V,onUnsavedChangesChange:O,validationRef:$,groups:_,questionIndexInPart:z,partId:J,headerExtra:M}){const{isGrouped:Q,currentGroupNumber:R,groupTitle:K,groupSubtitle:L,groupContent:X,imageUrl:W,setGroupTitle:Y,setGroupSubtitle:Z,setGroupContent:ee,setImageUrl:te}=G({partId:J,groups:_,questionIndexInPart:z,initialData:{title:(()=>{if(F?.content?.passageTitle)return F.content.passageTitle;const e=F?.questionGroup?.payload;return e?.title?e.title:F?.title?F.title:""})(),subtitle:(()=>{if(F?.content?.passageSubtitle)return F.content.passageSubtitle;const e=F?.questionGroup?.payload;return e?.subtitle?e.subtitle:""})(),content:(()=>{if(F?.content?.passage)return F.content.passage;const e=F?.questionGroup?.payload;return e?.content?e.content:F?.groupContent?F.groupContent:""})(),imageUrl:(()=>{const e=F?.questionGroup?.payload;return e?.imageUrl?e.imageUrl:F?.groupImageUrl?F.groupImageUrl:""})()}}),re=()=>F?.content?.question?F.content.question:F?.question?F.question:F?.blanks?.[0]?.text?F.blanks[0].text:"",ne=()=>F?.correctAnswer?.answers&&Array.isArray(F.correctAnswer.answers)?F.correctAnswer.answers:F?.answers&&F.answers.length>0?F.answers:F?.blanks?.[0]?.correctAnswer?[F.blanks[0].correctAnswer]:[],se=()=>void 0!==F?.correctAnswer?.caseSensitive?F.correctAnswer.caseSensitive:F?.caseSensitive||!1,ie=()=>void 0!==F?.content?.showHints?Boolean(F.content.showHints):void 0!==F?.showHints&&Boolean(F.showHints),ae=()=>Array.isArray(F?.content?.hints)?F.content.hints:Array.isArray(F?.hints)?F.hints:[],[le,oe]=n(re()),[ce,de]=n(ne()),[me,he]=n(se()),[ge,ue]=n(F?.explanation||""),[pe,xe]=n(F?.points||1),[be,fe]=n(ie()),[Ne,we]=n(ae()),[ve,ye]=n(""),[Ce,ke]=n(F?.isExample||!1),[qe,Ue]=n(()=>F?.content?.imageUrl||F?.imageUrl||""),[Se,Ie]=n(!1),[je,Ae]=n(""),{previewUrl:Ee}=A(qe),He=je||Ee||"",{previewUrl:Ge}=A(W),Te=Ge||"",[Fe,Be]=n({}),De=s(F),Pe=s(!1),Ve=s(P),Oe=s("");i(()=>{Ve.current=P},[P]);const $e=j(e=>{Ve.current?.(e)},300),_e=a(()=>T(le),[le]),ze=a(()=>function(e){const t=T(e);return t.length>0?Math.max(...t):-1}(le),[le]);i(()=>{if(ze>=0){const e=ze+1;de(t=>{if(t.length!==e){const r=[...t];if(r.length>e)return r.slice(0,e);for(;r.length<e;)r.push("");return r}return t})}},[ze]),i(()=>{$&&($.current={triggerValidation:async()=>Je(),getFormData:()=>({question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:Q?"":qe,groupImageUrl:Q?W:"",showHints:be,hints:be?Ne:[],isExample:Ce,title:Q?K:"",subtitle:Q?L:"",groupContent:Q?X:"",groupNumber:R,groups:_})})},[$,le,ce,me,ge,pe,Q,qe,W,be,Ne,K,L,X,R,_]),i(()=>{JSON.stringify(De.current)!==JSON.stringify(F)&&(Pe.current=!0,De.current=F,F?(oe(re()),de(ne()),he(se()),ue(F.explanation||""),xe(F.points||1),fe(ie()),we(ae()),ke(F.isExample||!1),Ue(F?.content?.imageUrl||F.imageUrl||"")):(oe(""),de([]),he(!1),ue(""),xe(1),fe(!1),we([]),ke(!1),Ue("")),setTimeout(()=>{Pe.current=!1},150))},[F]),i(()=>{if(Pe.current){const e=JSON.stringify({question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:qe,groupImageUrl:W,showHints:be,hints:Ne,isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_});return void(Oe.current=e)}const e={question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:Q?"":qe,groupImageUrl:Q?W:"",showHints:be,hints:be?Ne:[],isExample:Ce,title:Q?K:"",subtitle:Q?L:"",groupContent:Q?X:"",groupNumber:R,groups:_},t=JSON.stringify(e);Oe.current!==t&&(Oe.current=t,$e(e))},[le,ce,me,ge,pe,Q,qe,W,be,Ne,Ce,K,L,X,R,_]);const Je=()=>{const e={};le.trim()?0===_e.length&&(e.question="Câu hỏi cần có ít nhất một chỗ trống {0}!"):e.question="Vui lòng nhập câu hỏi!";const t=Array.from({length:_e.length},(e,t)=>t);!_e.every((e,r)=>e===t[r])&&_e.length>0&&(e.question="Các chỗ trống phải đánh số liên tục từ {0}!"),e.answers={};return(_e.length>0?_e:[0]).forEach(t=>{ce[t]&&ce[t].trim()||(e.answers[t]="Vui lòng nhập đáp án!")}),0===Object.keys(e.answers).length&&delete e.answers,Be(e),!e.question&&!e.answers};if(Se){const r={question:le,answers:ce,caseSensitive:me,explanation:ge,imageUrl:Q?"":He||qe,showHints:be,hints:be?Ne:[],isExample:Ce,isGroup:Q,groupTitle:Q?K:"",groupSubtitle:Q?L:"",groupContent:Q?X:"",groupImageUrl:Q?Te||W:"",groupNumber:Q?R:void 0};return t("div",{className:"space-y-4",children:[e(h,{children:e(u,{children:t("div",{className:"flex items-center justify-between",children:[e(p,{children:"Fill In Blank (Preview)"}),t(l,{variant:"outline",size:"sm",onClick:()=>Ie(!1),children:[e(b,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(I,{questionData:r,autoFillCorrectAnswers:!0,isReviewMode:!1,explanation:ge})]})}return t("div",{className:"space-y-4",children:[Q&&e(H,{groupNumber:R,title:K,subtitle:L,content:X,imageUrl:W,onTitleChange:Y,onSubtitleChange:Z,onContentChange:ee,onImageChange:te,uploadIdPrefix:`fill-in-blank-${J??"group"}`}),t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e(u,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 shadow-sm",children:e(w,{className:"h-4 w-4 text-white"})}),t("div",{children:[e(p,{className:"text-base font-bold text-gray-900",children:"Điền vào chỗ trống"}),e("p",{className:"text-xs text-gray-500",children:"Hỗ trợ nhiều chỗ trống trong một câu hỏi"})]})]}),t("div",{className:"flex items-center gap-2",children:[M,t(l,{variant:"outline",size:"sm",onClick:()=>Ie(!0),disabled:!le?.trim()||0===_e.length,className:"gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700",children:[e(f,{className:"h-4 w-4"}),"Xem trước"]})]})]})}),t(g,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"flex flex-wrap items-center gap-6",children:[e(m,{defaultValue:pe,onChange:e=>xe(Number(e.target.value)||1)}),t("div",{className:"flex items-center gap-2",children:[e(x,{id:"show-suggested-words",checked:be,onCheckedChange:fe}),e(o,{htmlFor:"show-suggested-words",className:"cursor-pointer text-sm font-medium text-gray-700",children:"Hiển thị từ gợi ý"})]}),t("div",{className:"flex items-center gap-2",children:[e(x,{id:"isExample",checked:Ce,onCheckedChange:ke}),e(o,{htmlFor:"isExample",className:"cursor-pointer text-sm font-medium text-gray-700",children:"Đánh dấu là câu hỏi Example"})]})]}),e("div",{className:"rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm",children:t("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100",children:e(N,{className:"h-4 w-4 text-blue-600"})}),t("div",{className:"text-sm text-blue-900",children:[e("p",{className:"font-semibold text-blue-800",children:"Hướng dẫn tạo câu hỏi"}),t("ul",{className:"mt-2 space-y-1.5 text-blue-700",children:[t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Sử dụng ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{0}"}),", ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{1}"}),", ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{2}"}),"... để đánh dấu các chỗ trống"]}),t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Hệ thống sẽ tự động tạo ô nhập đáp án cho mỗi chỗ trống"]}),t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Ví dụ: “He ","{0}"," 10 years old and she ","{1}"," 12.”"]})]})]})]})}),!Q&&t("div",{className:"space-y-2",children:[t(o,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(U,{className:"h-4 w-4 text-indigo-500"}),"Hình ảnh ",e("span",{className:"text-sm font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(E,{id:"fill-in-blank-image",label:"",accept:"image/*",value:qe||"",onChange:e=>{Ue(e)},onPresignedUrlChange:Ae,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),He&&e(r,{src:He,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),t("div",{className:"space-y-2",children:[t(o,{htmlFor:"question",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-indigo-500"}),"Câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(d,{id:"question",value:le,onChange:e=>{oe(e.target.value),Fe.question&&Be(e=>({...e,question:void 0}))},placeholder:"Ví dụ: He {0} 10 years old and she {1} 12.",rows:3,className:"min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(Fe.question?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),Fe.question&&e("p",{className:"text-sm text-red-600",children:Fe.question}),_e.length>0&&e("div",{className:"flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2",children:t("span",{className:"text-sm text-green-700",children:["✓ Đã phát hiện ",e("strong",{children:_e.length})," chỗ trống: ",_e.map(t=>e("code",{className:"mx-0.5 rounded bg-green-100 px-1.5 py-0.5 font-mono text-green-800",children:`{${t}}`},t))]})})]}),t("div",{className:"space-y-4",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100",children:e(w,{className:"h-3.5 w-3.5 text-indigo-600"})}),t(o,{className:"text-base font-semibold text-gray-800",children:["Đáp án cho các chỗ trống ",e("span",{className:"text-red-500",children:"*"})]})]}),t("div",{className:"grid gap-6 lg:grid-cols-2",children:[e("div",{className:"space-y-3",children:(_e.length>0?_e:[0]).map(r=>t("div",{className:"rounded-xl border-2 p-4 transition-all "+(ce[r]?"border-green-200 bg-gradient-to-r from-green-50 to-emerald-50":"border-gray-200 bg-white"),children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl font-mono font-bold transition-all "+(ce[r]?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-indigo-100 text-indigo-700"),children:`{${r}}`}),e(c,{value:ce[r]||"",onChange:e=>((e,t)=>{const r=[...ce];for(;r.length<=e;)r.push("");r[e]=t,de(r),Fe.answers?.[e]&&Be(t=>{const r={...t.answers};return delete r[e],{...t,answers:Object.keys(r).length>0?r:void 0}})})(r,e.target.value),placeholder:`Đáp án cho {${r}}`,className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(Fe.answers?.[r]?"border-red-400 focus:border-red-500 focus:ring-red-200":"")})]}),Fe.answers?.[r]&&e("p",{className:"mt-2 ml-13 text-sm text-red-600",children:Fe.answers[r]})]},r))}),be&&t("div",{className:"space-y-4 rounded-xl border-2 border-purple-200 bg-gradient-to-r from-purple-50 to-indigo-50 p-4",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-purple-100",children:e(q,{className:"h-3.5 w-3.5 text-purple-600"})}),t(o,{className:"text-base font-semibold text-gray-800",children:["Danh sách từ gợi ý ",t("span",{className:"text-sm font-normal text-gray-500",children:["(",Ne.length," từ)"]})]})]}),t("div",{className:"flex gap-2",children:[e(c,{value:ve,onChange:e=>ye(e.target.value),placeholder:"Nhập từ gợi ý mới...",className:"flex-1 border-gray-200 bg-white",onKeyDown:e=>{"Enter"===e.key&&ve.trim()&&(e.preventDefault(),Ne.includes(ve.trim())||we([...Ne,ve.trim()]),ye(""))}}),t(l,{type:"button",variant:"outline",size:"sm",onClick:()=>{ve.trim()&&!Ne.includes(ve.trim())&&(we([...Ne,ve.trim()]),ye(""))},disabled:!ve.trim(),className:"gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700",children:[e(C,{className:"h-4 w-4"}),"Thêm"]})]}),Ne.length>0?e("div",{className:"flex flex-wrap gap-2",children:Ne.map((r,n)=>t("div",{className:"flex items-center gap-2 rounded-lg border border-purple-200 bg-white px-3 py-2 shadow-sm transition-all hover:border-purple-300 hover:shadow",children:[e("span",{className:"text-sm font-medium text-gray-700",children:r}),e("button",{type:"button",onClick:()=>{we(Ne.filter((e,t)=>t!==n))},className:"rounded-full p-0.5 text-gray-400 transition-colors hover:bg-red-100 hover:text-red-500",children:e(k,{className:"h-3.5 w-3.5"})})]},n))}):e("p",{className:"text-center text-sm text-gray-500 italic",children:"Chưa có từ gợi ý nào. Thêm từ để hiển thị cho học sinh."})]})]})]}),le&&t("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[t("div",{className:"mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600",children:[e(f,{className:"h-3.5 w-3.5"}),"Xem trước câu hoàn chỉnh"]}),e("p",{className:"text-sm text-gray-700",children:(()=>{let e=le;return _e.forEach(t=>{const r=ce[t]||"_____";e=e.replace(`{${t}}`,`[${r}]`)}),e})()})]}),t("div",{className:"flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300",children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(y,{className:"h-4 w-4 text-gray-600"})}),t("div",{className:"space-y-0.5",children:[e(o,{htmlFor:"case-sensitive",className:"font-semibold text-gray-800",children:"Phân biệt hoa/thường"}),e("p",{className:"text-sm text-gray-500",children:"Bật nếu đáp án cần phân biệt chữ hoa/thường"})]})]}),e(x,{id:"case-sensitive",checked:me,onCheckedChange:he})]}),V&&V.length>0&&t("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),t("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:V.map((t,r)=>e("li",{children:t},r))})]})]})]})]}),t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(u,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(S,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e(p,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"text-xs text-gray-500 mt-0.5",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(g,{className:"px-4 pb-4",children:e(d,{id:"explanation",value:ge,onChange:e=>ue(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function FillInBlankCreator(t){return e(F,{...t})}
@@ -1 +1 @@
1
- import{FillInBlankCreator as e}from"./FillInBlankCreator";export const fillInBlankRegistration={component:e,transformInitialData:e=>{let s=null;const t=e?.content,n=e?.answer,i=e?.explanation||n?.explanation||"",a=e?.questionGroup||{},o=a?.payload||{},r=o?.imageUrl||n?.imageUrl||t?.imageUrl||"",l=o?.title||n?.title||"",p=o?.content||o?.passage||n?.groupContent||"",u=a?.groupNumber||n?.groupNumber||1;if(n?.question&&Array.isArray(n?.answers))s={question:n.question,answers:n.answers,caseSensitive:n?.caseSensitive??!1,imageUrl:r,showHints:n?.showHints||!1,hints:n?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,points:n?.points||1,title:l,groupContent:p,groupNumber:u,groups:n?.groups||e?.groups};else if(t?.question&&Array.isArray(n?.answers))s={question:t.question,answers:n.answers,caseSensitive:n?.caseSensitive??!1,imageUrl:r,showHints:t?.showHints||!1,hints:t?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,points:n?.points||t?.points||1,title:l,groupContent:p,groupNumber:u,groups:n?.groups||e?.groups};else if(n?.blanks&&Array.isArray(n.blanks)){const t=n.blanks[0];s={question:t?.text||"",answers:n.blanks.map(e=>e.correctAnswer||""),caseSensitive:n?.caseSensitive??!1,showHints:n?.showHints||!1,hints:n?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,blanks:n.blanks,imageUrl:r,title:l,groupContent:p,groupNumber:u,groups:n?.groups||e?.groups}}else if(t&&void 0!==t.text&&!t.blanks)s={question:t.text||"",answers:[n?.correctAnswer||""],caseSensitive:n?.caseSensitive??!1,showHints:t?.showHints||!1,hints:t?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,imageUrl:r,title:l,groupContent:p,groupNumber:u,groups:n?.groups||e?.groups};else if(t?.blanks){const a=n?.answers||{},o=t.blanks.map(e=>({...e,correctAnswer:a[e.id]||e.correctAnswer||""})),g=o[0];s={question:g?.text||"",answers:o.map(e=>e.correctAnswer||""),caseSensitive:n?.caseSensitive??!1,showHints:t?.showHints||!1,hints:t?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,blanks:o,imageUrl:r,title:l,groupContent:p,groupNumber:u,groups:n?.groups||e?.groups}}return s},getExtraProps:e=>({validationRef:e.validationRef,onUnsavedChangesChange:e.onUnsavedChangesChange,groups:e.groups,questionIndexInPart:e.questionIndexInPart,partId:e.partId}),wrapOnSave:(e,s)=>({type:s.questionType,content:s.state.content,points:e?.points||1,level:s.state.level,difficulty:s.state.difficulty,skill:s.state.skill,answer:e}),wrapOnChange:(e,s)=>({type:s.questionType,content:s.state.content,points:e?.points||1,level:s.state.level,difficulty:s.state.difficulty,skill:s.state.skill,answer:e})};
1
+ import{FillInBlankCreator as e}from"./FillInBlankCreator";export const fillInBlankRegistration={component:e,transformInitialData:e=>{let s=null;const t=e?.content,n=e?.answer,i=e?.explanation||n?.explanation||"",a=e?.questionGroup||{},r=a?.payload||{},o=r?.imageUrl||"",l=t?.imageUrl||n?.imageUrl||o,p=r?.title||n?.title||"",u=r?.content||r?.passage||n?.groupContent||"",g=a?.groupNumber||n?.groupNumber||1;if(n?.question&&Array.isArray(n?.answers))s={question:n.question,answers:n.answers,caseSensitive:n?.caseSensitive??!1,imageUrl:l,groupImageUrl:o,showHints:n?.showHints||!1,hints:n?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,points:n?.points||1,title:p,groupContent:u,groupNumber:g,groups:n?.groups||e?.groups};else if(t?.question&&Array.isArray(n?.answers))s={question:t.question,answers:n.answers,caseSensitive:n?.caseSensitive??!1,imageUrl:l,groupImageUrl:o,showHints:t?.showHints||!1,hints:t?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,points:n?.points||t?.points||1,title:p,groupContent:u,groupNumber:g,groups:n?.groups||e?.groups};else if(n?.blanks&&Array.isArray(n.blanks)){const t=n.blanks[0];s={question:t?.text||"",answers:n.blanks.map(e=>e.correctAnswer||""),caseSensitive:n?.caseSensitive??!1,showHints:n?.showHints||!1,hints:n?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,blanks:n.blanks,imageUrl:l,groupImageUrl:o,title:p,groupContent:u,groupNumber:g,groups:n?.groups||e?.groups}}else if(t&&void 0!==t.text&&!t.blanks)s={question:t.text||"",answers:[n?.correctAnswer||""],caseSensitive:n?.caseSensitive??!1,showHints:t?.showHints||!1,hints:t?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,imageUrl:l,groupImageUrl:o,title:p,groupContent:u,groupNumber:g,groups:n?.groups||e?.groups};else if(t?.blanks){const a=n?.answers||{},r=t.blanks.map(e=>({...e,correctAnswer:a[e.id]||e.correctAnswer||""})),m=r[0];s={question:m?.text||"",answers:r.map(e=>e.correctAnswer||""),caseSensitive:n?.caseSensitive??!1,showHints:t?.showHints||!1,hints:t?.hints||[],explanation:i,isExample:e?.isExample||n?.isExample||!1,blanks:r,imageUrl:l,groupImageUrl:o,title:p,groupContent:u,groupNumber:g,groups:n?.groups||e?.groups}}return s},getExtraProps:e=>({validationRef:e.validationRef,onUnsavedChangesChange:e.onUnsavedChangesChange,groups:e.groups,questionIndexInPart:e.questionIndexInPart,partId:e.partId}),wrapOnSave:(e,s)=>({type:s.questionType,content:s.state.content,points:e?.points||1,level:s.state.level,difficulty:s.state.difficulty,skill:s.state.skill,answer:e}),wrapOnChange:(e,s)=>({type:s.questionType,content:s.state.content,points:e?.points||1,level:s.state.level,difficulty:s.state.difficulty,skill:s.state.skill,answer:e})};
@@ -1 +1 @@
1
- export const transformFillInBlank=e=>{const t=e.answer,r=!!(t?.title||t?.subtitle||t?.groupContent||t?.imageUrl),s=r?{title:t.title||"",subtitle:t.subtitle||"",content:t.groupContent||"",imageUrl:t.imageUrl||""}:void 0,n=t?.groupNumber||1;if(t?.question&&Array.isArray(t?.answers)){const e={question:t.question,showHints:t.showHints||!1,hints:t.showHints&&t.hints||[]};return t.imageUrl&&!r&&(e.imageUrl=t.imageUrl),{apiContent:e,apiCorrectAnswer:{answers:t.answers,caseSensitive:t.caseSensitive||!1},isExample:t.isExample||!1,basicGroupData:s,groupNumber:n}}if(!t?.blanks||0===t.blanks.length)return{apiContent:{},apiCorrectAnswer:{}};return{apiContent:{blanks:(t.blanks||[]).map(e=>({id:e.id,text:e.text}))},apiCorrectAnswer:{answers:(t.blanks||[]).reduce((e,t)=>(t.id&&t.correctAnswer&&(e[t.id]=t.correctAnswer),e),{}),caseSensitive:t.caseSensitive||!1},basicGroupData:s,groupNumber:n}};
1
+ function e(e){const t=function(e){if(!e)return[];const t=/\{(\d+)\}/g,n=[];let r;for(;null!==(r=t.exec(e));){const e=parseInt(r[1],10);n.includes(e)||n.push(e)}return n.sort((e,t)=>e-t)}(e);return t.length>0?Math.max(...t):-1}export const transformFillInBlank=t=>{const n=t.answer,r=!!(n?.title||n?.subtitle||n?.groupContent||n?.groupImageUrl)?{title:n.title||"",subtitle:n.subtitle||"",content:n.groupContent||"",imageUrl:n.groupImageUrl||""}:void 0,s=n?.groupNumber||1;if(n?.question&&Array.isArray(n?.answers)){const t=e(n.question||""),i=t>=0?t+1:1,a=n.answers.length>i?n.answers.slice(0,i):n.answers,o={question:n.question,showHints:n.showHints||!1,hints:n.showHints&&n.hints||[]};return n.imageUrl&&(o.imageUrl=n.imageUrl),{apiContent:o,apiCorrectAnswer:{answers:a,caseSensitive:n.caseSensitive||!1},isExample:n.isExample||!1,basicGroupData:r,groupNumber:s}}if(!n?.blanks||0===n.blanks.length)return{apiContent:{},apiCorrectAnswer:{}};const i=(n.blanks||[]).map(e=>({id:e.id,text:e.text})),a=(n.blanks||[]).reduce((e,t)=>(t.id&&t.correctAnswer&&(e[t.id]=t.correctAnswer),e),{}),o={blanks:i};return n.imageUrl&&(o.imageUrl=n.imageUrl),{apiContent:o,apiCorrectAnswer:{answers:a,caseSensitive:n.caseSensitive||!1},basicGroupData:r,groupNumber:s}};
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
2
  import type { WordFillStructuredFormClientProps } from '../../_shared/types/word-fill-structured-form.type';
3
- export declare function WordFillStructuredFormClient({ questionData, isReviewMode, userAnswer, onAnswerChange, autoFillCorrectAnswers, explanation, }: WordFillStructuredFormClientProps): React.JSX.Element;
3
+ export declare function WordFillStructuredFormClient({ questionData, isReviewMode, userAnswer, onAnswerChange, autoFillCorrectAnswers, explanation, questionNumber, }: WordFillStructuredFormClientProps): React.JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import t,{useState as a,useEffect as n,useMemo as s,useCallback as l}from"react";import{Check as o,X as i,FileText as d}from"lucide-react";import{usePresignedFileUrl as c}from"../../../../shared/lib/hooks";import{cn as m}from"../../../../shared/lib/utils";export function WordFillStructuredFormClient({questionData:g,isReviewMode:u=!1,userAnswer:b,onAnswerChange:p,autoFillCorrectAnswers:x=!1,explanation:h}){const{title:f="",imageUrl:N,content:w="",answers:y={},caseSensitive:v=!1}=g,[C,j]=a(()=>b?{...b}:x?{...y}:{});n(()=>{b&&j({...b})},[b]);const{previewUrl:E,isLoading:A}=c(N||""),L=s(()=>{const e=new Set,r=/\{([\w\d]+)\}/g;let t;for(;null!==(t=r.exec(w));)e.add(t[1]);return Array.from(e).sort((e,r)=>{const t=parseInt(e),a=parseInt(r);return isNaN(t)||isNaN(a)?e.localeCompare(r):t-a})},[w]),k=L.every(e=>C[e]?.trim()),$=u&&L.every(e=>{const r=(C[e]||"").trim(),t=(y[e]||"").trim();return v?r===t:r.toLowerCase()===t.toLowerCase()}),F=h||g.explanation,S=l((e,r)=>{u||(j(t=>({...t,[e]:r})),p?.(e,r))},[u,p]),T=l((t,a)=>{const n=C[t]||"",s=y[t]||"",l=v?n.trim():n.trim().toLowerCase(),d=v?s.trim():s.trim().toLowerCase(),c=u&&l===d,g=u&&!!n&&l!==d;return r("span",{className:"relative mx-1 inline-flex items-baseline align-baseline",children:[e("span",{className:"absolute -top-4 left-1 text-[10px] font-semibold text-gray-400",children:t}),e("input",{type:"text",value:n,onChange:e=>S(t,e.target.value),disabled:u,placeholder:"",className:m("inline-block min-w-[90px] max-w-[200px] rounded-t border-b-2 bg-slate-50/40 px-2 py-0.5 text-center text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",u?c?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":g?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800":"border-slate-400 text-slate-800 focus:border-teal-600")}),u&&n&&(c?e(o,{className:"ml-0.5 h-3.5 w-3.5 text-green-600"}):e(i,{className:"ml-0.5 h-3.5 w-3.5 text-red-600"}))]},a)},[y,v,C,S,u]),D=s(()=>{if("undefined"==typeof window)return w;const r=(new DOMParser).parseFromString(w||"<p></p>","text/html"),a=/\{([\w\d]+)\}/g,n=(r,s)=>{if(r.nodeType===Node.TEXT_NODE){const n=r.textContent||"",l=[];let o,i=0;for(a.lastIndex=0;null!==(o=a.exec(n));)o.index>i&&l.push(n.slice(i,o.index)),l.push(T(o[1],`${s}-blank-${o.index}`)),i=o.index+o[0].length;return i<n.length&&l.push(n.slice(i)),0===l.length?null:1===l.length?l[0]:e(t.Fragment,{children:l},s)}if(r.nodeType!==Node.ELEMENT_NODE)return null;const l=r,o=l.tagName.toLowerCase(),i={key:s};Array.from(l.attributes).forEach(e=>{"class"!==e.name?"style"!==e.name?i[e.name]=e.value:i.style=e.value.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,r)=>{const[t,a]=r.split(":").map(e=>e.trim());return t&&a?(e[t.replace(/-([a-z])/g,(e,r)=>r.toUpperCase())]=a,e):e},{}):i.className=e.value});const d=Array.from(l.childNodes).map((e,r)=>n(e,`${s}-${r}`)).filter(e=>null!==e);return t.createElement(o,i,...d)};return Array.from(r.body.childNodes).map((e,r)=>n(e,`root-${r}`)).filter(e=>null!==e)},[w,T]);return e("div",{className:"space-y-4",children:r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 text-white shadow-sm",children:e(d,{className:"h-4 w-4"})}),e("span",{className:"text-sm font-medium text-gray-700",children:"Điền từ vào biểu mẫu có cấu trúc"})]}),u&&k&&r("div",{className:m("flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",$?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e($?o:i,{className:"h-3.5 w-3.5"}),$?"Đúng":"Sai"]})]}),r("div",{className:"space-y-5 p-5",children:[f&&e("h2",{className:"text-center text-xl font-bold text-gray-900",children:f}),(E||N)&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gray-50",children:A?e("div",{className:"flex h-48 w-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-teal-500"})}):e("img",{src:E||N,alt:f,className:"max-h-56 w-auto object-contain p-2"})})}),e("div",{className:"structured-form-content space-y-2 rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5 text-base leading-loose text-gray-800",children:D}),u&&!$&&r("div",{className:"rounded-lg border border-green-200 bg-green-50 p-3",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-green-600",children:"Đáp án đúng"}),e("div",{className:"mt-2 flex flex-wrap gap-2",children:L.map(e=>r("span",{className:"rounded-lg border border-green-300 bg-green-100 px-3 py-1.5 text-sm font-semibold text-green-800",children:[`{${e}}`,": ",y[e]]},e))})]}),u&&F&&r("div",{className:"rounded-lg border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-3",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-blue-600",children:"Giải thích"}),e("p",{className:"mt-1 text-sm text-blue-800",children:F})]})]})]})})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import r,{useState as l,useEffect as n,useMemo as a,useCallback as i}from"react";import{Check as o,X as s}from"lucide-react";import{usePresignedFileUrl as d}from"../../../../shared/lib/hooks";import{cn as c}from"../../../../shared/lib/utils";export function WordFillStructuredFormClient({questionData:p,isReviewMode:m=!1,userAnswer:u,onAnswerChange:g,autoFillCorrectAnswers:b=!1,explanation:f,questionNumber:h}){const{title:x="",imageUrl:w,content:y="",answers:v={},blankStyle:k,caseSensitive:N=!1,wordBank:_}=p,A=function(e){const t=String(e||"").toLowerCase();return"letter"===t||"box"===t?"letter":"phrase"===t||"sentence"===t?"phrase":"word"}(k),C=function(e){if(!Array.isArray(e)||0===e.length)return;const t=e.map(e=>{if(null==e)return null;if("object"!=typeof e||Array.isArray(e))return null;const t=e;if(Array.isArray(t.options)){const e=t.options.map(e=>String(e??"").trim()).filter(e=>e.length>0);return e.length>=2?{options:e}:null}const r=String(t.word??"").trim();return r?t.isExample?{word:r,isExample:!0}:{word:r}:null});return t.some(e=>null!=e)?t:void 0}(_),S=a(()=>function(e){if(!e?.length)return[];const t=[];for(const r of e)if(r&&"word"in r&&"string"==typeof r.word&&r.word){const e="isExample"in r&&Boolean(r.isExample);t.push(e?{word:r.word,isExample:!0}:{word:r.word})}return t}(C),[C]),$=String(p.viewMode||""),B=S.length>0&&("WITH_WORD_BANK"===$||!C?.some(e=>null!=e&&"options"in e&&Array.isArray(e.options))),[E,W]=l(()=>u?{...u}:b?{...v}:{});n(()=>{u&&W({...u})},[u]);const{previewUrl:L,isLoading:j}=d(w||""),T=a(()=>{const e=new Set,t=/\{([\w\d]+)\}/g;let r;for(;null!==(r=t.exec(y));)e.add(r[1]);return Array.from(e).sort((e,t)=>{const r=parseInt(e),l=parseInt(t);return isNaN(r)||isNaN(l)?e.localeCompare(t):r-l})},[y]),F=T.every(e=>E[e]?.trim()),O=m&&T.every(e=>{const t=(E[e]||"").trim(),r=(v[e]||"").trim();return N?t===r:t.toLowerCase()===r.toLowerCase()}),D=String(f||p.explanation||"").trim(),H="number"==typeof h&&h>0?`Bài ${h}`:"",M=i((e,t)=>{m||(W(r=>({...r,[e]:t})),g?.(e,t))},[m,g]),z=i((r,l)=>{const n=E[r]||"",a=v[r]||"",i=N?n.trim():n.trim().toLowerCase(),d=N?a.trim():a.trim().toLowerCase(),p=m&&i===d,u=m&&!!n&&i!==d,g="letter"===A,b="phrase"===A,f=g?1:void 0,h=function(e,t,r){if(!e?.length)return null;const l=Number(t);let n;if(Number.isFinite(l)&&l>=0&&l<e.length)n=e[l];else{const l=r.indexOf(t);l>=0&&l<e.length&&(n=e[l])}return null!=n&&"options"in n&&n.options?.length?n.options:null}(C,r,T),x=Boolean(h&&h.length>=2),w=g?{display:"inline-block",width:"1.625rem",height:"1.625rem",minWidth:"1.625rem",maxWidth:"1.625rem",padding:0,margin:"0 1px",borderWidth:2,borderStyle:"solid",borderColor:m?p?"#10b981":u?"#f43f5e":"#f59e0b":"#94a3b8",borderRadius:4,textAlign:"center",fontSize:"0.8125rem",fontWeight:700,lineHeight:"1.375rem",textTransform:"uppercase",backgroundColor:m?p?"#ecfdf5":u?"#fff1f2":"#fffbeb":"#ffffff",color:m?p?"#065f46":u?"#9f1239":"#92400e":"#1e293b",verticalAlign:"middle",outline:"none",boxSizing:"border-box"}:void 0,y=m?p?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":u?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800":"border-slate-400 text-slate-800 focus:border-teal-600";return t("span",{className:c("relative inline-flex items-center",g?"mx-px":"mx-1 align-baseline"),style:g?{display:"inline-flex",verticalAlign:"middle",whiteSpace:"nowrap",flexShrink:0}:void 0,children:[!g&&e("span",{className:"absolute -top-4 left-1 text-[10px] font-semibold text-gray-400",children:r}),x&&h?t("select",{value:n,disabled:m,onChange:e=>M(r,e.target.value),"aria-label":`Blank ${r}`,className:c("inline-block min-w-[7rem] max-w-[14rem] rounded border bg-white px-2 py-1 text-sm font-semibold outline-none transition-all align-baseline",y,!m&&"hover:border-teal-500 focus:border-teal-600 focus:ring-1 focus:ring-teal-200"),children:[e("option",{value:"",children:"—"}),h.map(t=>e("option",{value:t,children:t},t))]}):e("input",{type:"text",value:n,onChange:e=>{const t=e.target.value,l=g?t.slice(-1).replace(/\s/g,""):t;M(r,l)},disabled:m,placeholder:"",maxLength:f,inputMode:g?"text":void 0,autoComplete:"off",spellCheck:!1,"aria-label":`Blank ${r}`,style:w,className:c(!g&&"inline-block bg-slate-50/40 text-center text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",!g&&(b?"min-w-[140px] max-w-[280px] rounded-t border-b-2 px-2 py-0.5":"min-w-[90px] max-w-[160px] rounded-t border-b-2 px-2 py-0.5"),!g&&y)}),m&&n&&(p?e(o,{className:"ml-0.5 h-3.5 w-3.5 shrink-0 text-green-600"}):e(s,{className:"ml-0.5 h-3.5 w-3.5 shrink-0 text-red-600"}))]},l)},[v,A,N,T,E,M,m,C]),I=a(()=>{if("undefined"==typeof window)return y;const t=(new DOMParser).parseFromString(y||"<p></p>","text/html"),l=/\{([\w\d]+)\}/g,n="letter"===A,a=(t,i)=>{if(t.nodeType===Node.TEXT_NODE)return((t,a)=>{if(!t)return null;const i=[];let o,s=0;for(l.lastIndex=0;null!==(o=l.exec(t));)o.index>s&&i.push({kind:"text",value:t.slice(s,o.index)}),i.push({kind:"blank",key:o[1],index:o.index}),s=o.index+o[0].length;if(s<t.length&&i.push({kind:"text",value:t.slice(s)}),!i.length)return null;if(!n){const t=i.map((e,t)=>"text"===e.kind?e.value:z(e.key,`${a}-blank-${e.index}-${t}`));return 1===t.length?t[0]:e(r.Fragment,{children:t},a)}const d=[];let c=[],p=0;const m=()=>{c.length&&(d.push(e("span",{style:{display:"inline",whiteSpace:"nowrap",verticalAlign:"baseline"},children:c},`${a}-w${p}`)),p+=1,c=[])};for(let e=0;e<i.length;e+=1){const t=i[e];if("blank"===t.kind){c.push(z(t.key,`${a}-blank-${t.index}-${e}`));continue}const r=t.value.split(/(\s+)/);for(let e=0;e<r.length;e+=1){const t=r[e];t&&(/^\s+$/.test(t)?(m(),d.push(t)):c.push(t))}}return m(),d.length?1===d.length?d[0]:e(r.Fragment,{children:d},a):null})(t.textContent||"",i);if(t.nodeType!==Node.ELEMENT_NODE)return null;const o=t,s=o.tagName.toLowerCase(),d={key:i};let c="",p={};Array.from(o.attributes).forEach(e=>{var t;if("class"!==e.name)return"style"===e.name?(t=e.value,void(p=t.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const[r,l]=t.split(":").map(e=>e.trim());return r&&l?(e[r.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())]=l,e):e},{}))):void(d[e.name]=e.value);c=e.value});const m=c.split(/\s+/).filter(Boolean);m.includes("wfsf-block")&&(p={display:"block",margin:"0 0 1.25rem 0",padding:"0.35rem 0 1rem 0",borderBottom:"1px solid #e2e8f0",...p}),m.includes("wfsf-item-no")&&(p={fontWeight:700,fontSize:"1rem",lineHeight:1.4,margin:"0 0 0.4rem 0",color:"#0f172a",...p}),"table"===s&&m.includes("dialogue")&&(p={width:"100%",borderCollapse:"collapse",tableLayout:"auto",...p}),"td"===s&&m.includes("speaker")?p={width:"1%",whiteSpace:"nowrap",verticalAlign:"top",paddingRight:"12px",paddingTop:"6px",paddingBottom:"6px",fontWeight:600,color:"#0f172a",...p}:"td"===s&&!m.includes("speaker")&&o.closest?.("table.dialogue")&&(p={verticalAlign:"top",paddingTop:"6px",paddingBottom:"6px",width:"auto",...p}),c&&(d.className=c),Object.keys(p).length>0&&(d.style=p);const u=Array.from(o.childNodes).map((e,t)=>a(e,`${i}-${t}`)).filter(e=>null!==e);return r.createElement(s,d,...u)};return Array.from(t.body.childNodes).map((e,t)=>a(e,`root-${t}`)).filter(e=>null!==e)},[A,y,z]);return e("div",{className:"space-y-4",children:t("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[(H||D||m&&F)&&e("div",{className:"border-b border-gray-100 bg-white px-5 py-4",children:t("div",{className:"flex items-start justify-between gap-3",children:[t("div",{className:"min-w-0 space-y-1.5",children:[H?e("h2",{className:"text-lg font-bold tracking-tight text-slate-900",children:H}):null,D?e("p",{className:"text-sm leading-relaxed whitespace-pre-line text-slate-700",children:D}):null]}),m&&F?t("div",{className:c("flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",O?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e(O?o:s,{className:"h-3.5 w-3.5"}),O?"Đúng":"Sai"]}):null]})}),t("div",{className:"space-y-5 p-5",children:[x?e("h3",{className:"text-center text-base font-semibold text-gray-900",children:x}):null,(L||w)&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gray-50",children:j?e("div",{className:"flex h-48 w-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-teal-500"})}):e("img",{src:L||w,alt:x||H||"Hình minh họa",className:"max-h-56 w-auto object-contain p-2"})})}),B?t("div",{className:"word-bank-shared space-y-2",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-gray-500",children:"Word Bank"}),e("div",{className:"flex flex-wrap gap-2",children:S.map((t,r)=>e("span",{className:c("rounded-lg border-2 px-3 py-1.5 text-sm font-semibold",t.isExample?"border-amber-300 bg-amber-50 text-amber-800":"border-sky-200 bg-sky-50 text-sky-900"),children:t.word},`wb-${r}-${t.word}`))})]}):null,e("div",{className:c("structured-form-content space-y-2 rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5 text-base leading-relaxed text-gray-800","[&_table.dialogue]:w-full [&_table.dialogue]:border-collapse [&_table.dialogue]:table-fixed","[&_table.dialogue_td.speaker]:w-[1%] [&_table.dialogue_td.speaker]:whitespace-nowrap","[&_table.dialogue_td.speaker]:align-top [&_table.dialogue_td.speaker]:pr-3","[&_table.dialogue_td.speaker]:font-semibold [&_table.dialogue_td.speaker]:text-slate-900","[&_table.dialogue_td]:align-top [&_table.dialogue_td]:py-1.5 [&_table.dialogue_td]:leading-relaxed","[&_table.dialogue_td:not(.speaker)]:w-auto","[&_table:not(.dialogue)]:w-full [&_table:not(.dialogue)]:border-collapse"),style:"letter"===A?{wordBreak:"normal",overflowWrap:"break-word",lineHeight:1.7}:void 0,children:I}),m&&!O&&t("div",{className:"rounded-lg border border-green-200 bg-green-50 p-3",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-green-600",children:"Đáp án đúng"}),e("div",{className:"mt-2 flex flex-wrap gap-2",children:T.map(e=>t("span",{className:"rounded-lg border border-green-300 bg-green-100 px-3 py-1.5 text-sm font-semibold text-green-800",children:[`{${e}}`,": ",v[e]]},e))})]})]})]})})}
@@ -1 +1 @@
1
- export const transformWordFillStructuredForm=t=>{const e=t.answer;if(!e?.content||!e?.answers)return{apiContent:{},apiCorrectAnswer:{}};const r=!!(e.groupTitle||e.groupSubtitle||e.groupContent||e.groupImageUrl);return{apiContent:{title:e.title||"",imageUrl:e.imageUrl||"",content:e.content},apiCorrectAnswer:{answers:e.answers,caseSensitive:e.caseSensitive??!1},basicGroupData:r?{title:e.groupTitle||"",subtitle:e.groupSubtitle||"",content:e.groupContent||"",imageUrl:e.groupImageUrl||""}:void 0,groupNumber:e.groupNumber||1}};
1
+ export const transformWordFillStructuredForm=e=>{const t=e.answer;if(!t?.content||!t?.answers)return{apiContent:{},apiCorrectAnswer:{}};const r=!!(t.groupTitle||t.groupSubtitle||t.groupContent||t.groupImageUrl),n={title:t.title||"",imageUrl:t.imageUrl||"",content:t.content};t.blankStyle&&(n.blankStyle=t.blankStyle),t.viewMode&&(n.viewMode=t.viewMode),Array.isArray(t.wordBank)&&t.wordBank.length>0&&(n.wordBank=t.wordBank);return{apiContent:n,apiCorrectAnswer:{answers:t.answers,caseSensitive:t.caseSensitive??!1},basicGroupData:r?{title:t.groupTitle||"",subtitle:t.groupSubtitle||"",content:t.groupContent||"",imageUrl:t.groupImageUrl||""}:void 0,groupNumber:t.groupNumber||1}};
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Button as s}from"../../../components/ui/button";import{Card as n,CardContent as t,CardHeader as o,CardTitle as a}from"../../../components/ui/card";import{Input as i}from"../../../components/ui/input";import{Check as c,Volume2 as l,X as d}from"lucide-react";import{useRef as u,useState as m}from"react";import{toast as p}from"sonner";import{t as g}from"../../../shared/lib/i18n";import{ReadAndColorObjectsClient as b}from"../types/read-and-color-objects/ReadAndColorObjectsClient";import{ArrangeLettersIntoWordsClient as h}from"../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient";import{WriteShortParagraphClient as w}from"../types/write-short-paragraph/WriteShortParagraphClient";import{LabelThePictureClient as N}from"../types/label-the-picture/LabelThePictureClient";import{LookPictureFillWordHintClient as C}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as f}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as x}from"../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient";import{WriteAShortLetterClient as A}from"../types/write-a-short-letter/WriteAShortLetterClient";import{LookPictureChooseCorrectAnswerClient as _}from"../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient";import{LookPictureFillBlankChooseAnswerClient as v}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as y}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";export function QuestionViewer({question:S,onSubmitAnswer:R,isReviewMode:L=!1,userAnswer:E}){const[T,I]=m(E||null),[O,W]=m(0),k=u(null),[D,q]=m(E||{}),P=S.answer,[Q,U]=m(E||Array(P?.blanks?.length||0).fill("")),[M,G]=m(E||{}),[$,H]=m(E||{}),[j,F]=m(E||{}),B=()=>{const e=S.answer.maxPlayCount||3;O>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(k.current?.play(),W(O+1))},K=()=>{const n=S.answer,{blanks:t,caseSensitive:o=!1}=n;return r("div",{className:"space-y-4",children:[t.map((s,n)=>{const a=Q[n]||"",l=L&&((e,r)=>{const s=t[e];return o?r===s:r.toLowerCase()===s.toLowerCase()})(n,a);return r("div",{className:"flex items-center space-x-2",children:[r("span",{className:"text-sm font-medium",children:["Chỗ trống ",n+1,":"]}),e(i,{value:a,onChange:e=>((e,r)=>{const s=[...Q];s[e]=r,U(s)})(n,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(L?l?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:L}),L&&l&&e(c,{className:"h-4 w-4 text-green-600"}),L&&!l&&a&&e(d,{className:"h-4 w-4 text-red-600"})]},n)}),!L&&e(s,{onClick:()=>{R&&R(Q)},disabled:Q.some(e=>""===e.trim()),children:"Nộp đáp án"})]})};return r(n,{children:[e(o,{children:r(a,{className:"flex items-center justify-between",children:[e("span",{children:"string"==typeof S.content?S.content:S.content?.question||"Câu hỏi"}),r("span",{className:"text-sm font-normal text-gray-500",children:[S.points," điểm"]})]})}),r(t,{className:"space-y-4",children:[(()=>{switch(S.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=S.answer,{isGroup:s,questionGroup:n}=S;let t={};return t=S.content&&"object"==typeof S.content?{...S.content,correctAnswer:S.correctAnswer?.answer??-1,id:S.id,isGroup:s,groupTitle:n?.payload?.title,groupContent:n?.payload?.content,groupImageUrl:n?.payload?.imageUrl,groupNumber:n?.groupNumber}:{...r,question:S.content||r?.question,id:S.id,isGroup:s,groupTitle:n?.payload?.title,groupContent:n?.payload?.content,groupImageUrl:n?.payload?.imageUrl,groupNumber:n?.groupNumber},e(x,{questionData:t,questionConfig:S.questionConfig,onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"FILL_IN_BLANK":return K();case"TRUE_FALSE":return(()=>{const n=S.answer,{correctAnswer:t}=n;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(s=>{const n=T===s.value;return e("div",{className:`flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${n?"border-blue-500 bg-blue-50":"border-gray-200 hover:border-gray-300"} ${L&&s.value===t?"border-green-500 bg-green-50":L&&n&&s.value!==t?"border-red-500 bg-red-50":""}`,onClick:()=>!L&&I(s.value),children:r("div",{className:"flex w-full items-center",children:[e("div",{className:`mr-3 h-4 w-4 rounded-full border-2 ${n?"border-blue-500 bg-blue-500":"border-gray-300"} ${L&&s.value===t?"border-green-500 bg-green-500":""}`,children:n&&e("div",{className:"m-auto h-2 w-2 rounded-full bg-white"})}),e("span",{className:"flex-1",children:s.label}),L&&s.value===t&&e(c,{className:"ml-2 h-4 w-4 text-green-600"}),L&&n&&s.value!==t&&e(d,{className:"ml-2 h-4 w-4 text-red-600"})]})},s.value.toString())}),!L&&e(s,{onClick:()=>{R&&R(T)},disabled:null===T,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const n=S.answer,{minWords:t,maxWords:o}=n,a=T?T.trim().split(/\s+/).length:0;return r("div",{className:"space-y-4",children:[r("div",{className:"text-sm text-gray-600",children:[t&&o?`Số từ: ${t} - ${o} từ`:t?`Tối thiểu: ${t} từ`:o?`Tối đa: ${o} từ`:"",T&&r("span",{className:"ml-2 "+(a>=(t||0)&&a<=(o||1/0)?"text-green-600":"text-red-600"),children:["(",a," từ)"]})]}),e("textarea",{value:T||"",onChange:e=>I(e.target.value),placeholder:"Viết câu trả lời của bạn ở đây...",className:"h-32 w-full resize-none rounded-lg border border-gray-300 p-3 focus:border-blue-500 focus:ring-1 focus:ring-blue-500",disabled:L}),!L&&e(s,{onClick:()=>{R&&R(T)},disabled:!T||T.trim().length<(t||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const n=S.answer,{audioUrl:t,blanks:o}=n;return r("div",{className:"space-y-4",children:[r("div",{className:"flex items-center gap-4 rounded-lg bg-blue-50 p-4",children:[r(s,{onClick:B,variant:"outline",className:"gap-2",children:[e(l,{className:"h-5 w-5"}),"Phát âm thanh (",O,"/",n.maxPlayCount||3,")"]}),e("audio",{ref:k,src:t})]}),e("div",{className:"space-y-3",children:o.map((s,n)=>r("div",{className:"flex items-center gap-3",children:[r("span",{className:"w-20 font-medium",children:["Blank ",n+1,":"]}),e(i,{value:Q[n],onChange:e=>{const r=[...Q];r[n]=e.target.value,U(r),I(r)},placeholder:"Nhập câu trả lời...",disabled:L,className:L?Q[n]?.toLowerCase()===o[n]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),L&&r("span",{className:"text-sm text-gray-600",children:["Đáp án: ",e("strong",{children:o[n]})]})]},n))})]})})();case"TICK_TRUE_OR_FALSE":case"TICK_YES_OR_NO":return(()=>{const s=S.answer,{statements:n}=s;return e("div",{className:"space-y-3",children:n.map(s=>e("div",{className:`rounded-lg border-2 p-4 ${L&&D[s.id]===s.isCorrect?"border-green-500 bg-green-50":""} ${L&&D[s.id]!==s.isCorrect?"border-red-500 bg-red-50":"border-gray-200"} `,children:r("div",{className:"flex items-start gap-4",children:[r("div",{className:"mt-1 flex gap-2",children:[e("button",{onClick:()=>{const e={...D,[s.id]:!0};q(e),I(e)},disabled:L,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!0===D[s.id]?"border-green-500 bg-green-500 text-white":"border-gray-300"} `,children:e(c,{className:"h-5 w-5"})}),e("button",{onClick:()=>{const e={...D,[s.id]:!1};q(e),I(e)},disabled:L,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!1===D[s.id]?"border-red-500 bg-red-500 text-white":"border-gray-300"} `,children:e(d,{className:"h-5 w-5"})})]}),e("p",{className:"flex-1",children:s.text}),L&&e("span",{className:"text-sm font-medium",children:s.isCorrect?"✓ Đúng":"✗ Sai"})]})},s.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=S.answer,s=S.correctAnswer||S.correct_answer,n=(r?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(h,{questionData:{subQuestions:n},onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const s=S.answer,{passage:n,blanks:t}=s,o=n.split(/\{(\d+)\}/);return e("div",{className:"space-y-4",children:e("div",{className:"prose max-w-none",children:e("div",{className:"text-lg leading-relaxed",children:o.map((s,n)=>{const o=parseInt(s);if(!isNaN(o)){const s=t.find(e=>e.index===o);return r("span",{className:"mx-1 inline-flex items-center",children:[e(i,{value:$[o]||"",onChange:e=>{const r={...$,[o]:e.target.value};H(r),I(r)},className:`inline-block h-8 w-32 text-center ${L&&$[o]===s?.correctAnswer?"border-green-500 bg-green-50":""} ${L&&$[o]!==s?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:L}),L&&r("span",{className:"ml-2 text-sm text-green-600",children:["(",s?.correctAnswer,")"]})]},n)}return e("span",{children:s},n)})})})})})();case"WRITE_SHORT_PARAGRAPH":return(()=>{const r=S.answer;return e(w,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=S.answer;return e(A,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=S.answer,s=S.content,n=S.questionGroup;let t=[];r?.subQuestions&&r.subQuestions.length>0?t=r.subQuestions:s?.imageUrl&&s?.word&&(t=[{id:S.id,imageUrl:s.imageUrl,word:s.word,correctAnswer:r?.correctAnswer||"",groupNumber:n?.groupNumber||1}]);const o=n?.payload?.hintLetters||r?.hintLetters||s?.hintLetters||[],a=n?.groupNumber||1,i={};o.length>0&&(i[a]=o);const c={subQuestions:t,hintLetters:o,hintLettersByGroup:i,groups:[{number:a,questionCount:t.length}]};return e(C,{questionData:c,userAnswer:E,isReviewMode:L,explanation:S.explanation,onSubmit:e=>{I(e),R?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=S.answer;return e(N,{questionData:r,userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=S.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{I(e),R?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=S.answer;return e(f,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=S.content,s=S.answer,n=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(_,{questionData:{subQuestions:n},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=S.content,s=S.answer,n=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(v,{questionData:{subQuestions:n},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=S.content,s=S.answer;return e(y,{questionData:{content:{grid:r?.grid||[],rows:r?.rows||3,columns:r?.columns||3,disabledCells:r?.disabledCells||[]},answer:{answers:s?.answers||{},missingLetters:s?.missingLetters||[],caseSensitive:s?.caseSensitive||!1}},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",S.type]})}})(),!L&&e("div",{className:"flex justify-end border-t pt-4",children:e(s,{onClick:()=>{R?.(T)},className:"bg-blue-500 hover:bg-blue-600",children:"Xác nhận đáp án"})})]})]})}
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Button as t}from"../../../components/ui/button";import{Card as n,CardContent as s,CardHeader as a,CardTitle as o}from"../../../components/ui/card";import{Input as i}from"../../../components/ui/input";import{Check as l,Volume2 as c,X as d}from"lucide-react";import{useRef as u,useState as m}from"react";import{toast as p}from"sonner";import{t as g}from"../../../shared/lib/i18n";import{ReadAndColorObjectsClient as b}from"../types/read-and-color-objects/ReadAndColorObjectsClient";import{ArrangeLettersIntoWordsClient as h}from"../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient";import{WriteShortParagraphClient as w}from"../types/write-short-paragraph/WriteShortParagraphClient";import{LabelThePictureClient as x}from"../types/label-the-picture/LabelThePictureClient";import{LookPictureFillWordHintClient as N}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as y}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as f}from"../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient";import{WriteAShortLetterClient as A}from"../types/write-a-short-letter/WriteAShortLetterClient";import{LookPictureChooseCorrectAnswerClient as v}from"../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient";import{LookPictureFillBlankChooseAnswerClient as S}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as C}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";import{WordFillStructuredFormClient as _}from"../types/word-fill-structured-form/WordFillStructuredFormClient";import{parseFillBlankStemLine as R}from"../_shared/utils/parseFillBlankStem";export function QuestionViewer({question:T,onSubmitAnswer:E,isReviewMode:I=!1,userAnswer:O}){const[k,W]=m(O||null),[$,D]=m(0),U=u(null),[q,M]=m(O||{}),P=T.answer,[B,F]=m(O||Array(P?.blanks?.length||0).fill("")),[j,H]=m(O||{}),[Q,G]=m(O||{}),[K,Y]=m(O||{}),V=()=>{const e=T.answer.maxPlayCount||3;$>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(U.current?.play(),D($+1))},z=()=>{switch(T.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=T.answer,{isGroup:t,questionGroup:n}=T;let s={};return s=T.content&&"object"==typeof T.content?{...T.content,correctAnswer:T.correctAnswer?.answer??-1,id:T.id,isGroup:t,groupTitle:n?.payload?.title,groupContent:n?.payload?.content,groupImageUrl:n?.payload?.imageUrl,groupNumber:n?.groupNumber}:{...r,question:T.content||r?.question,id:T.id,isGroup:t,groupTitle:n?.payload?.title,groupContent:n?.payload?.content,groupImageUrl:n?.payload?.imageUrl,groupNumber:n?.groupNumber},e(f,{questionData:s,questionConfig:T.questionConfig,onSubmit:e=>{W(e),E?.(e)},isReviewMode:I,userAnswer:O})})();case"FILL_IN_BLANK":return(()=>{const n=T.content&&"object"==typeof T.content?T.content:{},s=T.answer??{},a=Boolean(s.caseSensitive),o="string"==typeof n.question?n.question:"string"==typeof T.content?T.content:"",c="box"===String(n.blankStyle||"").toLowerCase()?"box":"underline",u=(()=>{if(Array.isArray(s.answers)&&s.answers.length>0)return s.answers.map(e=>Array.isArray(e)?String(e[0]??""):String(e??""));if(Array.isArray(s.blanks)&&s.blanks.length>0)return"string"==typeof s.blanks[0]?s.blanks.map(e=>String(e??"")):s.blanks.map(e=>String(e?.correctAnswer??""));const e=[...o.matchAll(/\{(\d+)\}/g)].map(e=>Number(e[1]));if(e.length>0){const r=Math.max(...e);return Array.from({length:r+1},()=>"")}return[""]})(),m=Array.isArray(B)&&B.length>0?B:Array.from({length:u.length},()=>""),p=(e,r)=>{const t=Array.from({length:Math.max(u.length,m.length,e+1)},(e,r)=>m[r]??"");t[e]=r,F(t),W(t)},g=(e,r)=>{const t=u[e]??"";return!!r&&(a?r===t:r.toLowerCase()===t.toLowerCase())},b=(r,t)=>R(r).map((r,n)=>{if("text"===r.kind)return r.text?e("span",{className:r.bold?"font-semibold":void 0,children:r.text},`${t}-t${n}`):null;const s=m[r.index]||"",a=I&&g(r.index,s),o="box"===c;return e("input",{type:"text",value:s,disabled:I,maxLength:o?Math.max(1,(u[r.index]||" ").length):void 0,"aria-label":`Blank ${r.index+1}`,onChange:e=>p(r.index,e.target.value),className:`mx-0.5 inline-block bg-transparent text-center outline-none ${o?"h-7 w-7 rounded border-2 px-0 text-sm font-semibold uppercase":"min-w-[4.5rem] border-0 border-b-2 px-1 text-base"} ${I?a?"border-green-500 bg-green-50 text-green-800":"border-red-500 bg-red-50 text-red-800":"border-slate-400"}`},`${t}-b${r.index}`)});if(o&&/\{\d+\}/.test(o)){const s=o.includes("\n")?o.split("\n"):[o];return r("div",{className:"space-y-3",children:[n.imageUrl?e("div",{className:"flex justify-center",children:e("img",{src:String(n.imageUrl),alt:"",className:"max-h-[200px] rounded-lg object-contain"})}):null,e("div",{className:"space-y-2 text-base leading-relaxed text-slate-800",children:s.map((r,t)=>e("div",{className:"flex flex-wrap items-baseline",children:b(r,`fill-l${t}`)},`fill-line-${t}`))}),I?r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50/80 p-2.5 text-sm text-emerald-800",children:[e("span",{className:"font-semibold",children:"Đáp án: "}),u.map((t,n)=>r("span",{className:"mr-2",children:["(",n+1,") ",e("span",{className:"font-bold underline",children:t||"—"})]},`ans-${n}`))]}):null,!I&&e(t,{onClick:()=>E?.(m),disabled:m.some(e=>!String(e??"").trim()),children:"Nộp đáp án"})]})}return r("div",{className:"space-y-4",children:[u.map((t,n)=>{const s=m[n]||"",a=I&&g(n,s);return r("div",{className:"flex items-center space-x-2",children:[r("span",{className:"text-sm font-medium",children:["Chỗ trống ",n+1,":"]}),e(i,{value:s,onChange:e=>p(n,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(I?a?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:I}),I&&a&&e(l,{className:"h-4 w-4 text-green-600"}),I&&!a&&s&&e(d,{className:"h-4 w-4 text-red-600"})]},n)}),!I&&e(t,{onClick:()=>{E&&E(m)},disabled:m.some(e=>""===String(e??"").trim()),children:"Nộp đáp án"})]})})();case"TRUE_FALSE":return(()=>{const n=T.answer,{correctAnswer:s}=n;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(t=>{const n=k===t.value;return e("div",{className:`flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${n?"border-blue-500 bg-blue-50":"border-gray-200 hover:border-gray-300"} ${I&&t.value===s?"border-green-500 bg-green-50":I&&n&&t.value!==s?"border-red-500 bg-red-50":""}`,onClick:()=>!I&&W(t.value),children:r("div",{className:"flex w-full items-center",children:[e("div",{className:`mr-3 h-4 w-4 rounded-full border-2 ${n?"border-blue-500 bg-blue-500":"border-gray-300"} ${I&&t.value===s?"border-green-500 bg-green-500":""}`,children:n&&e("div",{className:"m-auto h-2 w-2 rounded-full bg-white"})}),e("span",{className:"flex-1",children:t.label}),I&&t.value===s&&e(l,{className:"ml-2 h-4 w-4 text-green-600"}),I&&n&&t.value!==s&&e(d,{className:"ml-2 h-4 w-4 text-red-600"})]})},t.value.toString())}),!I&&e(t,{onClick:()=>{E&&E(k)},disabled:null===k,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const n=T.answer,{minWords:s,maxWords:a}=n,o=k?k.trim().split(/\s+/).length:0;return r("div",{className:"space-y-4",children:[r("div",{className:"text-sm text-gray-600",children:[s&&a?`Số từ: ${s} - ${a} từ`:s?`Tối thiểu: ${s} từ`:a?`Tối đa: ${a} từ`:"",k&&r("span",{className:"ml-2 "+(o>=(s||0)&&o<=(a||1/0)?"text-green-600":"text-red-600"),children:["(",o," từ)"]})]}),e("textarea",{value:k||"",onChange:e=>W(e.target.value),placeholder:"Viết câu trả lời của bạn ở đây...",className:"h-32 w-full resize-none rounded-lg border border-gray-300 p-3 focus:border-blue-500 focus:ring-1 focus:ring-blue-500",disabled:I}),!I&&e(t,{onClick:()=>{E&&E(k)},disabled:!k||k.trim().length<(s||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const n=T.answer,{audioUrl:s,blanks:a}=n;return r("div",{className:"space-y-4",children:[r("div",{className:"flex items-center gap-4 rounded-lg bg-blue-50 p-4",children:[r(t,{onClick:V,variant:"outline",className:"gap-2",children:[e(c,{className:"h-5 w-5"}),"Phát âm thanh (",$,"/",n.maxPlayCount||3,")"]}),e("audio",{ref:U,src:s})]}),e("div",{className:"space-y-3",children:a.map((t,n)=>r("div",{className:"flex items-center gap-3",children:[r("span",{className:"w-20 font-medium",children:["Blank ",n+1,":"]}),e(i,{value:B[n],onChange:e=>{const r=[...B];r[n]=e.target.value,F(r),W(r)},placeholder:"Nhập câu trả lời...",disabled:I,className:I?B[n]?.toLowerCase()===a[n]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),I&&r("span",{className:"text-sm text-gray-600",children:["Đáp án: ",e("strong",{children:a[n]})]})]},n))})]})})();case"TICK_TRUE_OR_FALSE":case"TICK_YES_OR_NO":return(()=>{const t=T.answer,{statements:n}=t;return e("div",{className:"space-y-3",children:n.map(t=>e("div",{className:`rounded-lg border-2 p-4 ${I&&q[t.id]===t.isCorrect?"border-green-500 bg-green-50":""} ${I&&q[t.id]!==t.isCorrect?"border-red-500 bg-red-50":"border-gray-200"} `,children:r("div",{className:"flex items-start gap-4",children:[r("div",{className:"mt-1 flex gap-2",children:[e("button",{onClick:()=>{const e={...q,[t.id]:!0};M(e),W(e)},disabled:I,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!0===q[t.id]?"border-green-500 bg-green-500 text-white":"border-gray-300"} `,children:e(l,{className:"h-5 w-5"})}),e("button",{onClick:()=>{const e={...q,[t.id]:!1};M(e),W(e)},disabled:I,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!1===q[t.id]?"border-red-500 bg-red-500 text-white":"border-gray-300"} `,children:e(d,{className:"h-5 w-5"})})]}),e("p",{className:"flex-1",children:t.text}),I&&e("span",{className:"text-sm font-medium",children:t.isCorrect?"✓ Đúng":"✗ Sai"})]})},t.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=T.answer,t=T.correctAnswer||T.correct_answer,n=(r?.subQuestions||[]).map(e=>{const r=t?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(h,{questionData:{subQuestions:n},onSubmit:e=>{W(e),E?.(e)},isReviewMode:I,userAnswer:O})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const t=T.answer,{passage:n,blanks:s}=t,a=n.split(/\{(\d+)\}/);return e("div",{className:"space-y-4",children:e("div",{className:"prose max-w-none",children:e("div",{className:"text-lg leading-relaxed",children:a.map((t,n)=>{const a=parseInt(t);if(!isNaN(a)){const t=s.find(e=>e.index===a);return r("span",{className:"mx-1 inline-flex items-center",children:[e(i,{value:Q[a]||"",onChange:e=>{const r={...Q,[a]:e.target.value};G(r),W(r)},className:`inline-block h-8 w-32 text-center ${I&&Q[a]===t?.correctAnswer?"border-green-500 bg-green-50":""} ${I&&Q[a]!==t?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:I}),I&&r("span",{className:"ml-2 text-sm text-green-600",children:["(",t?.correctAnswer,")"]})]},n)}return e("span",{children:t},n)})})})})})();case"WRITE_SHORT_PARAGRAPH":return(()=>{const r=T.answer;return e(w,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{W(e),E?.(e)},isReviewMode:I,userAnswer:O})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=T.answer;return e(A,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{W(e),E?.(e)},isReviewMode:I,userAnswer:O})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=T.answer,t=T.content,n=T.questionGroup;let s=[];r?.subQuestions&&r.subQuestions.length>0?s=r.subQuestions:t?.imageUrl&&t?.word&&(s=[{id:T.id,imageUrl:t.imageUrl,word:t.word,correctAnswer:r?.correctAnswer||"",groupNumber:n?.groupNumber||1}]);const a=n?.payload?.hintLetters||r?.hintLetters||t?.hintLetters||[],o=n?.groupNumber||1,i={};a.length>0&&(i[o]=a);const l={subQuestions:s,hintLetters:a,hintLettersByGroup:i,groups:[{number:o,questionCount:s.length}]};return e(N,{questionData:l,userAnswer:O,isReviewMode:I,explanation:T.explanation,onSubmit:e=>{W(e),E?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=T.answer;return e(x,{questionData:r,userAnswer:O,isReviewMode:I,onSubmit:e=>{W(e),E?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=T.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{W(e),E?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=T.answer;return e(y,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:O,isReviewMode:I,onSubmit:e=>{W(e),E?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=T.content,t=T.answer,n=(r?.subQuestions||t?.subQuestions||[]).map(e=>{const r=t?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(v,{questionData:{subQuestions:n},userAnswer:O,isReviewMode:I,onSubmit:e=>{W(e),E?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=T.content,t=T.answer,n=(r?.subQuestions||t?.subQuestions||[]).map(e=>{const r=t?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(S,{questionData:{subQuestions:n},userAnswer:O,isReviewMode:I,onSubmit:e=>{W(e),E?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=T.content,t=T.answer;return e(C,{questionData:{content:{grid:r?.grid||[],rows:r?.rows||3,columns:r?.columns||3,disabledCells:r?.disabledCells||[]},answer:{answers:t?.answers||{},missingLetters:t?.missingLetters||[],caseSensitive:t?.caseSensitive||!1}},userAnswer:O,isReviewMode:I,onSubmit:e=>{W(e),E?.(e)}})})();case"WORD_FILL_PARAGRAPH":return(()=>{const t=T.content&&"object"==typeof T.content?T.content:{},n=T.answer??{},s=String(t.paragraph||""),a=Array.isArray(n.answers)?n.answers.map(e=>String(e??"")):[],o=(Array.isArray(t.wordBank)?t.wordBank:[]).map(e=>{if("string"==typeof e)return{word:e,options:[]};const r=Array.isArray(e?.options)?e.options.map(String):[];return{word:String(e?.word??r[0]??""),options:r,isExample:Boolean(e?.isExample)}}),i=String(t.viewMode||(o.some(e=>e.options.length)?"WITH_OPTIONS":"DEFAULT")).toUpperCase(),l=[...s.matchAll(/\{(\d+)\}/g)].map(e=>Number(e[1])),c=l.length>0?Math.max(...l)+1:Math.max(a.length,o.length,1),d=Array.isArray(O)?O:Array.isArray(k)?k:Array.from({length:c},()=>""),u=(e,r)=>{const t=Array.from({length:c},(e,r)=>d[r]??"");t[e]=r,W(t)},m=(t,n)=>t.split(/(\{\d+\})/g).map((t,s)=>{const l=/^\{(\d+)\}$/.exec(t);if(!l)return e("span",{children:t},`${n}-t${s}`);const c=Number(l[1]),m=o[c],p=m?.options?.length?m.options:o.flatMap(e=>e.options).filter(Boolean),g=d[c]??"",b=a[c]??"",h=I&&g&&b&&g.toLowerCase()===b.toLowerCase();return"WITH_OPTIONS"===i&&p.length>0?r("select",{value:g,disabled:I,onChange:e=>u(c,e.target.value),className:"mx-1 inline-block min-w-[7rem] rounded border px-2 py-1 text-sm "+(I?h?"border-green-500 bg-green-50":"border-red-500 bg-red-50":"border-slate-300"),children:[e("option",{value:"",children:"—"}),p.map(r=>e("option",{value:r,children:r},r))]},`${n}-b${c}`):e("input",{type:"text",value:g,disabled:I,onChange:e=>u(c,e.target.value),className:"mx-1 inline-block min-w-[5rem] border-0 border-b-2 px-1 text-center outline-none "+(I?h?"border-green-500 bg-green-50":"border-red-500 bg-red-50":"border-slate-400")},`${n}-b${c}`)});return s.trim()?r("div",{className:"space-y-3",children:[t.title?e("h3",{className:"text-center text-lg font-bold uppercase text-slate-800",children:t.title}):null,t.imageUrl?e("div",{className:"flex justify-center",children:e("img",{src:String(t.imageUrl),alt:"",className:"max-h-[180px] rounded-lg object-contain"})}):null,e("div",{className:"space-y-2 text-base leading-relaxed text-slate-800",children:s.split("\n").map((r,t)=>e("div",{className:"flex flex-wrap items-baseline",children:m(r,`wfp-${t}`)},`wfp-l${t}`))}),I&&a.length>0?r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50/80 p-2.5 text-sm text-emerald-800",children:[e("span",{className:"font-semibold",children:"Đáp án: "}),a.map((t,n)=>r("span",{className:"mr-2",children:["(",n+1,") ",e("span",{className:"font-bold underline",children:t||"—"})]},`wfp-a${n}`))]}):null]}):e("div",{className:"text-sm text-gray-500",children:"Không có nội dung đoạn văn."})})();case"WORD_FILL_STRUCTURED_FORM":return(()=>{const r=T.content&&"object"==typeof T.content?T.content:{},t=T.answer??T.correctAnswer??{},n=t?.answers&&"object"==typeof t.answers&&!Array.isArray(t.answers)?t.answers:{},s=String(r.content||""),a=String(r.blankStyle||"").toLowerCase(),o="letter"===a||"box"===a?"letter":"phrase"===a||"sentence"===a?"phrase":"word",i=O&&"object"==typeof O&&!Array.isArray(O)?O:k&&"object"==typeof k&&!Array.isArray(k)?k:I?n:{},l="number"==typeof T.questionNumber&&T.questionNumber>0?T.questionNumber:void 0,c=Array.isArray(r.wordBank)?r.wordBank:void 0,d=c?.map(e=>{if(null==e||"object"!=typeof e)return null;const r=e;if(Array.isArray(r.options)){const e=r.options.map(e=>String(e??"").trim()).filter(Boolean);return e.length>=2?{options:e}:null}const t=String(r.word??"").trim();return t?r.isExample?{word:t,isExample:!0}:{word:t}:null}),u=Boolean(d?.some(e=>null!=e&&"options"in e&&Array.isArray(e.options)&&(e.options?.length??0)>=2)),m=Boolean(d?.some(e=>null!=e&&"word"in e&&e.word)),p=String(r.viewMode||"").trim()||(u?"WITH_OPTIONS":m?"WITH_WORD_BANK":void 0);return e(_,{questionData:{title:String(r.title||""),imageUrl:r.imageUrl?String(r.imageUrl):void 0,content:s,blankStyle:o,answers:n,caseSensitive:Boolean(t?.caseSensitive),explanation:T.explanation,...p?{viewMode:p}:{},...(u||m)&&d?{wordBank:d}:{}},userAnswer:i,isReviewMode:I,autoFillCorrectAnswers:I&&0===Object.keys(i).length,explanation:T.explanation,questionNumber:l,onAnswerChange:(e,r)=>{const t=k&&"object"==typeof k&&!Array.isArray(k)?k:{};W({...t,[e]:r})}})})();default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",T.type]})}},J=(()=>{if("WORD_FILL_STRUCTURED_FORM"===T.type)return null;if("FILL_IN_BLANK"===T.type||"WORD_FILL_PARAGRAPH"===T.type)return"Câu hỏi";if("string"==typeof T.content)return L(T.content)||"Câu hỏi";const e=T.content,r=e?.title||e?.question||e?.paragraph||"";return e?.title?L(e.title)||"Câu hỏi":"string"==typeof r&&(/\{\d+\}/.test(r)||/\*\*/.test(r))?"Câu hỏi":L(r)||"Câu hỏi"})();return"WORD_FILL_STRUCTURED_FORM"===T.type?e("div",{className:"space-y-4",children:z()}):r(n,{children:[J?e(a,{children:r(o,{className:"flex items-center justify-between",children:[e("span",{className:"min-w-0 pr-2 text-base font-semibold leading-snug",children:J}),r("span",{className:"shrink-0 text-sm font-normal text-gray-500",children:[T.points," điểm"]})]})}):null,e(s,{className:J?"space-y-4":"space-y-4 pt-4",children:z()})]})}function L(e){return String(e||"").replace(/\*\*/g,"").replace(/\{(\d+)\}/g,"___").replace(/\s+/g," ").trim()}
@@ -1 +1 @@
1
- const e=new Set(["READ_PASSAGE_AND_ANSWER_QUESTIONS","LOOK_PICTURE_FILL_WORD_HINT","LABEL_THE_PICTURE","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING"]);export function transformResultAnswersForReview(e,s=[]){const o=function(e){const r=new Map;return e.forEach(e=>{const t=[e.partId??"unknown",e.questionGroup?.id??"none",e.questionType].join(":");r.set(t,[...r.get(t)??[],e])}),r}(e),i={parts:[],studentAnswers:{},correctAnswersMap:new Map,isCorrectMap:{},correctAnswerTextMap:{},correctOptionIdMap:{},cellAnswersMap:{},essayGradingMap:{},partScoresMap:{},rawCorrectAnswerMap:{},rawStudentAnswerMap:{},questionScoreMap:{}},u=[];for(const e of o.values()){e.sort((e,r)=>e.questionNumber-r.questionNumber);const o=e[0];if(!o)continue;const c=s.find(e=>String(e.id)===String(o.partId)),p=r(c,o.questionNumber),A=a(c?.name)??o.partNo??0;i.parts.push({partId:o.partId,partNo:A,questionType:o.questionType,name:c?.name,instructions:p?.name||p?.instructions||c?.instructions,audioUrl:c?.audioUrl,questions:e.map(t)});const _=e.filter(e=>!e.isExample);u.push({score:_.reduce((e,r)=>e+(r.score??0),0),maxScore:_.reduce((e,r)=>e+r.maxScore,0),correctCount:_.filter(e=>!0===e.isCorrect).length,totalCount:_.length}),e.forEach(e=>n(i,e))}const c=i.parts.map((e,r)=>r).sort((e,r)=>{const t=i.parts[e],n=i.parts[r];return t.partNo-n.partNo||(t.questions[0]?.question_number??0)-(n.questions[0]?.question_number??0)});return i.parts=c.map(e=>i.parts[e]),c.forEach((e,r)=>{i.partScoresMap[r]=u[e]}),i}export const transformDetailedResultForReview=transformResultAnswersForReview;function r(e,r){const t=r-1;return e?.sections?.find(e=>null!=e.startIndex&&null!=e.endIndex&&t>=e.startIndex&&t<=e.endIndex)}function t(e){const r=o(e.questionContent);return{id:e.questionId,content:{...r,_correctAnswer:e.correctAnswer},correct_answer:e.correctAnswer,total_points:String(e.maxScore),question_type:e.questionType,is_example:e.isExample??!1,explanation:e.explanation??"",difficult_level:"MEDIUM",question_skill_type:"",exam_id:"",part_id:e.partId??"",part_no:e.partNo??0,question_number:e.questionNumber,questionGroup:e.questionGroup??null,score:e.score}}function n(r,t){const n=function(e,r,t){if(null==t)return;const n=o(t);if("CHOOSE_THE_CORRECT_ANSWER"===r||"CHOOSE_CORRECT_ADJECTIVE"===r){const r=s(e,n.answer);return r.length>1?r:r[0]}if("READ_PASSAGE_AND_ANSWER_QUESTIONS"===r&&i(n.subAnswers))return Object.fromEntries(Object.entries(n.subAnswers).map(([e,r])=>[e,"number"==typeof r?`${e}-opt-${r}`:r]));if("LABEL_THE_PICTURE"===r)return n.answers??t;if("LOOK_PICTURE_FILL_WORD_HINT"===r&&"string"==typeof n.answer&&n.answer.includes(","))return Object.fromEntries(n.answer.split(",").map((r,t)=>[`${e}-blank-${t}`,r.trim()]));if("WORD_FILL_PARAGRAPH"===r){if(Array.isArray(n.answers))return n.answers;if(Array.isArray(n.answer))return n.answer;if(Array.isArray(t))return t;if("string"==typeof t)return[t];return Object.keys(n).filter(e=>/^\d+$/.test(e)).sort((e,r)=>Number(e)-Number(r)).map(e=>n[e])}return"READ_AND_COLOR_OBJECTS"===r||"IMAGE_OBJECT_MATCHING"===r?n.answers??t:"WRITE_A_SHORT_LETTER"===r?"string"==typeof t?t:n.answer??"":n.answer??t}(t.questionId,t.questionType,t.studentAnswer);void 0!==n&&(e.has(t.questionType)&&i(n)?Object.assign(r.studentAnswers,n):r.studentAnswers[t.questionId]=n),r.correctAnswersMap.set(t.questionId,t.correctAnswer),r.isCorrectMap[t.questionId]=t.isCorrect,r.rawCorrectAnswerMap[t.questionId]=t.correctAnswer,r.rawStudentAnswerMap[t.questionId]=t.studentAnswer,r.questionScoreMap[t.questionId]={score:t.score,maxScore:t.maxScore},function(e,r){const t=o(r.correctAnswer),n=s(r.questionId,t.answer);n.length&&(e.correctOptionIdMap[r.questionId]=n.join(","));"READ_PASSAGE_AND_ANSWER_QUESTIONS"===r.questionType&&i(t.answers)&&Object.entries(t.answers).forEach(([r,t])=>{"number"==typeof t&&(e.correctOptionIdMap[r]=`${r}-opt-${t}`)});const a=function(e){if(Array.isArray(e.answers))return e.answers.map(String);if(i(e.answers))return Object.values(e.answers).map(String);const r=e.answer??e.correctAnswer;return null==r?[]:[String(r)]}(t);a[0]&&(e.correctAnswerTextMap[r.questionId]=a[0]);"WORD_FILL_PARAGRAPH"===r.questionType&&a.forEach((t,n)=>{e.correctAnswerTextMap[`${r.questionId}-blank-${n}`]=t});"LABEL_THE_PICTURE"===r.questionType&&i(t.labels)&&Object.entries(t.labels).forEach(([r,t])=>{e.correctAnswerTextMap[r]=String(t)});"FILL_MISSING_WORDS_IN_GRID"===r.questionType&&i(t.cells)&&(e.cellAnswersMap[r.questionId]={cells:t.cells,caseSensitive:!0===t.caseSensitive})}(r,t),"WRITE_A_SHORT_LETTER"!==t.questionType&&"WRITE_SHORT_PARAGRAPH"!==t.questionType||(r.essayGradingMap[t.questionId]={score:t.teacherScore??t.score,maxScore:t.maxScore,teacherFeedback:t.teacherFeedback??null,writingAssessment:t.writingAssessment??null})}function s(e,r){return(Array.isArray(r)?r:[r]).filter(e=>"number"==typeof e||"string"==typeof e).map(r=>"number"==typeof r?`${e}-opt-${r}`:r)}function o(e){return i(e)?e:{}}function i(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function a(e){const r=e?.match(/\d+/);return r?Number(r[0]):void 0}
1
+ const e=new Set(["READ_PASSAGE_AND_ANSWER_QUESTIONS","LOOK_PICTURE_FILL_WORD_HINT","LABEL_THE_PICTURE","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING"]);export function transformResultAnswersForReview(e,s=[]){const o=function(e){const r=new Map;return e.forEach(e=>{const t=[e.partId??"unknown",e.questionGroup?.id??"none",e.questionType].join(":");r.set(t,[...r.get(t)??[],e])}),r}(e),i={parts:[],studentAnswers:{},correctAnswersMap:new Map,isCorrectMap:{},correctAnswerTextMap:{},correctOptionIdMap:{},cellAnswersMap:{},essayGradingMap:{},partScoresMap:{},rawCorrectAnswerMap:{},rawStudentAnswerMap:{},questionScoreMap:{}},u=[];for(const e of o.values()){e.sort((e,r)=>e.questionNumber-r.questionNumber);const o=e[0];if(!o)continue;const c=s.find(e=>String(e.id)===String(o.partId)),p=r(c,o.questionNumber),A=a(c?.name)??o.partNo??0;i.parts.push({partId:o.partId,partNo:A,questionType:o.questionType,name:c?.name,instructions:p?.name||p?.instructions||c?.instructions,audioUrl:c?.audioUrl,questions:e.map(t)});const _=e.filter(e=>!e.isExample);u.push({score:_.reduce((e,r)=>e+(r.score??0),0),maxScore:_.reduce((e,r)=>e+r.maxScore,0),correctCount:_.filter(e=>!0===e.isCorrect).length,totalCount:_.length}),e.forEach(e=>n(i,e))}const c=i.parts.map((e,r)=>r).sort((e,r)=>{const t=i.parts[e],n=i.parts[r];return t.partNo-n.partNo||(t.questions[0]?.question_number??0)-(n.questions[0]?.question_number??0)});return i.parts=c.map(e=>i.parts[e]),c.forEach((e,r)=>{i.partScoresMap[r]=u[e]}),i}export const transformDetailedResultForReview=transformResultAnswersForReview;function r(e,r){const t=r-1;return e?.sections?.find(e=>null!=e.startIndex&&null!=e.endIndex&&t>=e.startIndex&&t<=e.endIndex)}function t(e){const r=o(e.questionContent);return{id:e.questionId,content:{...r,_correctAnswer:e.correctAnswer},correct_answer:e.correctAnswer,total_points:String(e.maxScore),question_type:e.questionType,is_example:e.isExample??!1,explanation:e.explanation??"",difficult_level:"MEDIUM",question_skill_type:"",exam_id:"",part_id:e.partId??"",part_no:e.partNo??0,question_number:e.questionNumber,questionGroup:e.questionGroup??null,score:e.score}}function n(r,t){const n=function(e,r,t){if(null==t)return;const n=o(t);if("CHOOSE_THE_CORRECT_ANSWER"===r||"CHOOSE_CORRECT_ADJECTIVE"===r){const r=n.answer??n.optionId??n.optionIds,t=s(e,r);return t.length>1?t:t[0]}if("READ_PASSAGE_AND_ANSWER_QUESTIONS"===r&&i(n.subAnswers))return Object.fromEntries(Object.entries(n.subAnswers).map(([e,r])=>[e,"number"==typeof r?`${e}-opt-${r}`:r]));if("LABEL_THE_PICTURE"===r)return n.answers??t;if("LOOK_PICTURE_FILL_WORD_HINT"===r&&"string"==typeof n.answer&&n.answer.includes(","))return Object.fromEntries(n.answer.split(",").map((r,t)=>[`${e}-blank-${t}`,r.trim()]));if("WORD_FILL_PARAGRAPH"===r){if(Array.isArray(n.answers))return n.answers;if(Array.isArray(n.answer))return n.answer;if(Array.isArray(t))return t;if("string"==typeof t)return[t];return Object.keys(n).filter(e=>/^\d+$/.test(e)).sort((e,r)=>Number(e)-Number(r)).map(e=>n[e])}return"READ_AND_COLOR_OBJECTS"===r||"IMAGE_OBJECT_MATCHING"===r?n.answers??t:"WRITE_A_SHORT_LETTER"===r?"string"==typeof t?t:n.answer??"":n.answer??t}(t.questionId,t.questionType,t.studentAnswer);void 0!==n&&(e.has(t.questionType)&&i(n)?Object.assign(r.studentAnswers,n):r.studentAnswers[t.questionId]=n),r.correctAnswersMap.set(t.questionId,t.correctAnswer),r.isCorrectMap[t.questionId]=t.isCorrect,r.rawCorrectAnswerMap[t.questionId]=t.correctAnswer,r.rawStudentAnswerMap[t.questionId]=t.studentAnswer,r.questionScoreMap[t.questionId]={score:t.score,maxScore:t.maxScore},function(e,r){const t=o(r.correctAnswer),n=s(r.questionId,t.answer);n.length&&(e.correctOptionIdMap[r.questionId]=n.join(","));"READ_PASSAGE_AND_ANSWER_QUESTIONS"===r.questionType&&i(t.answers)&&Object.entries(t.answers).forEach(([r,t])=>{"number"==typeof t&&(e.correctOptionIdMap[r]=`${r}-opt-${t}`)});const a=function(e){if(Array.isArray(e.answers))return e.answers.map(String);if(i(e.answers))return Object.values(e.answers).map(String);const r=e.answer??e.correctAnswer;return null==r?[]:[String(r)]}(t);a[0]&&(e.correctAnswerTextMap[r.questionId]=a[0]);"WORD_FILL_PARAGRAPH"===r.questionType&&a.forEach((t,n)=>{e.correctAnswerTextMap[`${r.questionId}-blank-${n}`]=t});"LABEL_THE_PICTURE"===r.questionType&&i(t.labels)&&Object.entries(t.labels).forEach(([r,t])=>{e.correctAnswerTextMap[r]=String(t)});"FILL_MISSING_WORDS_IN_GRID"===r.questionType&&i(t.cells)&&(e.cellAnswersMap[r.questionId]={cells:t.cells,caseSensitive:!0===t.caseSensitive})}(r,t),"WRITE_A_SHORT_LETTER"!==t.questionType&&"WRITE_SHORT_PARAGRAPH"!==t.questionType||(r.essayGradingMap[t.questionId]={score:t.teacherScore??t.score,maxScore:t.maxScore,teacherFeedback:t.teacherFeedback??null,writingAssessment:t.writingAssessment??null})}function s(e,r){return(Array.isArray(r)?r:[r]).filter(e=>"number"==typeof e||"string"==typeof e).map(r=>"number"==typeof r?`${e}-opt-${r}`:r)}function o(e){return i(e)?e:{}}function i(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function a(e){const r=e?.match(/\d+/);return r?Number(r[0]):void 0}
@@ -3,6 +3,7 @@ interface CambridgeYleAudioPlayerProps {
3
3
  onPlay?: () => void;
4
4
  onPause?: () => void;
5
5
  compact?: boolean;
6
+ dense?: boolean;
6
7
  }
7
- export default function CambridgeYleAudioPlayer({ audioSrc, onPlay, onPause, compact, }: CambridgeYleAudioPlayerProps): import("react").JSX.Element;
8
+ export default function CambridgeYleAudioPlayer({ audioSrc, onPlay, onPause, compact, dense, }: CambridgeYleAudioPlayerProps): import("react").JSX.Element;
8
9
  export {};
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as a,useState as r,useEffect as n,useMemo as l}from"react";import{Pause as s,Play as i,RotateCcw as o}from"lucide-react";import{usePresignedFileUrl as c}from"../../../shared/lib/hooks/usePresignedFileUrl";const d=[.75,1,1.25,1.5];export default function u({audioSrc:u,onPlay:h,onPause:m,compact:f=!1}){const{previewUrl:p}=c(u),v=a(null),[b,x]=r(!1),[g,w]=r(0),[N,y]=r(0),[k,M]=r(1),L=l(()=>{const e=[];let t=7;for(let a=0;a<E;a++){t=(9301*t+49297)%233280;const r=.25+t/233280*.75,n=.25*Math.sin(a/(E-1)*Math.PI)+.75;e.push(Math.max(.2,r*n))}return e},[E=48]);var E;n(()=>{const e=v.current;if(!e)return;const t=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&y(e.duration)},a=()=>{w(e.currentTime),t()},r=()=>{x(!1),w(0)};return t(),e.addEventListener("loadedmetadata",t),e.addEventListener("durationchange",t),e.addEventListener("canplay",t),e.addEventListener("timeupdate",a),e.addEventListener("ended",r),()=>{e.removeEventListener("loadedmetadata",t),e.removeEventListener("durationchange",t),e.removeEventListener("canplay",t),e.removeEventListener("timeupdate",a),e.removeEventListener("ended",r)}},[]),n(()=>{v.current&&(v.current.playbackRate=k)},[k]);const j=()=>{const e=v.current;e&&(b?(e.pause(),x(!1),m?.()):(e.play().catch(()=>{x(!1)}),x(!0),h?.()))},C=e=>{const t=Math.max(0,e),a=Math.floor(t/60),r=Math.floor(t%60);return`${String(a).padStart(2,"0")}:${String(r).padStart(2,"0")}`},P=N>0?g/N:0,S=100*P,T=Math.max(0,N-g);if(f)return t("div",{className:"relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3 shadow-sm transition-all hover:border-blue-200 hover:shadow-md",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),t("div",{className:"flex items-center gap-3 sm:gap-4",children:[t("button",{type:"button",onClick:j,className:"relative flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-coral-500 text-white shadow-lg shadow-coral-500/30 transition-all duration-200 hover:scale-105 hover:bg-coral-600 active:scale-95","aria-label":b?"Pause":"Play",children:[b?e(s,{className:"h-5 w-5 fill-current"}):e(i,{className:"ml-1 h-5 w-5 fill-current"}),b&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-coral-400 opacity-40"})]}),t("div",{className:"flex min-w-0 flex-1 flex-col justify-center gap-2",children:[t("div",{className:"flex items-center justify-between px-1",children:[e("span",{className:"text-sm font-black uppercase tracking-widest text-blue-900 font-[family-name:var(--font-nunito)]",children:"Audio"}),t("div",{className:"flex items-center gap-1.5 font-mono text-xs font-bold text-slate-500 tabular-nums",children:[e("span",{className:"text-blue-600",children:C(g)}),e("span",{children:"/"}),e("span",{children:C(N)})]})]}),t("div",{className:"relative flex items-center",children:[e("div",{className:"h-3 w-full overflow-hidden rounded-full bg-blue-50 shadow-inner",children:e("div",{className:"h-full rounded-full bg-blue-500 transition-[width] duration-150",style:{width:`${S}%`}})}),e("input",{type:"range",min:"0",max:N||0,step:"0.1",value:g,onChange:e=>{const t=parseFloat(e.target.value);v.current&&(v.current.currentTime=t),w(t)},className:"absolute inset-0 w-full cursor-pointer opacity-0","aria-label":"Seek"})]})]})]})]});const $=56,F=26.5,R=2*Math.PI*F;return t("div",{className:"relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3.5 shadow-sm transition-shadow hover:border-blue-200 hover:shadow-md",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),e("div",{className:"pointer-events-none absolute -right-6 -top-6 h-20 w-20 rounded-full bg-blue-100 opacity-60 blur-xl"}),t("div",{className:"relative flex items-center gap-3",children:[t("button",{type:"button",onClick:()=>{const e=v.current;e&&(e.currentTime=Math.max(0,e.currentTime-5),w(e.currentTime))},className:"relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-slate-500 transition-all hover:bg-slate-100 hover:text-blue-600 active:scale-95","aria-label":"Rewind 5 seconds",title:"Quay lại 5 giây",children:[e(o,{className:"h-4 w-4",strokeWidth:2.5}),e("span",{className:"pointer-events-none absolute bottom-1 right-1 text-[8px] font-black leading-none",children:"5"})]}),t("div",{className:"relative shrink-0",style:{width:$,height:$},children:[t("svg",{width:$,height:$,className:"absolute inset-0 -rotate-90","aria-hidden":!0,children:[e("circle",{cx:28,cy:28,r:F,fill:"none",stroke:"rgb(239 246 255)",strokeWidth:3}),e("circle",{cx:28,cy:28,r:F,fill:"none",stroke:"url(#audio-ring-gradient)",strokeWidth:3,strokeLinecap:"round",strokeDasharray:R,strokeDashoffset:R*(1-P),className:"transition-[stroke-dashoffset] duration-150"}),e("defs",{children:t("linearGradient",{id:"audio-ring-gradient",x1:"0",y1:"0",x2:"1",y2:"1",children:[e("stop",{offset:"0%",stopColor:"#3B82F6"}),e("stop",{offset:"100%",stopColor:"#06b6d4"})]})})]}),t("button",{type:"button",onClick:j,className:"absolute inset-1.5 flex items-center justify-center rounded-full bg-coral-500 text-white shadow-lg shadow-coral-500/30 transition-all hover:shadow-xl hover:shadow-coral-500/50 hover:bg-coral-600 active:scale-95","aria-label":b?"Pause":"Play",children:[b?e(s,{className:"h-4 w-4 fill-current"}):e(i,{className:"ml-0.5 h-4 w-4 fill-current"}),b&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-coral-400 opacity-40"})]})]}),t("div",{className:"flex min-w-0 flex-1 flex-col gap-1",children:[e("div",{onClick:e=>{const t=v.current;if(!t||!N)return;const a=e.currentTarget.getBoundingClientRect(),r=Math.min(1,Math.max(0,(e.clientX-a.left)/a.width))*N;t.currentTime=r,w(r)},className:"group/wave relative flex h-8 cursor-pointer items-center gap-[2px]",role:"slider","aria-label":"Seek","aria-valuemin":0,"aria-valuemax":N||0,"aria-valuenow":g,children:L.map((t,a)=>{const r=(a+.5)/48,n=r<=P,l=Math.abs(r-P)<1/48/2;return e("span",{className:`flex-1 rounded-full transition-all duration-200 ${n?"bg-blue-500":"bg-slate-200 group-hover/wave:bg-slate-300"} ${l&&b?"animate-wave-pulse":""}`,style:{height:100*t+"%"}},a)})}),t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-baseline gap-1.5",children:[t("span",{className:"font-mono text-sm font-bold tabular-nums text-slate-800",children:["−",C(T)]}),t("span",{className:"font-mono text-[10px] tabular-nums text-slate-400",children:["/ ",C(N)]})]}),e("div",{className:"flex items-center gap-0.5 rounded-full border border-slate-200 bg-white p-0.5 shadow-sm",children:d.map(e=>t("button",{type:"button",onClick:()=>M(e),className:"rounded-full px-2 py-0.5 font-mono text-[10px] font-semibold tabular-nums transition-all "+(k===e?"bg-coral-500 text-white shadow-sm":"text-slate-500 hover:bg-slate-100 hover:text-slate-700"),children:[e,"×"]},e))})]})]})]})]})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as a,useState as r,useEffect as l,useMemo as n}from"react";import{Pause as s,Play as i,RotateCcw as o}from"lucide-react";import{usePresignedFileUrl as d}from"../../../shared/lib/hooks/usePresignedFileUrl";const c=[.75,1,1.25,1.5];export default function u({audioSrc:u,onPlay:h,onPause:m,compact:b=!1,dense:f=!1}){const{previewUrl:p}=d(u),v=a(null),[x,g]=r(!1),[w,N]=r(0),[y,k]=r(0),[M,L]=r(1),E=n(()=>{const e=[];let t=7;for(let a=0;a<j;a++){t=(9301*t+49297)%233280;const r=.25+t/233280*.75,l=.25*Math.sin(a/(j-1)*Math.PI)+.75;e.push(Math.max(.2,r*l))}return e},[j=48]);var j;l(()=>{const e=v.current;if(!e)return;const t=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&k(e.duration)},a=()=>{N(e.currentTime),t()},r=()=>{g(!1),N(0)};return t(),e.addEventListener("loadedmetadata",t),e.addEventListener("durationchange",t),e.addEventListener("canplay",t),e.addEventListener("timeupdate",a),e.addEventListener("ended",r),()=>{e.removeEventListener("loadedmetadata",t),e.removeEventListener("durationchange",t),e.removeEventListener("canplay",t),e.removeEventListener("timeupdate",a),e.removeEventListener("ended",r)}},[]),l(()=>{v.current&&(v.current.playbackRate=M)},[M]);const C=()=>{const e=v.current;e&&(x?(e.pause(),g(!1),m?.()):(e.play().catch(()=>{g(!1)}),g(!0),h?.()))},P=e=>{const t=Math.max(0,e),a=Math.floor(t/60),r=Math.floor(t%60);return`${String(a).padStart(2,"0")}:${String(r).padStart(2,"0")}`},S=y>0?w/y:0,T=100*S,$=Math.max(0,y-w);if(b)return t("div",{className:f?"relative w-full overflow-hidden rounded-2xl border-2 border-blue-200/90 bg-gradient-to-r from-blue-50/90 via-white to-blue-50/90 p-2.5 sm:p-3 shadow-xs transition-all hover:border-blue-400 hover:shadow-sm":"relative w-full overflow-hidden rounded-2xl border-2 border-blue-200/90 bg-gradient-to-r from-blue-50/90 via-white to-blue-50/90 p-3 sm:p-4 shadow-xs transition-all hover:border-blue-400 hover:shadow-sm",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),t("div",{className:"flex items-center gap-3 sm:gap-4",children:[t("button",{type:"button",onClick:C,className:(f?"h-10 w-10 sm:h-11 sm:w-11":"h-12 w-12")+" relative flex shrink-0 items-center justify-center rounded-full bg-gradient-to-tr from-blue-600 to-indigo-600 text-white shadow-md shadow-blue-500/30 transition-all duration-200 hover:scale-105 hover:from-blue-700 hover:to-indigo-700 active:scale-95","aria-label":x?"Pause":"Play",children:[x?e(s,{className:(f?"h-4 w-4 sm:h-5 sm:w-5":"h-5 w-5")+" fill-current"}):e(i,{className:(f?"ml-0.5 h-4 w-4 sm:h-5 sm:w-5":"ml-0.5 h-5 w-5")+" fill-current"}),x&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-blue-400 opacity-40"})]}),t("div",{className:"flex min-w-0 flex-1 flex-col justify-center gap-1.5",children:[t("div",{className:"flex items-center justify-between px-0.5",children:[e("span",{className:"text-xs font-black uppercase tracking-widest text-blue-900",children:"AUDIO"}),t("div",{className:"flex items-center gap-1 rounded-md bg-white px-2 py-0.5 text-xs font-mono font-bold text-slate-600 border border-blue-100 shadow-2xs",children:[e("span",{className:"text-blue-600",children:P(w)}),e("span",{className:"text-slate-300",children:"/"}),e("span",{children:P(y)})]})]}),t("div",{className:"relative flex items-center",children:[e("div",{className:"h-2.5 w-full overflow-hidden rounded-full bg-blue-100/80 shadow-inner",children:e("div",{className:"h-full rounded-full bg-gradient-to-r from-blue-500 to-indigo-600 transition-[width] duration-150",style:{width:`${T}%`}})}),e("input",{type:"range",min:"0",max:y||0,step:"0.1",value:w,onChange:e=>{const t=parseFloat(e.target.value);v.current&&(v.current.currentTime=t),N(t)},className:"absolute inset-0 w-full cursor-pointer opacity-0","aria-label":"Seek"})]})]})]})]});const F=56,D=26.5,I=2*Math.PI*D;return t("div",{className:"relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3.5 shadow-sm transition-shadow hover:border-blue-200 hover:shadow-md",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),e("div",{className:"pointer-events-none absolute -right-6 -top-6 h-20 w-20 rounded-full bg-blue-100 opacity-60 blur-xl"}),t("div",{className:"relative flex items-center gap-3",children:[t("button",{type:"button",onClick:()=>{const e=v.current;e&&(e.currentTime=Math.max(0,e.currentTime-5),N(e.currentTime))},className:"relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-slate-500 transition-all hover:bg-slate-100 hover:text-blue-600 active:scale-95","aria-label":"Rewind 5 seconds",title:"Quay lại 5 giây",children:[e(o,{className:"h-4 w-4",strokeWidth:2.5}),e("span",{className:"pointer-events-none absolute bottom-1 right-1 text-[8px] font-black leading-none",children:"5"})]}),t("div",{className:"relative shrink-0",style:{width:F,height:F},children:[t("svg",{width:F,height:F,className:"absolute inset-0 -rotate-90","aria-hidden":!0,children:[e("circle",{cx:28,cy:28,r:D,fill:"none",stroke:"rgb(239 246 255)",strokeWidth:3}),e("circle",{cx:28,cy:28,r:D,fill:"none",stroke:"url(#audio-ring-gradient)",strokeWidth:3,strokeLinecap:"round",strokeDasharray:I,strokeDashoffset:I*(1-S),className:"transition-[stroke-dashoffset] duration-150"}),e("defs",{children:t("linearGradient",{id:"audio-ring-gradient",x1:"0",y1:"0",x2:"1",y2:"1",children:[e("stop",{offset:"0%",stopColor:"#3B82F6"}),e("stop",{offset:"100%",stopColor:"#06b6d4"})]})})]}),t("button",{type:"button",onClick:C,className:"absolute inset-1.5 flex items-center justify-center rounded-full bg-gradient-to-tr from-blue-600 to-indigo-600 text-white shadow-lg shadow-blue-500/30 transition-all hover:shadow-xl hover:shadow-blue-500/50 hover:from-blue-700 hover:to-indigo-700 active:scale-95","aria-label":x?"Pause":"Play",children:[x?e(s,{className:"h-4 w-4 fill-current"}):e(i,{className:"ml-0.5 h-4 w-4 fill-current"}),x&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-blue-400 opacity-40"})]})]}),t("div",{className:"flex min-w-0 flex-1 flex-col gap-1",children:[e("div",{onClick:e=>{const t=v.current;if(!t||!y)return;const a=e.currentTarget.getBoundingClientRect(),r=Math.min(1,Math.max(0,(e.clientX-a.left)/a.width))*y;t.currentTime=r,N(r)},className:"group/wave relative flex h-8 cursor-pointer items-center gap-[2px]",role:"slider","aria-label":"Seek","aria-valuemin":0,"aria-valuemax":y||0,"aria-valuenow":w,children:E.map((t,a)=>{const r=(a+.5)/48,l=r<=S,n=Math.abs(r-S)<1/48/2;return e("span",{className:`flex-1 rounded-full transition-all duration-200 ${l?"bg-blue-500":"bg-slate-200 group-hover/wave:bg-slate-300"} ${n&&x?"animate-wave-pulse":""}`,style:{height:100*t+"%"}},a)})}),t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-baseline gap-1.5",children:[t("span",{className:"font-mono text-sm font-bold tabular-nums text-slate-800",children:["−",P($)]}),t("span",{className:"font-mono text-[10px] tabular-nums text-slate-400",children:["/ ",P(y)]})]}),e("div",{className:"flex items-center gap-0.5 rounded-full border border-slate-200 bg-white p-0.5 shadow-sm",children:c.map(e=>t("button",{type:"button",onClick:()=>L(e),className:"rounded-full px-2 py-0.5 font-mono text-[10px] font-semibold tabular-nums transition-all "+(M===e?"bg-coral-500 text-white shadow-sm":"text-slate-500 hover:bg-slate-100 hover:text-slate-700"),children:[e,"×"]},e))})]})]})]})]})}
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as r,useEffect as n,useMemo as s,useCallback as a,memo as o}from"react";import{ResolvedImage as l}from"../../../components/common/ResolvedImage";import{cn as i}from"../../../shared/lib/utils";import{transformWordFillStructuredForm as d}from"../../../components/exams/take/exam-taking.utils";import{englishCertQuestionDomId as c}from"./english-cert-scroll";function b(e){const t=Number(e);return Number.isFinite(t)?String(t+1):e}function u(e,t){const r=e.trim();return t?r:r.toLowerCase()}function m(e,t){const r=Object.keys(e).map(e=>Number(e)).filter(e=>Number.isFinite(e)).sort((e,t)=>e-t);if(0===r.length)return`Question ${t}`;const n=r[0]+1,s=r[r.length-1]+1;return n===s?`Question ${n}`:`Questions ${n}–${s}`}const p={},f=o(function({question:a,initialAnswers:o,onBlankChange:d,isReviewMode:c}){const m=r(null),p=r(d);p.current=d;const f=r(!1),x=r(o),g=s(()=>a.content.replace(/\{([\w\d]+)\}/g,(e,t)=>`\n <span class="ec-blank-wrapper" data-blank-wrapper="${t}">\n <span class="ec-blank-number" data-blank-number="${t}">${b(t)}</span>\n <input type="text" data-blank-key="${t}" class="ec-blank-input" />\n </span>\n `),[a.content]);return n(()=>{const e=m.current;if(!e)return;f.current||(e.innerHTML=g,f.current=!0);const t=e.querySelectorAll("input.ec-blank-input"),r=[];return t.forEach(t=>{const n=t.getAttribute("data-blank-key")||"",s=e.querySelector(`[data-blank-wrapper="${n}"]`),o=x.current[n]||"",l=a.answers[n]||"",d=o.trim().length>0,m=d&&u(o,a.caseSensitive)===u(l,a.caseSensitive),f=c&&!m&&l.trim().length>0;t.value=f?`${d?o:"(empty)"} → ${l}`:o,t.disabled=c,t.className=i("ec-blank-input inline-block min-w-[100px] max-w-[200px] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] bg-blue-50/30 px-2 py-0.5 text-base font-semibold leading-tight text-[#0f1730] align-baseline outline-none transition-all hover:bg-blue-50/60 focus:border-solid focus:border-[#0b57d0] focus:bg-blue-50/80",c?m?"border-solid border-[#22c55e] bg-emerald-50 text-[#15803d]":d?"border-solid border-[#ef4444] bg-rose-50 text-red-700":"border-solid border-[#f97316] bg-amber-50 text-[#c2410c]":"");const g=e.querySelector(`[data-blank-number="${n}"]`);g&&(g.textContent=b(n),g.className=i("ec-blank-number mr-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-bold leading-none text-white align-baseline shadow-xs",c?m?"bg-[#22c55e]":d?"bg-[#ef4444]":"bg-[#f97316]":"bg-[#0b57d0]")),s&&(s.className=i("ec-blank-wrapper inline-flex items-baseline whitespace-nowrap align-baseline",c?"mx-1":"mx-0.5"));const h=e=>{const t=e.target;p.current(n,t.value)};t.addEventListener("input",h),r.push({el:t,handler:h})}),()=>{r.forEach(({el:e,handler:t})=>{e.removeEventListener("input",t)})}},[g,c]),t("div",{className:"space-y-3",children:[a.title&&e("h3",{className:"text-base font-semibold text-[#0f1730]",children:a.title}),a.imageUrl&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-[#d9e4f2]",children:e(l,{src:a.imageUrl,alt:a.title||"Question image",className:"max-h-56 w-auto object-contain"})})}),e("div",{ref:m,className:"ec-structured-content w-full overflow-x-auto rounded-xl border border-[#d9e4f2] bg-[#f4f9ff] p-5 text-base leading-relaxed text-[#0f1730] [&_table]:w-full [&_table]:border-collapse [&_td]:border [&_td]:border-[#d9e4f2] [&_td]:px-4 [&_td]:py-3 [&_td]:align-top [&_th]:border [&_th]:border-[#d9e4f2] [&_th]:bg-[#e9f3ff] [&_th]:px-4 [&_th]:py-3 [&_th]:text-left [&_th]:font-semibold"}),c&&Object.keys(a.answers).length>0&&t("div",{className:"rounded-xl border border-[#d9e4f2] bg-white/70 p-3",children:[e("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-[#64748b]",children:"Correct answers"}),e("div",{className:"mt-1 flex flex-wrap gap-1.5",children:Object.entries(a.answers).map(([e,r])=>t("span",{className:"rounded border border-[#b9edc8] bg-[#ddf8e7] px-2 py-0.5 text-xs font-semibold text-[#15803d]",children:[e,": ",r]},e))})]})]})});export function EcWordFillStructuredForm({part:n,answers:o,onAnswerChange:i,isReviewMode:b=!1}){const{questions:u,instruction:x,groupTitle:g,groupContent:h,groupImageUrl:w}=s(()=>d(n.questions),[n.questions]),N=r(o);N.current=o;const v=r(i);v.current=i;const k=a((e,t,r)=>{const n=N.current[e]||{};v.current(e,{...n,[t]:r})},[]),y=s(()=>{const e={};return u.forEach(t=>{e[t.id]=(e,r)=>k(t.id,e,r)}),e},[u,k]);return t("div",{className:"flex flex-col gap-4",children:[x&&e("div",{className:"rounded-xl border border-[#d9e4f2] bg-[#e9f3ff] px-4 py-2.5 text-sm font-semibold uppercase tracking-wide text-[#0b57d0]",children:x}),g&&e("h2",{className:"text-center text-lg font-bold uppercase text-[#0b57d0]",children:g}),w&&e("div",{className:"flex items-center justify-center",children:e(l,{src:w,alt:g||"Group image",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),e("div",{className:"flex flex-col gap-4",children:u.map(r=>t("div",{id:c(r.id),className:"scroll-mt-24 rounded-2xl border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_14px_35px_rgba(13,71,161,0.10)] backdrop-blur-md",children:[e("div",{className:"mb-2 flex items-center gap-2",children:e("span",{className:"inline-flex items-center rounded-full bg-gradient-to-b from-[#1f7cff] to-[#0b57d0] px-3 py-1 text-xs font-bold text-white shadow-sm",children:m(r.answers,r.questionNumber)})}),e(f,{question:r,initialAnswers:o[r.id]||p,onBlankChange:y[r.id],isReviewMode:b})]},r.id))})]})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as r,useEffect as n,useMemo as s,useCallback as o,memo as l}from"react";import{ResolvedImage as a}from"../../../components/common/ResolvedImage";import{cn as i}from"../../../shared/lib/utils";import{transformWordFillStructuredForm as d}from"../../../components/exams/take/exam-taking.utils";import{englishCertQuestionDomId as c}from"./english-cert-scroll";function b(e){const t=Number(e);return Number.isFinite(t)?String(t+1):e}function u(e,t){const r=e.trim();return t?r:r.toLowerCase()}function m(e,t){const r=Object.keys(e).map(e=>Number(e)).filter(e=>Number.isFinite(e)).sort((e,t)=>e-t);if(0===r.length)return`Question ${t}`;const n=r[0]+1,s=r[r.length-1]+1;return n===s?`Question ${n}`:`Questions ${n}–${s}`}const p={},x=l(function({question:o,initialAnswers:l,onBlankChange:d,isReviewMode:c}){const m=r(null),p=r(d);p.current=d;const x=r(!1),f=r(l),h=s(()=>o.content.replace(/\{([\w\d]+)\}/g,(e,t)=>`\n <span class="ec-blank-wrapper" data-blank-wrapper="${t}">\n <span class="ec-blank-number" data-blank-number="${t}">${b(t)}</span>\n <input type="text" data-blank-key="${t}" class="ec-blank-input" />\n </span>\n `),[o.content]);return n(()=>{const e=m.current;if(!e)return;x.current||(e.innerHTML=h,x.current=!0);const t=e.querySelectorAll("input.ec-blank-input"),r=[];return t.forEach(t=>{const n=t.getAttribute("data-blank-key")||"",s=e.querySelector(`[data-blank-wrapper="${n}"]`),l=f.current[n]||"",a=o.answers[n]||"",d=l.trim().length>0,m=d&&u(l,o.caseSensitive)===u(a,o.caseSensitive),x=c&&!m&&a.trim().length>0;t.value=x?`${d?l:"(empty)"} → ${a}`:l,t.disabled=c;const h=o.blankStyle||"word",g="letter"===h,w="phrase"===h;g&&(t.maxLength=Math.max(1,(a||" ").trim().length||1)),t.className=i("ec-blank-input inline-block bg-blue-50/30 text-base font-semibold leading-tight text-[#0f1730] align-baseline outline-none transition-all hover:bg-blue-50/60 focus:bg-blue-50/80",g?"h-8 w-8 rounded border-2 border-[#1167e8] px-0 text-center uppercase":w?"min-w-[140px] max-w-[280px] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] px-2 py-0.5 text-center focus:border-solid focus:border-[#0b57d0]":"min-w-[100px] max-w-[200px] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] px-2 py-0.5 text-center focus:border-solid focus:border-[#0b57d0]",c?m?"border-solid border-[#22c55e] bg-emerald-50 text-[#15803d]":d?"border-solid border-[#ef4444] bg-rose-50 text-red-700":"border-solid border-[#f97316] bg-amber-50 text-[#c2410c]":""),g&&(t.style.display="inline-block",t.style.width="1.75rem",t.style.height="1.75rem",t.style.minWidth="1.75rem",t.style.maxWidth="1.75rem",t.style.padding="0",t.style.margin="0 2px",t.style.borderWidth="2px",t.style.borderStyle="solid",t.style.borderRadius="4px",t.style.textAlign="center",t.style.fontWeight="700",t.style.textTransform="uppercase",t.style.boxSizing="border-box");const y=e.querySelector(`[data-blank-number="${n}"]`);y&&(y.textContent=b(n),y.className=i("ec-blank-number mr-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-bold leading-none text-white align-baseline shadow-xs",c?m?"bg-[#22c55e]":d?"bg-[#ef4444]":"bg-[#f97316]":"bg-[#0b57d0]")),s&&(s.className=i("ec-blank-wrapper inline-flex items-baseline whitespace-nowrap align-baseline",c?"mx-1":"mx-0.5"));const k=e=>{const t=e.target;p.current(n,t.value)};t.addEventListener("input",k),r.push({el:t,handler:k})}),()=>{r.forEach(({el:e,handler:t})=>{e.removeEventListener("input",t)})}},[h,c]),t("div",{className:"space-y-3",children:[o.title&&e("h3",{className:"text-base font-semibold text-[#0f1730]",children:o.title}),o.imageUrl&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-[#d9e4f2]",children:e(a,{src:o.imageUrl,alt:o.title||"Question image",className:"max-h-56 w-auto object-contain"})})}),e("div",{ref:m,className:"ec-structured-content w-full overflow-x-auto rounded-xl border border-[#d9e4f2] bg-[#f4f9ff] p-5 text-base leading-relaxed text-[#0f1730] [&_table]:w-full [&_table]:border-collapse [&_td]:border [&_td]:border-[#d9e4f2] [&_td]:px-4 [&_td]:py-3 [&_td]:align-top [&_th]:border [&_th]:border-[#d9e4f2] [&_th]:bg-[#e9f3ff] [&_th]:px-4 [&_th]:py-3 [&_th]:text-left [&_th]:font-semibold"}),c&&Object.keys(o.answers).length>0&&t("div",{className:"rounded-xl border border-[#d9e4f2] bg-white/70 p-3",children:[e("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-[#64748b]",children:"Correct answers"}),e("div",{className:"mt-1 flex flex-wrap gap-1.5",children:Object.entries(o.answers).map(([e,r])=>t("span",{className:"rounded border border-[#b9edc8] bg-[#ddf8e7] px-2 py-0.5 text-xs font-semibold text-[#15803d]",children:[e,": ",r]},e))})]})]})});export function EcWordFillStructuredForm({part:n,answers:l,onAnswerChange:i,isReviewMode:b=!1}){const{questions:u,instruction:f,groupTitle:h,groupContent:g,groupImageUrl:w}=s(()=>d(n.questions),[n.questions]),y=r(l);y.current=l;const k=r(i);k.current=i;const N=o((e,t,r)=>{const n=y.current[e]||{};k.current(e,{...n,[t]:r})},[]),v=s(()=>{const e={};return u.forEach(t=>{e[t.id]=(e,r)=>N(t.id,e,r)}),e},[u,N]);return t("div",{className:"flex flex-col gap-4",children:[f&&e("div",{className:"rounded-xl border border-[#d9e4f2] bg-[#e9f3ff] px-4 py-2.5 text-sm font-semibold uppercase tracking-wide text-[#0b57d0]",children:f}),h&&e("h2",{className:"text-center text-lg font-bold uppercase text-[#0b57d0]",children:h}),w&&e("div",{className:"flex items-center justify-center",children:e(a,{src:w,alt:h||"Group image",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),e("div",{className:"flex flex-col gap-4",children:u.map(r=>t("div",{id:c(r.id),className:"scroll-mt-24 rounded-2xl border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_14px_35px_rgba(13,71,161,0.10)] backdrop-blur-md",children:[e("div",{className:"mb-2 flex items-center gap-2",children:e("span",{className:"inline-flex items-center rounded-full bg-gradient-to-b from-[#1f7cff] to-[#0b57d0] px-3 py-1 text-xs font-bold text-white shadow-sm",children:m(r.answers,r.questionNumber)})}),e(x,{question:r,initialAnswers:l[r.id]||p,onBlankChange:v[r.id],isReviewMode:b})]},r.id))})]})}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * as Api from './api';
2
2
  export * as Components from './components';
3
+ export * as Mcp from './mcp';
3
4
  export * as Exams from './components/exams';
4
5
  export * as Questions from './components/questions';
5
6
  export * as Results from './components/results';
@@ -15,6 +16,8 @@ export { QuestionCreator } from './components/questions/creator/QuestionCreator'
15
16
  export type { QuestionCreatorProps } from './components/questions/creator/QuestionCreator';
16
17
  export { QuestionGroupCreator } from './components/questions/creator/QuestionGroupCreator';
17
18
  export type { QuestionGroupCreatorProps } from './components/questions/creator/QuestionGroupCreator';
19
+ export { DifficultyLevel, EditQuestionBankDialog, QuestionBankEditorType, QuestionEditorRenderer, } from './components/questions/question-bank';
20
+ export type { EditQuestionBankDialogProps, QuestionBankEditItem, QuestionEditorChangeData, QuestionEditorRendererProps, SkillType as QuestionBankSkillType, UpdateQuestionBankDto, } from './components/questions/question-bank';
18
21
  export { renderDedicatedComponent } from './components/questions/creator/dedicated-component-router';
19
22
  export type { DedicatedComponentRouterProps, ValidationRef, } from './components/questions/creator/dedicated-component-router';
20
23
  export { questionTypeRegistry } from './components/questions/creator/question-type-registry';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*as Api from"./api";export*as Components from"./components";export*as Exams from"./components/exams";export*as Questions from"./components/questions";export*as Results from"./components/results";export*as Themes from"./components/themes";export*as Shared from"./shared";export{buildExamTemplatePayload,createContestRoomsApi,createExamCreateApi,createExamEntryApi,createExamQuestionsApi,createExamQuestionsFetchApi,createExamTakingApi,createMockContestRoomsApi,createMockCreateExamApi,createMockExamEntryApi,createMockExamQuestionsApi,createMockExamTakingApi,createMockResultReviewApi,createResultReviewApi}from"./api";export{CreateExamPageContainer,ExamPreviewDialog,ExamQuestionsPageContainer,ExamTakingPageContainer,ExamViewDialog,StudentContestRoomsPageContainer,StudentExamEntryPageContainer}from"./components/exams";export{ResultReviewPageContainer}from"./components/results";export{QuestionCreator}from"./components/questions/creator/QuestionCreator";export{QuestionGroupCreator}from"./components/questions/creator/QuestionGroupCreator";export{renderDedicatedComponent}from"./components/questions/creator/dedicated-component-router";export{questionTypeRegistry}from"./components/questions/creator/question-type-registry";export{QUESTION_TYPE_CONFIG,QUESTION_TYPES_WITH_DEDICATED_COMPONENTS}from"./components/questions/_shared/config/question-types.config";export{validateQuestion}from"./components/questions/_shared/utils/question-validation";export{buildAnswerPayload}from"./components/questions/_shared/utils/answer-builder";export*from"./components/questions/_shared/types";
1
+ export*as Api from"./api";export*as Components from"./components";export*as Mcp from"./mcp";export*as Exams from"./components/exams";export*as Questions from"./components/questions";export*as Results from"./components/results";export*as Themes from"./components/themes";export*as Shared from"./shared";export{buildExamTemplatePayload,createContestRoomsApi,createExamCreateApi,createExamEntryApi,createExamQuestionsApi,createExamQuestionsFetchApi,createExamTakingApi,createMockContestRoomsApi,createMockCreateExamApi,createMockExamEntryApi,createMockExamQuestionsApi,createMockExamTakingApi,createMockResultReviewApi,createResultReviewApi}from"./api";export{CreateExamPageContainer,ExamPreviewDialog,ExamQuestionsPageContainer,ExamTakingPageContainer,ExamViewDialog,StudentContestRoomsPageContainer,StudentExamEntryPageContainer}from"./components/exams";export{ResultReviewPageContainer}from"./components/results";export{QuestionCreator}from"./components/questions/creator/QuestionCreator";export{QuestionGroupCreator}from"./components/questions/creator/QuestionGroupCreator";export{DifficultyLevel,EditQuestionBankDialog,QuestionBankEditorType,QuestionEditorRenderer}from"./components/questions/question-bank";export{renderDedicatedComponent}from"./components/questions/creator/dedicated-component-router";export{questionTypeRegistry}from"./components/questions/creator/question-type-registry";export{QUESTION_TYPE_CONFIG,QUESTION_TYPES_WITH_DEDICATED_COMPONENTS}from"./components/questions/_shared/config/question-types.config";export{validateQuestion}from"./components/questions/_shared/utils/question-validation";export{buildAnswerPayload}from"./components/questions/_shared/utils/answer-builder";export*from"./components/questions/_shared/types";
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ declare const schema: z.ZodObject<{
3
+ OE_API_URL: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
4
+ OE_API_TOKEN: z.ZodString;
5
+ OE_HTTP_TIMEOUT_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
6
+ }, z.core.$strip>;
7
+ export type McpConfig = z.infer<typeof schema> & {
8
+ packageName: string;
9
+ packageVersion: string;
10
+ };
11
+ export declare function loadMcpConfig(): McpConfig;
12
+ export {};
@@ -0,0 +1 @@
1
+ import{readFileSync as e}from"node:fs";import{dirname as r,join as o}from"node:path";import{fileURLToPath as n}from"node:url";import{z as t}from"zod";const s=function(){try{const t=r(n(import.meta.url));return JSON.parse(e(o(t,"..","..","package.json"),"utf8"))}catch{return{name:"@tinyweb_dev/oe-exam-sdk",version:"0.0.0"}}}(),a=t.object({OE_API_URL:t.string().url().transform(e=>e.replace(/\/+$/,"")),OE_API_TOKEN:t.string().min(1),OE_HTTP_TIMEOUT_MS:t.coerce.number().int().positive().default(15e3)});let i=null;export function loadMcpConfig(){if(i)return i;const e=a.safeParse(process.env);if(!e.success){const r=e.error.issues.map(e=>` • ${e.path.join(".")||"<root>"}: ${e.message}`).join("\n");console.error(`\n[oe-exam-mcp] Configuration error:\n${r}\n\nRequired: OE_API_URL and OE_API_TOKEN.\n`),process.exit(1)}return i={...e.data,packageName:s.name,packageVersion:s.version},i}
@@ -0,0 +1,6 @@
1
+ export interface McpRequestOptions<TBody = unknown> {
2
+ method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
3
+ url: string;
4
+ body?: TBody;
5
+ }
6
+ export declare function mcpRequest<T>(opts: McpRequestOptions): Promise<T>;
@@ -0,0 +1 @@
1
+ import{ErrorCode as t,McpError as o}from"@modelcontextprotocol/sdk/types.js";import{loadMcpConfig as r}from"./config.js";export async function mcpRequest(n){const s=r(),a=new AbortController,i=setTimeout(()=>a.abort(),s.OE_HTTP_TIMEOUT_MS);try{const r=await fetch(`${s.OE_API_URL}${n.url}`,{method:n.method,signal:a.signal,headers:{Accept:"application/json","Content-Type":"application/json",...e(s.OE_API_TOKEN)},body:void 0===n.body?void 0:JSON.stringify(n.body)}),i=await r.text(),c=i?JSON.parse(i):void 0;if(!r.ok){const e=function(t){if(!t||"object"!=typeof t)return;const o=t.message;return Array.isArray(o)?o.join("; "):"string"==typeof o?o:void 0}(c)??r.statusText,s=400===r.status||404===r.status||422===r.status?t.InvalidParams:t.InternalError;throw new o(s,`[${n.method} ${n.url}] ${r.status}: ${e}`)}return c}catch(r){if(r instanceof o)throw r;throw new o(t.InternalError,r instanceof Error?r.message:"Unknown HTTP error")}finally{clearTimeout(i)}}function e(t){return t?t.startsWith("oeexam_")?{"X-API-Key":t}:{Authorization:`Bearer ${t}`}:{}}
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ export declare function startOeExamMcpServer(): Promise<void>;
3
+ export { loadMcpConfig } from './config.js';
4
+ export { registerAllMcpTools } from './tools/index.js';
5
+ export { registerExamTools } from './tools/exams.js';
6
+ export { registerResultTools } from './tools/results.js';
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as o}from"@modelcontextprotocol/sdk/server/stdio.js";import{loadMcpConfig as r}from"./config.js";import{registerAllMcpTools as s}from"./tools/index.js";export async function startOeExamMcpServer(){const t=r(),a=new e({name:t.packageName,version:t.packageVersion},{capabilities:{tools:{},logging:{}},instructions:"OE Exam MCP server. Use exam_json_template_generate then exam_create_from_json to create an exam, and exam_result_get to view a result review."});s(a),await a.connect(new o);const c=async()=>{await a.close().catch(e=>console.error("[oe-exam-mcp] server.close failed",e)),process.exit(0)};process.on("SIGINT",()=>{c()}),process.on("SIGTERM",()=>{c()})}import.meta.url===`file://${process.argv[1]}`&&startOeExamMcpServer().catch(e=>{console.error("[oe-exam-mcp] Fatal startup error",e),process.exit(1)});export{loadMcpConfig}from"./config.js";export{registerAllMcpTools}from"./tools/index.js";export{registerExamTools}from"./tools/exams.js";export{registerResultTools}from"./tools/results.js";
@@ -0,0 +1,4 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ export declare function buildToolResult<T>(value: T): CallToolResult;
3
+ export declare function wrapHandler<TInput extends Record<string, unknown>>(toolName: string, fn: (input: TInput) => Promise<CallToolResult>): (input: TInput) => Promise<CallToolResult>;
4
+ export declare function unwrapData<T>(value: unknown): T;
@@ -0,0 +1 @@
1
+ import{ErrorCode as t,McpError as r}from"@modelcontextprotocol/sdk/types.js";export function buildToolResult(t){return{content:[{type:"text",text:"string"==typeof t?t:JSON.stringify(t,null,2)}],...t&&"object"==typeof t?{structuredContent:t}:{}}}export function wrapHandler(e,o){return async n=>{try{return await o(n)}catch(o){if(o instanceof r)throw o;throw console.error(`[oe-exam-mcp] ${e} failed`,o),new r(t.InternalError,o instanceof Error?o.message:"Unknown error")}}}export function unwrapData(t){return t&&"object"==typeof t&&"data"in t?t.data:t}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerExamTools(server: McpServer): void;