@tinyweb_dev/oe-exam-sdk 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -5
- package/dist/components/exams/ExamPreviewDialog.d.ts +7 -6
- package/dist/components/exams/ExamPreviewDialog.js +1 -1
- package/dist/components/exams/index.d.ts +0 -1
- package/dist/components/exams/take/components/QuestionRenderer.js +1 -1
- package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -1
- package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -1
- package/dist/components/exams/take/exam-taking.utils.d.ts +0 -13
- package/dist/components/exams/take/exam-taking.utils.js +1 -1
- package/dist/components/exams/take/utils/answer-transformers.js +1 -1
- package/dist/components/exams/take/utils/question-transformers.d.ts +7 -17
- package/dist/components/exams/take/utils/question-transformers.js +1 -1
- package/dist/components/questions/_shared/config/question-types.config.js +1 -1
- package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +36 -0
- package/dist/components/questions/_shared/types/cross-out-word-group.type.d.ts +40 -0
- package/dist/components/questions/_shared/types/index.d.ts +1 -0
- package/dist/components/questions/_shared/types/index.js +1 -1
- package/dist/components/questions/_shared/types/true-false-group.type.d.ts +31 -0
- package/dist/components/questions/_shared/types/true-false-group.type.js +1 -0
- package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +11 -17
- package/dist/components/questions/creator/question-type-registry.js +1 -1
- package/dist/components/questions/index.d.ts +0 -1
- package/dist/components/questions/index.js +1 -1
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.d.ts +2 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +1 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.d.ts +2 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +1 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/index.d.ts +3 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/index.js +1 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.d.ts +4 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +1 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/register.d.ts +2 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/register.js +1 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/transform.d.ts +2 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.d.ts +2 -0
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.d.ts +2 -0
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/index.d.ts +5 -0
- package/dist/components/questions/types/cross-out-word-group/index.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.d.ts +6 -0
- package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/register.d.ts +2 -0
- package/dist/components/questions/types/cross-out-word-group/register.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/transform.d.ts +2 -0
- package/dist/components/questions/types/cross-out-word-group/transform.js +1 -0
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.d.ts +2 -0
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -0
- package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.d.ts +2 -0
- package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +1 -0
- package/dist/components/questions/types/true-false-group/index.d.ts +5 -0
- package/dist/components/questions/types/true-false-group/index.js +1 -0
- package/dist/components/questions/types/true-false-group/map-true-false-group-data.d.ts +4 -0
- package/dist/components/questions/types/true-false-group/map-true-false-group-data.js +1 -0
- package/dist/components/questions/types/true-false-group/register.d.ts +2 -0
- package/dist/components/questions/types/true-false-group/register.js +1 -0
- package/dist/components/questions/types/true-false-group/transform.d.ts +2 -0
- package/dist/components/questions/types/true-false-group/transform.js +1 -0
- package/dist/components/questions/types/word-fill-structured-form/index.d.ts +1 -0
- package/dist/components/questions/types/word-fill-structured-form/index.js +1 -1
- package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.d.ts +7 -0
- package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.js +1 -0
- package/dist/components/questions/types/word-fill-structured-form/register.js +1 -1
- package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -1
- package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.d.ts +8 -0
- package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.js +1 -0
- package/dist/components/questions/viewer/CrossOutWordGroupViewer.d.ts +7 -0
- package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +1 -0
- package/dist/components/questions/viewer/QuestionViewer.js +1 -1
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +4 -0
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +1 -0
- package/dist/components/questions/viewer/TrueFalseGroupViewer.d.ts +8 -0
- package/dist/components/questions/viewer/TrueFalseGroupViewer.js +1 -0
- package/dist/components/questions/viewer/WordFillStructuredFormViewer.d.ts +9 -0
- package/dist/components/questions/viewer/WordFillStructuredFormViewer.js +1 -0
- package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +4 -0
- package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +1 -0
- package/dist/components/questions/viewer/index.d.ts +6 -0
- package/dist/components/questions/viewer/index.js +1 -1
- package/dist/components/results/renderers/review-question-body-dedicated.js +1 -1
- package/dist/components/results/review-data.js +1 -1
- package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +1 -1
- package/dist/mcp/config.d.ts +12 -0
- package/dist/mcp/config.js +1 -0
- package/dist/mcp/http-client.d.ts +6 -0
- package/dist/mcp/http-client.js +1 -0
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools/_helpers.d.ts +4 -0
- package/dist/mcp/tools/_helpers.js +1 -0
- package/dist/mcp/tools/exams.d.ts +2 -0
- package/dist/mcp/tools/exams.js +1 -0
- package/dist/mcp/tools/index.d.ts +2 -0
- package/dist/mcp/tools/index.js +1 -0
- package/dist/mcp/tools/results.d.ts +2 -0
- package/dist/mcp/tools/results.js +1 -0
- package/dist/shared/constants/question-skills.js +1 -1
- package/dist/shared/lib/i18n/messages/en/admin.d.ts +4 -11
- package/dist/shared/lib/i18n/messages/en/admin.js +1 -1
- package/dist/shared/lib/i18n/messages/en.d.ts +4 -11
- package/dist/shared/lib/i18n/messages/vi/admin.d.ts +4 -11
- package/dist/shared/lib/i18n/messages/vi/admin.js +1 -1
- package/dist/shared/lib/i18n/messages/vi.d.ts +4 -11
- package/dist/shared/lib/utils/question-reverse-transform.js +1 -1
- package/dist/shared/lib/utils/question-transform.js +1 -1
- package/dist/shared/types/common.types.d.ts +1 -1
- package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +41 -0
- package/dist/shared/types/questions/choose-the-correct-answer-group.js +1 -0
- package/dist/shared/types/questions/cross-out-word-group.d.ts +41 -0
- package/dist/shared/types/questions/cross-out-word-group.js +1 -0
- package/dist/shared/types/questions/index.d.ts +2 -0
- package/dist/shared/types/questions/index.js +1 -1
- package/package.json +166 -169
- package/dist/components/exams/ExamPreviewPartContent.d.ts +0 -18
- package/dist/components/exams/ExamPreviewPartContent.js +0 -1
- package/dist/components/exams/ExamPreviewSidebar.d.ts +0 -17
- package/dist/components/exams/ExamPreviewSidebar.js +0 -1
- package/dist/components/exams/exam-preview.types.d.ts +0 -34
- package/dist/components/exams/exam-preview.types.js +0 -1
- package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +0 -30
- package/dist/components/questions/_shared/utils/parseFillBlankStem.js +0 -1
- package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +0 -2
- package/dist/components/questions/question-bank/EditQuestionBankDialog.js +0 -1
- package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -23
- package/dist/components/questions/question-bank/QuestionEditorRenderer.js +0 -1
- package/dist/components/questions/question-bank/difficulty.d.ts +0 -15
- package/dist/components/questions/question-bank/difficulty.js +0 -1
- package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -5
- package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -1
- package/dist/components/questions/question-bank/editor-adapters/index.d.ts +0 -5
- package/dist/components/questions/question-bank/editor-adapters/index.js +0 -1
- package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -6
- package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -1
- package/dist/components/questions/question-bank/editor-adapters/types.d.ts +0 -1
- package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +0 -8
- package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +0 -1
- package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +0 -5
- package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +0 -1
- package/dist/components/questions/question-bank/index.d.ts +0 -4
- package/dist/components/questions/question-bank/index.js +0 -1
- package/dist/components/questions/question-bank/types.d.ts +0 -59
- /package/dist/components/questions/{question-bank/editor-adapters/types.js → _shared/types/choose-the-correct-answer-group.type.js} +0 -0
- /package/dist/components/questions/{question-bank/types.js → _shared/types/cross-out-word-group.type.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as o}from"react/jsx-runtime";import{CrossOutWordGroupClient as r}from"../types/cross-out-word-group/CrossOutWordGroupClient";import{mapQuestionToCrossOutWordGroupData as s}from"../types/cross-out-word-group/map-cross-out-word-group-data";export function CrossOutWordGroupViewer({question:e,isReviewMode:t=!1}){const u=s(e);return o(r,{questionData:u,isReviewMode:t,userAnswers:t?u.items.map(o=>o.correctAnswer):[]})}
|
|
@@ -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 i}from"../../../components/ui/card";import{Input as a}from"../../../components/ui/input";import{Check as c,Volume2 as l,X as u}from"lucide-react";import{useRef as d,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 w}from"../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient";import{WriteShortParagraphClient as h}from"../types/write-short-paragraph/WriteShortParagraphClient";import{LabelThePictureClient as N}from"../types/label-the-picture/LabelThePictureClient";import{LookPictureFillWordHintClient as _}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as C}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as A}from"../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient";import{WriteAShortLetterClient as R}from"../types/write-a-short-letter/WriteAShortLetterClient";import{LookPictureChooseCorrectAnswerClient as f}from"../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient";import{LookPictureFillBlankChooseAnswerClient as v}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as x}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";import{WordFillStructuredFormViewer as S}from"./WordFillStructuredFormViewer";import{ChooseTheCorrectAnswerGroupViewer as y}from"./ChooseTheCorrectAnswerGroupViewer";import{TrueFalseGroupViewer as E}from"./TrueFalseGroupViewer";import{CrossOutWordGroupViewer as O}from"./CrossOutWordGroupViewer";import{SpontaneousQaGroupViewer as T}from"./SpontaneousQaGroupViewer";import{WordOrderAndMatchGroupViewer as L}from"./WordOrderAndMatchGroupViewer";export function QuestionViewer({question:W,onSubmitAnswer:I,isReviewMode:D=!1,userAnswer:q}){const[U,P]=m(q||null),[k,G]=m(0),M=d(null),[Q,F]=m(q||{}),H=W.answer,[$,j]=m(q||Array(H?.blanks?.length||0).fill("")),[B,K]=m(q||{}),[V,Y]=m(q||{}),[z,J]=m(q||{}),X=()=>{const e=W.answer.maxPlayCount||3;k>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(M.current?.play(),G(k+1))},Z=()=>{const t=W.answer,{blanks:n,caseSensitive:o=!1}=t;return r("div",{className:"space-y-4",children:[n.map((s,t)=>{const i=$[t]||"",l=D&&((e,r)=>{const s=n[e];return o?r===s:r.toLowerCase()===s.toLowerCase()})(t,i);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(a,{value:i,onChange:e=>((e,r)=>{const s=[...$];s[e]=r,j(s)})(t,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(D?l?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:D}),D&&l&&e(c,{className:"h-4 w-4 text-green-600"}),D&&!l&&i&&e(u,{className:"h-4 w-4 text-red-600"})]},t)}),!D&&e(s,{onClick:()=>{I&&I($)},disabled:$.some(e=>""===e.trim()),children:"Nộp đáp án"})]})};return r(t,{children:[e(o,{children:r(i,{className:"flex items-center justify-between",children:[e("span",{children:"string"==typeof W.content?W.content:W.content?.question||W.content?.title||"Câu hỏi"}),r("span",{className:"text-sm font-normal text-gray-500",children:[W.points," điểm"]})]})}),r(n,{className:"space-y-4",children:[(()=>{switch(W.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=W.answer,{isGroup:s,questionGroup:t}=W;let n={};return n=W.content&&"object"==typeof W.content?{...W.content,correctAnswer:W.correctAnswer?.answer??-1,id:W.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber}:{...r,question:W.content||r?.question,id:W.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber},e(A,{questionData:n,questionConfig:W.questionConfig,onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"CHOOSE_THE_CORRECT_ANSWER_GROUP":return e(y,{question:W,isReviewMode:D,userAnswer:q});case"TRUE_FALSE_GROUP":return e(E,{question:W,isReviewMode:D,userAnswer:q});case"CROSS_OUT_WORD_GROUP":return e(O,{question:W,isReviewMode:D});case"SPONTANEOUS_QA_GROUP":return e(T,{question:W});case"WORD_ORDER_AND_MATCH_GROUP":return e(L,{question:W});case"FILL_IN_BLANK":return Z();case"TRUE_FALSE":return(()=>{const t=W.answer,{correctAnswer:n}=t;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(s=>{const t=U===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"} ${D&&s.value===n?"border-green-500 bg-green-50":D&&t&&s.value!==n?"border-red-500 bg-red-50":""}`,onClick:()=>!D&&P(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"} ${D&&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}),D&&s.value===n&&e(c,{className:"ml-2 h-4 w-4 text-green-600"}),D&&t&&s.value!==n&&e(u,{className:"ml-2 h-4 w-4 text-red-600"})]})},s.value.toString())}),!D&&e(s,{onClick:()=>{I&&I(U)},disabled:null===U,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const t=W.answer,{minWords:n,maxWords:o}=t,i=U?U.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ừ`:"",U&&r("span",{className:"ml-2 "+(i>=(n||0)&&i<=(o||1/0)?"text-green-600":"text-red-600"),children:["(",i," từ)"]})]}),e("textarea",{value:U||"",onChange:e=>P(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:D}),!D&&e(s,{onClick:()=>{I&&I(U)},disabled:!U||U.trim().length<(n||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const t=W.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:X,variant:"outline",className:"gap-2",children:[e(l,{className:"h-5 w-5"}),"Phát âm thanh (",k,"/",t.maxPlayCount||3,")"]}),e("audio",{ref:M,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(a,{value:$[t],onChange:e=>{const r=[...$];r[t]=e.target.value,j(r),P(r)},placeholder:"Nhập câu trả lời...",disabled:D,className:D?$[t]?.toLowerCase()===o[t]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),D&&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=W.answer,{statements:t}=s;return e("div",{className:"space-y-3",children:t.map(s=>e("div",{className:`rounded-lg border-2 p-4 ${D&&Q[s.id]===s.isCorrect?"border-green-500 bg-green-50":""} ${D&&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};F(e),P(e)},disabled:D,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};F(e),P(e)},disabled:D,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(u,{className:"h-5 w-5"})})]}),e("p",{className:"flex-1",children:s.text}),D&&e("span",{className:"text-sm font-medium",children:s.isCorrect?"✓ Đúng":"✗ Sai"})]})},s.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=W.answer,s=W.correctAnswer||W.correct_answer,t=(r?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(w,{questionData:{subQuestions:t},onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const s=W.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(a,{value:V[o]||"",onChange:e=>{const r={...V,[o]:e.target.value};Y(r),P(r)},className:`inline-block h-8 w-32 text-center ${D&&V[o]===s?.correctAnswer?"border-green-500 bg-green-50":""} ${D&&V[o]!==s?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:D}),D&&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=W.answer;return e(h,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=W.answer;return e(R,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=W.answer,s=W.content,t=W.questionGroup;let n=[];r?.subQuestions&&r.subQuestions.length>0?n=r.subQuestions:s?.imageUrl&&s?.word&&(n=[{id:W.id,imageUrl:s.imageUrl,word:s.word,correctAnswer:r?.correctAnswer||"",groupNumber:t?.groupNumber||1}]);const o=t?.payload?.hintLetters||r?.hintLetters||s?.hintLetters||[],i=t?.groupNumber||1,a={};o.length>0&&(a[i]=o);const c={subQuestions:n,hintLetters:o,hintLettersByGroup:a,groups:[{number:i,questionCount:n.length}]};return e(_,{questionData:c,userAnswer:q,isReviewMode:D,explanation:W.explanation,onSubmit:e=>{P(e),I?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=W.answer;return e(N,{questionData:r,userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=W.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{P(e),I?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=W.answer;return e(C,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=W.content,s=W.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(f,{questionData:{subQuestions:t},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=W.content,s=W.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(v,{questionData:{subQuestions:t},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=W.content,s=W.answer;return e(x,{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:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"WORD_FILL_STRUCTURED_FORM":return e(S,{question:W,isReviewMode:D,userAnswer:q,onSubmitAnswer:I});default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",W.type]})}})(),!D&&e("div",{className:"flex justify-end border-t pt-4",children:e(s,{onClick:()=>{I?.(U)},className:"bg-blue-500 hover:bg-blue-600",children:"Xác nhận đáp án"})})]})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";function r(e){return function(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{}}function n(e,t=""){return"string"==typeof e?e:t}function s(e,t){const s=r(e),i=r(s.content),o=r(s.correctAnswer),a=Array.isArray(o.expectedAnswers)?o.expectedAnswers.map(e=>String(e??"")):[];return{questionText:n(i.questionText)||n(i.question)||n(s.questionText),audioUrl:n(i.audioUrl)||void 0,expectedAnswers:a,isExample:!0===s.isExample,points:"number"==typeof s.points?s.points:1,questionNumber:"number"==typeof s.questionNumber?s.questionNumber:t+1}}export function SpontaneousQaGroupViewer({question:i}){const{instruction:o,items:a}=function(e){const t=r(e.content),i=r(e.spontaneousQAList??t.spontaneousQAList??t),o=Array.isArray(i.items)?i.items:Array.isArray(t.items)?t.items:[];return{instruction:n(r(i.meta).instruction)||n(r(t.meta).instruction)||n(e.explanation)||"Talk about the topic.",items:o.map(s)}}(i);return t("div",{className:"space-y-4",children:[o?e("div",{className:"rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800",children:o}):null,a.map((r,n)=>t("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[t("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gray-50 px-4 py-2.5",children:[t("span",{className:"text-sm font-medium text-gray-700",children:["Câu ",r.questionNumber||n+1]}),t("span",{className:"text-xs text-gray-500",children:[r.points||0," điểm"]})]}),t("div",{className:"space-y-2 p-4",children:[e("p",{className:"text-sm font-semibold text-gray-800",children:r.questionText||"Câu hỏi chưa được nhập"}),r.audioUrl?t("p",{className:"text-xs text-gray-500",children:["Audio: ",r.audioUrl]}):null,r.expectedAnswers.length>0?t("div",{className:"rounded-lg border border-emerald-100 bg-emerald-50 px-3 py-2 text-sm text-emerald-800",children:[e("p",{className:"mb-1 font-medium",children:"Gợi ý đáp án"}),e("ul",{className:"list-disc space-y-1 pl-4",children:r.expectedAnswers.map(t=>e("li",{children:t},t))})]}):null]})]},`${r.questionNumber}-${n}`))]})}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Question } from '../../../shared/types/entities/question.types';
|
|
2
|
+
type TrueFalseGroupViewerProps = {
|
|
3
|
+
question: Question;
|
|
4
|
+
isReviewMode?: boolean;
|
|
5
|
+
userAnswer?: boolean[];
|
|
6
|
+
};
|
|
7
|
+
export declare function TrueFalseGroupViewer({ question, isReviewMode, userAnswer, }: TrueFalseGroupViewerProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e}from"react/jsx-runtime";import{TrueFalseGroupClient as r}from"../types/true-false-group/TrueFalseGroupClient";import{mapQuestionToTrueFalseGroupData as s}from"../types/true-false-group/map-true-false-group-data";export function TrueFalseGroupViewer({question:t,isReviewMode:o=!1,userAnswer:u}){const i=s(t),a=o&&!u?i.items.map(e=>e.correctAnswer):u;return e(r,{questionData:i,isReviewMode:o,userAnswers:a})}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Question } from '../../../shared/types/entities/question.types';
|
|
2
|
+
type WordFillStructuredFormViewerProps = {
|
|
3
|
+
question: Question;
|
|
4
|
+
isReviewMode?: boolean;
|
|
5
|
+
userAnswer?: Record<string, string>;
|
|
6
|
+
onSubmitAnswer?: (answer: Record<string, string>) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function WordFillStructuredFormViewer({ question, isReviewMode, userAnswer, onSubmitAnswer, }: WordFillStructuredFormViewerProps): import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as r}from"react/jsx-runtime";import{useState as e}from"react";import{WordFillStructuredFormClient as t}from"../types/word-fill-structured-form/WordFillStructuredFormClient";import{mapQuestionToWfsfData as o}from"../types/word-fill-structured-form/map-wfsf-question-data";export function WordFillStructuredFormViewer({question:i,isReviewMode:n=!1,userAnswer:s,onSubmitAnswer:u}){const m=o(i),[a,l]=e(()=>s??{});return r(t,{questionData:m,isReviewMode:n,userAnswer:s,autoFillCorrectAnswers:n&&!s,instruction:m.instruction,questionNumber:i.questionNumber,explanation:i.explanation,onAnswerChange:(r,e)=>{const t={...a,[r]:e};l(t),u?.(t)}})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";function t(e){return function(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{}}function s(e,r=""){return"string"==typeof e?e:r}function a(e,r){const a=t(e),n=t(a.content),i=t(a.correctAnswer);return{scrambledWords:Array.isArray(n.scrambledWords)?n.scrambledWords.map(e=>String(e??"")):[],orderedPhrase:s(i.orderedPhrase),matchLabel:s(i.matchLabel),isExample:!0===a.isExample,points:"number"==typeof a.points?a.points:1,questionNumber:"number"==typeof a.questionNumber?a.questionNumber:r+1}}export function WordOrderAndMatchGroupViewer({question:n}){const{instruction:i,matchOptions:d,items:o}=function(e){const r=t(e.content),n=t(e.wordOrderMatchList??r.wordOrderMatchList??r),i=t(n.meta??r.meta),d=Array.isArray(n.items)?n.items:Array.isArray(r.items)?r.items:[];return{instruction:s(i.instruction)||s(e.explanation)||"Put the phrases in order, then match the labels.",matchOptions:Array.isArray(i.matchOptions)?i.matchOptions.map(e=>String(e??"")):[],items:d.map(a)}}(n);return r("div",{className:"space-y-4",children:[i?e("div",{className:"rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800",children:i}):null,d.length>0?e("div",{className:"flex flex-wrap gap-2",children:d.map(r=>e("span",{className:"rounded-md border border-gray-200 bg-white px-2 py-1 text-xs font-semibold text-gray-700",children:r},r))}):null,o.map((t,s)=>r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gray-50 px-4 py-2.5",children:[r("span",{className:"text-sm font-medium text-gray-700",children:["Câu ",t.questionNumber||s+1,t.isExample?" · Example":""]}),r("span",{className:"text-xs text-gray-500",children:[t.points||0," điểm"]})]}),r("div",{className:"space-y-2 p-4",children:[e("div",{className:"flex flex-wrap gap-1.5",children:t.scrambledWords.map((r,t)=>e("span",{className:"rounded-md border border-dashed border-teal-300 bg-teal-50 px-2 py-1 text-sm text-teal-800",children:r},`${r}-${t}`))}),r("p",{className:"text-sm text-gray-800",children:[e("span",{className:"font-medium text-gray-500",children:"Câu đúng: "}),t.orderedPhrase||"—"]}),r("p",{className:"text-sm text-gray-800",children:[e("span",{className:"font-medium text-gray-500",children:"Nhãn: "}),t.matchLabel||"—"]})]})]},`${t.questionNumber}-${s}`))]})}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
export { QuestionViewer } from './QuestionViewer';
|
|
2
2
|
export { QuestionGroupViewer } from './QuestionGroupViewer';
|
|
3
|
+
export { WordFillStructuredFormViewer } from './WordFillStructuredFormViewer';
|
|
4
|
+
export { ChooseTheCorrectAnswerGroupViewer } from './ChooseTheCorrectAnswerGroupViewer';
|
|
5
|
+
export { TrueFalseGroupViewer } from './TrueFalseGroupViewer';
|
|
6
|
+
export { CrossOutWordGroupViewer } from './CrossOutWordGroupViewer';
|
|
7
|
+
export { SpontaneousQaGroupViewer } from './SpontaneousQaGroupViewer';
|
|
8
|
+
export { WordOrderAndMatchGroupViewer } from './WordOrderAndMatchGroupViewer';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{QuestionViewer}from"./QuestionViewer";export{QuestionGroupViewer}from"./QuestionGroupViewer";
|
|
1
|
+
export{QuestionViewer}from"./QuestionViewer";export{QuestionGroupViewer}from"./QuestionGroupViewer";export{WordFillStructuredFormViewer}from"./WordFillStructuredFormViewer";export{ChooseTheCorrectAnswerGroupViewer}from"./ChooseTheCorrectAnswerGroupViewer";export{TrueFalseGroupViewer}from"./TrueFalseGroupViewer";export{CrossOutWordGroupViewer}from"./CrossOutWordGroupViewer";export{SpontaneousQaGroupViewer}from"./SpontaneousQaGroupViewer";export{WordOrderAndMatchGroupViewer}from"./WordOrderAndMatchGroupViewer";
|
|
@@ -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
|
|
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:{}},
|
|
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
|
|
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
|
@@ -15,8 +15,6 @@ export { QuestionCreator } from './components/questions/creator/QuestionCreator'
|
|
|
15
15
|
export type { QuestionCreatorProps } from './components/questions/creator/QuestionCreator';
|
|
16
16
|
export { QuestionGroupCreator } from './components/questions/creator/QuestionGroupCreator';
|
|
17
17
|
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
18
|
export { renderDedicatedComponent } from './components/questions/creator/dedicated-component-router';
|
|
21
19
|
export type { DedicatedComponentRouterProps, ValidationRef, } from './components/questions/creator/dedicated-component-router';
|
|
22
20
|
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{
|
|
1
|
+
export*as Api from"./api";export*as Components from"./components";export*as Exams from"./components/exams";export*as Questions from"./components/questions";export*as Results from"./components/results";export*as Themes from"./components/themes";export*as Shared from"./shared";export{buildExamTemplatePayload,createContestRoomsApi,createExamCreateApi,createExamEntryApi,createExamQuestionsApi,createExamQuestionsFetchApi,createExamTakingApi,createMockContestRoomsApi,createMockCreateExamApi,createMockExamEntryApi,createMockExamQuestionsApi,createMockExamTakingApi,createMockResultReviewApi,createResultReviewApi}from"./api";export{CreateExamPageContainer,ExamPreviewDialog,ExamQuestionsPageContainer,ExamTakingPageContainer,ExamViewDialog,StudentContestRoomsPageContainer,StudentExamEntryPageContainer}from"./components/exams";export{ResultReviewPageContainer}from"./components/results";export{QuestionCreator}from"./components/questions/creator/QuestionCreator";export{QuestionGroupCreator}from"./components/questions/creator/QuestionGroupCreator";export{renderDedicatedComponent}from"./components/questions/creator/dedicated-component-router";export{questionTypeRegistry}from"./components/questions/creator/question-type-registry";export{QUESTION_TYPE_CONFIG,QUESTION_TYPES_WITH_DEDICATED_COMPONENTS}from"./components/questions/_shared/config/question-types.config";export{validateQuestion}from"./components/questions/_shared/utils/question-validation";export{buildAnswerPayload}from"./components/questions/_shared/utils/answer-builder";export*from"./components/questions/_shared/types";
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
1
|
+
import{registerExamTools as o}from"./exams.js";import{registerResultTools as r}from"./results.js";export function registerAllMcpTools(s){o(s),r(s)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as t}from"zod";import{mcpRequest as e}from"../http-client.js";import{buildToolResult as n,wrapHandler as o}from"./_helpers.js";export function registerResultTools(r){r.registerTool("exam_result_get",{title:"Get exam result review",description:"Fetch student exam result review by attemptId. Optional token supports public result review links.",inputSchema:{attemptId:t.string().min(1),token:t.string().min(1).optional(),includeAttempt:t.boolean().default(!0)}},o("exam_result_get",async({attemptId:t,token:o,includeAttempt:r})=>{const i=o?`?token=${encodeURIComponent(String(o))}`:"",s=await e({method:"GET",url:`/api/v1/student/results/${encodeURIComponent(String(t))}${i}`}),l=!1===r?null:await e({method:"GET",url:`/api/v1/student/attempts/${encodeURIComponent(String(t))}${i}`});return n({result:s,attempt:l})}))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SkillEnum as E}from"../types/common.types";export const QUESTION_TYPE_SKILLS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:E.LISTENING,LISTEN_AND_TICK_ANSWER:E.LISTENING,LISTEN_AND_FILL_NAME_NUMBER:E.LISTENING,LISTENING_FILL_BLANK:E.LISTENING,LISTENING_COLOR:E.LISTENING,LISTENING:E.LISTENING,LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:E.SPEAKING,LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:E.SPEAKING,LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:E.SPEAKING,LISTEN_AND_SPEAK_ANSWER:E.SPEAKING,LISTEN_AND_SPEAK_IMAGE_GROUP:E.SPEAKING,LISTEN_AND_SPEAK_QUESTION_LIST:E.SPEAKING,LISTEN_AND_SPEAK_COMPARE_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_WITH_STORY_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_ODD_ONE_OUT:E.SPEAKING,LISTEN_AND_SPEAK_INFO_EXCHANGE:E.SPEAKING,READ_DISPLAYED_CONTENT:E.SPEAKING,SPEAKING_DESCRIBE_IMAGE:E.SPEAKING,SPONTANEOUS_QA:E.SPEAKING,SPEAKING_CONVERSATION:E.SPEAKING,SPEAKING_CUE_CARD:E.SPEAKING,GN_SPEAKING_INTERVIEW:E.SPEAKING,CHOOSE_THE_CORRECT_ANSWER:E.READING,CHOOSE_CORRECT_ADJECTIVE:E.READING,FILL_IN_BLANK:E.READING,FILL_MISSING_WORDS_IN_GRID:E.READING,READ_PASSAGE_AND_ANSWER_QUESTIONS:E.READING,ANSWER_THE_QUESTION:E.READING,WORD_ORDERING:E.READING,WORD_FILL_PARAGRAPH:E.READING,MATCH_BY_WRITING_ANSWER:E.READING,LABEL_THE_PICTURE:E.READING,LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:E.READING,LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:E.READING,LOOK_PICTURE_FILL_WORD_HINT:E.READING,READ_AND_COLOR_OBJECTS:E.READING,IMAGE_OBJECT_MATCHING:E.READING,TRUE_FALSE:E.READING,MATCHING:E.READING,ORDERING:E.READING,TICK_TRUE_OR_FALSE:E.READING,TICK_YES_OR_NO:E.READING,READING_TICK_CROSS:E.READING,ARRANGE_LETTERS_INTO_WORDS:E.READING,READ_PASSAGE_AND_COMPLETE_STORY:E.READING,CLOZE_TEST_WITH_TICK_BOX:E.READING,READ_AND_CHOOSE_APPROPRIATE_WORD:E.READING,READING_LETTER_ARRANGE:E.READING,READING_PASSAGE_FILL:E.READING,FILL_BLANK:E.READING,WORD_FILL_STRUCTURED_FORM:E.READING,WRITE_A_SHORT_LETTER:E.WRITING,WRITE_CORRECT_VERB_FORM:E.WRITING,WRITE_SHORT_PARAGRAPH:E.WRITING,WRITE_SENTENCES:E.WRITING,ESSAY:E.WRITING,COLORING:E.WRITING};export function getQuestionTypeSkill(E){return QUESTION_TYPE_SKILLS[E]||"READING"}export function getQuestionTypesBySkill(E){return Object.entries(QUESTION_TYPE_SKILLS).filter(([_,N])=>N===E).map(([E,_])=>E)}export const QUESTION_TYPES_BY_SKILL={LISTENING:getQuestionTypesBySkill("LISTENING"),SPEAKING:getQuestionTypesBySkill("SPEAKING"),READING:getQuestionTypesBySkill("READING"),WRITING:getQuestionTypesBySkill("WRITING")};export const MARKETING_QUESTION_TYPES_BY_SKILL={[E.LISTENING]:[],[E.SPEAKING]:[],[E.READING]:["CHOOSE_THE_CORRECT_ANSWER","CHOOSE_CORRECT_ADJECTIVE","FILL_IN_BLANK","FILL_MISSING_WORDS_IN_GRID","LABEL_THE_PICTURE","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","READ_PASSAGE_AND_ANSWER_QUESTIONS"],[E.WRITING]:["WRITE_A_SHORT_LETTER","WRITE_CORRECT_VERB_FORM","WRITE_SENTENCES"]};export function isMarketingQuestionType(E){return Object.values(MARKETING_QUESTION_TYPES_BY_SKILL).flat().includes(E)}export const QUESTION_TYPE_LABELS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:"Nghe - Điền từ có hình",LISTEN_AND_TICK_ANSWER:"Nghe - Đánh dấu",LISTEN_AND_FILL_NAME_NUMBER:"Nghe - Điền tên/số",LISTENING_FILL_BLANK:"Nghe - Điền chỗ trống",LISTENING_COLOR:"Nghe - Tô màu",LISTENING:"Nghe",CHOOSE_THE_CORRECT_ANSWER:"Chọn đáp án đúng",CHOOSE_CORRECT_ADJECTIVE:"Chọn tính từ",FILL_IN_BLANK:"Điền vào chỗ trống",FILL_MISSING_WORDS_IN_GRID:"Điền từ vào lưới",READ_PASSAGE_AND_ANSWER_QUESTIONS:"Đọc hiểu - Trả lời",LABEL_THE_PICTURE:"Gắn nhãn hình ảnh",LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:"Nhìn ảnh - Chọn đáp án",LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:"Nhìn ảnh - Điền và chọn",LOOK_PICTURE_FILL_WORD_HINT:"Nhìn ảnh - Điền từ gợi ý",READ_AND_COLOR_OBJECTS:"Đọc - Tô màu",IMAGE_OBJECT_MATCHING:"Ghép hình ảnh - Đối tượng",TRUE_FALSE:"Đúng/Sai",MATCHING:"Ghép nối",ORDERING:"Sắp xếp",TICK_TRUE_OR_FALSE:"Đánh dấu Đúng/Sai",TICK_YES_OR_NO:"Đánh dấu Có/Không",READING_TICK_CROSS:"Đọc - Đánh dấu",ARRANGE_LETTERS_INTO_WORDS:"Sắp xếp chữ cái thành từ",READ_PASSAGE_AND_COMPLETE_STORY:"Đọc - Hoàn thành câu chuyện",CLOZE_TEST_WITH_TICK_BOX:"Điền từ - Đánh dấu",READ_AND_CHOOSE_APPROPRIATE_WORD:"Đọc - Chọn từ phù hợp",READING_LETTER_ARRANGE:"Đọc - Sắp xếp chữ cái",READING_PASSAGE_FILL:"Đọc - Điền đoạn văn",FILL_BLANK:"Điền chỗ trống",ANSWER_THE_QUESTION:"Nghe - Đọc câu hỏi và viết đáp án",WORD_ORDERING:"Sắp xếp từ",WORD_FILL_PARAGRAPH:"Điền từ vào đoạn văn",MATCH_BY_WRITING_ANSWER:"Ghép đáp án bằng cách viết",WORD_FILL_STRUCTURED_FORM:"Điền từ vào biểu mẫu có cấu trúc",WRITE_A_SHORT_LETTER:"Viết thư",WRITE_CORRECT_VERB_FORM:"Chia động từ",WRITE_SHORT_PARAGRAPH:"Viết đoạn văn ngắn",WRITE_SENTENCES:"Viết câu",ESSAY:"Viết luận",COLORING:"Tô màu",LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:"Nghe và chọn trong nhóm đáp án",LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:"Nghe và chọn đối tượng trong cảnh",LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:"Nghe và kéo thả đối tượng vào cảnh",LISTEN_AND_SPEAK_ANSWER:"Nghe và trả lời (Speaking)",LISTEN_AND_SPEAK_IMAGE_GROUP:"Nghe, nhìn nhóm ảnh và trả lời (Speaking)",LISTEN_AND_SPEAK_QUESTION_LIST:"Nghe, chọn câu hỏi và trả lời (Speaking)",LISTEN_AND_SPEAK_COMPARE_IMAGES:"Nghe và so sánh ảnh (Speaking)",LISTEN_AND_SPEAK_WITH_STORY_IMAGES:"Nghe, nhìn ảnh truyện và kể lại (Speaking)",LISTEN_AND_SPEAK_ODD_ONE_OUT:"Nghe, chọn hình khác loại và giải thích (Speaking)",LISTEN_AND_SPEAK_INFO_EXCHANGE:"Trao đổi thông tin (Speaking)",READ_DISPLAYED_CONTENT:"Đọc nội dung hiển thị (Speaking)",SPEAKING_DESCRIBE_IMAGE:"Nhìn tranh và mô tả (Speaking Part 2)",SPONTANEOUS_QA:"Hỏi đáp tự phát (Speaking Part 3)",SPEAKING_CONVERSATION:"Hội thoại Speaking (KFS Speaking)",GN_SPEAKING_INTERVIEW:"GN Speaking - Hỏi đáp (Virtual + Real)"};export function getQuestionTypeLabel(E){return QUESTION_TYPE_LABELS[E]||E}export const SKILL_METADATA={[E.LISTENING]:{name:"Nghe",emoji:"🎧",color:"text-blue-600",bgColor:"bg-blue-50",borderColor:"border-blue-200",description:"Kỹ năng nghe hiểu"},[E.SPEAKING]:{name:"Nói",emoji:"🗣️",color:"text-green-600",bgColor:"bg-green-50",borderColor:"border-green-200",description:"Kỹ năng giao tiếp"},[E.READING]:{name:"Đọc",emoji:"📖",color:"text-purple-600",bgColor:"bg-purple-50",borderColor:"border-purple-200",description:"Kỹ năng đọc hiểu"},[E.WRITING]:{name:"Viết",emoji:"✏️",color:"text-orange-600",bgColor:"bg-orange-50",borderColor:"border-orange-200",description:"Kỹ năng viết"}};
|
|
1
|
+
import{SkillEnum as E}from"../types/common.types";export const QUESTION_TYPE_SKILLS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:E.LISTENING,LISTEN_AND_TICK_ANSWER:E.LISTENING,LISTEN_AND_FILL_NAME_NUMBER:E.LISTENING,LISTENING_FILL_BLANK:E.LISTENING,LISTENING_COLOR:E.LISTENING,LISTENING:E.LISTENING,LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:E.SPEAKING,LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:E.SPEAKING,LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:E.SPEAKING,LISTEN_AND_SPEAK_ANSWER:E.SPEAKING,LISTEN_AND_SPEAK_IMAGE_GROUP:E.SPEAKING,LISTEN_AND_SPEAK_QUESTION_LIST:E.SPEAKING,LISTEN_AND_SPEAK_COMPARE_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_WITH_STORY_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_ODD_ONE_OUT:E.SPEAKING,LISTEN_AND_SPEAK_INFO_EXCHANGE:E.SPEAKING,READ_DISPLAYED_CONTENT:E.SPEAKING,SPEAKING_DESCRIBE_IMAGE:E.SPEAKING,SPONTANEOUS_QA:E.SPEAKING,SPEAKING_CONVERSATION:E.SPEAKING,SPEAKING_CUE_CARD:E.SPEAKING,GN_SPEAKING_INTERVIEW:E.SPEAKING,CHOOSE_THE_CORRECT_ANSWER:E.READING,CHOOSE_THE_CORRECT_ANSWER_GROUP:E.READING,CHOOSE_CORRECT_ADJECTIVE:E.READING,FILL_IN_BLANK:E.READING,FILL_MISSING_WORDS_IN_GRID:E.READING,READ_PASSAGE_AND_ANSWER_QUESTIONS:E.READING,ANSWER_THE_QUESTION:E.READING,WORD_ORDERING:E.READING,WORD_FILL_PARAGRAPH:E.READING,MATCH_BY_WRITING_ANSWER:E.READING,LABEL_THE_PICTURE:E.READING,LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:E.READING,LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:E.READING,LOOK_PICTURE_FILL_WORD_HINT:E.READING,READ_AND_COLOR_OBJECTS:E.READING,IMAGE_OBJECT_MATCHING:E.READING,TRUE_FALSE:E.READING,TRUE_FALSE_GROUP:E.READING,CROSS_OUT_WORD_GROUP:E.READING,SPONTANEOUS_QA_GROUP:E.SPEAKING,WORD_ORDER_AND_MATCH_GROUP:E.READING,MATCHING:E.READING,ORDERING:E.READING,TICK_TRUE_OR_FALSE:E.READING,TICK_YES_OR_NO:E.READING,READING_TICK_CROSS:E.READING,ARRANGE_LETTERS_INTO_WORDS:E.READING,READ_PASSAGE_AND_COMPLETE_STORY:E.READING,CLOZE_TEST_WITH_TICK_BOX:E.READING,READ_AND_CHOOSE_APPROPRIATE_WORD:E.READING,READING_LETTER_ARRANGE:E.READING,READING_PASSAGE_FILL:E.READING,FILL_BLANK:E.READING,WORD_FILL_STRUCTURED_FORM:E.READING,WRITE_A_SHORT_LETTER:E.WRITING,WRITE_CORRECT_VERB_FORM:E.WRITING,WRITE_SHORT_PARAGRAPH:E.WRITING,WRITE_SENTENCES:E.WRITING,ESSAY:E.WRITING,COLORING:E.WRITING};export function getQuestionTypeSkill(E){return QUESTION_TYPE_SKILLS[E]||"READING"}export function getQuestionTypesBySkill(E){return Object.entries(QUESTION_TYPE_SKILLS).filter(([_,N])=>N===E).map(([E,_])=>E)}export const QUESTION_TYPES_BY_SKILL={LISTENING:getQuestionTypesBySkill("LISTENING"),SPEAKING:getQuestionTypesBySkill("SPEAKING"),READING:getQuestionTypesBySkill("READING"),WRITING:getQuestionTypesBySkill("WRITING")};export const MARKETING_QUESTION_TYPES_BY_SKILL={[E.LISTENING]:[],[E.SPEAKING]:[],[E.READING]:["CHOOSE_THE_CORRECT_ANSWER","CHOOSE_CORRECT_ADJECTIVE","FILL_IN_BLANK","FILL_MISSING_WORDS_IN_GRID","LABEL_THE_PICTURE","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","READ_PASSAGE_AND_ANSWER_QUESTIONS"],[E.WRITING]:["WRITE_A_SHORT_LETTER","WRITE_CORRECT_VERB_FORM","WRITE_SENTENCES"]};export function isMarketingQuestionType(E){return Object.values(MARKETING_QUESTION_TYPES_BY_SKILL).flat().includes(E)}export const QUESTION_TYPE_LABELS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:"Nghe - Điền từ có hình",LISTEN_AND_TICK_ANSWER:"Nghe - Đánh dấu",LISTEN_AND_FILL_NAME_NUMBER:"Nghe - Điền tên/số",LISTENING_FILL_BLANK:"Nghe - Điền chỗ trống",LISTENING_COLOR:"Nghe - Tô màu",LISTENING:"Nghe",CHOOSE_THE_CORRECT_ANSWER:"Chọn đáp án đúng",CHOOSE_THE_CORRECT_ANSWER_GROUP:"Chọn đáp án đúng (nhóm)",CHOOSE_CORRECT_ADJECTIVE:"Chọn tính từ",FILL_IN_BLANK:"Điền vào chỗ trống",FILL_MISSING_WORDS_IN_GRID:"Điền từ vào lưới",READ_PASSAGE_AND_ANSWER_QUESTIONS:"Đọc hiểu - Trả lời",LABEL_THE_PICTURE:"Gắn nhãn hình ảnh",LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:"Nhìn ảnh - Chọn đáp án",LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:"Nhìn ảnh - Điền và chọn",LOOK_PICTURE_FILL_WORD_HINT:"Nhìn ảnh - Điền từ gợi ý",READ_AND_COLOR_OBJECTS:"Đọc - Tô màu",IMAGE_OBJECT_MATCHING:"Ghép hình ảnh - Đối tượng",TRUE_FALSE:"Đúng/Sai",TRUE_FALSE_GROUP:"Đúng/Sai (nhóm)",CROSS_OUT_WORD_GROUP:"Gạch từ / sửa từ (nhóm)",SPONTANEOUS_QA_GROUP:"Hỏi đáp tự phát (nhóm)",WORD_ORDER_AND_MATCH_GROUP:"Sắp xếp từ và nối nhãn (nhóm)",MATCHING:"Ghép nối",ORDERING:"Sắp xếp",TICK_TRUE_OR_FALSE:"Đánh dấu Đúng/Sai",TICK_YES_OR_NO:"Đánh dấu Có/Không",READING_TICK_CROSS:"Đọc - Đánh dấu",ARRANGE_LETTERS_INTO_WORDS:"Sắp xếp chữ cái thành từ",READ_PASSAGE_AND_COMPLETE_STORY:"Đọc - Hoàn thành câu chuyện",CLOZE_TEST_WITH_TICK_BOX:"Điền từ - Đánh dấu",READ_AND_CHOOSE_APPROPRIATE_WORD:"Đọc - Chọn từ phù hợp",READING_LETTER_ARRANGE:"Đọc - Sắp xếp chữ cái",READING_PASSAGE_FILL:"Đọc - Điền đoạn văn",FILL_BLANK:"Điền chỗ trống",ANSWER_THE_QUESTION:"Nghe - Đọc câu hỏi và viết đáp án",WORD_ORDERING:"Sắp xếp từ",WORD_FILL_PARAGRAPH:"Điền từ vào đoạn văn",MATCH_BY_WRITING_ANSWER:"Ghép đáp án bằng cách viết",WORD_FILL_STRUCTURED_FORM:"Điền từ vào biểu mẫu có cấu trúc",WRITE_A_SHORT_LETTER:"Viết thư",WRITE_CORRECT_VERB_FORM:"Chia động từ",WRITE_SHORT_PARAGRAPH:"Viết đoạn văn ngắn",WRITE_SENTENCES:"Viết câu",ESSAY:"Viết luận",COLORING:"Tô màu",LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:"Nghe và chọn trong nhóm đáp án",LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:"Nghe và chọn đối tượng trong cảnh",LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:"Nghe và kéo thả đối tượng vào cảnh",LISTEN_AND_SPEAK_ANSWER:"Nghe và trả lời (Speaking)",LISTEN_AND_SPEAK_IMAGE_GROUP:"Nghe, nhìn nhóm ảnh và trả lời (Speaking)",LISTEN_AND_SPEAK_QUESTION_LIST:"Nghe, chọn câu hỏi và trả lời (Speaking)",LISTEN_AND_SPEAK_COMPARE_IMAGES:"Nghe và so sánh ảnh (Speaking)",LISTEN_AND_SPEAK_WITH_STORY_IMAGES:"Nghe, nhìn ảnh truyện và kể lại (Speaking)",LISTEN_AND_SPEAK_ODD_ONE_OUT:"Nghe, chọn hình khác loại và giải thích (Speaking)",LISTEN_AND_SPEAK_INFO_EXCHANGE:"Trao đổi thông tin (Speaking)",READ_DISPLAYED_CONTENT:"Đọc nội dung hiển thị (Speaking)",SPEAKING_DESCRIBE_IMAGE:"Nhìn tranh và mô tả (Speaking Part 2)",SPONTANEOUS_QA:"Hỏi đáp tự phát (Speaking Part 3)",SPEAKING_CONVERSATION:"Hội thoại Speaking (KFS Speaking)",GN_SPEAKING_INTERVIEW:"GN Speaking - Hỏi đáp (Virtual + Real)"};export function getQuestionTypeLabel(E){return QUESTION_TYPE_LABELS[E]||E}export const SKILL_METADATA={[E.LISTENING]:{name:"Nghe",emoji:"🎧",color:"text-blue-600",bgColor:"bg-blue-50",borderColor:"border-blue-200",description:"Kỹ năng nghe hiểu"},[E.SPEAKING]:{name:"Nói",emoji:"🗣️",color:"text-green-600",bgColor:"bg-green-50",borderColor:"border-green-200",description:"Kỹ năng giao tiếp"},[E.READING]:{name:"Đọc",emoji:"📖",color:"text-purple-600",bgColor:"bg-purple-50",borderColor:"border-purple-200",description:"Kỹ năng đọc hiểu"},[E.WRITING]:{name:"Viết",emoji:"✏️",color:"text-orange-600",bgColor:"bg-orange-50",borderColor:"border-orange-200",description:"Kỹ năng viết"}};
|
|
@@ -118,12 +118,6 @@ declare const admin: {
|
|
|
118
118
|
readonly 'admin.questionBank.dialog.edit.save': "Save changes";
|
|
119
119
|
readonly 'admin.questionBank.dialog.edit.codeError': "Please enter question code";
|
|
120
120
|
readonly 'admin.questionBank.dialog.edit.titleError': "Please enter title";
|
|
121
|
-
readonly 'admin.questionBank.dialog.edit.loading': "Loading...";
|
|
122
|
-
readonly 'admin.questionBank.dialog.edit.unsupportedNote': "Note:";
|
|
123
|
-
readonly 'admin.questionBank.dialog.edit.unsupportedType': "Question type \"{type}\" is not editable in this UI. Please use the exam creator to edit this question.";
|
|
124
|
-
readonly 'admin.questionBank.dialog.edit.contentLabel': "Question content";
|
|
125
|
-
readonly 'admin.questionBank.dialog.edit.answerLabel': "Correct answer";
|
|
126
|
-
readonly 'admin.questionBank.dialog.edit.explanationLabel': "Explanation";
|
|
127
121
|
readonly 'admin.questionBank.dialog.delete.title': "Confirm Delete Question";
|
|
128
122
|
readonly 'admin.questionBank.dialog.delete.message': "Are you sure you want to delete question";
|
|
129
123
|
readonly 'admin.questionBank.dialog.delete.warning': "This action cannot be undone.";
|
|
@@ -1393,6 +1387,9 @@ declare const admin: {
|
|
|
1393
1387
|
readonly 'admin.exams.shuffle.info3': "Questions within the same Part are randomly shuffled";
|
|
1394
1388
|
readonly 'admin.exams.shuffle.info4': "Variants are linked to the parent exam for management";
|
|
1395
1389
|
readonly 'admin.exams.shuffle.countLabel': "Number of variants to create";
|
|
1390
|
+
readonly 'admin.exams.shuffle.strategyLabel': "Existing exam handling option";
|
|
1391
|
+
readonly 'admin.exams.shuffle.keepOld': "Keep existing exams and add new ones";
|
|
1392
|
+
readonly 'admin.exams.shuffle.replaceAll': "Create a completely new set";
|
|
1396
1393
|
readonly 'admin.exams.shuffle.existingVariants': "Existing exams ({count})";
|
|
1397
1394
|
readonly 'admin.exams.shuffle.deleteAll': "Delete all ({count})";
|
|
1398
1395
|
readonly 'admin.exams.shuffle.loading': "Loading...";
|
|
@@ -1483,25 +1480,21 @@ declare const admin: {
|
|
|
1483
1480
|
readonly 'admin.exams.preview.totalPoints': "Total Points:";
|
|
1484
1481
|
readonly 'admin.exams.preview.progress': "Progress";
|
|
1485
1482
|
readonly 'admin.exams.preview.loading': "Loading...";
|
|
1486
|
-
readonly 'admin.exams.preview.questionNavigation': "
|
|
1483
|
+
readonly 'admin.exams.preview.questionNavigation': "Question Navigation";
|
|
1487
1484
|
readonly 'admin.exams.preview.all': "All";
|
|
1488
1485
|
readonly 'admin.exams.preview.allQuestions': "All {count} questions";
|
|
1489
1486
|
readonly 'admin.exams.preview.partQuestions': "Part {part}: {count} questions";
|
|
1490
1487
|
readonly 'admin.exams.preview.collapse': "Collapse";
|
|
1491
1488
|
readonly 'admin.exams.preview.detail': "Details";
|
|
1492
1489
|
readonly 'admin.exams.preview.questionIndex': "Question {index} / {total}";
|
|
1493
|
-
readonly 'admin.exams.preview.partIndex': "Part {index} / {total}";
|
|
1494
1490
|
readonly 'admin.exams.preview.noQuestions': "No questions yet";
|
|
1495
1491
|
readonly 'admin.exams.preview.loadingQuestions': "Loading questions...";
|
|
1496
1492
|
readonly 'admin.exams.preview.retry': "Retry";
|
|
1497
1493
|
readonly 'admin.exams.preview.examNoQuestions': "This exam has no questions yet.";
|
|
1498
1494
|
readonly 'admin.exams.preview.prevQuestion': "Previous Question";
|
|
1499
|
-
readonly 'admin.exams.preview.prevPart': "Previous Part";
|
|
1500
1495
|
readonly 'admin.exams.preview.prev': "Prev";
|
|
1501
1496
|
readonly 'admin.exams.preview.nextQuestion': "Next Question";
|
|
1502
|
-
readonly 'admin.exams.preview.nextPart': "Next Part";
|
|
1503
1497
|
readonly 'admin.exams.preview.next': "Next";
|
|
1504
|
-
readonly 'admin.exams.preview.questionLabel': "Question {number}";
|
|
1505
1498
|
readonly 'admin.exams.preview.footerNote': "This is a preview mode to check the layout and content of the exam.";
|
|
1506
1499
|
readonly 'admin.exams.preview.close': "Close";
|
|
1507
1500
|
readonly 'admin.exams.preview.loadError': "Unable to load questions. Please try again.";
|