@tinyweb_dev/oe-exam-sdk 0.1.19 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +5 -4
  2. package/dist/api/result-review.types.d.ts +1 -0
  3. package/dist/components/exams/ExamPreviewDialog.d.ts +6 -7
  4. package/dist/components/exams/ExamPreviewDialog.js +1 -1
  5. package/dist/components/exams/ExamPreviewPartContent.d.ts +18 -0
  6. package/dist/components/exams/ExamPreviewPartContent.js +1 -0
  7. package/dist/components/exams/ExamPreviewSidebar.d.ts +17 -0
  8. package/dist/components/exams/ExamPreviewSidebar.js +1 -0
  9. package/dist/components/exams/exam-preview.types.d.ts +34 -0
  10. package/dist/components/exams/exam-preview.types.js +1 -0
  11. package/dist/components/exams/index.d.ts +1 -0
  12. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +1 -1
  13. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -1
  14. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -1
  15. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +1 -1
  16. package/dist/components/exams/take/exam-taking.utils.d.ts +13 -0
  17. package/dist/components/exams/take/exam-taking.utils.js +1 -1
  18. package/dist/components/exams/take/types.d.ts +3 -0
  19. package/dist/components/exams/take/utils/question-transformers.d.ts +13 -0
  20. package/dist/components/exams/take/utils/question-transformers.js +1 -1
  21. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +25 -0
  22. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +30 -0
  23. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +1 -0
  24. package/dist/components/questions/index.d.ts +1 -0
  25. package/dist/components/questions/index.js +1 -1
  26. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +2 -0
  27. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +1 -0
  28. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +23 -0
  29. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -0
  30. package/dist/components/questions/question-bank/difficulty.d.ts +15 -0
  31. package/dist/components/questions/question-bank/difficulty.js +1 -0
  32. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +5 -0
  33. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +1 -0
  34. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +5 -0
  35. package/dist/components/questions/question-bank/editor-adapters/index.js +1 -0
  36. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +6 -0
  37. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +1 -0
  38. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +1 -0
  39. package/dist/components/questions/question-bank/editor-adapters/types.js +1 -0
  40. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +8 -0
  41. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +1 -0
  42. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +5 -0
  43. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +1 -0
  44. package/dist/components/questions/question-bank/index.d.ts +4 -0
  45. package/dist/components/questions/question-bank/index.js +1 -0
  46. package/dist/components/questions/question-bank/types.d.ts +59 -0
  47. package/dist/components/questions/question-bank/types.js +1 -0
  48. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -1
  49. package/dist/components/questions/types/choose-the-correct-answer/index.d.ts +1 -0
  50. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -1
  51. package/dist/components/questions/types/choose-the-correct-answer/normalize.d.ts +27 -0
  52. package/dist/components/questions/types/choose-the-correct-answer/normalize.js +1 -0
  53. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -1
  54. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -1
  55. package/dist/components/questions/types/fill-in-blank/register.js +1 -1
  56. package/dist/components/questions/types/fill-in-blank/transform.js +1 -1
  57. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.d.ts +1 -1
  58. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  59. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -1
  60. package/dist/components/questions/viewer/QuestionViewer.js +1 -1
  61. package/dist/components/results/review-data.js +1 -1
  62. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.d.ts +2 -1
  63. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -1
  64. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -1
  65. package/dist/index.d.ts +3 -0
  66. package/dist/index.js +1 -1
  67. package/dist/mcp/config.d.ts +12 -0
  68. package/dist/mcp/config.js +1 -0
  69. package/dist/mcp/http-client.d.ts +6 -0
  70. package/dist/mcp/http-client.js +1 -0
  71. package/dist/mcp/index.d.ts +6 -0
  72. package/dist/mcp/index.js +2 -0
  73. package/dist/mcp/tools/_helpers.d.ts +4 -0
  74. package/dist/mcp/tools/_helpers.js +1 -0
  75. package/dist/mcp/tools/exams.d.ts +2 -0
  76. package/dist/mcp/tools/exams.js +1 -0
  77. package/dist/mcp/tools/index.d.ts +2 -0
  78. package/dist/mcp/tools/index.js +1 -0
  79. package/dist/mcp/tools/results.d.ts +2 -0
  80. package/dist/mcp/tools/results.js +1 -0
  81. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -1
  82. package/dist/shared/lib/i18n/messages/en/admin.js +1 -1
  83. package/dist/shared/lib/i18n/messages/en.d.ts +11 -1
  84. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -1
  85. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -1
  86. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -1
  87. package/package.json +163 -155
@@ -0,0 +1 @@
1
+ import{mkdir as e,writeFile as t}from"node:fs/promises";import{dirname as n,resolve as i}from"node:path";import{z as a}from"zod";import{mcpRequest as o}from"../http-client.js";import{buildToolResult as r,unwrapData as s,wrapHandler as l}from"./_helpers.js";const p=a.record(a.string(),a.unknown()).describe("Full API question payload or short form: { question, options, answer, points?, partId?, partNo? }."),u=(a.record(a.string(),a.unknown()).describe("Part payload: { name?, partId?, questions: [...] }."),a.record(a.string(),a.unknown()).describe("Template payload accepted by PATCH /manage/exams/:id/template.")),m=a.enum(["PRE_A1","A1","A2","B1","B2","C1","C2"]);export function registerExamTools(g){g.registerTool("exam_templates_search",{title:"Search exam templates",description:"Search available OE Exam templates. Use this before exam_create_with_questions to choose examTemplateId.",inputSchema:{search:a.string().optional().describe("Template name keyword."),page:a.number().int().positive().default(1).describe("1-based page number."),limit:a.number().int().positive().max(50).default(9).describe("Page size sent as API size.")}},l("exam_templates_search",async e=>{const t=new URLSearchParams({page:String(e.page??1),size:String(e.limit??9)});e.search&&t.set("filter",`name:like:${e.search}`);const n=await o({method:"GET",url:`/api/v1/exam-templates?${t}`});return r({templates:k(n)})})),g.registerTool("exam_template_get",{title:"Get exam template",description:"Get one OE Exam template by id. The returned templatePayload can be passed as template to exam_create_with_questions.",inputSchema:{id:a.string().min(1).describe("Template id from exam_templates_search.")}},l("exam_template_get",async e=>{const t=s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(String(e.id))}`}));return r({template:t,templatePayload:P(t)})})),g.registerTool("exam_question_template_get",{title:"Get question payload template",description:"Return easy-to-fill question JSON templates for exam_create_with_questions. Use this before creating questions if you need the expected shape.",inputSchema:{templateId:a.string().min(1).optional().describe("Template id. If provided, generate question payloads from template.parts."),type:a.string().default("CHOOSE_THE_CORRECT_ANSWER").describe("Fallback question type when templateId is omitted."),count:a.number().int().positive().max(100).default(1).describe("Fallback count when templateId is omitted."),partId:a.union([a.string(),a.number()]).default(1),partNo:a.number().int().positive().default(1),skill:a.string().default("READING")}},l("exam_question_template_get",async e=>{const t="string"==typeof e.templateId?s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(e.templateId)}`})):void 0;return r({usage:"Pass questions directly into exam_create_with_questions.questions. Fill content/correctAnswer for each generated API-format payload.",templateId:t?.id,questions:t?b(t):S(e)})})),g.registerTool("exam_json_template_generate",{title:"Get exam JSON template",description:"Generate a user-friendly examJson skeleton from templateTitle or templateId. Edit this JSON, then pass it to exam_create_from_json.",inputSchema:{templateTitle:a.string().optional().describe("Template title to search, e.g. Achievers 2026 Round 1."),templateId:a.string().optional().describe("Exact template id. Preferred when known."),name:a.string().optional().describe("Exam name to put in the JSON."),examLevel:m.optional().describe("Optional exam level for the generated JSON."),difficulty:a.enum(["EASY","MEDIUM","HARD"]).optional().describe("Optional difficulty for the generated JSON."),durationMinutes:a.number().int().positive().optional().describe("Optional duration for the generated JSON.")}},l("exam_json_template_generate",async a=>{const o=await f({templateId:a.templateId,templateTitle:a.templateTitle}),s=function(e,t){const n=Array.isArray(e.parts)?e.parts.filter(c):[];return{name:v(t.name,`${e.title??e.name??"OE Exam"} Draft`),templateId:e.id,templateTitle:e.title??e.name,totalScore:O(e.totalScore??e.total_questions,y(n)),examLevel:_(t.examLevel)??_(e.examLevel??e.exam_level??e.level)??"A1",difficulty:E(t.difficulty)??"MEDIUM",durationMinutes:O(t.durationMinutes,45),isComplete:!1,isActive:!1,parts:n.map((e,t)=>function(e,t){const n=O(e.startIndex,0),i=O(e.questionCount,O(e.endIndex,n)-n+1);return{id:e.id??t+1,name:e.name??`Part ${t+1}`,questionType:v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),skill:v(e.skill,"READING"),questions:Array.from({length:Math.max(i,0)},(t,i)=>function(e,t){const n=v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),i=(Array.isArray(e.defaultValue)&&e.defaultValue.length>0?e.defaultValue:Array.from({length:O(e.optionCount,4)},(e,t)=>String.fromCharCode(65+t))).map(String);return n.includes("FILL")||n.includes("WRITE")?{question:`Question ${t}`,answer:""}:n.includes("SPEAK")?{prompt:`Question ${t}`,expectedAnswer:""}:{question:`Question ${t}`,options:i,answer:i[0]??"A"}}(e,n+i+1))}}(e,t))}}(o,a),l=JSON.stringify(s,null,2),p=i(`${u=String(a.name??o.title??o.name??"exam"),u.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,80)||"exam"}.json`);var u;return await e(n(p),{recursive:!0}),await t(p,`${l}\n`,"utf8"),r({usage:"Edit filePath, then call exam_create_from_json with the edited JSON string.",templateId:o.id,templateTitle:o.title??o.name,filePath:p,examJson:s,examJsonString:l})})),g.registerTool("exam_create_from_json",{title:"Create exam from JSON",description:"Create an exam from a user-friendly JSON payload grouped by parts. Searches template by title, patches template, maps questions to template.parts, then creates questions.",inputSchema:{examJson:a.string().optional().describe("JSON string with { name, templateTitle/templateId, parts: [{ questions: [...] }] }. Use this for copy/paste."),exam:a.record(a.string(),a.unknown()).optional().describe("Same payload as object if the client supports object arguments.")}},l("exam_create_from_json",async e=>{const t=function(e,t){if(c(t))return t;if("string"!=typeof e||!e.trim())throw new Error("examJson or exam is required");const n=JSON.parse(e);if(!c(n))throw new Error("examJson must be a JSON object");return n}(e.examJson,e.exam),n=await f(t),i=function(e,t){const n=Array.isArray(t.parts)?t.parts.filter(c):[],i=Array.isArray(e.parts)?e.parts.filter(c):[{questions:e.questions}];return i.flatMap((e,t)=>{const i=function(e,t,n){const i=A(e.partId??e.id),a=A(e.name);return t.find(e=>String(e.id)===i)??t.find(e=>"string"==typeof e.name&&e.name===a)??t[n]??{id:n+1,questionType:"CHOOSE_THE_CORRECT_ANSWER",skill:"READING",optionCount:4}}(e,n,t);return(Array.isArray(e.questions)?e.questions.filter(c):[]).map((e,n)=>function(e,t,n,i){const a=O(t.startIndex,0),o=w({questionType:v(e.questionType??e.type??t.questionType,"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:O(e.questionNumber,a+i+1),partId:String(e.partId??t.id??n+1),partNo:O(e.partNo,n+1),sectionId:R(e.sectionId??t.sectionId),skill:v(e.questionSkillType??e.skill??t.skill,"READING"),optionCount:O(t.optionCount,4),defaultValue:Array.isArray(t.defaultValue)?t.defaultValue:void 0});return q(function(e){const t=c(e.content)?{...e.content}:{};"string"==typeof e.question&&(t.question=e.question);"string"==typeof e.text&&(t.question=e.text);Array.isArray(e.options)&&(t.optionType=t.optionType??"text",t.options=e.options);return d({...e,content:c(t)?t:e.content,correctAnswer:void 0===e.answer&&void 0===e.expectedAnswer?e.correctAnswer:x(e.questionType,e.answer??e.expectedAnswer,t.options)})}({...o,...e}),i)}(e,i,t,n))})}(t,n),a=function(e,t,n){return{exam:d({name:v(e.name,"New OE Exam"),code:A(e.code)??h(v(e.name,"New OE Exam")),totalScore:O(e.totalScore,n.length||100),description:A(e.description)??A(e.templateTitle)??"",isComplete:T(e.isComplete,!1),isActive:T(e.isActive,!1),examTemplateId:t,examLevel:_(e.examLevel),difficulty:E(e.difficulty),durationMinutes:R(e.durationMinutes)})}}(t,n.id,i),l=s(await o({method:"POST",url:"/api/v1/manage/exams",body:a.exam}));await o({method:"PATCH",url:`/api/v1/manage/exams/${encodeURIComponent(l.id)}/template`,body:{template:P(n)}});const p=0===i.length?null:await o({method:"POST",url:"/api/v1/manage/questions/batch",body:{examId:l.id,questions:i}});return r({exam:l,templateId:n.id,questions:p,generatedQuestionCount:i.length})})),g.registerTool("exam_create_with_questions",{title:"Create exam with questions",description:"Create an exam, optionally patch template, then bulk create questions via /api/v1/manage/questions/batch. If you do not know examTemplateId/template, call exam_templates_search then exam_template_get first.",inputSchema:{name:a.string().min(1),code:a.string().min(1).optional(),examLevel:m.optional().describe("Valid exam level only: PRE_A1, A1, A2, B1, B2, C1, C2. Do not use template title/id here."),difficulty:a.enum(["EASY","MEDIUM","HARD"]).optional(),totalScore:a.number().nonnegative().optional(),durationMinutes:a.number().int().positive().optional(),description:a.string().optional(),isComplete:a.boolean().optional(),isActive:a.boolean().optional(),examTemplateId:a.string().min(1).optional(),template:u.optional(),questions:a.array(p).default([])}},l("exam_create_with_questions",async e=>{const{template:t,questions:n,...i}=e,a=s(await o({method:"POST",url:"/api/v1/manage/exams",body:d(i)})),l=c(t)?t:await async function(e){if("string"!=typeof e||!e)return;const t=s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(e)}`}));return P(t)}(i.examTemplateId);c(l)&&await o({method:"PATCH",url:`/api/v1/manage/exams/${encodeURIComponent(a.id)}/template`,body:{template:l}});const p=n.map(q),u=0===p.length?null:await o({method:"POST",url:"/api/v1/manage/questions/batch",body:{examId:a.id,questions:p}});return r({exam:a,questions:u})}))}function d(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function c(e){return Boolean(e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).length>0)}async function f(e){const t=A(e.templateId);if(t)return s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(t)}`}));const n=A(e.templateTitle);if(!n)throw new Error("templateTitle or templateId is required");const i=new URLSearchParams({page:"1",size:"10",filter:`name:like:${n}`}),a=k(await o({method:"GET",url:`/api/v1/exam-templates?${i}`})),r=a.find(e=>(e.title??e.name)===n),l=r??a[0];if(!l?.id)throw new Error(`No exam template found for title: ${n}`);return s(await o({method:"GET",url:`/api/v1/exam-templates/${encodeURIComponent(l.id)}`}))}function y(e){return e.reduce((e,t)=>e+O(t.questionCount,0),0)}function x(e,t,n){if(void 0===t)return;const i=String(e??"");return i.includes("FILL")||i.includes("WRITE")?{answers:[String(t)]}:i.includes("SPEAK")?{expectedAnswer:String(t)}:{answer:g(t,n)}}function g(e,t){if("string"==typeof e&&Array.isArray(t)){const n=t.map(String).findIndex(t=>t===e);if(n>=0)return n}return I(e)}function h(e){return e.toUpperCase().replace(/[^A-Z0-9]+/g,"_").replace(/^_|_$/g,"").slice(0,40)||"OE_EXAM"}function A(e){return"string"==typeof e&&e?e:void 0}function T(e,t){return"boolean"==typeof e?e:t}function _(e){return"string"==typeof e&&m.safeParse(e).success?e:void 0}function E(e){return"string"==typeof e&&["EASY","MEDIUM","HARD"].includes(e)?e:void 0}function q(e,t){if(e.questionType)return d({...e,partId:void 0===e.partId?void 0:String(e.partId),partNo:e.partNo??1,questionNumber:e.questionNumber??t+1,points:e.points??1,addToQuestionBank:e.addToQuestionBank??!1,isExample:e.isExample??!1});const n=Array.isArray(e.options)?e.options:["A","B","C","D"];return d({questionType:e.type??"CHOOSE_THE_CORRECT_ANSWER",content:e.content??{question:String(e.question??e.text??`Question ${t+1}`),optionType:"text",options:n},correctAnswer:e.correctAnswer??{answer:I(e.answer??e.correctAnswerIndex??0)},points:e.points??1,explanation:e.explanation??"",questionNumber:e.questionNumber??t+1,partId:String(e.partId??1),partNo:e.partNo??1,sectionId:e.sectionId,questionSkillType:e.questionSkillType??e.skill??"READING",addToQuestionBank:e.addToQuestionBank??!1,isExample:e.isExample??!1})}function I(e){if("number"==typeof e)return e;if("string"==typeof e&&/^[A-E]$/i.test(e))return e.toUpperCase().charCodeAt(0)-65;const t=Number(e);return Number.isFinite(t)?t:0}function S(e){return Array.from({length:Number(e.count??1)},(t,n)=>w({questionType:String(e.type??"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:n+1,partId:String(e.partId??1),partNo:Number(e.partNo??1),skill:String(e.skill??"READING")}))}function b(e){return(Array.isArray(e.parts)?e.parts.filter(c):[]).flatMap((e,t)=>{const n=O(e.startIndex,0),i=O(e.questionCount,O(e.endIndex,n)-n+1);return Array.from({length:Math.max(i,0)},(i,a)=>w({questionType:v(e.questionType,"CHOOSE_THE_CORRECT_ANSWER"),questionNumber:n+a+1,partId:String(e.id??t+1),partNo:t+1,sectionId:R(e.sectionId),skill:v(e.skill,"READING"),optionCount:O(e.optionCount,4),defaultValue:Array.isArray(e.defaultValue)?e.defaultValue:void 0}))})}function w(e){return d({questionType:e.questionType,content:C(e.questionType,e.questionNumber,e.optionCount,e.defaultValue),correctAnswer:N(e.questionType),points:1,explanation:"",questionNumber:e.questionNumber,partId:e.partId,partNo:e.partNo,sectionId:e.sectionId,questionSkillType:e.skill,addToQuestionBank:!1,isExample:!1})}function C(e,t,n=4,i){const a=(i?.length?i:Array.from({length:n},(e,t)=>String.fromCharCode(65+t))).map(String);return e.includes("FILL")||e.includes("WRITE")?{question:`Question ${t}`,text:""}:e.includes("SPEAK")?{prompt:`Question ${t}`}:e.includes("LISTEN")?{question:`Question ${t}`,audioUrl:"",optionType:"text",options:a}:{question:`Question ${t}`,optionType:"text",options:a}}function N(e){return e.includes("FILL")||e.includes("WRITE")?{answers:[""]}:e.includes("SPEAK")?{expectedAnswer:""}:{answer:0}}function v(e,t){return"string"==typeof e&&e?e:t}function O(e,t){const n=Number(e);return Number.isFinite(n)?n:t}function R(e){const t=Number(e);return Number.isFinite(t)?t:void 0}function k(e){const t=s(e);return Array.isArray(t)?t:t&&"object"==typeof t&&"data"in t&&Array.isArray(t.data)?t.data:[]}function P(e){const t=Array.isArray(e.parts)?e.parts:[];return d({id:e.id,title:e.title??e.name,subTitle:e.subTitle??e.sub_title??"",theme:e.theme,totalQuestions:e.totalQuestions??e.total_questions,totalParts:e.totalParts??e.total_parts??t.length,description:e.description??"",examLevel:e.examLevel??e.exam_level??e.level??"",type:e.type??null,parts:t})}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerAllMcpTools(server: McpServer): void;
@@ -0,0 +1 @@
1
+ import{registerExamTools as o}from"./exams.js";import{registerResultTools as r}from"./results.js";export function registerAllMcpTools(s){o(s),r(s)}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerResultTools(server: McpServer): void;
@@ -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})}))}
@@ -118,6 +118,12 @@ 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";
121
127
  readonly 'admin.questionBank.dialog.delete.title': "Confirm Delete Question";
122
128
  readonly 'admin.questionBank.dialog.delete.message': "Are you sure you want to delete question";
123
129
  readonly 'admin.questionBank.dialog.delete.warning': "This action cannot be undone.";
@@ -1480,21 +1486,25 @@ declare const admin: {
1480
1486
  readonly 'admin.exams.preview.totalPoints': "Total Points:";
1481
1487
  readonly 'admin.exams.preview.progress': "Progress";
1482
1488
  readonly 'admin.exams.preview.loading': "Loading...";
1483
- readonly 'admin.exams.preview.questionNavigation': "Question Navigation";
1489
+ readonly 'admin.exams.preview.questionNavigation': "Part Navigation";
1484
1490
  readonly 'admin.exams.preview.all': "All";
1485
1491
  readonly 'admin.exams.preview.allQuestions': "All {count} questions";
1486
1492
  readonly 'admin.exams.preview.partQuestions': "Part {part}: {count} questions";
1487
1493
  readonly 'admin.exams.preview.collapse': "Collapse";
1488
1494
  readonly 'admin.exams.preview.detail': "Details";
1489
1495
  readonly 'admin.exams.preview.questionIndex': "Question {index} / {total}";
1496
+ readonly 'admin.exams.preview.partIndex': "Part {index} / {total}";
1490
1497
  readonly 'admin.exams.preview.noQuestions': "No questions yet";
1491
1498
  readonly 'admin.exams.preview.loadingQuestions': "Loading questions...";
1492
1499
  readonly 'admin.exams.preview.retry': "Retry";
1493
1500
  readonly 'admin.exams.preview.examNoQuestions': "This exam has no questions yet.";
1494
1501
  readonly 'admin.exams.preview.prevQuestion': "Previous Question";
1502
+ readonly 'admin.exams.preview.prevPart': "Previous Part";
1495
1503
  readonly 'admin.exams.preview.prev': "Prev";
1496
1504
  readonly 'admin.exams.preview.nextQuestion': "Next Question";
1505
+ readonly 'admin.exams.preview.nextPart': "Next Part";
1497
1506
  readonly 'admin.exams.preview.next': "Next";
1507
+ readonly 'admin.exams.preview.questionLabel': "Question {number}";
1498
1508
  readonly 'admin.exams.preview.footerNote': "This is a preview mode to check the layout and content of the exam.";
1499
1509
  readonly 'admin.exams.preview.close': "Close";
1500
1510
  readonly 'admin.exams.preview.loadError': "Unable to load questions. Please try again.";