@tinyweb_dev/oe-exam-sdk 0.1.24 → 0.1.27

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 (133) hide show
  1. package/README.md +5 -4
  2. package/dist/components/exams/ExamPreviewDialog.d.ts +7 -6
  3. package/dist/components/exams/ExamPreviewDialog.js +1 -1
  4. package/dist/components/exams/index.d.ts +0 -1
  5. package/dist/components/exams/take/components/QuestionRenderer.js +1 -1
  6. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -1
  7. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -1
  8. package/dist/components/exams/take/exam-taking.utils.d.ts +0 -13
  9. package/dist/components/exams/take/exam-taking.utils.js +1 -1
  10. package/dist/components/exams/take/utils/answer-transformers.js +1 -1
  11. package/dist/components/exams/take/utils/question-transformers.d.ts +7 -17
  12. package/dist/components/exams/take/utils/question-transformers.js +1 -1
  13. package/dist/components/questions/_shared/config/question-types.config.js +1 -1
  14. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +36 -0
  15. package/dist/components/questions/_shared/types/cross-out-word-group.type.d.ts +40 -0
  16. package/dist/components/questions/_shared/types/index.d.ts +1 -0
  17. package/dist/components/questions/_shared/types/index.js +1 -1
  18. package/dist/components/questions/_shared/types/true-false-group.type.d.ts +31 -0
  19. package/dist/components/questions/_shared/types/true-false-group.type.js +1 -0
  20. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +29 -26
  21. package/dist/components/questions/creator/question-type-registry.js +1 -1
  22. package/dist/components/questions/index.d.ts +0 -1
  23. package/dist/components/questions/index.js +1 -1
  24. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -1
  25. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -1
  26. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.d.ts +2 -0
  27. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +1 -0
  28. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.d.ts +2 -0
  29. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +1 -0
  30. package/dist/components/questions/types/choose-the-correct-answer-group/index.d.ts +2 -0
  31. package/dist/components/questions/types/choose-the-correct-answer-group/index.js +1 -0
  32. package/dist/components/questions/types/choose-the-correct-answer-group/register.d.ts +2 -0
  33. package/dist/components/questions/types/choose-the-correct-answer-group/register.js +1 -0
  34. package/dist/components/questions/types/choose-the-correct-answer-group/transform.d.ts +2 -0
  35. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +1 -0
  36. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.d.ts +2 -0
  37. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +1 -0
  38. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.d.ts +2 -0
  39. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +1 -0
  40. package/dist/components/questions/types/cross-out-word-group/index.d.ts +4 -0
  41. package/dist/components/questions/types/cross-out-word-group/index.js +1 -0
  42. package/dist/components/questions/types/cross-out-word-group/register.d.ts +2 -0
  43. package/dist/components/questions/types/cross-out-word-group/register.js +1 -0
  44. package/dist/components/questions/types/cross-out-word-group/transform.d.ts +2 -0
  45. package/dist/components/questions/types/cross-out-word-group/transform.js +1 -0
  46. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.d.ts +2 -0
  47. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -0
  48. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.d.ts +2 -0
  49. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +1 -0
  50. package/dist/components/questions/types/true-false-group/index.d.ts +4 -0
  51. package/dist/components/questions/types/true-false-group/index.js +1 -0
  52. package/dist/components/questions/types/true-false-group/register.d.ts +2 -0
  53. package/dist/components/questions/types/true-false-group/register.js +1 -0
  54. package/dist/components/questions/types/true-false-group/transform.d.ts +2 -0
  55. package/dist/components/questions/types/true-false-group/transform.js +1 -0
  56. package/dist/components/questions/types/word-fill-structured-form/WfsfWordBankEditor.d.ts +14 -0
  57. package/dist/components/questions/types/word-fill-structured-form/WfsfWordBankEditor.js +1 -0
  58. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.d.ts +1 -1
  59. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  60. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.d.ts +1 -1
  61. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -1
  62. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -1
  63. package/dist/components/questions/types/word-fill-structured-form/transform.d.ts +5 -2
  64. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -1
  65. package/dist/components/questions/types/word-fill-structured-form/wfsf-word-bank.utils.d.ts +24 -0
  66. package/dist/components/questions/types/word-fill-structured-form/wfsf-word-bank.utils.js +1 -0
  67. package/dist/components/questions/viewer/QuestionViewer.js +1 -1
  68. package/dist/components/results/renderers/review-question-body-dedicated.js +1 -1
  69. package/dist/components/results/review-data.js +1 -1
  70. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -1
  71. package/dist/index.d.ts +1 -2
  72. package/dist/index.js +1 -1
  73. package/dist/mcp/config.d.ts +12 -0
  74. package/dist/mcp/config.js +1 -0
  75. package/dist/mcp/http-client.d.ts +6 -0
  76. package/dist/mcp/http-client.js +1 -0
  77. package/dist/mcp/index.d.ts +6 -0
  78. package/dist/mcp/index.js +2 -0
  79. package/dist/mcp/tools/_helpers.d.ts +4 -0
  80. package/dist/mcp/tools/_helpers.js +1 -0
  81. package/dist/mcp/tools/exams.d.ts +2 -0
  82. package/dist/mcp/tools/exams.js +1 -0
  83. package/dist/mcp/tools/index.d.ts +2 -0
  84. package/dist/mcp/tools/index.js +1 -0
  85. package/dist/mcp/tools/results.d.ts +2 -0
  86. package/dist/mcp/tools/results.js +1 -0
  87. package/dist/shared/constants/question-skills.js +1 -1
  88. package/dist/shared/lib/i18n/messages/en/admin.d.ts +4 -11
  89. package/dist/shared/lib/i18n/messages/en/admin.js +1 -1
  90. package/dist/shared/lib/i18n/messages/en.d.ts +4 -11
  91. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +4 -11
  92. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -1
  93. package/dist/shared/lib/i18n/messages/vi.d.ts +4 -11
  94. package/dist/shared/lib/utils/question-reverse-transform.js +1 -1
  95. package/dist/shared/lib/utils/question-transform.js +1 -1
  96. package/dist/shared/types/common.types.d.ts +1 -1
  97. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +41 -0
  98. package/dist/shared/types/questions/choose-the-correct-answer-group.js +1 -0
  99. package/dist/shared/types/questions/cross-out-word-group.d.ts +41 -0
  100. package/dist/shared/types/questions/cross-out-word-group.js +1 -0
  101. package/dist/shared/types/questions/index.d.ts +2 -0
  102. package/dist/shared/types/questions/index.js +1 -1
  103. package/package.json +165 -163
  104. package/dist/components/exams/ExamPreviewPartContent.d.ts +0 -18
  105. package/dist/components/exams/ExamPreviewPartContent.js +0 -1
  106. package/dist/components/exams/ExamPreviewSidebar.d.ts +0 -17
  107. package/dist/components/exams/ExamPreviewSidebar.js +0 -1
  108. package/dist/components/exams/exam-preview.types.d.ts +0 -34
  109. package/dist/components/exams/exam-preview.types.js +0 -1
  110. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +0 -30
  111. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +0 -1
  112. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +0 -2
  113. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +0 -1
  114. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -23
  115. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +0 -1
  116. package/dist/components/questions/question-bank/difficulty.d.ts +0 -15
  117. package/dist/components/questions/question-bank/difficulty.js +0 -1
  118. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -5
  119. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -1
  120. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +0 -5
  121. package/dist/components/questions/question-bank/editor-adapters/index.js +0 -1
  122. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -6
  123. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -1
  124. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +0 -1
  125. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +0 -8
  126. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +0 -1
  127. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +0 -5
  128. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +0 -1
  129. package/dist/components/questions/question-bank/index.d.ts +0 -4
  130. package/dist/components/questions/question-bank/index.js +0 -1
  131. package/dist/components/questions/question-bank/types.d.ts +0 -59
  132. /package/dist/components/questions/{question-bank/editor-adapters/types.js → _shared/types/choose-the-correct-answer-group.type.js} +0 -0
  133. /package/dist/components/questions/{question-bank/types.js → _shared/types/cross-out-word-group.type.js} +0 -0
@@ -1 +1 @@
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:m,isReviewMode:p=!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:_}=m,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(m.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=p&&T.every(e=>{const t=(E[e]||"").trim(),r=(v[e]||"").trim();return N?t===r:t.toLowerCase()===r.toLowerCase()}),z=String(f||m.explanation||"").trim(),D="number"==typeof h&&h>0?`Bài ${h}`:"",H=i((e,t)=>{p||(W(r=>({...r,[e]:t})),g?.(e,t))},[p,g]),M=i((r,l)=>{const n=E[r]||"",a=v[r]||"",i=N?n.trim():n.trim().toLowerCase(),d=N?a.trim():a.trim().toLowerCase(),m=p&&i===d,u=p&&!!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:p?m?"#10b981":u?"#f43f5e":"#f59e0b":"#94a3b8",borderRadius:4,textAlign:"center",fontSize:"0.8125rem",fontWeight:700,lineHeight:"1.375rem",textTransform:"uppercase",backgroundColor:p?m?"#ecfdf5":u?"#fff1f2":"#fffbeb":"#ffffff",color:p?m?"#065f46":u?"#9f1239":"#92400e":"#1e293b",verticalAlign:"middle",outline:"none",boxSizing:"border-box"}:void 0,y=p?m?"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:p,onChange:e=>H(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,!p&&"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;H(r,l)},disabled:p,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-[9rem] max-w-[18rem] w-[14ch] rounded-t border-b-2 px-2 py-0.5":"min-w-[4.5rem] max-w-[7.5rem] w-[8ch] rounded-t border-b-2 px-1 py-0.5"),!g&&y),size:g?1:b?18:10}),p&&n&&(m?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,H,p,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:M(e.key,`${a}-blank-${e.index}-${t}`));return 1===t.length?t[0]:e(r.Fragment,{children:t},a)}const d=[];let c=[],m=0;const p=()=>{c.length&&(d.push(e("span",{style:{display:"inline",whiteSpace:"nowrap",verticalAlign:"baseline"},children:c},`${a}-w${m}`)),m+=1,c=[])};for(let e=0;e<i.length;e+=1){const t=i[e];if("blank"===t.kind){c.push(M(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)?(p(),d.push(t)):c.push(t))}}return p(),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="",m={};Array.from(o.attributes).forEach(e=>{var t;if("class"!==e.name)return"style"===e.name?(t=e.value,void(m=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 p=c.split(/\s+/).filter(Boolean);p.includes("wfsf-block")&&(m={display:"block",margin:"0 0 1.25rem 0",padding:"0.35rem 0 1rem 0",borderBottom:"1px solid #e2e8f0",...m}),p.includes("wfsf-item-no")&&(m={fontWeight:700,fontSize:"1rem",lineHeight:1.4,margin:"0 0 0.4rem 0",color:"#0f172a",...m}),"table"===s&&p.includes("dialogue")&&(m={width:"100%",borderCollapse:"collapse",tableLayout:"auto",...m}),"td"===s&&p.includes("speaker")?m={width:"1%",whiteSpace:"nowrap",verticalAlign:"top",paddingRight:"12px",paddingTop:"6px",paddingBottom:"6px",fontWeight:600,color:"#0f172a",...m}:"td"===s&&!p.includes("speaker")&&o.closest?.("table.dialogue")&&(m={verticalAlign:"top",paddingTop:"6px",paddingBottom:"6px",width:"auto",...m}),c&&(d.className=c),Object.keys(m).length>0&&(d.style=m);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,M]);return e("div",{className:"space-y-4",children:t("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[(D||z||p&&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:[D?e("h2",{className:"text-lg font-bold tracking-tight text-slate-900",children:D}):null,z?e("p",{className:"text-sm leading-relaxed whitespace-pre-line text-slate-700",children:z}):null]}),p&&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||D||"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}),p&&!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
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import t,{useState as a,useEffect as l,useMemo as n,useCallback as s}from"react";import{Check as o,X as i,FileText as d,Library as m}from"lucide-react";import{usePresignedFileUrl as c}from"../../../../shared/lib/hooks";import{cn as b}from"../../../../shared/lib/utils";import{detectWfsfBankMode as p,isOptionsWordEntry as u,normalizeSharedWordBank as g}from"./wfsf-word-bank.utils";export function WordFillStructuredFormClient({questionData:x,isReviewMode:h=!1,userAnswer:f,onAnswerChange:w,autoFillCorrectAnswers:N=!1,instruction:y,questionNumber:v,explanation:k}){const{title:C="",imageUrl:S,content:E="",answers:A={},caseSensitive:T=!1,viewMode:j,wordBank:O=[],instruction:I}=x,[L,$]=a(()=>f?{...f}:N?{...A}:{}),[W,_]=a(null);l(()=>{f&&$({...f})},[f]);const B=n(()=>p(j,O),[j,O]),D=n(()=>"WITH_WORD_BANK"===B?g(O):[],[B,O]),F=n(()=>{const e=new Set;return Object.values(L).forEach(r=>{const t=String(r||"").trim().toLowerCase();t&&e.add(t)}),e},[L]),M=n(()=>{const e=new Map;return"WITH_OPTIONS"===B&&Array.isArray(O)?(O.forEach((r,t)=>{u(r)&&e.set(String(t),r.options.map(e=>String(e??"")))}),e):e},[B,O]),{previewUrl:H,isLoading:P}=c(S||""),R=n(()=>{const e=new Set,r=/\{([\w\d]+)\}/g;let t;for(;null!==(t=r.exec(E));)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})},[E]),U=R.every(e=>L[e]?.trim()),q=h&&R.every(e=>{const r=(L[e]||"").trim(),t=(A[e]||"").trim();return T?r===t:r.toLowerCase()===t.toLowerCase()}),z=String(k||x.explanation||"").trim(),K=String(y||I||z||"").trim()||"Điền từ vào biểu mẫu có cấu trúc",G=z&&z!==K?z:"",X=s((e,r)=>{h||($(t=>({...t,[e]:r})),w?.(e,r))},[h,w]),J=s((e,r)=>{h||($(t=>({...t,[e]:r})),w?.(e,r),_(null))},[h,w]),Q=s((t,a)=>{const l=L[t]||"",n=A[t]||"",s=T?l.trim():l.trim().toLowerCase(),d=T?n.trim():n.trim().toLowerCase(),m=h&&s===d,c=h&&!!l&&s!==d,p=M.get(t);if("WITH_OPTIONS"===B&&p&&p.length>=2){const n=W===t;return r("span",{className:"relative mx-1 inline-flex flex-col items-center align-baseline",children:[r("button",{type:"button",onClick:()=>!h&&_(n?null:t),disabled:h,className:b("inline-flex min-w-[90px] items-center justify-center rounded border-2 border-dashed px-2 py-0.5 text-center text-sm font-semibold transition-all",h?m?"border-emerald-500 bg-emerald-50 text-emerald-800":c?"border-rose-500 bg-rose-50 text-rose-800":"border-amber-500 bg-amber-50 text-amber-800":"border-teal-400 bg-teal-50/50 text-teal-800 hover:bg-teal-50"),children:[l||`{${t}}`,h&&l&&(m?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"}))]}),n&&!h&&e("div",{className:"absolute left-1/2 top-full z-10 mt-1 flex -translate-x-1/2 flex-wrap gap-1 rounded-lg border border-teal-200 bg-white p-1.5 shadow-lg",children:p.map(r=>e("button",{type:"button",onClick:()=>J(t,r),className:b("rounded-md border px-2 py-1 text-xs font-medium transition-colors",l===r?"border-teal-500 bg-teal-100 text-teal-800":"border-gray-200 bg-white text-gray-700 hover:border-teal-300 hover:bg-teal-50"),children:r},r))})]},a)}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:l,onChange:e=>X(t,e.target.value),disabled:h,placeholder:"",className:b("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",h?m?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":c?"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")}),h&&l&&(m?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)},[A,B,T,W,L,X,J,h,M]),V=n(()=>{if("undefined"==typeof window)return E;const r=(new DOMParser).parseFromString(E||"<p></p>","text/html"),a=/\{([\w\d]+)\}/g,l=(r,n)=>{if(r.nodeType===Node.TEXT_NODE){const l=r.textContent||"",s=[];let o,i=0;for(a.lastIndex=0;null!==(o=a.exec(l));)o.index>i&&s.push(l.slice(i,o.index)),s.push(Q(o[1],`${n}-blank-${o.index}`)),i=o.index+o[0].length;return i<l.length&&s.push(l.slice(i)),0===s.length?null:1===s.length?s[0]:e(t.Fragment,{children:s},n)}if(r.nodeType!==Node.ELEMENT_NODE)return null;const s=r,o=s.tagName.toLowerCase(),i={key:n};Array.from(s.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(s.childNodes).map((e,r)=>l(e,`${n}-${r}`)).filter(e=>null!==e);return t.createElement(o,i,...d)};return Array.from(r.body.childNodes).map((e,r)=>l(e,`root-${r}`)).filter(e=>null!==e)},[E,Q]);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 gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:[r("div",{className:"flex min-w-0 items-start gap-3",children:[e("div",{className:"mt-0.5 flex h-8 w-8 shrink-0 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"})}),r("div",{className:"min-w-0",children:["number"==typeof v&&v>0?r("p",{className:"text-[11px] font-semibold uppercase tracking-wider text-teal-700",children:["Bài ",v]}):null,e("span",{className:"block text-sm font-medium leading-snug text-gray-800",children:K})]})]}),h&&U&&r("div",{className:b("flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",q?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e(q?o:i,{className:"h-3.5 w-3.5"}),q?"Đúng":"Sai"]})]}),r("div",{className:"space-y-5 p-5",children:[C&&e("h2",{className:"text-center text-xl font-bold text-gray-900",children:C}),(H||S)&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gray-50",children:P?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:H||S,alt:C,className:"max-h-56 w-auto object-contain p-2"})})}),"WITH_WORD_BANK"===B&&D.length>0&&r("div",{className:"rounded-lg border border-purple-200 bg-gradient-to-r from-purple-50 to-pink-50 p-3",children:[r("div",{className:"mb-2 flex items-center gap-2",children:[e(m,{className:"h-4 w-4 text-purple-600"}),e("span",{className:"text-xs font-semibold uppercase tracking-wider text-purple-700",children:"Word Bank"})]}),e("div",{className:"flex flex-wrap gap-2",children:D.map((e,t)=>{const a=F.has(e.word.trim().toLowerCase());return r("span",{className:b("rounded-full border px-3 py-1 text-sm font-medium",e.isExample?"border-amber-300 bg-amber-100 text-amber-800":a?"border-purple-200 bg-white/60 text-purple-400 line-through":"border-purple-300 bg-white text-purple-800"),children:[e.word,e.isExample?" (ex)":""]},`${e.word}-${t}`)})})]}),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:V}),h&&!q&&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:R.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}}`,": ",A[e]]},e))})]}),h&&G&&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:G})]})]})]})})}
@@ -1,2 +1,2 @@
1
1
  import type { WordFillStructuredFormCreatorProps } from '../../_shared/types/word-fill-structured-form.type';
2
- export declare function WordFillStructuredFormCreator({ initialData, onSave, onCancel, onChange, onUnsavedChangesChange: _onUnsavedChangesChange, validationRef, externalErrors, groupConfig, groupType, groups, questionIndexInPart, partId, }: WordFillStructuredFormCreatorProps): import("react").JSX.Element;
2
+ export declare function WordFillStructuredFormCreator({ initialData, onSave, onCancel, onChange, onUnsavedChangesChange: _onUnsavedChangesChange, validationRef, externalErrors, groupConfig, groupType, groups, questionIndexInPart, partId, viewMode: viewModeProp, }: WordFillStructuredFormCreatorProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as n,useEffect as r,useRef as a,useMemo as i,useCallback as l}from"react";import{useEditor as s,EditorContent as c}from"@tiptap/react";import o from"@tiptap/starter-kit";import d from"@tiptap/extension-text-align";import{Table as m,TableRow as h,TableCell as g,TableHeader as u}from"@tiptap/extension-table";import{List as p,ListOrdered as x,Bold as b,Italic as f,AlignLeft as N,AlignCenter as v,AlignRight as w,TableIcon as y,Plus as C,Minus as k,Trash2 as S,BookOpen as A,Eye as I,Pencil as U,CheckCircle2 as T,PenLine as j,Info as G,MessageSquare as H,ImageIcon as B}from"lucide-react";import{Button as D}from"../../../../components/ui/button";import{Label as R}from"../../../../components/ui/label";import{Input as O}from"../../../../components/ui/input";import{Textarea as P}from"../../../../components/ui/textarea";import{Switch as F}from"../../../../components/ui/switch";import{PointsInput as L}from"../../../../components/ui/points-input";import{Card as E,CardContent as z,CardHeader as J,CardTitle as $}from"../../../../components/ui/card";import{FileUpload as q}from"../../../../components/ui/file-upload";import{ImagePreview as X}from"../../../../components/ui/image-preview";import{WordFillStructuredFormClient as M}from"./WordFillStructuredFormClient";import{useDebouncedCallback as Q,usePresignedFileUrl as V}from"../../../../shared/lib/hooks";import{BasicQuestionGroupCard as K}from"../../groups/BasicQuestionGroupCard";import{DocumentGroupCard as W}from"../../groups/DocumentGroupCard";import{useBasicQuestionGroup as _,getGroupNumberForIndex as Y}from"../../_shared/hooks/useBasicQuestionGroup";const Z=({onClick:t,active:n,disabled:r,title:a,children:i,className:l=""})=>e("button",{type:"button",onClick:t,disabled:r,title:a,className:`inline-flex h-7 min-w-7 items-center justify-center rounded px-1.5 text-sm transition-colors disabled:opacity-30\n ${n?"bg-teal-100 text-teal-700":"text-gray-500 hover:bg-gray-100 hover:text-gray-800"}\n ${l}`,children:i}),ee=()=>e("div",{className:"mx-1 h-4 w-px bg-gray-200"}),te=({editor:n})=>n?t("div",{className:"flex flex-wrap items-center gap-0.5 border-b border-gray-100 bg-gray-50/70 px-2 py-1.5 rounded-t-lg",children:[e(Z,{onClick:()=>n.chain().focus().toggleBold().run(),active:n.isActive("bold"),title:"Đậm",children:e(b,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().toggleItalic().run(),active:n.isActive("italic"),title:"Nghiêng",children:e(f,{className:"h-3.5 w-3.5"})}),e(ee,{}),e(Z,{onClick:()=>n.chain().focus().toggleHeading({level:2}).run(),active:n.isActive("heading",{level:2}),className:"px-2 text-xs font-bold",children:"H2"}),e(Z,{onClick:()=>n.chain().focus().toggleHeading({level:3}).run(),active:n.isActive("heading",{level:3}),className:"px-2 text-xs font-bold",children:"H3"}),e(Z,{onClick:()=>n.chain().focus().toggleHeading({level:4}).run(),active:n.isActive("heading",{level:4}),className:"px-2 text-xs font-bold",children:"H4"}),e(ee,{}),e(Z,{onClick:()=>n.chain().focus().toggleBulletList().run(),active:n.isActive("bulletList"),title:"Danh sách",children:e(p,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().toggleOrderedList().run(),active:n.isActive("orderedList"),title:"Danh sách có số",children:e(x,{className:"h-3.5 w-3.5"})}),e(ee,{}),e(Z,{onClick:()=>n.chain().focus().setTextAlign("left").run(),active:n.isActive({textAlign:"left"}),title:"Trái",children:e(N,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().setTextAlign("center").run(),active:n.isActive({textAlign:"center"}),title:"Giữa",children:e(v,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().setTextAlign("right").run(),active:n.isActive({textAlign:"right"}),title:"Phải",children:e(w,{className:"h-3.5 w-3.5"})}),e(ee,{}),t(Z,{onClick:()=>n.chain().focus().insertTable({rows:5,cols:2,withHeaderRow:!1}).run(),title:"Chèn bảng 2 cột × 5 hàng",className:"gap-1 px-2 text-xs",children:[e(y,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Bảng 2 cột"})]}),t(Z,{onClick:()=>n.chain().focus().addRowAfter().run(),disabled:!n.can().addRowAfter(),title:"Thêm hàng",className:"gap-1 px-2 text-xs",children:[e(C,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(Z,{onClick:()=>n.chain().focus().deleteRow().run(),disabled:!n.can().deleteRow(),title:"Xóa hàng",className:"gap-1 px-2 text-xs",children:[e(k,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(Z,{onClick:()=>n.chain().focus().deleteTable().run(),disabled:!n.can().deleteTable(),title:"Xóa bảng",className:"gap-1 px-2 text-xs text-red-400 hover:text-red-600",children:[e(S,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Bảng"})]})]}):null;export function WordFillStructuredFormCreator({initialData:p,onSave:b,onCancel:f,onChange:N,onUnsavedChangesChange:v,validationRef:w,externalErrors:y,groupConfig:C,groupType:k,groups:S,questionIndexInPart:Z=0,partId:ee=""}){const[ne,re]=n(!1),[ae,ie]=n(p?.points??1),[le,se]=n(p?.caseSensitive??!1),[ce,oe]=n(p?.explanation||""),[de,me]=n(""),{isGrouped:he,isFirstInGroup:ge,currentGroupNumber:ue,groupTitle:pe,groupSubtitle:xe,groupContent:be,imageUrl:fe,setGroupTitle:Ne,setGroupSubtitle:ve,setGroupContent:we,setImageUrl:ye}=_({partId:ee,groups:S,questionIndexInPart:Z,initialData:{title:p?.groupTitle||"",subtitle:p?.groupSubtitle||"",content:p?.groupContent||"",imageUrl:p?.groupImageUrl||""}}),Ce=(k||"BASIC").toUpperCase(),ke=!(C&&C.length>0)||C.includes("content"),Se=ue||Y(Z,S),[Ae,Ie]=n(p?.title||""),[Ue,Te]=n(p?.imageUrl||""),je=Ae,Ge=Ue,He=Ie,Be=Te,{previewUrl:De}=V(Ge),Re=de||De||"",Oe=p?.content||"<p></p>",[Pe,Fe]=n(Oe),[Le,Ee]=n(p?.answers||{}),ze=a(""),Je=a(p),$e=a(!1),qe=a(Pe),Xe=a(N);r(()=>{Xe.current=N},[N]);const Me=i(()=>[o.configure({heading:{levels:[2,3,4]}}),d.configure({types:["heading","paragraph"]}),m.configure({resizable:!0}),h,g,u],[]),Qe=i(()=>({attributes:{class:"tiptap-editor prose prose-sm max-w-none p-4 focus:outline-none min-h-[400px]"}}),[]),Ve=l(({editor:e})=>{const t=e.getHTML();t!==qe.current&&(qe.current=t,Fe(t))},[]),Ke=s({immediatelyRender:!1,extensions:Me,content:Pe,editorProps:Qe,onUpdate:Ve});r(()=>{qe.current=Pe},[Pe]),r(()=>{if(!(JSON.stringify(Je.current)!==JSON.stringify(p)))return;if(Je.current=p,!p?.content||"<p></p>"===p.content)return;$e.current=!0,Ie(p?.title||""),Te(p?.imageUrl||""),ie(p?.points??1),se(p?.caseSensitive??!1),oe(p?.explanation||""),Ee(p?.answers||{});const e=p.content;e!==qe.current&&(qe.current=e,Fe(e),Ke&&!Ke.isDestroyed&&Ke.commands.setContent(e)),setTimeout(()=>{$e.current=!1},150)},[p,Ke]);const We=Q(e=>{Xe.current?.(e)},300),_e=JSON.stringify(Le),Ye=i(()=>{const e=new Set,t=/\{([\w\d]+)\}/g;let n;for(;null!==(n=t.exec(Pe));)e.add(n[1]);return Array.from(e).sort((e,t)=>{const n=parseInt(e),r=parseInt(t);return isNaN(n)||isNaN(r)?e.localeCompare(t):n-r})},[Pe]);return r(()=>{Ee(e=>{let t=!1;const n={...e};return Object.keys(n).forEach(e=>{Ye.includes(e)||(delete n[e],t=!0)}),Ye.forEach(e=>{e in n||(n[e]="",t=!0)}),t?n:e})},[Ye]),r(()=>{const e={title:je,imageUrl:Ge,content:Pe,answers:Le,points:ae,caseSensitive:le,explanation:ce,groupTitle:pe,groupSubtitle:xe,groupContent:be,groupImageUrl:fe,groupNumber:Se};if($e.current)return void(ze.current=JSON.stringify(e));const t=JSON.stringify(e);t!==ze.current&&(ze.current=t,We(e))},[je,Ge,Pe,_e,ae,le,ce,pe,xe,be,fe,Se]),r(()=>{w&&(w.current={triggerValidation:async()=>{if(!Pe||"<p></p>"===Pe)return!1;if(0===Ye.length)return!1;for(const e of Ye)if(!Le[e]||""===Le[e].trim())return!1;return!0},getFormData:()=>({title:je,imageUrl:Ge,content:Pe,answers:Le,points:ae,caseSensitive:le,explanation:ce,groupTitle:pe,groupSubtitle:xe,groupContent:be,groupImageUrl:fe,groupNumber:Se})})},[w,je,Ge,Pe,Le,ae,le,ce,Ye,pe,xe,be,fe,Se]),t("div",ne?{className:"space-y-4",children:[e(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:e(J,{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-teal-500 to-cyan-600 shadow-sm",children:e(I,{className:"h-4 w-4 text-white"})}),t("div",{children:[e($,{className:"text-base font-bold text-gray-900",children:"Điền từ – Xem trước"}),e("p",{className:"text-xs text-gray-500",children:"Chế độ xem trước với đáp án đúng đã được điền sẵn"})]})]}),t(D,{variant:"outline",size:"sm",onClick:()=>re(!1),className:"gap-2 border-teal-200 text-teal-700 hover:bg-teal-50",children:[e(U,{className:"h-4 w-4"}),"Quay lại Edit"]})]})})}),e(M,{questionData:{title:je,imageUrl:Ge,content:Pe,answers:Le,points:ae,caseSensitive:le},isReviewMode:!0,userAnswer:Le})]}:{className:"space-y-4",children:[he&&"BASIC"===Ce&&e(K,{groupNumber:Se,title:pe,subtitle:xe,content:be,imageUrl:fe,onTitleChange:Ne,onSubtitleChange:ve,onContentChange:we,onImageChange:ye,groupConfig:k?C:void 0,isReadOnly:!ge,uploadIdPrefix:`word-fill-structured-${ee??"group"}`}),he&&"DOCUMENT"===Ce&&e(W,{groupNumber:Se,title:pe,subtitle:xe,content:be,imageUrl:fe,onTitleChange:Ne,onSubtitleChange:ve,onContentChange:we,onImageChange:ye,groupConfig:k?C:void 0,isReadOnly:!ge,uploadIdPrefix:`word-fill-structured-${ee??"document"}`}),t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-500 via-cyan-500 to-emerald-500"}),e(J,{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-gray-100",children:e(j,{className:"h-4 w-4 text-teal-600"})}),t("div",{children:[e($,{className:"text-base font-bold text-gray-900",children:"Điền từ vào biểu mẫu có cấu trúc"}),t("p",{className:"text-xs text-gray-500",children:["Nhập nội dung, đặt chỗ trống bằng ","{0}"," ","{1}","… rồi điền đáp án bên dưới"]})]})]}),t(D,{variant:"outline",size:"sm",onClick:()=>re(!0),disabled:0===Ye.length||Ye.some(e=>!Le[e]?.trim()),className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(I,{className:"h-4 w-4"}),"Xem trước"]})]})}),t(z,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"flex flex-wrap items-center gap-3",children:[e(L,{value:ae,onChange:e=>ie(Number(e.target.value)||1)}),t("div",{role:"button",tabIndex:0,onClick:()=>se(!le),onKeyDown:e=>{" "!==e.key&&"Enter"!==e.key||se(!le)},style:{paddingBlock:"calc(var(--spacing) * 1)"},className:"inline-flex cursor-pointer items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none "+(le?"border-teal-300 bg-teal-50 text-teal-700":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"),children:[e(F,{checked:le,onCheckedChange:se,className:le?"data-[state=checked]:bg-teal-500 pointer-events-none":"pointer-events-none",tabIndex:-1}),"Phân biệt Hoa / thường"]})]}),t("div",{className:"space-y-2",children:[t(R,{htmlFor:"wfsf-title",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(H,{className:"h-4 w-4 text-teal-500"}),"Tiêu đề",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(O,{id:"wfsf-title",value:je,onChange:e=>He(e.target.value),placeholder:"Nhập tiêu đề câu hỏi...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100"})]}),t("div",{className:"space-y-2",children:[t(R,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(B,{className:"h-4 w-4 text-teal-500"}),"Hình ảnh",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(q,{id:"wfsf-image",label:"",accept:"image/*",value:Ge||"",onChange:e=>Be(e),onPresignedUrlChange:me,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),Re&&e(X,{src:Re,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),y&&y.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 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:y.map((t,n)=>e("li",{children:t},n))})]})]})]})]}),ke&&t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e(J,{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-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(x,{className:"h-3.5 w-3.5 text-teal-600"})}),e($,{className:"text-base font-semibold text-gray-800",children:"Nội dung cấu trúc"})]}),Ye.length>0&&t("span",{className:"inline-flex items-center rounded-full border border-teal-200 bg-teal-50 px-2.5 py-0.5 text-xs font-semibold text-teal-700",children:[Ye.length," chỗ trống"]})]})}),t(z,{className:"space-y-3 px-4 pb-4",children:[t("div",{className:"overflow-hidden rounded-xl border border-gray-200 transition-all focus-within:border-teal-300 focus-within:ring-2 focus-within:ring-teal-100",children:[e(te,{editor:Ke}),e(c,{editor:Ke})]}),t("div",{className:"flex items-start gap-2 rounded-lg border border-teal-100 bg-teal-50/60 px-3 py-2.5",children:[e(G,{className:"mt-0.5 h-4 w-4 shrink-0 text-teal-500"}),t("p",{className:"text-xs text-teal-700",children:["Nhập ",e("strong",{children:"{0}"}),", ",e("strong",{children:"{1}"}),", ",e("strong",{children:"{2}"}),"… vào văn bản để tạo ô trống. Đáp án sẽ tự động xuất hiện bên dưới để bạn nhập."]})]})]})]}),Ye.length>0&&t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e(J,{className:"px-4 py-3",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-teal-100",children:e(T,{className:"h-3.5 w-3.5 text-teal-600"})}),t($,{className:"text-base font-semibold text-gray-800",children:["Đáp án ",t("span",{className:"text-xs font-normal text-gray-500",children:["(",Ye.length," chỗ trống)"]})]})]})}),t(z,{className:"px-4 pb-4",children:[e("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",children:Ye.map(n=>{const r=!Le[n]||""===Le[n].trim();return t("div",{className:"rounded-xl border-2 p-3 transition-all duration-200 "+(r?"border-gray-200 bg-white":"border-teal-200 bg-gradient-to-br from-teal-50 to-cyan-50 shadow-sm shadow-teal-100"),children:[t(R,{className:"mb-1.5 flex items-center justify-between text-sm font-semibold text-gray-700",children:[t("span",{children:["Chỗ trống ",e("code",{className:"rounded bg-gray-100 px-1 text-teal-700",children:`{${n}}`})]}),r&&e("span",{className:"text-[10px] font-normal italic text-red-500",children:"*Bắt buộc"})]}),e(O,{value:Le[n]||"",onChange:e=>Ee(t=>({...t,[n]:e.target.value})),placeholder:"Đáp án đúng...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100 "+(r?"":"border-teal-200")}),!r&&t("span",{className:"mt-1.5 inline-flex items-center gap-1 rounded-full bg-teal-100 px-2 py-0.5 text-[10px] font-semibold text-teal-700",children:[e(T,{className:"h-3 w-3"}),"Đã nhập"]})]},n)})}),Ye.some(e=>!Le[e]?.trim())&&t("div",{className:"mt-4 flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-amber-100",children:e("span",{className:"text-lg",children:"⚠️"})}),t("div",{className:"text-sm text-amber-800",children:[e("p",{className:"font-semibold",children:"Còn chỗ trống chưa có đáp án"}),t("p",{className:"mt-0.5 text-amber-700",children:["Vui lòng điền đáp án đúng cho tất cả các ô được đánh dấu ",e("span",{className:"text-red-500 font-medium",children:"*Bắt buộc"}),"."]})]})]})]})]}),t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(J,{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(A,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e($,{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 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"mt-0.5 text-xs text-gray-500",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(z,{className:"px-4 pb-4",children:e(P,{value:ce,onChange:e=>oe(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] resize-none border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200"})})]})]})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as n,useEffect as r,useRef as a,useMemo as i,useCallback as s}from"react";import{useEditor as l,EditorContent as o}from"@tiptap/react";import c from"@tiptap/starter-kit";import d from"@tiptap/extension-text-align";import m from"@tiptap/extension-underline";import{Table as h,TableRow as g,TableCell as u,TableHeader as p}from"@tiptap/extension-table";import{List as f,ListOrdered as x,Bold as b,Italic as N,Underline as w,Strikethrough as v,AlignLeft as y,AlignCenter as C,AlignRight as k,TableIcon as I,Plus as S,Minus as T,Trash2 as O,Undo2 as A,Redo2 as B,BookOpen as H,Eye as U,Pencil as j,CheckCircle2 as W,PenLine as D,Info as P,MessageSquare as R,ImageIcon as _}from"lucide-react";import{Button as G}from"../../../../components/ui/button";import{Label as J}from"../../../../components/ui/label";import{Input as F}from"../../../../components/ui/input";import{Textarea as L}from"../../../../components/ui/textarea";import{Switch as E}from"../../../../components/ui/switch";import{PointsInput as K}from"../../../../components/ui/points-input";import{Card as M,CardContent as z,CardHeader as $,CardTitle as q}from"../../../../components/ui/card";import{FileUpload as V}from"../../../../components/ui/file-upload";import{ImagePreview as X}from"../../../../components/ui/image-preview";import{WordFillStructuredFormClient as Q}from"./WordFillStructuredFormClient";import{WfsfWordBankEditor as Y}from"./WfsfWordBankEditor";import{useDebouncedCallback as Z,usePresignedFileUrl as ee}from"../../../../shared/lib/hooks";import{BasicQuestionGroupCard as te}from"../../groups/BasicQuestionGroupCard";import{DocumentGroupCard as ne}from"../../groups/DocumentGroupCard";import{useBasicQuestionGroup as re,getGroupNumberForIndex as ae}from"../../_shared/hooks/useBasicQuestionGroup";import{bankPayloadForMode as ie,detectWfsfBankMode as se,emptySharedBank as le,normalizeOptionsWordBank as oe,normalizeSharedWordBank as ce}from"./wfsf-word-bank.utils";const de=({onClick:t,active:n,disabled:r,title:a,children:i,className:s=""})=>e("button",{type:"button",onClick:t,disabled:r,title:a,className:`inline-flex h-7 min-w-7 items-center justify-center rounded px-1.5 text-sm transition-colors disabled:opacity-30\n ${n?"bg-teal-100 text-teal-700":"text-gray-500 hover:bg-gray-100 hover:text-gray-800"}\n ${s}`,children:i}),me=()=>e("div",{className:"mx-1 h-4 w-px bg-gray-200"}),he=({editor:n})=>n?t("div",{className:"flex flex-wrap items-center gap-0.5 border-b border-gray-100 bg-gray-50/70 px-2 py-1.5 rounded-t-lg",children:[e(de,{onClick:()=>n.chain().focus().undo().run(),disabled:!n.can().chain().focus().undo().run(),title:"Hoàn tác",children:e(A,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().redo().run(),disabled:!n.can().chain().focus().redo().run(),title:"Làm lại",children:e(B,{className:"h-3.5 w-3.5"})}),e(me,{}),e(de,{onClick:()=>n.chain().focus().toggleBold().run(),active:n.isActive("bold"),title:"Đậm",children:e(b,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleItalic().run(),active:n.isActive("italic"),title:"Nghiêng",children:e(N,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleUnderline().run(),active:n.isActive("underline"),title:"Gạch dưới",children:e(w,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleStrike().run(),active:n.isActive("strike"),title:"Gạch ngang",children:e(v,{className:"h-3.5 w-3.5"})}),e(me,{}),e(de,{onClick:()=>n.chain().focus().toggleHeading({level:2}).run(),active:n.isActive("heading",{level:2}),className:"px-2 text-xs font-bold",children:"H2"}),e(de,{onClick:()=>n.chain().focus().toggleHeading({level:3}).run(),active:n.isActive("heading",{level:3}),className:"px-2 text-xs font-bold",children:"H3"}),e(de,{onClick:()=>n.chain().focus().toggleHeading({level:4}).run(),active:n.isActive("heading",{level:4}),className:"px-2 text-xs font-bold",children:"H4"}),e(me,{}),e(de,{onClick:()=>n.chain().focus().toggleBulletList().run(),active:n.isActive("bulletList"),title:"Danh sách",children:e(f,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleOrderedList().run(),active:n.isActive("orderedList"),title:"Danh sách có số",children:e(x,{className:"h-3.5 w-3.5"})}),e(me,{}),e(de,{onClick:()=>n.chain().focus().setTextAlign("left").run(),active:n.isActive({textAlign:"left"}),title:"Trái",children:e(y,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().setTextAlign("center").run(),active:n.isActive({textAlign:"center"}),title:"Giữa",children:e(C,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().setTextAlign("right").run(),active:n.isActive({textAlign:"right"}),title:"Phải",children:e(k,{className:"h-3.5 w-3.5"})}),e(me,{}),t(de,{onClick:()=>n.chain().focus().insertTable({rows:5,cols:2,withHeaderRow:!1}).run(),title:"Chèn bảng 2 cột × 5 hàng",className:"gap-1 px-2 text-xs",children:[e(I,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Bảng 2 cột"})]}),t(de,{onClick:()=>n.chain().focus().addRowAfter().run(),disabled:!n.can().addRowAfter(),title:"Thêm hàng",className:"gap-1 px-2 text-xs",children:[e(S,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(de,{onClick:()=>n.chain().focus().deleteRow().run(),disabled:!n.can().deleteRow(),title:"Xóa hàng",className:"gap-1 px-2 text-xs",children:[e(T,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(de,{onClick:()=>n.chain().focus().deleteTable().run(),disabled:!n.can().deleteTable(),title:"Xóa bảng",className:"gap-1 px-2 text-xs text-red-400 hover:text-red-600",children:[e(O,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Bảng"})]})]}):null;export function WordFillStructuredFormCreator({initialData:f,onSave:b,onCancel:N,onChange:w,onUnsavedChangesChange:v,validationRef:y,externalErrors:C,groupConfig:k,groupType:I,groups:S,questionIndexInPart:T=0,partId:O="",viewMode:A}){const[B,de]=n(!1),[me,ge]=n(f?.points??1),[ue,pe]=n(f?.caseSensitive??!1),[fe,xe]=n(f?.instruction||f?.explanation||""),[be,Ne]=n(f?.explanation||""),[we,ve]=n(""),[ye,Ce]=n(f?.blankStyle),ke=se(f?.viewMode??A,f?.wordBank),[Ie,Se]=n(ke),[Te,Oe]=n(()=>"WITH_WORD_BANK"===ke?ce(f?.wordBank):[]),[Ae,Be]=n(()=>"WITH_OPTIONS"===ke?oe(f?.wordBank,Object.keys(f?.answers||{})):[]),{isGrouped:He,isFirstInGroup:Ue,currentGroupNumber:je,groupTitle:We,groupSubtitle:De,groupContent:Pe,imageUrl:Re,setGroupTitle:_e,setGroupSubtitle:Ge,setGroupContent:Je,setImageUrl:Fe}=re({partId:O,groups:S,questionIndexInPart:T,initialData:{title:f?.groupTitle||"",subtitle:f?.groupSubtitle||"",content:f?.groupContent||"",imageUrl:f?.groupImageUrl||""}}),Le=(I||"BASIC").toUpperCase(),Ee=!(k&&k.length>0)||k.includes("content"),Ke=je||ae(T,S),[Me,ze]=n(f?.title||""),[$e,qe]=n(f?.imageUrl||""),Ve=Me,Xe=$e,Qe=ze,Ye=qe,{previewUrl:Ze}=ee(Xe),et=we||Ze||"",tt=f?.content||"<p></p>",[nt,rt]=n(tt),[at,it]=n(f?.answers||{}),st=a(""),lt=a(f),ot=a(!1),ct=a(nt),dt=a(w);r(()=>{dt.current=w},[w]);const mt=i(()=>[c.configure({heading:{levels:[2,3,4]}}),m,d.configure({types:["heading","paragraph"]}),h.configure({resizable:!0}),g,u,p],[]),ht=i(()=>({attributes:{class:"tiptap-editor prose prose-sm max-w-none p-4 focus:outline-none min-h-[400px]"}}),[]),gt=s(({editor:e})=>{const t=e.getHTML();t!==ct.current&&(ct.current=t,rt(t))},[]),ut=l({immediatelyRender:!1,extensions:mt,content:nt,editorProps:ht,onUpdate:gt});r(()=>{ct.current=nt},[nt]),r(()=>{if(!(JSON.stringify(lt.current)!==JSON.stringify(f)))return;if(lt.current=f,!f?.content||"<p></p>"===f.content)return;ot.current=!0,ze(f?.title||""),qe(f?.imageUrl||""),ge(f?.points??1),pe(f?.caseSensitive??!1),xe(f?.instruction||f?.explanation||"");{const e=String(f?.instruction||f?.explanation||"").trim(),t=String(f?.explanation||"").trim();Ne(t&&t!==e?t:"")}it(f?.answers||{}),Ce(f?.blankStyle);const e=se(f?.viewMode??A,f?.wordBank);Se(e),"WITH_WORD_BANK"===e?Oe(ce(f?.wordBank)):"WITH_OPTIONS"===e?Be(oe(f?.wordBank,Object.keys(f?.answers||{}))):(Oe([]),Be([]));const t=f.content;t!==ct.current&&(ct.current=t,rt(t),ut&&!ut.isDestroyed&&ut.commands.setContent(t)),setTimeout(()=>{ot.current=!1},150)},[f,ut,A]);const pt=Z(e=>{dt.current?.(e)},300),ft=JSON.stringify(at),xt=i(()=>{const e=new Set,t=/\{([\w\d]+)\}/g;let n;for(;null!==(n=t.exec(nt));)e.add(n[1]);return Array.from(e).sort((e,t)=>{const n=parseInt(e),r=parseInt(t);return isNaN(n)||isNaN(r)?e.localeCompare(t):n-r})},[nt]);r(()=>{it(e=>{let t=!1;const n={...e};return Object.keys(n).forEach(e=>{xt.includes(e)||(delete n[e],t=!0)}),xt.forEach(e=>{e in n||(n[e]="",t=!0)}),t?n:e})},[xt]),r(()=>{"WITH_OPTIONS"===Ie&&Be(e=>{const t=oe(e,xt);return 0===e.length&&xt.length>0?xt.map(()=>null):JSON.stringify(e)===JSON.stringify(t)?e:t})},[Ie,xt]);const bt=JSON.stringify(Te),Nt=JSON.stringify(Ae),wt=s(()=>{const e=ie(Ie,Te,Ae);return{title:Ve,imageUrl:Xe,content:nt,answers:at,points:me,caseSensitive:ue,instruction:fe,explanation:be,blankStyle:ye,...e,groupTitle:We,groupSubtitle:De,groupContent:Pe,groupImageUrl:Re,groupNumber:Ke}},[Ie,Te,Ae,Ve,Xe,nt,at,me,ue,fe,be,ye,We,De,Pe,Re,Ke]),vt=s(e=>{Se(e),"WITH_WORD_BANK"===e?Oe(e=>e.length>0?e:le()):"WITH_OPTIONS"===e&&Be(e=>e.length===xt.length?e:e.length>0?oe(e,xt):xt.map(()=>({options:["",""]})))},[xt]);return r(()=>{const e=wt();if(ot.current)return void(st.current=JSON.stringify(e));const t=JSON.stringify(e);t!==st.current&&(st.current=t,pt(e))},[wt,ft,bt,Nt,Ie,ye]),r(()=>{y&&(y.current={triggerValidation:async()=>{if(!nt||"<p></p>"===nt)return!1;if(0===xt.length)return!1;for(const e of xt)if(!at[e]||""===at[e].trim())return!1;if("WITH_WORD_BANK"===Ie){if(Te.map(e=>e.word.trim()).filter(Boolean).length<1)return!1}if("WITH_OPTIONS"===Ie)for(let e=0;e<xt.length;e+=1){const t=Ae[e];if(null==t)continue;const n=t.options.map(e=>e.trim()).filter(Boolean);if(n.length<2)return!1;const r=xt[e],a=(at[r]||"").trim();if(a&&!n.includes(a))return!1}return!0},getFormData:()=>wt()})},[y,wt,nt,xt,at,Ie,Te,Ae]),t("div",B?{className:"space-y-4",children:[e(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:e($,{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-teal-500 to-cyan-600 shadow-sm",children:e(U,{className:"h-4 w-4 text-white"})}),t("div",{children:[e(q,{className:"text-base font-bold text-gray-900",children:"Điền từ – Xem trước"}),e("p",{className:"text-xs text-gray-500",children:"Chế độ xem trước với đáp án đúng đã được điền sẵn"})]})]}),t(G,{variant:"outline",size:"sm",onClick:()=>de(!1),className:"gap-2 border-teal-200 text-teal-700 hover:bg-teal-50",children:[e(j,{className:"h-4 w-4"}),"Quay lại Edit"]})]})})}),e(Q,{questionData:wt(),isReviewMode:!0,userAnswer:at,instruction:fe})]}:{className:"space-y-4",children:[He&&"BASIC"===Le&&e(te,{groupNumber:Ke,title:We,subtitle:De,content:Pe,imageUrl:Re,onTitleChange:_e,onSubtitleChange:Ge,onContentChange:Je,onImageChange:Fe,groupConfig:I?k:void 0,isReadOnly:!Ue,uploadIdPrefix:`word-fill-structured-${O??"group"}`}),He&&"DOCUMENT"===Le&&e(ne,{groupNumber:Ke,title:We,subtitle:De,content:Pe,imageUrl:Re,onTitleChange:_e,onSubtitleChange:Ge,onContentChange:Je,onImageChange:Fe,groupConfig:I?k:void 0,isReadOnly:!Ue,uploadIdPrefix:`word-fill-structured-${O??"document"}`}),t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-500 via-cyan-500 to-emerald-500"}),e($,{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-gray-100",children:e(D,{className:"h-4 w-4 text-teal-600"})}),t("div",{children:[e(q,{className:"text-base font-bold text-gray-900",children:"Điền từ vào biểu mẫu có cấu trúc"}),t("p",{className:"text-xs text-gray-500",children:["Nhập nội dung, đặt chỗ trống bằng ","{0}"," ","{1}","… rồi điền đáp án bên dưới"]})]})]}),t(G,{variant:"outline",size:"sm",onClick:()=>de(!0),disabled:0===xt.length||xt.some(e=>!at[e]?.trim()),className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(U,{className:"h-4 w-4"}),"Xem trước"]})]})}),t(z,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"space-y-2",children:[t(J,{htmlFor:"wfsf-instruction",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(P,{className:"h-4 w-4 text-teal-500"}),"Instruction",e("span",{className:"text-sm font-normal text-gray-500",children:"(hướng dẫn bài tập)"})]}),e(L,{id:"wfsf-instruction",value:fe,onChange:e=>xe(e.target.value),placeholder:"VD: Complete the words in the sentences. Use the words in the box.",rows:2,className:"min-h-[64px] resize-y border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100"})]}),t("div",{className:"flex flex-wrap items-center gap-3",children:[e(K,{value:me,onChange:e=>ge(Number(e.target.value)||1)}),t("div",{role:"button",tabIndex:0,onClick:()=>pe(!ue),onKeyDown:e=>{" "!==e.key&&"Enter"!==e.key||pe(!ue)},style:{paddingBlock:"calc(var(--spacing) * 1)"},className:"inline-flex cursor-pointer items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none "+(ue?"border-teal-300 bg-teal-50 text-teal-700":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"),children:[e(E,{checked:ue,onCheckedChange:pe,className:ue?"data-[state=checked]:bg-teal-500 pointer-events-none":"pointer-events-none",tabIndex:-1}),"Phân biệt Hoa / thường"]})]}),t("div",{className:"space-y-2",children:[t(J,{htmlFor:"wfsf-title",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(R,{className:"h-4 w-4 text-teal-500"}),"Tiêu đề",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(F,{id:"wfsf-title",value:Ve,onChange:e=>Qe(e.target.value),placeholder:"Nhập tiêu đề câu hỏi...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100"})]}),t("div",{className:"space-y-2",children:[t(J,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(_,{className:"h-4 w-4 text-teal-500"}),"Hình ảnh",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(V,{id:"wfsf-image",label:"",accept:"image/*",value:Xe||"",onChange:e=>Ye(e),onPresignedUrlChange:ve,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),et&&e(X,{src:et,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),C&&C.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 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:C.map((t,n)=>e("li",{children:t},n))})]})]})]})]}),Ee&&t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e($,{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-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(x,{className:"h-3.5 w-3.5 text-teal-600"})}),e(q,{className:"text-base font-semibold text-gray-800",children:"Nội dung cấu trúc"})]}),xt.length>0&&t("span",{className:"inline-flex items-center rounded-full border border-teal-200 bg-teal-50 px-2.5 py-0.5 text-xs font-semibold text-teal-700",children:[xt.length," chỗ trống"]})]})}),t(z,{className:"space-y-3 px-4 pb-4",children:[t("div",{className:"overflow-hidden rounded-xl border border-gray-200 transition-all focus-within:border-teal-300 focus-within:ring-2 focus-within:ring-teal-100",children:[e(he,{editor:ut}),e(o,{editor:ut})]}),t("div",{className:"flex items-start gap-2 rounded-lg border border-teal-100 bg-teal-50/60 px-3 py-2.5",children:[e(P,{className:"mt-0.5 h-4 w-4 shrink-0 text-teal-500"}),t("p",{className:"text-xs text-teal-700",children:["Nhập ",e("strong",{children:"{0}"}),", ",e("strong",{children:"{1}"}),", ",e("strong",{children:"{2}"}),"… vào văn bản để tạo ô trống. Đáp án sẽ tự động xuất hiện bên dưới để bạn nhập."]})]})]})]}),e(Y,{mode:Ie,onModeChange:vt,sharedBank:Te,onSharedBankChange:Oe,optionsBank:Ae,onOptionsBankChange:Be,blankKeys:xt,answers:at,onAnswerChange:(e,t)=>it(n=>({...n,[e]:t}))}),xt.length>0&&"WITH_OPTIONS"!==Ie&&t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e($,{className:"px-4 py-3",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-teal-100",children:e(W,{className:"h-3.5 w-3.5 text-teal-600"})}),t(q,{className:"text-base font-semibold text-gray-800",children:["Đáp án ",t("span",{className:"text-xs font-normal text-gray-500",children:["(",xt.length," chỗ trống)"]})]})]})}),t(z,{className:"px-4 pb-4",children:[e("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",children:xt.map(n=>{const r=!at[n]||""===at[n].trim();return t("div",{className:"rounded-xl border-2 p-3 transition-all duration-200 "+(r?"border-gray-200 bg-white":"border-teal-200 bg-gradient-to-br from-teal-50 to-cyan-50 shadow-sm shadow-teal-100"),children:[t(J,{className:"mb-1.5 flex items-center justify-between text-sm font-semibold text-gray-700",children:[t("span",{children:["Chỗ trống ",e("code",{className:"rounded bg-gray-100 px-1 text-teal-700",children:`{${n}}`})]}),r&&e("span",{className:"text-[10px] font-normal italic text-red-500",children:"*Bắt buộc"})]}),e(F,{value:at[n]||"",onChange:e=>it(t=>({...t,[n]:e.target.value})),placeholder:"Đáp án đúng...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100 "+(r?"":"border-teal-200")}),!r&&t("span",{className:"mt-1.5 inline-flex items-center gap-1 rounded-full bg-teal-100 px-2 py-0.5 text-[10px] font-semibold text-teal-700",children:[e(W,{className:"h-3 w-3"}),"Đã nhập"]})]},n)})}),xt.some(e=>!at[e]?.trim())&&t("div",{className:"mt-4 flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-amber-100",children:e("span",{className:"text-lg",children:"⚠️"})}),t("div",{className:"text-sm text-amber-800",children:[e("p",{className:"font-semibold",children:"Còn chỗ trống chưa có đáp án"}),t("p",{className:"mt-0.5 text-amber-700",children:["Vui lòng điền đáp án đúng cho tất cả các ô được đánh dấu ",e("span",{className:"text-red-500 font-medium",children:"*Bắt buộc"}),"."]})]})]})]})]}),t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e($,{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(H,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e(q,{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 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"mt-0.5 text-xs text-gray-500",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(z,{className:"px-4 pb-4",children:e(L,{value:be,onChange:e=>Ne(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] resize-none border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200"})})]})]})}
@@ -1 +1 @@
1
- import{WordFillStructuredFormCreator as t}from"./WordFillStructuredFormCreator";export const wordFillStructuredFormRegistration={component:t,transformInitialData:t=>{const e=t?.content||{},n=t?.answer||{},o=t?.correctAnswer||n?.correctAnswer||{},a=t?.total_points?parseFloat(t.total_points):n?.points||t?.points||1,i=t?.explanation||n?.explanation||"";let s=e?.content||n?.content||"";const r=e?.blocks||n?.blocks;!s&&r?.length&&(s=r.map(t=>(t.heading?`<h3>${t.heading}</h3>`:"")+(t.content||"")).join(""));const l=t?.questionGroup?.payload;return{title:e?.title||n?.title||"",imageUrl:e?.imageUrl||n?.imageUrl||"",content:s,answers:o?.answers||n?.answers||{},caseSensitive:o?.caseSensitive??n?.caseSensitive??!1,explanation:i,points:a,groupTitle:l?.title||n?.groupTitle||"",groupSubtitle:l?.subtitle||n?.groupSubtitle||"",groupContent:l?.content||n?.groupContent||"",groupImageUrl:l?.imageUrl||n?.groupImageUrl||""}},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef,groupConfig:t.groupConfig,groupType:t.groupType,groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
1
+ import{WordFillStructuredFormCreator as t}from"./WordFillStructuredFormCreator";import{detectWfsfBankMode as e,normalizeOptionsWordBank as n,normalizeSharedWordBank as o}from"./wfsf-word-bank.utils";export const wordFillStructuredFormRegistration={component:t,transformInitialData:t=>{const i=t?.content||{},a=t?.answer||{},r=t?.correctAnswer||a?.correctAnswer||{},s=t?.total_points?parseFloat(t.total_points):a?.points||t?.points||1,l=String(a?.instruction||t?.instruction||t?.explanation||""),p=String(a?.explanation||"");let u=i?.content||a?.content||"";const c=i?.blocks||a?.blocks;!u&&c?.length&&(u=c.map(t=>(t.heading?`<h3>${t.heading}</h3>`:"")+(t.content||"")).join(""));const g=i?.wordBank??a?.wordBank,d=e(i?.viewMode??a?.viewMode,g),w=function(t){const e=new Set,n=/\{([\w\d]+)\}/g;let o;for(;null!==(o=n.exec(t));)e.add(o[1]);return Array.from(e).sort((t,e)=>{const n=parseInt(t,10),o=parseInt(e,10);return Number.isNaN(n)||Number.isNaN(o)?t.localeCompare(e):n-o})}(String(u||""));let f=g,m="FREE_TYPE"===d?void 0:d;"WITH_WORD_BANK"===d?(f=o(g),m="WITH_WORD_BANK"):"WITH_OPTIONS"===d?(f=n(g,w),m="WITH_OPTIONS"):f=void 0;const v=t?.questionGroup?.payload;return{title:i?.title||a?.title||"",imageUrl:i?.imageUrl||a?.imageUrl||"",content:u,answers:r?.answers||a?.answers||{},caseSensitive:r?.caseSensitive??a?.caseSensitive??!1,blankStyle:i?.blankStyle??a?.blankStyle,viewMode:m,wordBank:f,instruction:l,explanation:p,points:s,groupTitle:v?.title||a?.groupTitle||"",groupSubtitle:v?.subtitle||a?.groupSubtitle||"",groupContent:v?.content||a?.groupContent||"",groupImageUrl:v?.imageUrl||a?.groupImageUrl||""}},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef,groupConfig:t.groupConfig,groupType:t.groupType,groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId,viewMode:t.viewMode}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
@@ -2,9 +2,12 @@ import type { TransformHandler } from '../../../../shared/lib/utils/question-tra
2
2
  /**
3
3
  * Transform WORD_FILL_STRUCTURED_FORM creator data → API format
4
4
  *
5
- * Creator (question.answer): { title, imageUrl, content, answers, caseSensitive, points, explanation }
5
+ * Creator (question.answer): {
6
+ * title, imageUrl, content, answers, caseSensitive, points, explanation,
7
+ * blankStyle?, viewMode?, wordBank?
8
+ * }
6
9
  * API:
7
- * content: { title, imageUrl, content }
10
+ * content: { title, imageUrl, content, blankStyle?, viewMode?, wordBank? }
8
11
  * correctAnswer: { answers, caseSensitive }
9
12
  */
10
13
  export declare const transformWordFillStructuredForm: TransformHandler;
@@ -1 +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
+ 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};"letter"!==t.blankStyle&&"word"!==t.blankStyle&&"phrase"!==t.blankStyle||(n.blankStyle=t.blankStyle),"WITH_OPTIONS"!==t.viewMode&&"WITH_WORD_BANK"!==t.viewMode||(n.viewMode=t.viewMode),Array.isArray(t.wordBank)&&(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}};
@@ -0,0 +1,24 @@
1
+ import type { WfsfOptionsWordBankEntry, WfsfSharedWordBankEntry, WfsfViewMode, WfsfWordBankEntry } from '../../_shared/types/word-fill-structured-form.type';
2
+ export type WfsfBankMode = 'FREE_TYPE' | WfsfViewMode;
3
+ export declare function isSharedWordEntry(entry: unknown): entry is WfsfSharedWordBankEntry;
4
+ export declare function isOptionsWordEntry(entry: unknown): entry is {
5
+ options: string[];
6
+ };
7
+ /** Detect mode from explicit viewMode and/or wordBank shape. */
8
+ export declare function detectWfsfBankMode(viewMode: unknown, wordBank: unknown): WfsfBankMode;
9
+ export declare function normalizeSharedWordBank(wordBank: unknown): WfsfSharedWordBankEntry[];
10
+ /**
11
+ * Align options bank length to blank count (numeric keys preferred).
12
+ * Non-numeric blank keys still get sequential slots by sorted order.
13
+ */
14
+ export declare function normalizeOptionsWordBank(wordBank: unknown, blankKeys: string[]): WfsfOptionsWordBankEntry[];
15
+ export declare function emptyOptionsBank(blankCount: number): WfsfOptionsWordBankEntry[];
16
+ export declare function emptySharedBank(): WfsfSharedWordBankEntry[];
17
+ /**
18
+ * Payload slice for save / onChange.
19
+ * FREE_TYPE sets viewMode: null so host can clear preserved bank fields.
20
+ */
21
+ export declare function bankPayloadForMode(mode: WfsfBankMode, sharedBank: WfsfSharedWordBankEntry[], optionsBank: WfsfOptionsWordBankEntry[]): {
22
+ viewMode: WfsfViewMode | null;
23
+ wordBank?: WfsfWordBankEntry[];
24
+ };
@@ -0,0 +1 @@
1
+ export function isSharedWordEntry(r){return!!r&&"object"==typeof r&&!Array.isArray(r)&&"word"in r&&!("options"in r)}export function isOptionsWordEntry(r){return!!r&&"object"==typeof r&&!Array.isArray(r)&&Array.isArray(r.options)}export function detectWfsfBankMode(r,n){if("WITH_OPTIONS"===r)return"WITH_OPTIONS";if("WITH_WORD_BANK"===r)return"WITH_WORD_BANK";if(!Array.isArray(n)||0===n.length)return"FREE_TYPE";const t=n.find(r=>null!=r);return isOptionsWordEntry(t)?"WITH_OPTIONS":isSharedWordEntry(t)?"WITH_WORD_BANK":n.some(r=>isOptionsWordEntry(r))?"WITH_OPTIONS":"FREE_TYPE"}export function normalizeSharedWordBank(r){return Array.isArray(r)?r.filter(isSharedWordEntry).map(r=>({word:String(r.word??""),...r.isExample?{isExample:!0}:{}})):[]}export function normalizeOptionsWordBank(r,n){const t=Array.isArray(r)?r:[],o=n.length,e=[];for(let r=0;r<o;r+=1){const o=n[r],i=parseInt(o,10),a=t[Number.isNaN(i)?r:i]??t[r]??null;null!=a?isOptionsWordEntry(a)&&a.options.length>=1?e.push({options:a.options.map(r=>String(r??""))}):e.push(null):e.push(null)}return e}export function emptyOptionsBank(r){return Array.from({length:r},()=>({options:["",""]}))}export function emptySharedBank(){return[{word:""}]}export function bankPayloadForMode(r,n,t){if("WITH_WORD_BANK"===r){return{viewMode:"WITH_WORD_BANK",wordBank:n.map(r=>({word:r.word.trim(),...r.isExample?{isExample:!0}:{}})).filter(r=>r.word.length>0)}}if("WITH_OPTIONS"===r){return{viewMode:"WITH_OPTIONS",wordBank:t.map(r=>{if(null==r)return null;const n=r.options.map(r=>r.trim()).filter(Boolean);return n.length<2?null:{options:n}})}}return{viewMode:null}}
@@ -1 +1 @@
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
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Button as s}from"../../../components/ui/button";import{Card as t,CardContent as n,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 x}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as f}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as C}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 y}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as v}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";export function QuestionViewer({question:R,onSubmitAnswer:S,isReviewMode:E=!1,userAnswer:L}){const[T,O]=m(L||null),[I,W]=m(0),k=u(null),[q,D]=m(L||{}),P=R.answer,[U,Q]=m(L||Array(P?.blanks?.length||0).fill("")),[G,M]=m(L||{}),[H,$]=m(L||{}),[j,F]=m(L||{}),[B,K]=m(L||{}),Y=()=>{const e=R.answer.maxPlayCount||3;I>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(k.current?.play(),W(I+1))},V=()=>{const t=R.answer,{blanks:n,caseSensitive:o=!1}=t;return r("div",{className:"space-y-4",children:[n.map((s,t)=>{const a=U[t]||"",l=E&&((e,r)=>{const s=n[e];return o?r===s:r.toLowerCase()===s.toLowerCase()})(t,a);return r("div",{className:"flex items-center space-x-2",children:[r("span",{className:"text-sm font-medium",children:["Chỗ trống ",t+1,":"]}),e(i,{value:a,onChange:e=>((e,r)=>{const s=[...U];s[e]=r,Q(s)})(t,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(E?l?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:E}),E&&l&&e(c,{className:"h-4 w-4 text-green-600"}),E&&!l&&a&&e(d,{className:"h-4 w-4 text-red-600"})]},t)}),!E&&e(s,{onClick:()=>{S&&S(U)},disabled:U.some(e=>""===e.trim()),children:"Nộp đáp án"})]})};return r(t,{children:[e(o,{children:r(a,{className:"flex items-center justify-between",children:[e("span",{children:"string"==typeof R.content?R.content:R.content?.question||"Câu hỏi"}),r("span",{className:"text-sm font-normal text-gray-500",children:[R.points," điểm"]})]})}),r(n,{className:"space-y-4",children:[(()=>{switch(R.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=R.answer,{isGroup:s,questionGroup:t}=R;let n={};return n=R.content&&"object"==typeof R.content?{...R.content,correctAnswer:R.correctAnswer?.answer??-1,id:R.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber}:{...r,question:R.content||r?.question,id:R.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber},e(C,{questionData:n,questionConfig:R.questionConfig,onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"CHOOSE_THE_CORRECT_ANSWER_GROUP":return(()=>{const s=R.content,t=R.correctAnswer||R.correct_answer||R.answer,n=s?.items||[],o=Array.isArray(t?.answer)?t.answer:[];return e("div",{className:"space-y-5",children:n.map((s,t)=>{const n=o[t],a=B[t],i=s?.content?.options||[];return r("div",{className:"space-y-2",children:[e("p",{className:"text-sm font-medium leading-relaxed",children:s?.content?.question||""}),e("div",{className:"flex flex-wrap gap-2",children:i.map(e=>{const s="string"==typeof e?e:e?.id,o="string"==typeof e?e:e?.text||s,i=a===s,c=E&&s===n,l=E&&i&&s!==n;return r("button",{disabled:E,onClick:()=>{const e={...B,[t]:s};K(e),O(e),S?.(e)},className:["rounded-md border px-3 py-1.5 text-sm transition-colors",c?"border-green-500 bg-green-50 text-green-800 font-semibold":"",l?"border-red-500 bg-red-50 text-red-800":"",i&&!E?"border-blue-500 bg-blue-50 text-blue-800":"",i||E?"":"border-gray-200 hover:border-gray-400"].filter(Boolean).join(" "),children:[o,c&&" ✓",l&&" ✗"]},s)})}),E&&r("p",{className:"text-xs text-gray-500",children:["Đáp án: ",e("strong",{children:i.find(e=>(e?.id??e)===n)?.text??n})]})]},t)})})})();case"FILL_IN_BLANK":return V();case"TRUE_FALSE":return(()=>{const t=R.answer,{correctAnswer:n}=t;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(s=>{const t=T===s.value;return e("div",{className:`flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${t?"border-blue-500 bg-blue-50":"border-gray-200 hover:border-gray-300"} ${E&&s.value===n?"border-green-500 bg-green-50":E&&t&&s.value!==n?"border-red-500 bg-red-50":""}`,onClick:()=>!E&&O(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 ${t?"border-blue-500 bg-blue-500":"border-gray-300"} ${E&&s.value===n?"border-green-500 bg-green-500":""}`,children:t&&e("div",{className:"m-auto h-2 w-2 rounded-full bg-white"})}),e("span",{className:"flex-1",children:s.label}),E&&s.value===n&&e(c,{className:"ml-2 h-4 w-4 text-green-600"}),E&&t&&s.value!==n&&e(d,{className:"ml-2 h-4 w-4 text-red-600"})]})},s.value.toString())}),!E&&e(s,{onClick:()=>{S&&S(T)},disabled:null===T,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const t=R.answer,{minWords:n,maxWords:o}=t,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:[n&&o?`Số từ: ${n} - ${o} từ`:n?`Tối thiểu: ${n} từ`:o?`Tối đa: ${o} từ`:"",T&&r("span",{className:"ml-2 "+(a>=(n||0)&&a<=(o||1/0)?"text-green-600":"text-red-600"),children:["(",a," từ)"]})]}),e("textarea",{value:T||"",onChange:e=>O(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:E}),!E&&e(s,{onClick:()=>{S&&S(T)},disabled:!T||T.trim().length<(n||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const t=R.answer,{audioUrl:n,blanks:o}=t;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:Y,variant:"outline",className:"gap-2",children:[e(l,{className:"h-5 w-5"}),"Phát âm thanh (",I,"/",t.maxPlayCount||3,")"]}),e("audio",{ref:k,src:n})]}),e("div",{className:"space-y-3",children:o.map((s,t)=>r("div",{className:"flex items-center gap-3",children:[r("span",{className:"w-20 font-medium",children:["Blank ",t+1,":"]}),e(i,{value:U[t],onChange:e=>{const r=[...U];r[t]=e.target.value,Q(r),O(r)},placeholder:"Nhập câu trả lời...",disabled:E,className:E?U[t]?.toLowerCase()===o[t]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),E&&r("span",{className:"text-sm text-gray-600",children:["Đáp án: ",e("strong",{children:o[t]})]})]},t))})]})})();case"TICK_TRUE_OR_FALSE":case"TICK_YES_OR_NO":return(()=>{const s=R.answer,{statements:t}=s;return e("div",{className:"space-y-3",children:t.map(s=>e("div",{className:`rounded-lg border-2 p-4 ${E&&q[s.id]===s.isCorrect?"border-green-500 bg-green-50":""} ${E&&q[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={...q,[s.id]:!0};D(e),O(e)},disabled:E,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!0===q[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={...q,[s.id]:!1};D(e),O(e)},disabled:E,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!1===q[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}),E&&e("span",{className:"text-sm font-medium",children:s.isCorrect?"✓ Đúng":"✗ Sai"})]})},s.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=R.answer,s=R.correctAnswer||R.correct_answer,t=(r?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(h,{questionData:{subQuestions:t},onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const s=R.answer,{passage:t,blanks:n}=s,o=t.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,t)=>{const o=parseInt(s);if(!isNaN(o)){const s=n.find(e=>e.index===o);return r("span",{className:"mx-1 inline-flex items-center",children:[e(i,{value:H[o]||"",onChange:e=>{const r={...H,[o]:e.target.value};$(r),O(r)},className:`inline-block h-8 w-32 text-center ${E&&H[o]===s?.correctAnswer?"border-green-500 bg-green-50":""} ${E&&H[o]!==s?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:E}),E&&r("span",{className:"ml-2 text-sm text-green-600",children:["(",s?.correctAnswer,")"]})]},t)}return e("span",{children:s},t)})})})})})();case"WRITE_SHORT_PARAGRAPH":return(()=>{const r=R.answer;return e(w,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=R.answer;return e(A,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=R.answer,s=R.content,t=R.questionGroup;let n=[];r?.subQuestions&&r.subQuestions.length>0?n=r.subQuestions:s?.imageUrl&&s?.word&&(n=[{id:R.id,imageUrl:s.imageUrl,word:s.word,correctAnswer:r?.correctAnswer||"",groupNumber:t?.groupNumber||1}]);const o=t?.payload?.hintLetters||r?.hintLetters||s?.hintLetters||[],a=t?.groupNumber||1,i={};o.length>0&&(i[a]=o);const c={subQuestions:n,hintLetters:o,hintLettersByGroup:i,groups:[{number:a,questionCount:n.length}]};return e(x,{questionData:c,userAnswer:L,isReviewMode:E,explanation:R.explanation,onSubmit:e=>{O(e),S?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=R.answer;return e(N,{questionData:r,userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=R.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{O(e),S?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=R.answer;return e(f,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=R.content,s=R.answer,t=(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:t},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=R.content,s=R.answer,t=(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(y,{questionData:{subQuestions:t},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=R.content,s=R.answer;return e(v,{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:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",R.type]})}})(),!E&&e("div",{className:"flex justify-end border-t pt-4",children:e(s,{onClick:()=>{S?.(T)},className:"bg-blue-500 hover:bg-blue-600",children:"Xác nhận đáp án"})})]})]})}
@@ -1 +1 @@
1
- "use client";import{jsx as e}from"react/jsx-runtime";import{transformChooseCorrectAdjective as r,transformChooseCorrectAnswer as t,transformColorObjects as s,transformFillInBlank as n,transformWriteCorrectVerbForm as i}from"../../../components/exams/take/utils/question-transformers";import{normalizeColoringAssignments as o}from"../../../components/exams/take/utils/coloring-assignments";import{ReviewChooseAdjectiveRenderer as a}from"./ReviewChooseAdjectiveRenderer";import{ReviewChooseBestAnswerRenderer as u}from"./ReviewChooseBestAnswerRenderer";import{ReviewColoringRenderer as c}from"./ReviewColoringRenderer";import{ReviewListenAndWriteRenderer as m}from"./ReviewListenAndWriteRenderer";import{ReviewListenChooseAnswerGroupRenderer as p}from"./ReviewListenChooseAnswerGroupRenderer";import{ReviewListenChooseObjectsRenderer as _}from"./ReviewListenChooseObjectsRenderer";import{ReviewListenDragObjectsRenderer as N}from"./ReviewListenDragObjectsRenderer";import{ReviewSpeakingRenderer as E}from"./ReviewSpeakingRenderer";import{mapSpeakingReviewItems as A}from"./review-speaking-map";export function renderDedicatedReviewBody(S){const{part:l,answers:R,isCorrectMap:I,correctAnswerTextMap:d,correctOptionIdMap:O,rawCorrectAnswerMap:w,questionScoreMap:T,hidePerQuestionGrading:M}=S,{partNo:g,questionType:q,questions:U}=l,D=U.length,L=U;switch(q){case"FILL_IN_BLANK":{const r=n(L);return e(m,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||r.instruction,title:r.title,imageUrl:r.imageUrl,audioUrl:r.audioUrl,exampleText:r.exampleText,exampleHighlight:r.exampleHighlight,questions:r.questions,answers:R,isCorrectMap:I,correctAnswerTextMap:d})}case"WRITE_CORRECT_VERB_FORM":{const r=i(L);return e(m,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||r.instruction,title:r.title,questions:r.questions,answers:R,isCorrectMap:I,correctAnswerTextMap:d})}case"CHOOSE_THE_CORRECT_ANSWER":{const r=t(L);return e(u,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||r.instruction,imageUrl:r.groupImageUrl,audioUrl:r.audioUrl||l.audioUrl,groupContent:r.groupContent,articles:r.articles,questions:r.questions,answers:R,isCorrectMap:I,correctOptionIdMap:O,example:r.example})}case"CHOOSE_CORRECT_ADJECTIVE":{const t=r(L);return e(a,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||t.instruction,questions:t.questions,answers:R,isCorrectMap:I,correctOptionIdMap:O})}case"READ_AND_COLOR_OBJECTS":{const r=s(L),t=R,n=U[0],i=n?C(w[n.id]).answers:{};return e(c,{partNumber:g,questionCount:D,instructions:r.instructions,instruction:l.instructions||r.instruction,partName:l.name,imageUrl:r.imageUrl,audioUrl:r.audioUrl,hintItems:r.hintItems,regions:r.regions,assignments:t,correctAnswers:i||{}})}case"IMAGE_OBJECT_MATCHING":{const r=s(L),t=o(R,r.hintItems),n=U[0],i=n?C(w[n.id]).answers:{};return e(c,{partNumber:g,questionCount:D,instructions:r.instructions,instruction:l.instructions||r.instruction,partName:l.name,imageUrl:r.imageUrl,audioUrl:r.audioUrl||l.audioUrl,hintItems:r.hintItems,regions:r.regions,assignments:t,correctAnswers:i||{}})}case"LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE":return e(_,{partNumber:g,questionCount:D,partName:l.name,questions:U,studentAnswers:R,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:T});case"LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP":return e(p,{partNumber:g,questionCount:D,partName:l.name,questions:U,studentAnswers:R,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:T});case"LISTEN_AND_DRAG_OBJECTS_INTO_SCENE":return e(N,{partNumber:g,questionCount:D,partName:l.name,questions:U,studentAnswers:R,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:T});case"READ_DISPLAYED_CONTENT":case"SPEAKING_DESCRIBE_IMAGE":case"SPEAKING_CUE_CARD":case"SPONTANEOUS_QA":case"LISTEN_AND_SPEAK_ANSWER":case"LISTEN_AND_SPEAK_IMAGE_GROUP":case"LISTEN_AND_SPEAK_QUESTION_LIST":case"LISTEN_AND_SPEAK_COMPARE_IMAGES":case"LISTEN_AND_SPEAK_WITH_STORY_IMAGES":case"LISTEN_AND_SPEAK_INFO_EXCHANGE":case"LISTEN_AND_SPEAK_ODD_ONE_OUT":{const r=A(U,q,R,I);return e(E,{partNumber:g,partName:l.name,questions:r,hidePerQuestionGrading:M})}default:return null}}function C(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}
1
+ "use client";import{jsx as e}from"react/jsx-runtime";import{transformChooseCorrectAdjective as r,transformChooseCorrectAnswer as t,transformChooseTheCorrectAnswerGroup as n,transformColorObjects as s,transformFillInBlank as i,transformWriteCorrectVerbForm as o}from"../../../components/exams/take/utils/question-transformers";import{normalizeColoringAssignments as a}from"../../../components/exams/take/utils/coloring-assignments";import{ReviewChooseAdjectiveRenderer as u}from"./ReviewChooseAdjectiveRenderer";import{ReviewChooseBestAnswerRenderer as c}from"./ReviewChooseBestAnswerRenderer";import{ReviewColoringRenderer as m}from"./ReviewColoringRenderer";import{ReviewListenAndWriteRenderer as p}from"./ReviewListenAndWriteRenderer";import{ReviewListenChooseAnswerGroupRenderer as _}from"./ReviewListenChooseAnswerGroupRenderer";import{ReviewListenChooseObjectsRenderer as N}from"./ReviewListenChooseObjectsRenderer";import{ReviewListenDragObjectsRenderer as E}from"./ReviewListenDragObjectsRenderer";import{ReviewSpeakingRenderer as A}from"./ReviewSpeakingRenderer";import{mapSpeakingReviewItems as C}from"./review-speaking-map";export function renderDedicatedReviewBody(R){const{part:l,answers:O,isCorrectMap:I,correctAnswerTextMap:d,correctOptionIdMap:T,rawCorrectAnswerMap:w,questionScoreMap:M,hidePerQuestionGrading:q}=R,{partNo:g,questionType:U,questions:D}=l,L=D.length,x=D;switch(U){case"FILL_IN_BLANK":{const r=i(x);return e(p,{partNumber:g,questionCount:L,partName:l.name,instruction:l.instructions||r.instruction,title:r.title,imageUrl:r.imageUrl,audioUrl:r.audioUrl,exampleText:r.exampleText,exampleHighlight:r.exampleHighlight,questions:r.questions,answers:O,isCorrectMap:I,correctAnswerTextMap:d})}case"WRITE_CORRECT_VERB_FORM":{const r=o(x);return e(p,{partNumber:g,questionCount:L,partName:l.name,instruction:l.instructions||r.instruction,title:r.title,questions:r.questions,answers:O,isCorrectMap:I,correctAnswerTextMap:d})}case"CHOOSE_THE_CORRECT_ANSWER":{const r=t(x);return e(c,{partNumber:g,questionCount:L,partName:l.name,instruction:l.instructions||r.instruction,imageUrl:r.groupImageUrl,audioUrl:r.audioUrl||l.audioUrl,groupContent:r.groupContent,articles:r.articles,questions:r.questions,answers:O,isCorrectMap:I,correctOptionIdMap:T,example:r.example})}case"CHOOSE_THE_CORRECT_ANSWER_GROUP":{const r=n(x);return e(c,{partNumber:g,questionCount:L,partName:l.name,instruction:l.instructions||r.instruction,questions:r.questions,answers:O,isCorrectMap:I,correctOptionIdMap:T,example:r.example})}case"CHOOSE_CORRECT_ADJECTIVE":{const t=r(x);return e(u,{partNumber:g,questionCount:L,partName:l.name,instruction:l.instructions||t.instruction,questions:t.questions,answers:O,isCorrectMap:I,correctOptionIdMap:T})}case"READ_AND_COLOR_OBJECTS":{const r=s(x),t=O,n=D[0],i=n?S(w[n.id]).answers:{};return e(m,{partNumber:g,questionCount:L,instructions:r.instructions,instruction:l.instructions||r.instruction,partName:l.name,imageUrl:r.imageUrl,audioUrl:r.audioUrl,hintItems:r.hintItems,regions:r.regions,assignments:t,correctAnswers:i||{}})}case"IMAGE_OBJECT_MATCHING":{const r=s(x),t=a(O,r.hintItems),n=D[0],i=n?S(w[n.id]).answers:{};return e(m,{partNumber:g,questionCount:L,instructions:r.instructions,instruction:l.instructions||r.instruction,partName:l.name,imageUrl:r.imageUrl,audioUrl:r.audioUrl||l.audioUrl,hintItems:r.hintItems,regions:r.regions,assignments:t,correctAnswers:i||{}})}case"LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE":return e(N,{partNumber:g,questionCount:L,partName:l.name,questions:D,studentAnswers:O,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:M});case"LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP":return e(_,{partNumber:g,questionCount:L,partName:l.name,questions:D,studentAnswers:O,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:M});case"LISTEN_AND_DRAG_OBJECTS_INTO_SCENE":return e(E,{partNumber:g,questionCount:L,partName:l.name,questions:D,studentAnswers:O,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:M});case"READ_DISPLAYED_CONTENT":case"SPEAKING_DESCRIBE_IMAGE":case"SPEAKING_CUE_CARD":case"SPONTANEOUS_QA":case"LISTEN_AND_SPEAK_ANSWER":case"LISTEN_AND_SPEAK_IMAGE_GROUP":case"LISTEN_AND_SPEAK_QUESTION_LIST":case"LISTEN_AND_SPEAK_COMPARE_IMAGES":case"LISTEN_AND_SPEAK_WITH_STORY_IMAGES":case"LISTEN_AND_SPEAK_INFO_EXCHANGE":case"LISTEN_AND_SPEAK_ODD_ONE_OUT":{const r=C(D,U,O,I);return e(A,{partNumber:g,partName:l.name,questions:r,hidePerQuestionGrading:q})}default:return null}}function S(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}
@@ -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=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}
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","CHOOSE_THE_CORRECT_ANSWER_GROUP"]);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:{}},c=[];for(const e of o.values()){e.sort((e,r)=>e.questionNumber-r.questionNumber);const o=e[0];if(!o)continue;const u=s.find(e=>String(e.id)===String(o.partId)),p=r(u,o.questionNumber),A=a(u?.name)??o.partNo??0;i.parts.push({partId:o.partId,partNo:A,questionType:o.questionType,name:u?.name,instructions:p?.name||p?.instructions||u?.instructions,audioUrl:u?.audioUrl,questions:e.map(t)});const _=e.filter(e=>!e.isExample);c.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 u=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=u.map(e=>i.parts[e]),u.forEach((e,r)=>{i.partScoresMap[r]=c[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_GROUP"===r){const r=n.answer;if(!Array.isArray(r))return;return Object.fromEntries(r.map((r,t)=>[`${e}-item-${t}`,"string"==typeof r?r:""]))}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(","));if("CHOOSE_THE_CORRECT_ANSWER_GROUP"===r.questionType){const n=Array.isArray(o(r.questionContent).items)?o(r.questionContent).items:[],s=Array.isArray(t.answer)?t.answer:[];n.forEach((t,n)=>{const o=`${r.questionId}-item-${n}`,i=t?.correctAnswer?.answer||s[n];"string"==typeof i&&i&&(e.correctOptionIdMap[o]=i)})}"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 +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 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 m(e,t){const r=e.trim();return t?r:r.toLowerCase()}function u(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=l(function({question:o,initialAnswers:l,onBlankChange:d,isReviewMode:c}){const u=r(null),p=r(d);p.current=d;const f=r(!1),x=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=u.current;if(!e)return;f.current||(e.innerHTML=h,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}"]`),l=x.current[n]||"",a=o.answers[n]||"",d=l.trim().length>0,u=d&&m(l,o.caseSensitive)===m(a,o.caseSensitive),f=c&&!u&&a.trim().length>0;t.value=f?`${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-[9rem] max-w-[18rem] w-[14ch] 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-[4.5rem] max-w-[7.5rem] w-[8ch] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] px-1 py-0.5 text-center focus:border-solid focus:border-[#0b57d0]",c?u?"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.size=w?18:10),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?u?"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:u,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:m,instruction:x,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 m.forEach(t=>{e[t.id]=(e,r)=>N(t.id,e,r)}),e},[m,N]);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}),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:m.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:u(r.answers,r.questionNumber)})}),e(f,{question:r,initialAnswers:l[r.id]||p,onBlankChange:v[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 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))})]})}
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,8 +16,6 @@ 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';
18
- export { DifficultyLevel, EditQuestionBankDialog, QuestionBankEditorType, QuestionEditorRenderer, } from './components/questions/question-bank';
19
- export type { EditQuestionBankDialogProps, QuestionBankEditItem, QuestionEditorChangeData, QuestionEditorRendererProps, SkillType as QuestionBankSkillType, UpdateQuestionBankDto, } from './components/questions/question-bank';
20
19
  export { renderDedicatedComponent } from './components/questions/creator/dedicated-component-router';
21
20
  export type { DedicatedComponentRouterProps, ValidationRef, } from './components/questions/creator/dedicated-component-router';
22
21
  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{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";
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{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;
@@ -0,0 +1 @@
1
+ import{mkdir as e,writeFile as t}from"node:fs/promises";import{dirname as n,resolve as i}from"node:path";import{z as a}from"zod";import{mcpRequest as o}from"../http-client.js";import{buildToolResult as r,unwrapData as s,wrapHandler as l}from"./_helpers.js";const p=a.record(a.string(),a.unknown()).describe("Full API question payload or short form: { question, options, answer, points?, partId?, partNo? }."),u=(a.record(a.string(),a.unknown()).describe("Part payload: { name?, partId?, questions: [...] }."),a.record(a.string(),a.unknown()).describe("Template payload accepted by PATCH /manage/exams/:id/template.")),m=a.enum(["PRE_A1","A1","A2","B1","B2","C1","C2"]);export function registerExamTools(g){g.registerTool("exam_templates_search",{title:"Search exam templates",description:"Search available OE Exam templates. Use this before exam_create_with_questions to choose examTemplateId.",inputSchema:{search:a.string().optional().describe("Template name keyword."),page:a.number().int().positive().default(1).describe("1-based page number."),limit:a.number().int().positive().max(50).default(9).describe("Page size sent as API size.")}},l("exam_templates_search",async e=>{const t=new URLSearchParams({page:String(e.page??1),size:String(e.limit??9)});e.search&&t.set("filter",`name:like:${e.search}`);const n=await o({method:"GET",url:`/api/v1/exam-templates?${t}`});return r({templates:k(n)})})),g.registerTool("exam_template_get",{title:"Get exam template",description:"Get one OE Exam template by id. The returned templatePayload can be passed as template to exam_create_with_questions.",inputSchema:{id:a.string().min(1).describe("Template id from exam_templates_search.")}},l("exam_template_get",async e=>{const t=s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(String(e.id))}`}));return r({template:t,templatePayload:P(t)})})),g.registerTool("exam_question_template_get",{title:"Get question payload template",description:"Return easy-to-fill question JSON templates for exam_create_with_questions. Use this before creating questions if you need the expected shape.",inputSchema:{templateId:a.string().min(1).optional().describe("Template id. If provided, generate question payloads from template.parts."),type:a.string().default("CHOOSE_THE_CORRECT_ANSWER").describe("Fallback question type when templateId is omitted."),count:a.number().int().positive().max(100).default(1).describe("Fallback count when templateId is omitted."),partId:a.union([a.string(),a.number()]).default(1),partNo:a.number().int().positive().default(1),skill:a.string().default("READING")}},l("exam_question_template_get",async e=>{const t="string"==typeof e.templateId?s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(e.templateId)}`})):void 0;return r({usage:"Pass questions directly into exam_create_with_questions.questions. Fill content/correctAnswer for each generated API-format payload.",templateId:t?.id,questions:t?b(t):S(e)})})),g.registerTool("exam_json_template_generate",{title:"Get exam JSON template",description:"Generate a user-friendly examJson skeleton from templateTitle or templateId. Edit this JSON, then pass it to exam_create_from_json.",inputSchema:{templateTitle:a.string().optional().describe("Template title to search, e.g. Achievers 2026 Round 1."),templateId:a.string().optional().describe("Exact template id. Preferred when known."),name:a.string().optional().describe("Exam name to put in the JSON."),examLevel:m.optional().describe("Optional exam level for the generated JSON."),difficulty:a.enum(["EASY","MEDIUM","HARD"]).optional().describe("Optional difficulty for the generated JSON."),durationMinutes:a.number().int().positive().optional().describe("Optional duration for the generated JSON.")}},l("exam_json_template_generate",async a=>{const o=await f({templateId:a.templateId,templateTitle:a.templateTitle}),s=function(e,t){const n=Array.isArray(e.parts)?e.parts.filter(c):[];return{name:v(t.name,`${e.title??e.name??"OE Exam"} Draft`),templateId:e.id,templateTitle:e.title??e.name,totalScore:O(e.totalScore??e.total_questions,y(n)),examLevel:_(t.examLevel)??_(e.examLevel??e.exam_level??e.level)??"A1",difficulty:E(t.difficulty)??"MEDIUM",durationMinutes:O(t.durationMinutes,45),isComplete:!1,isActive:!1,parts:n.map((e,t)=>function(e,t){const n=O(e.startIndex,0),i=O(e.questionCount,O(e.endIndex,n)-n+1);return{id:e.id??t+1,name:e.name??`Part ${t+1}`,questionType:v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),skill:v(e.skill,"READING"),questions:Array.from({length:Math.max(i,0)},(t,i)=>function(e,t){const n=v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),i=(Array.isArray(e.defaultValue)&&e.defaultValue.length>0?e.defaultValue:Array.from({length:O(e.optionCount,4)},(e,t)=>String.fromCharCode(65+t))).map(String);return n.includes("FILL")||n.includes("WRITE")?{question:`Question ${t}`,answer:""}:n.includes("SPEAK")?{prompt:`Question ${t}`,expectedAnswer:""}:{question:`Question ${t}`,options:i,answer:i[0]??"A"}}(e,n+i+1))}}(e,t))}}(o,a),l=JSON.stringify(s,null,2),p=i(`${u=String(a.name??o.title??o.name??"exam"),u.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,80)||"exam"}.json`);var u;return await e(n(p),{recursive:!0}),await t(p,`${l}\n`,"utf8"),r({usage:"Edit filePath, then call exam_create_from_json with the edited JSON string.",templateId:o.id,templateTitle:o.title??o.name,filePath:p,examJson:s,examJsonString:l})})),g.registerTool("exam_create_from_json",{title:"Create exam from JSON",description:"Create an exam from a user-friendly JSON payload grouped by parts. Searches template by title, patches template, maps questions to template.parts, then creates questions.",inputSchema:{examJson:a.string().optional().describe("JSON string with { name, templateTitle/templateId, parts: [{ questions: [...] }] }. Use this for copy/paste."),exam:a.record(a.string(),a.unknown()).optional().describe("Same payload as object if the client supports object arguments.")}},l("exam_create_from_json",async e=>{const t=function(e,t){if(c(t))return t;if("string"!=typeof e||!e.trim())throw new Error("examJson or exam is required");const n=JSON.parse(e);if(!c(n))throw new Error("examJson must be a JSON object");return n}(e.examJson,e.exam),n=await f(t),i=function(e,t){const n=Array.isArray(t.parts)?t.parts.filter(c):[],i=Array.isArray(e.parts)?e.parts.filter(c):[{questions:e.questions}];return i.flatMap((e,t)=>{const i=function(e,t,n){const i=A(e.partId??e.id),a=A(e.name);return t.find(e=>String(e.id)===i)??t.find(e=>"string"==typeof e.name&&e.name===a)??t[n]??{id:n+1,questionType:"CHOOSE_THE_CORRECT_ANSWER",skill:"READING",optionCount:4}}(e,n,t);return(Array.isArray(e.questions)?e.questions.filter(c):[]).map((e,n)=>function(e,t,n,i){const a=O(t.startIndex,0),o=w({questionType:v(e.questionType??e.type??t.questionType,"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:O(e.questionNumber,a+i+1),partId:String(e.partId??t.id??n+1),partNo:O(e.partNo,n+1),sectionId:R(e.sectionId??t.sectionId),skill:v(e.questionSkillType??e.skill??t.skill,"READING"),optionCount:O(t.optionCount,4),defaultValue:Array.isArray(t.defaultValue)?t.defaultValue:void 0});return q(function(e){const t=c(e.content)?{...e.content}:{};"string"==typeof e.question&&(t.question=e.question);"string"==typeof e.text&&(t.question=e.text);Array.isArray(e.options)&&(t.optionType=t.optionType??"text",t.options=e.options);return d({...e,content:c(t)?t:e.content,correctAnswer:void 0===e.answer&&void 0===e.expectedAnswer?e.correctAnswer:x(e.questionType,e.answer??e.expectedAnswer,t.options)})}({...o,...e}),i)}(e,i,t,n))})}(t,n),a=function(e,t,n){return{exam:d({name:v(e.name,"New OE Exam"),code:A(e.code)??h(v(e.name,"New OE Exam")),totalScore:O(e.totalScore,n.length||100),description:A(e.description)??A(e.templateTitle)??"",isComplete:T(e.isComplete,!1),isActive:T(e.isActive,!1),examTemplateId:t,examLevel:_(e.examLevel),difficulty:E(e.difficulty),durationMinutes:R(e.durationMinutes)})}}(t,n.id,i),l=s(await o({method:"POST",url:"/api/v1/manage/exams",body:a.exam}));await o({method:"PATCH",url:`/api/v1/manage/exams/${encodeURIComponent(l.id)}/template`,body:{template:P(n)}});const p=0===i.length?null:await o({method:"POST",url:"/api/v1/manage/questions/batch",body:{examId:l.id,questions:i}});return r({exam:l,templateId:n.id,questions:p,generatedQuestionCount:i.length})})),g.registerTool("exam_create_with_questions",{title:"Create exam with questions",description:"Create an exam, optionally patch template, then bulk create questions via /api/v1/manage/questions/batch. If you do not know examTemplateId/template, call exam_templates_search then exam_template_get first.",inputSchema:{name:a.string().min(1),code:a.string().min(1).optional(),examLevel:m.optional().describe("Valid exam level only: PRE_A1, A1, A2, B1, B2, C1, C2. Do not use template title/id here."),difficulty:a.enum(["EASY","MEDIUM","HARD"]).optional(),totalScore:a.number().nonnegative().optional(),durationMinutes:a.number().int().positive().optional(),description:a.string().optional(),isComplete:a.boolean().optional(),isActive:a.boolean().optional(),examTemplateId:a.string().min(1).optional(),template:u.optional(),questions:a.array(p).default([])}},l("exam_create_with_questions",async e=>{const{template:t,questions:n,...i}=e,a=s(await o({method:"POST",url:"/api/v1/manage/exams",body:d(i)})),l=c(t)?t:await async function(e){if("string"!=typeof e||!e)return;const t=s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(e)}`}));return P(t)}(i.examTemplateId);c(l)&&await o({method:"PATCH",url:`/api/v1/manage/exams/${encodeURIComponent(a.id)}/template`,body:{template:l}});const p=n.map(q),u=0===p.length?null:await o({method:"POST",url:"/api/v1/manage/questions/batch",body:{examId:a.id,questions:p}});return r({exam:a,questions:u})}))}function d(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function c(e){return Boolean(e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).length>0)}async function f(e){const t=A(e.templateId);if(t)return s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(t)}`}));const n=A(e.templateTitle);if(!n)throw new Error("templateTitle or templateId is required");const i=new URLSearchParams({page:"1",size:"10",filter:`name:like:${n}`}),a=k(await o({method:"GET",url:`/api/v1/exam-templates?${i}`})),r=a.find(e=>(e.title??e.name)===n),l=r??a[0];if(!l?.id)throw new Error(`No exam template found for title: ${n}`);return s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(l.id)}`}))}function y(e){return e.reduce((e,t)=>e+O(t.questionCount,0),0)}function x(e,t,n){if(void 0===t)return;const i=String(e??"");return i.includes("FILL")||i.includes("WRITE")?{answers:[String(t)]}:i.includes("SPEAK")?{expectedAnswer:String(t)}:{answer:g(t,n)}}function g(e,t){if("string"==typeof e&&Array.isArray(t)){const n=t.map(String).findIndex(t=>t===e);if(n>=0)return n}return I(e)}function h(e){return e.toUpperCase().replace(/[^A-Z0-9]+/g,"_").replace(/^_|_$/g,"").slice(0,40)||"OE_EXAM"}function A(e){return"string"==typeof e&&e?e:void 0}function T(e,t){return"boolean"==typeof e?e:t}function _(e){return"string"==typeof e&&m.safeParse(e).success?e:void 0}function E(e){return"string"==typeof e&&["EASY","MEDIUM","HARD"].includes(e)?e:void 0}function q(e,t){if(e.questionType)return d({...e,partId:void 0===e.partId?void 0:String(e.partId),partNo:e.partNo??1,questionNumber:e.questionNumber??t+1,points:e.points??1,addToQuestionBank:e.addToQuestionBank??!1,isExample:e.isExample??!1});const n=Array.isArray(e.options)?e.options:["A","B","C","D"];return d({questionType:e.type??"CHOOSE_THE_CORRECT_ANSWER",content:e.content??{question:String(e.question??e.text??`Question ${t+1}`),optionType:"text",options:n},correctAnswer:e.correctAnswer??{answer:I(e.answer??e.correctAnswerIndex??0)},points:e.points??1,explanation:e.explanation??"",questionNumber:e.questionNumber??t+1,partId:String(e.partId??1),partNo:e.partNo??1,sectionId:e.sectionId,questionSkillType:e.questionSkillType??e.skill??"READING",addToQuestionBank:e.addToQuestionBank??!1,isExample:e.isExample??!1})}function I(e){if("number"==typeof e)return e;if("string"==typeof e&&/^[A-E]$/i.test(e))return e.toUpperCase().charCodeAt(0)-65;const t=Number(e);return Number.isFinite(t)?t:0}function S(e){return Array.from({length:Number(e.count??1)},(t,n)=>w({questionType:String(e.type??"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:n+1,partId:String(e.partId??1),partNo:Number(e.partNo??1),skill:String(e.skill??"READING")}))}function b(e){return(Array.isArray(e.parts)?e.parts.filter(c):[]).flatMap((e,t)=>{const n=O(e.startIndex,0),i=O(e.questionCount,O(e.endIndex,n)-n+1);return Array.from({length:Math.max(i,0)},(i,a)=>w({questionType:v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:n+a+1,partId:String(e.id??t+1),partNo:t+1,sectionId:R(e.sectionId),skill:v(e.skill,"READING"),optionCount:O(e.optionCount,4),defaultValue:Array.isArray(e.defaultValue)?e.defaultValue:void 0}))})}function w(e){return d({questionType:e.questionType,content:C(e.questionType,e.questionNumber,e.optionCount,e.defaultValue),correctAnswer:N(e.questionType),points:1,explanation:"",questionNumber:e.questionNumber,partId:e.partId,partNo:e.partNo,sectionId:e.sectionId,questionSkillType:e.skill,addToQuestionBank:!1,isExample:!1})}function C(e,t,n=4,i){const a=(i?.length?i:Array.from({length:n},(e,t)=>String.fromCharCode(65+t))).map(String);return e.includes("FILL")||e.includes("WRITE")?{question:`Question ${t}`,text:""}:e.includes("SPEAK")?{prompt:`Question ${t}`}:e.includes("LISTEN")?{question:`Question ${t}`,audioUrl:"",optionType:"text",options:a}:{question:`Question ${t}`,optionType:"text",options:a}}function N(e){return e.includes("FILL")||e.includes("WRITE")?{answers:[""]}:e.includes("SPEAK")?{expectedAnswer:""}:{answer:0}}function v(e,t){return"string"==typeof e&&e?e:t}function O(e,t){const n=Number(e);return Number.isFinite(n)?n:t}function R(e){const t=Number(e);return Number.isFinite(t)?t:void 0}function k(e){const t=s(e);return Array.isArray(t)?t:t&&"object"==typeof t&&"data"in t&&Array.isArray(t.data)?t.data:[]}function P(e){const t=Array.isArray(e.parts)?e.parts:[];return d({id:e.id,title:e.title??e.name,subTitle:e.subTitle??e.sub_title??"",theme:e.theme,totalQuestions:e.totalQuestions??e.total_questions,totalParts:e.totalParts??e.total_parts??t.length,description:e.description??"",examLevel:e.examLevel??e.exam_level??e.level??"",type:e.type??null,parts:t})}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerAllMcpTools(server: McpServer): void;
@@ -0,0 +1 @@
1
+ import{registerExamTools as o}from"./exams.js";import{registerResultTools as r}from"./results.js";export function registerAllMcpTools(s){o(s),r(s)}