@tinyweb_dev/oe-exam-sdk 0.1.20 → 0.1.24

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 +4 -5
  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 +16 -1
  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 +2 -1
  66. package/dist/index.js +1 -1
  67. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -4
  68. package/dist/shared/lib/i18n/messages/en/admin.js +1 -1
  69. package/dist/shared/lib/i18n/messages/en.d.ts +11 -4
  70. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -4
  71. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -1
  72. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -4
  73. package/package.json +163 -163
  74. package/dist/mcp/config.d.ts +0 -12
  75. package/dist/mcp/config.js +0 -1
  76. package/dist/mcp/http-client.d.ts +0 -6
  77. package/dist/mcp/http-client.js +0 -1
  78. package/dist/mcp/index.d.ts +0 -6
  79. package/dist/mcp/index.js +0 -2
  80. package/dist/mcp/tools/_helpers.d.ts +0 -4
  81. package/dist/mcp/tools/_helpers.js +0 -1
  82. package/dist/mcp/tools/exams.d.ts +0 -2
  83. package/dist/mcp/tools/exams.js +0 -1
  84. package/dist/mcp/tools/index.d.ts +0 -2
  85. package/dist/mcp/tools/index.js +0 -1
  86. package/dist/mcp/tools/results.d.ts +0 -2
  87. package/dist/mcp/tools/results.js +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @tinyweb_dev/oe-exam-sdk
2
2
 
3
- SDK tái sử dụng cho hệ thống OE Exam: màn tạo đề, quản lý câu hỏi, làm bài, xem kết quả, theme giao diện và MCP tools.
3
+ SDK tái sử dụng cho hệ thống OE Exam: màn tạo đề, quản lý câu hỏi, làm bài, xem kết quả theme giao diện.
4
4
 
5
5
  ## Cài đặt
6
6
 
@@ -91,7 +91,6 @@ export function ResultReviewPage({ attemptId, token, router }) {
91
91
  @tinyweb_dev/oe-exam-sdk # export tổng hợp thường dùng
92
92
  @tinyweb_dev/oe-exam-sdk/api # API adapters, constants, types
93
93
  @tinyweb_dev/oe-exam-sdk/shared # shared constants/types/helpers
94
- @tinyweb_dev/oe-exam-sdk/mcp # MCP server entry và tools
95
94
  @tinyweb_dev/oe-exam-sdk/components # shared UI components
96
95
  @tinyweb_dev/oe-exam-sdk/components/exams # màn tạo đề / câu hỏi / làm bài / vào phòng
97
96
  @tinyweb_dev/oe-exam-sdk/components/exams/entry # màn vào phòng (entry) + state machine
@@ -238,16 +237,16 @@ Các report shell, renderer, transform helper, polling/status helper và result-
238
237
 
239
238
  ## MCP server
240
239
 
241
- SDK expose bin:
240
+ MCP đã tách sang package riêng:
242
241
 
243
242
  ```bash
244
- oe-exam-mcp
243
+ npm install @tinyweb_dev/oe-exam-mcp-server
245
244
  ```
246
245
 
247
246
  Chạy bằng PAT/API token. Nếu token có dạng `oeexam_...`, MCP tự gửi qua `X-API-Key`; token khác sẽ gửi qua `Authorization: Bearer`.
248
247
 
249
248
  ```bash
250
- OE_API_URL=http://localhost:3000 OE_API_TOKEN=... npx oe-exam-mcp
249
+ OE_API_URL=http://localhost:3000 OE_API_TOKEN=... npx @tinyweb_dev/oe-exam-mcp-server
251
250
  ```
252
251
 
253
252
  Tools hiện có:
@@ -87,6 +87,7 @@ export interface WritingCriterion {
87
87
  code: string;
88
88
  label: string;
89
89
  band: number;
90
+ maxPoints?: number;
90
91
  evidence: string;
91
92
  }
92
93
  export interface WritingAssessment {
@@ -10,11 +10,10 @@ export interface ExamPreviewDialogProps {
10
10
  title?: string;
11
11
  }
12
12
  /**
13
- * Read-only popup that previews an exam (đề) by id: the layout mirrors the
14
- * exam taking experience (left sidebar with exam info + progress + question
15
- * navigation, right pane showing one question at a time) but creates NO attempt
16
- * exam + questions are fetched through {@link ExamQuestionsSdkApi}, so it
17
- * matches exactly how the đề looks. Each question renders via {@link QuestionViewer}
18
- * in review mode (correct answers highlighted).
13
+ * Read-only popup that previews an exam by id.
14
+ *
15
+ * UI mirrors the host app: one Part per screen (all questions of that part),
16
+ * Next/Prev navigates between parts. No attempt is created data comes from
17
+ * {@link ExamQuestionsSdkApi}. Questions render via QuestionViewer in review mode.
19
18
  */
20
- export declare function ExamPreviewDialog({ open, onOpenChange, examId, api, title }: ExamPreviewDialogProps): import("react").JSX.Element;
19
+ export declare function ExamPreviewDialog({ open, onOpenChange, examId, api, title, }: ExamPreviewDialogProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as a,useMemo as n,useRef as i,useState as s}from"react";import{ArrowLeft as r,ArrowRight as l,Eye as d,Loader2 as m}from"lucide-react";import{Badge as c}from"../../components/ui/badge";import{Button as o}from"../../components/ui/button";import{Card as p,CardContent as x,CardHeader as u,CardTitle as h}from"../../components/ui/card";import{Dialog as g,DialogContent as f,DialogHeader as N,DialogTitle as v}from"../../components/ui/dialog";import{Progress as b}from"../../components/ui/progress";import{useT as y}from"../../shared/lib/i18n";import{QuestionViewer as w}from"../questions/viewer/QuestionViewer";function j(e){return e&&"object"==typeof e&&"data"in e?e.data:e}function I(e){switch(e?.toUpperCase()){case"EASY":return"bg-green-100 text-green-800";case"MEDIUM":return"bg-orange-100 text-orange-800";case"HARD":return"bg-red-100 text-red-800";default:return"bg-gray-100 text-gray-800"}}export function ExamPreviewDialog({open:q,onOpenChange:S,examId:C,api:Q,title:k}){const E=y(),[P,z]=s(null),[A,M]=s([]),[$,D]=s(0),[T,O]=s("all"),[R,U]=s(!1),[H,V]=s(!1),[Y,B]=s(null),[F,G]=s(0),J=i(null);a(()=>{if(!q||!C)return;let e=!1;return V(!0),B(null),D(0),O("all"),(async()=>{try{const t=j(await Q.getExam(C)),a=Q.getQuestions?j(await Q.getQuestions(C)):[];if(e)return;const n=[...a].sort((e,t)=>(e.questionNumber??0)-(t.questionNumber??0));z(t),M(n)}catch{e||B(E("admin.exams.preview.loadError"))}finally{e||V(!1)}})(),()=>{e=!0}},[q,C,F,Q,E]),a(()=>{q||(z(null),M([]),D(0),O("all"),U(!1),B(null))},[q]);const K=n(()=>{const e=(P?.template?.parts??[]).map(e=>({id:e.id,name:e.name||e.title})),t=new Set(e.map(e=>e.id));for(const a of A){const n=a.partId??"";n&&!t.has(n)&&(t.add(n),e.push({id:n}))}return e},[P,A]),L=e=>{const t=K.findIndex(t=>t.id===(e??""));return t>=0?t+1:1},W=n(()=>{const e=new Map;for(const t of A){const a=t.partId??"";if(!e.has(a)){const t=K.findIndex(e=>e.id===a),n=t>=0?t+1:1;e.set(a,{partId:a,partNo:n,partName:K[t]?.name||`Part ${n}`,questions:[]})}e.get(a).questions.push(t)}return Array.from(e.values()).sort((e,t)=>e.partNo-t.partNo)},[A,K]),X=P?.name??k??"",Z=P?.template?.totalTime??P?.duration??0,_=function(e){return e.reduce((e,t)=>e+(Number(t.points)||0),0)}(A),ee=A[$],te=A.length>0?($+1)/A.length*100:0,ae=e=>{D(e),J.current?.scrollTo({top:0,behavior:"smooth"})},ne="all"===T?A:A.filter(e=>L(e.partId)===T);return e(g,{open:q,onOpenChange:S,children:t(f,{className:"max-h-[95vh] max-w-7xl overflow-y-auto",children:[e(N,{children:t(v,{className:"flex items-center gap-2",children:[e(d,{className:"h-5 w-5 text-blue-600"}),E("admin.exams.preview.title",{name:X})]})}),t("div",{className:"flex h-[80vh] flex-col gap-4 sm:flex-row",children:[e("div",{className:"w-full overflow-y-auto border-b border-gray-200 sm:w-80 sm:border-b-0 sm:border-r",children:t("div",{className:"space-y-4 p-4",children:[t(p,{children:[e(u,{className:"pb-3",children:e(h,{className:"text-base",children:E("admin.exams.preview.examInfo")})}),t(x,{className:"space-y-3",children:[t("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm text-gray-600",children:E("admin.exams.preview.duration")}),e("span",{className:"text-sm font-medium",children:E("admin.exams.view.minutes",{count:String(Z)})})]}),t("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm text-gray-600",children:E("admin.exams.preview.questionCount")}),e("span",{className:"text-sm font-medium",children:H?"...":E("admin.exams.preview.questions",{count:String(A.length)})})]}),t("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm text-gray-600",children:E("admin.exams.preview.totalPoints")}),e("span",{className:"text-sm font-medium",children:E("admin.exams.preview.points",{count:String(_)})})]})]})]}),!H&&A.length>0&&e(p,{children:e(x,{className:"p-4",children:t("div",{className:"space-y-2",children:[t("div",{className:"flex justify-between text-sm",children:[e("span",{children:E("admin.exams.preview.progress")}),t("span",{className:"font-medium",children:[$+1,"/",A.length]})]}),e(b,{value:te,className:"h-2"})]})})}),H?e("div",{className:"flex items-center justify-center py-4",children:t("div",{className:"flex items-center gap-2 text-gray-500",children:[e(m,{className:"h-4 w-4 animate-spin"}),e("span",{className:"text-sm",children:E("admin.exams.preview.loading")})]})}):A.length>0&&t("div",{className:"space-y-3",children:[e("h4",{className:"text-sm font-medium text-gray-700",children:E("admin.exams.preview.questionNavigation")}),t("div",{className:"flex flex-wrap gap-2",children:[e(o,{variant:"all"===T?"default":"outline",size:"sm",onClick:()=>O("all"),className:"h-8 px-3 text-xs font-medium",children:E("admin.exams.preview.all")}),W.map(e=>t(o,{variant:T===e.partNo?"default":"outline",size:"sm",onClick:()=>{O(e.partNo);const t=e.questions[0];if(t){const e=A.findIndex(e=>e.id===t.id);-1!==e&&ae(e)}},className:"h-8 px-3 text-xs font-medium",children:["P",e.partNo]},e.partId||`part-${e.partNo}`))]}),e("div",{className:"rounded bg-gray-50 p-2 text-xs",children:e("span",{className:"font-medium",children:"all"===T?E("admin.exams.preview.allQuestions",{count:String(A.length)}):E("admin.exams.preview.partQuestions",{part:String(T),count:String(W.find(e=>e.partNo===T)?.questions.length??0)})})}),e("div",{className:"rounded-lg bg-gray-50 p-3",children:e("div",{className:"grid max-h-64 grid-cols-6 gap-2 overflow-y-auto pr-2",children:ne.map(t=>{const a=A.findIndex(e=>e.id===t.id);return e(o,{variant:"ghost",size:"sm",onClick:()=>ae(a),className:"h-8 w-full p-0 text-xs font-medium transition-all duration-200 "+(a===$?"border-2 border-blue-500 bg-blue-500 text-white hover:bg-blue-600":"border border-gray-300 bg-white hover:bg-gray-100"),children:t.questionNumber},t.id)})})})]}),!H&&A.length>0&&t("div",{children:[t("div",{className:"mb-2 flex items-center justify-between",children:[e("h4",{className:"text-sm font-medium text-gray-700",children:X}),e(o,{variant:"ghost",size:"sm",onClick:()=>U(e=>!e),className:"h-6 px-2 text-xs text-gray-600 hover:text-gray-900",children:E(R?"admin.exams.preview.collapse":"admin.exams.preview.detail")})]}),e("div",{className:"rounded-lg bg-gray-50 p-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{children:[t("span",{className:"text-sm font-medium text-gray-900",children:["Total: ",E("admin.exams.preview.questions",{count:String(A.length)})]}),e("div",{className:"mt-1 flex gap-3",children:t("span",{className:"text-xs text-gray-600",children:[W.length," part(s)"]})})]}),e("div",{className:"text-xs text-gray-500",children:E("admin.exams.view.minutes",{count:String(Z)})})]})}),R&&e("div",{className:"mt-3 space-y-2",children:W.map(a=>{const n=L(ee?.partId)===a.partNo;return e("div",{className:"cursor-pointer rounded-lg border-2 p-3 transition-all duration-200 "+(n?"border-blue-400 bg-blue-50 shadow-md":"border-gray-200 bg-white hover:border-gray-300"),onClick:()=>{const e=a.questions[0];if(e){const t=A.findIndex(t=>t.id===e.id);-1!==t&&(ae(t),O(a.partNo))}},children:t("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm font-medium "+(n?"text-blue-900":"text-gray-900"),children:a.partName}),e(c,{variant:"secondary",className:"text-xs "+(n?"bg-blue-200 text-blue-800":"bg-gray-100 text-gray-700"),children:E("admin.exams.preview.questions",{count:String(a.questions.length)})})]})},a.partId||`part-${a.partNo}`)})})]})]})}),e("div",{className:"flex-1 overflow-y-auto",children:t("div",{className:"flex h-full flex-col",children:[t("div",{className:"flex items-center justify-between border-b pb-4",children:[t("div",{children:[e("h3",{className:"text-lg font-semibold",children:H?e("span",{className:"text-gray-400",children:E("admin.exams.preview.loading")}):A.length>0?E("admin.exams.preview.questionIndex",{index:String($+1),total:String(A.length)}):E("admin.exams.preview.noQuestions")}),ee&&e("div",{className:"mt-1 inline-flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-1.5",children:e("span",{className:"text-sm font-semibold text-blue-700",children:(e=>{const t=K.findIndex(t=>t.id===(e??""));return K[t]?.name||`Part ${t>=0?t+1:1}`})(ee.partId)})})]}),ee&&t("div",{className:"flex items-center gap-2",children:[e(c,{variant:"outline",children:E("admin.exams.preview.points",{count:String(Number(ee.points)||1)})}),e(c,{className:I(ee.difficulty),children:ee.difficulty??"N/A"})]})]}),e("div",{ref:J,className:"flex-1 overflow-y-auto py-4",children:H?t("div",{className:"flex flex-col items-center justify-center space-y-4 py-12",children:[e(m,{className:"h-8 w-8 animate-spin text-blue-500"}),e("p",{className:"text-gray-600",children:E("admin.exams.preview.loadingQuestions")})]}):Y?e("div",{className:"flex flex-col items-center justify-center space-y-4 py-12",children:t("div",{className:"rounded-lg border border-red-200 bg-red-50 p-6 text-center",children:[e("p",{className:"font-medium text-red-800",children:Y}),e(o,{variant:"outline",onClick:()=>G(e=>e+1),className:"mt-4",children:E("admin.exams.preview.retry")})]})}):0===A.length?e("div",{className:"flex flex-col items-center justify-center space-y-4 py-12",children:e("div",{className:"rounded-lg border border-yellow-200 bg-yellow-50 p-6 text-center",children:e("p",{className:"font-medium text-yellow-800",children:E("admin.exams.preview.examNoQuestions")})})}):ee?e(w,{question:ee,isReviewMode:!0}):null}),!H&&A.length>0&&t("div",{className:"mt-auto flex items-center justify-between border-t border-gray-200 bg-gray-50 px-4 py-3",children:[t(o,{variant:"outline",onClick:()=>{$>0&&ae($-1)},disabled:0===$,className:"flex h-9 items-center gap-2 px-3 text-sm",children:[e(r,{className:"h-4 w-4"}),e("span",{className:"hidden sm:inline",children:E("admin.exams.preview.prevQuestion")}),e("span",{className:"sm:hidden",children:E("admin.exams.preview.prev")})]}),t("div",{className:"text-sm text-gray-600",children:[$+1," / ",A.length]}),t(o,{variant:"outline",onClick:()=>{$<A.length-1&&ae($+1)},disabled:$===A.length-1,className:"flex h-9 items-center gap-2 px-3 text-sm",children:[e("span",{className:"hidden sm:inline",children:E("admin.exams.preview.nextQuestion")}),e("span",{className:"sm:hidden",children:E("admin.exams.preview.next")}),e(l,{className:"h-4 w-4"})]})]})]})})]}),e("div",{className:"border-t pt-4",children:t("div",{className:"flex items-center justify-between",children:[e("div",{className:"text-sm text-gray-600",children:E("admin.exams.preview.footerNote")}),e(o,{variant:"outline",onClick:()=>S(!1),className:"border-gray-300",children:E("admin.exams.preview.close")})]})})]})})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as r,useMemo as a,useRef as n,useState as o}from"react";import{Eye as i}from"lucide-react";import{Button as l}from"../../components/ui/button";import{Dialog as s,DialogContent as m,DialogHeader as c,DialogTitle as u}from"../../components/ui/dialog";import{useT as p}from"../../shared/lib/i18n";import{ExamPreviewPartContent as d}from"./ExamPreviewPartContent";import{ExamPreviewSidebar as x}from"./ExamPreviewSidebar";import{buildOrderedParts as h,getQuestionNumber as f,getQuestionPoints as g,groupQuestionsByPart as P,sumQuestionPoints as v,unwrapApiData as w}from"./exam-preview.types";export function ExamPreviewDialog({open:N,onOpenChange:y,examId:b,api:E,title:T}){const j=p(),[q,C]=o(null),[D,I]=o([]),[Q,O]=o(0),[R,k]=o(!1),[B,G]=o(!1),[S,z]=o(null),[A,F]=o(0),H=n(null);r(()=>{if(!N||!b)return;let e=!1;return G(!0),z(null),O(0),(async()=>{try{const t=w(await E.getExam(b)),r=E.getQuestions?w(await E.getQuestions(b)):[];if(e)return;const a=[...r].sort((e,t)=>f(e)-f(t));C(t),I(a)}catch{e||z(j("admin.exams.preview.loadError"))}finally{e||G(!1)}})(),()=>{e=!0}},[N,b,A,E,j]),r(()=>{N||(C(null),I([]),O(0),k(!1),z(null))},[N]);const J=a(()=>h(q?.template?.parts??[],D),[q,D]),K=a(()=>P(D,J),[D,J]);r(()=>{0!==K.length?Q>K.length-1&&O(K.length-1):0!==Q&&O(0)},[Q,K.length]);const L=q?.name??T??"",M=q?.template?.totalTime??q?.duration??0,U=v(D),V=K[Q]??null,W=V?.questions??[],X=K.length,Y=X>0?(Q+1)/X*100:0,Z=V?.partName??"",$=W.reduce((e,t)=>e+g(t),0),_=()=>{H.current?.scrollTo({top:0,behavior:"smooth"})};return e(s,{open:N,onOpenChange:y,children:t(m,{className:"max-h-[95vh] max-w-7xl overflow-y-auto",children:[e(c,{children:t(u,{className:"flex items-center gap-2",children:[e(i,{className:"h-5 w-5 text-blue-600"}),j("admin.exams.preview.title",{name:L})]})}),t("div",{className:"flex h-[80vh] flex-col gap-4 sm:flex-row",children:[e(x,{examName:L,duration:M,totalPoints:U,questions:D,questionsByPart:K,currentPart:V,currentPartIndex:Q,totalParts:X,progress:Y,loading:B,showPartsDetail:R,onTogglePartsDetail:()=>k(e=>!e),onGoToPart:e=>{e<0||e>=X||(O(e),_())}}),e(d,{loading:B,error:S,questions:D,currentPart:V,currentPartQuestions:W,currentPartIndex:Q,totalParts:X,partDisplayName:Z,partTotalPoints:$,contentRef:H,onRetry:()=>F(e=>e+1),onPreviousPart:()=>{Q>0&&(O(Q-1),_())},onNextPart:()=>{Q<X-1&&(O(Q+1),_())}})]}),e("div",{className:"border-t pt-4",children:t("div",{className:"flex items-center justify-between",children:[e("div",{className:"text-sm text-gray-600",children:j("admin.exams.preview.footerNote")}),e(l,{variant:"outline",onClick:()=>y(!1),className:"border-gray-300",children:j("admin.exams.preview.close")})]})})]})})}
@@ -0,0 +1,18 @@
1
+ import type { RefObject } from 'react';
2
+ import { type PreviewQuestion, type QuestionsByPart } from './exam-preview.types';
3
+ export interface ExamPreviewPartContentProps {
4
+ loading: boolean;
5
+ error: string | null;
6
+ questions: PreviewQuestion[];
7
+ currentPart: QuestionsByPart | null;
8
+ currentPartQuestions: PreviewQuestion[];
9
+ currentPartIndex: number;
10
+ totalParts: number;
11
+ partDisplayName: string;
12
+ partTotalPoints: number;
13
+ contentRef: RefObject<HTMLDivElement | null>;
14
+ onRetry: () => void;
15
+ onPreviousPart: () => void;
16
+ onNextPart: () => void;
17
+ }
18
+ export declare function ExamPreviewPartContent({ loading, error, questions, currentPart, currentPartQuestions, currentPartIndex, totalParts, partDisplayName, partTotalPoints, contentRef, onRetry, onPreviousPart, onNextPart, }: ExamPreviewPartContentProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import{ArrowLeft as r,ArrowRight as n,Loader2 as t}from"lucide-react";import{Badge as a}from"../../components/ui/badge";import{Button as s}from"../../components/ui/button";import{QuestionViewer as l}from"../questions/viewer/QuestionViewer";import{useT as d}from"../../shared/lib/i18n";import{difficultyBadgeClass as m,getQuestionDifficulty as c,getQuestionNumber as o,getQuestionPoints as p}from"./exam-preview.types";export function ExamPreviewPartContent({loading:x,error:u,questions:h,currentPart:v,currentPartQuestions:b,currentPartIndex:f,totalParts:N,partDisplayName:g,partTotalPoints:w,contentRef:y,onRetry:P,onPreviousPart:j,onNextPart:q}){const S=d();return e("div",{className:"flex-1 overflow-y-auto",children:i("div",{className:"flex h-full flex-col",children:[i("div",{className:"flex items-center justify-between border-b pb-4",children:[i("div",{children:[e("h3",{className:"text-lg font-semibold",children:x?e("span",{className:"text-gray-400",children:S("admin.exams.preview.loading")}):N>0?S("admin.exams.preview.partIndex",{index:String(f+1),total:String(N)}):S("admin.exams.preview.noQuestions")}),v&&i("div",{className:"mt-1 inline-flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-1.5",children:[e("span",{className:"text-sm font-semibold text-blue-700",children:g}),e("span",{className:"text-xs text-blue-600",children:S("admin.exams.preview.questions",{count:String(b.length)})})]})]}),v&&e("div",{className:"flex items-center gap-2",children:e(a,{variant:"outline",children:S("admin.exams.preview.points",{count:String(w)})})})]}),e("div",{ref:y,className:"flex-1 overflow-y-auto py-4",children:x?i("div",{className:"flex flex-col items-center justify-center space-y-4 py-12",children:[e(t,{className:"h-8 w-8 animate-spin text-blue-500"}),e("p",{className:"text-gray-600",children:S("admin.exams.preview.loadingQuestions")})]}):u?e("div",{className:"flex flex-col items-center justify-center space-y-4 py-12",children:i("div",{className:"rounded-lg border border-red-200 bg-red-50 p-6 text-center",children:[e("p",{className:"font-medium text-red-800",children:u}),e(s,{variant:"outline",onClick:P,className:"mt-4",children:S("admin.exams.preview.retry")})]})}):0===h.length?e("div",{className:"flex flex-col items-center justify-center space-y-4 py-12",children:e("div",{className:"rounded-lg border border-yellow-200 bg-yellow-50 p-6 text-center",children:e("p",{className:"font-medium text-yellow-800",children:S("admin.exams.preview.examNoQuestions")})})}):b.length>0?e("div",{className:"space-y-6",children:b.map(r=>{const n=o(r),t=c(r);return i("div",{id:`preview-q-${r.id}`,className:"rounded-xl border border-gray-200 bg-white p-4 shadow-sm",children:[i("div",{className:"mb-3 flex items-center justify-between gap-2 border-b border-gray-100 pb-3",children:[e("span",{className:"inline-flex h-7 min-w-7 items-center justify-center rounded-md bg-blue-100 px-2 text-xs font-bold text-blue-700",children:S("admin.exams.preview.questionLabel",{number:String(n)})}),i("div",{className:"flex items-center gap-2",children:[e(a,{variant:"outline",children:S("admin.exams.preview.points",{count:String(p(r))})}),e(a,{className:m(t),children:t})]})]}),e(l,{question:r,isReviewMode:!0})]},r.id)})}):null}),!x&&N>0&&i("div",{className:"mt-auto flex items-center justify-between border-t border-gray-200 bg-gray-50 px-4 py-3",children:[i(s,{variant:"outline",onClick:j,disabled:0===f,className:"flex h-9 items-center gap-2 px-3 text-sm",children:[e(r,{className:"h-4 w-4"}),e("span",{className:"hidden sm:inline",children:S("admin.exams.preview.prevPart")}),e("span",{className:"sm:hidden",children:S("admin.exams.preview.prev")})]}),i("div",{className:"text-sm text-gray-600",children:[f+1," / ",N]}),i(s,{variant:"outline",onClick:q,disabled:f===N-1,className:"flex h-9 items-center gap-2 px-3 text-sm",children:[e("span",{className:"hidden sm:inline",children:S("admin.exams.preview.nextPart")}),e("span",{className:"sm:hidden",children:S("admin.exams.preview.next")}),e(n,{className:"h-4 w-4"})]})]})]})})}
@@ -0,0 +1,17 @@
1
+ import { type PreviewQuestion, type QuestionsByPart } from './exam-preview.types';
2
+ export interface ExamPreviewSidebarProps {
3
+ examName: string;
4
+ duration: number;
5
+ totalPoints: number;
6
+ questions: PreviewQuestion[];
7
+ questionsByPart: QuestionsByPart[];
8
+ currentPart: QuestionsByPart | null;
9
+ currentPartIndex: number;
10
+ totalParts: number;
11
+ progress: number;
12
+ loading: boolean;
13
+ showPartsDetail: boolean;
14
+ onTogglePartsDetail: () => void;
15
+ onGoToPart: (index: number) => void;
16
+ }
17
+ export declare function ExamPreviewSidebar({ examName, duration, totalPoints, questions, questionsByPart, currentPart, currentPartIndex, totalParts, progress, loading, showPartsDetail, onTogglePartsDetail, onGoToPart, }: ExamPreviewSidebarProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Badge as t}from"../../components/ui/badge";import{Button as s}from"../../components/ui/button";import{Card as i,CardContent as r,CardHeader as n,CardTitle as m}from"../../components/ui/card";import{Progress as l}from"../../components/ui/progress";import{Loader2 as d}from"lucide-react";import{useT as c}from"../../shared/lib/i18n";import{getQuestionNumber as o}from"./exam-preview.types";export function ExamPreviewSidebar({examName:p,duration:x,totalPoints:h,questions:u,questionsByPart:g,currentPart:v,currentPartIndex:N,totalParts:f,progress:b,loading:y,showPartsDetail:w,onTogglePartsDetail:j,onGoToPart:q}){const P=c();return e("div",{className:"w-full overflow-y-auto border-b border-gray-200 sm:w-80 sm:border-b-0 sm:border-r",children:a("div",{className:"space-y-4 p-4",children:[a(i,{children:[e(n,{className:"pb-3",children:e(m,{className:"text-base",children:P("admin.exams.preview.examInfo")})}),a(r,{className:"space-y-3",children:[a("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm text-gray-600",children:P("admin.exams.preview.duration")}),e("span",{className:"text-sm font-medium",children:P("admin.exams.view.minutes",{count:String(x)})})]}),a("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm text-gray-600",children:P("admin.exams.preview.questionCount")}),e("span",{className:"text-sm font-medium",children:y?"...":P("admin.exams.preview.questions",{count:String(u.length)})})]}),a("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm text-gray-600",children:P("admin.exams.preview.totalPoints")}),e("span",{className:"text-sm font-medium",children:P("admin.exams.preview.points",{count:String(h)})})]})]})]}),!y&&f>0&&e(i,{children:e(r,{className:"p-4",children:a("div",{className:"space-y-2",children:[a("div",{className:"flex justify-between text-sm",children:[e("span",{children:P("admin.exams.preview.progress")}),a("span",{className:"font-medium",children:[N+1,"/",f]})]}),e(l,{value:b,className:"h-2"})]})})}),y?e("div",{className:"flex items-center justify-center py-4",children:a("div",{className:"flex items-center gap-2 text-gray-500",children:[e(d,{className:"h-4 w-4 animate-spin"}),e("span",{className:"text-sm",children:P("admin.exams.preview.loading")})]})}):f>0&&a("div",{className:"space-y-3",children:[e("h4",{className:"text-sm font-medium text-gray-700",children:P("admin.exams.preview.questionNavigation")}),e("div",{className:"flex flex-wrap gap-2",children:g.map((e,t)=>a(s,{variant:t===N?"default":"outline",size:"sm",onClick:()=>q(t),className:"h-8 px-3 text-xs font-medium",children:["P",e.partNo]},e.partId||`part-${e.partNo}`))}),v&&e("div",{className:"rounded bg-gray-50 p-2 text-xs",children:e("span",{className:"font-medium",children:P("admin.exams.preview.partQuestions",{part:String(v.partNo),count:String(v.questions.length)})})}),v&&e("div",{className:"rounded-lg bg-gray-50 p-3",children:e("div",{className:"grid max-h-64 grid-cols-6 gap-2 overflow-y-auto pr-2",children:v.questions.map(a=>e(s,{variant:"ghost",size:"sm",onClick:()=>{const e=document.getElementById(`preview-q-${a.id}`);e?.scrollIntoView({behavior:"smooth",block:"start"})},className:"h-8 w-full border border-gray-300 bg-white p-0 text-xs font-medium transition-all duration-200 hover:bg-gray-100",children:o(a)},a.id))})})]}),!y&&u.length>0&&a("div",{children:[a("div",{className:"mb-2 flex items-center justify-between",children:[e("h4",{className:"text-sm font-medium text-gray-700",children:p}),e(s,{variant:"ghost",size:"sm",onClick:j,className:"h-6 px-2 text-xs text-gray-600 hover:text-gray-900",children:P(w?"admin.exams.preview.collapse":"admin.exams.preview.detail")})]}),e("div",{className:"rounded-lg bg-gray-50 p-3",children:a("div",{className:"flex items-center justify-between",children:[a("div",{children:[a("span",{className:"text-sm font-medium text-gray-900",children:["Total:"," ",P("admin.exams.preview.questions",{count:String(u.length)})]}),e("div",{className:"mt-1 flex gap-3",children:a("span",{className:"text-xs text-gray-600",children:[g.length," part(s)"]})})]}),e("div",{className:"text-xs text-gray-500",children:P("admin.exams.view.minutes",{count:String(x)})})]})}),w&&e("div",{className:"mt-3 space-y-2",children:g.map((s,i)=>{const r=i===N;return e("div",{className:"cursor-pointer rounded-lg border-2 p-3 transition-all duration-200 "+(r?"border-blue-400 bg-blue-50 shadow-md":"border-gray-200 bg-white hover:border-gray-300"),onClick:()=>q(i),children:a("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-sm font-medium "+(r?"text-blue-900":"text-gray-900"),children:s.partName}),e(t,{variant:"secondary",className:"text-xs "+(r?"bg-blue-200 text-blue-800":"bg-gray-100 text-gray-700"),children:P("admin.exams.preview.questions",{count:String(s.questions.length)})})]})},s.partId||`part-detail-${s.partNo}`)})})]})]})})}
@@ -0,0 +1,34 @@
1
+ import type { ExamPart, ExamQuestion } from '../../shared/types/entities/exam.types';
2
+ export type PreviewQuestion = Partial<ExamQuestion> & {
3
+ id?: string;
4
+ partId?: string;
5
+ questionNumber?: number;
6
+ points?: number | string;
7
+ difficulty?: string;
8
+ };
9
+ export interface QuestionsByPart {
10
+ partId: string;
11
+ partNo: number;
12
+ partName: string;
13
+ questions: PreviewQuestion[];
14
+ }
15
+ export interface OrderedPart {
16
+ id: string;
17
+ name?: string;
18
+ }
19
+ /** SDK fetch helpers return either `{ data: T }` or the bare value. */
20
+ export declare function unwrapApiData<T>(res: {
21
+ data: T;
22
+ } | T): T;
23
+ export declare function sumQuestionPoints(questions: PreviewQuestion[]): number;
24
+ export declare function getQuestionPoints(question: PreviewQuestion): number;
25
+ export declare function getQuestionNumber(question: PreviewQuestion): number;
26
+ export declare function getQuestionDifficulty(question: PreviewQuestion): string;
27
+ export declare function difficultyBadgeClass(difficulty?: string): string;
28
+ /**
29
+ * Build ordered parts from template first, then any extra part ids on questions.
30
+ */
31
+ export declare function buildOrderedParts(templateParts: Array<ExamPart & {
32
+ title?: string;
33
+ }> | undefined, questions: PreviewQuestion[]): OrderedPart[];
34
+ export declare function groupQuestionsByPart(questions: PreviewQuestion[], orderedParts: OrderedPart[]): QuestionsByPart[];
@@ -0,0 +1 @@
1
+ export function unwrapApiData(t){return t&&"object"==typeof t&&"data"in t?t.data:t}export function sumQuestionPoints(t){return t.reduce((t,e)=>t+(Number(e.points)||0),0)}export function getQuestionPoints(t){return Number(t.points)||1}export function getQuestionNumber(t){return t.questionNumber??0}export function getQuestionDifficulty(t){return t.difficulty??"N/A"}export function difficultyBadgeClass(t){switch(t?.toUpperCase()){case"EASY":return"bg-green-100 text-green-800";case"MEDIUM":return"bg-orange-100 text-orange-800";case"HARD":return"bg-red-100 text-red-800";default:return"bg-gray-100 text-gray-800"}}export function buildOrderedParts(t,e){const r=(t??[]).map(t=>({id:t.id,name:t.name||t.title})),n=new Set(r.map(t=>t.id));for(const t of e){const e=t.partId??"";e&&!n.has(e)&&(n.add(e),r.push({id:e}))}return r}export function groupQuestionsByPart(t,e){const r=new Map;for(const n of t){const t=n.partId??"";if(!r.has(t)){const n=e.findIndex(e=>e.id===t),o=n>=0?n+1:1;r.set(t,{partId:t,partNo:o,partName:e[n]?.name||`Part ${o}`,questions:[]})}r.get(t).questions.push(n)}return Array.from(r.values()).sort((t,e)=>t.partNo-e.partNo)}
@@ -32,4 +32,5 @@ export { EXAM_DIFFICULTY, ExamViewDialog } from './ExamViewDialog';
32
32
  export type { ExamDifficulty, ExamViewDialogExam, ExamViewDialogProps } from './ExamViewDialog';
33
33
  export { ExamPreviewDialog } from './ExamPreviewDialog';
34
34
  export type { ExamPreviewDialogProps } from './ExamPreviewDialog';
35
+ export type { PreviewQuestion, QuestionsByPart } from './exam-preview.types';
35
36
  export { QuestionStatus, clearQuestionStatuses, getQuestionStatus, initializeQuestionStatusesFromAPI, setQuestionStatus } from './exam-question-status';
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{ZoomIn as n}from"lucide-react";import{useState as l}from"react";import{cn as o}from"../../../../../shared/lib/utils";import{ResolvedImage as i}from"../../../../../components/common/ResolvedImage";import s from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import a from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";function c(e){if("string"==typeof e)return e;if("number"==typeof e||"boolean"==typeof e)return String(e);if(Array.isArray(e))return e.map(e=>c(e)).filter(Boolean).join(" ");if(e&&"object"==typeof e){const t=e;if("string"==typeof t.content)return t.content;if("string"==typeof t.question)return t.question;if("string"==typeof t.text)return t.text;const r=Object.entries(t).filter(([e,t])=>/^\d+$/.test(e)&&"string"==typeof t).sort(([e],[t])=>Number(e)-Number(t)).map(([,e])=>e);if(r.length>0)return r.join("")}return""}function m({imageUrl:o}){const[s,a]=l(!1);return t(r,{children:[t("div",{className:"relative flex items-center justify-center",children:[e(i,{src:o,alt:"Question illustration",className:"max-h-[250px] cursor-pointer rounded-lg object-contain shadow-md",onClick:()=>a(!0)}),e("button",{type:"button",onClick:()=>a(!0),className:"absolute right-2 top-2 rounded-full bg-white/80 p-1.5 shadow-md hover:bg-white","aria-label":"Zoom image",children:e(n,{className:"h-4 w-4 text-gray-600"})})]}),s&&e("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/70",onClick:()=>a(!1),children:e(i,{src:o,alt:"Question illustration (zoomed)",className:"max-h-[90vh] max-w-[90vw] rounded-lg object-contain",showLoadingState:!1})})]})}function u({example:r}){return t("div",{className:"rounded-lg border-2 border-orange-300 bg-white p-4",children:[e("p",{className:"mb-3 text-sm font-semibold text-gray-700",children:"Example:"}),t("p",{className:"mb-3 text-[19px] font-bold text-[#001590]",children:[r.questionNumber,". ",c(r.questionText)]}),r.imageUrl&&e("div",{className:"mb-3",children:e(m,{imageUrl:r.imageUrl})}),"image"===r.optionType?e("div",{className:"grid gap-3 px-2",style:{gridTemplateColumns:`repeat(${r.options.length}, minmax(0, 1fr))`},children:r.options.map(n=>{const l=n.id===r.correctOptionId;return t("div",{className:"relative flex flex-col items-center gap-1 rounded-[16px] border-[3px] p-4 pt-6 mt-3 transition-colors "+(l?"border-green-500 bg-green-50":"border-blue-200 bg-white"),children:[e("div",{className:"absolute -top-[14px] left-1/2 -translate-x-1/2 bg-blue-600 text-white w-[28px] h-[28px] rounded-full flex items-center justify-center font-bold text-[15px] shadow-sm",children:n.label}),e(i,{src:n.text,alt:`Option ${n.label}`,className:"w-40 rounded object-contain mb-2"}),e("div",{className:o("w-7 h-7 rounded-full flex items-center justify-center border-[3px] transition-all mt-auto",l?"bg-green-500 border-green-500 text-white":"border-gray-300 bg-white text-transparent"),children:e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3.5",strokeLinecap:"round",strokeLinejoin:"round",children:e("polyline",{points:"20 6 9 17 4 12"})})})]},n.id)})}):e("div",{className:"flex flex-col gap-2 pl-2",children:r.options.map(n=>{const l=n.id===r.correctOptionId;return t("div",{className:"flex items-center gap-3 rounded-lg border-2 px-3 py-2 text-left transition-colors "+(l?"border-green-500 bg-green-50":"border-blue-200 bg-white"),children:[t("span",{className:"text-sm font-medium text-gray-600",children:[n.label,"."]}),e("span",{className:"flex-1 text-sm text-gray-700",children:c(n.text)}),e("div",{className:"flex h-5 w-5 items-center justify-center border-2 rounded-full "+(l?"border-green-500 bg-green-500":"border-gray-300 bg-white"),children:l&&e("div",{className:"h-2 w-2 rounded-full bg-white"})})]},n.id)})})]})}function p({question:r,selectedOptionIds:n,onSelect:l,isReviewMode:s,id:a}){const d=Boolean(r.multipleAnswers||r.correctOptionIds?.length),u=r.correctOptionIds?.length?r.correctOptionIds:r.correctOptionId?[r.correctOptionId]:[],p=e=>{const t=n.includes(e),r=u.includes(e);return s?r?"border-green-500 bg-green-50":t&&!r?"border-red-500 bg-red-50":"border-blue-200 bg-white":t?"border-blue-500 bg-[#f4f9ff]":"border-blue-200 bg-white hover:border-blue-300 hover:bg-blue-50/30"},b=e=>{const t=n.includes(e),r=u.includes(e);return s?r?"border-green-500 bg-green-500":t&&!r?"border-red-500 bg-red-500":"border-gray-300 bg-white":t?"border-blue-500 bg-blue-500":"border-gray-300 bg-white"},g=e=>s?n.includes(e)||u.includes(e):n.includes(e);return t("div",{id:a,className:"mb-6",children:[t("p",{className:"mb-3 text-[19px] font-bold text-[#001590]",children:[r.questionNumber,". ",c(r.questionText)]}),d&&e("p",{className:"mb-3 pl-2 text-sm font-medium text-blue-600",children:"Chọn nhiều đáp án"}),r.imageUrl&&e("div",{className:"mb-3",children:e(m,{imageUrl:r.imageUrl})}),"image"===r.optionType?e("div",{className:"grid gap-3 px-2",style:{gridTemplateColumns:`repeat(${r.options.length}, minmax(0, 1fr))`},children:r.options.map(r=>t("button",{type:"button",onClick:()=>!s&&l(r.id),disabled:s,className:`relative flex flex-col items-center gap-1 rounded-[16px] border-[3px] p-4 pt-6 mt-3 transition-colors ${p(r.id)} ${s?"cursor-default":"cursor-pointer"}`,children:[e("div",{className:"absolute -top-[14px] left-1/2 -translate-x-1/2 bg-blue-600 text-white w-[28px] h-[28px] rounded-full flex items-center justify-center font-bold text-[15px] shadow-sm",children:r.label}),e(i,{src:r.text,alt:`Option ${r.label}`,className:"w-35 rounded object-contain mb-2"}),e("div",{className:o("w-7 h-7 rounded-full flex items-center justify-center border-[3px] transition-all mt-auto",g(r.id)?"bg-blue-600 border-blue-600 text-white":"border-gray-300 bg-white text-transparent"),children:e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3.5",strokeLinecap:"round",strokeLinejoin:"round",children:e("polyline",{points:"20 6 9 17 4 12"})})})]},r.id))}):e("div",{className:"flex flex-col gap-2 pl-2",children:r.options.map(r=>t("button",{type:"button",onClick:()=>!s&&l(r.id),disabled:s,className:`flex items-center gap-3 rounded-lg border-2 px-3 py-2 text-left transition-colors ${p(r.id)} ${s?"cursor-default":"cursor-pointer"}`,children:[t("span",{className:"text-sm font-medium text-gray-600",children:[r.label,"."]}),e("span",{className:"flex-1 text-sm text-gray-700",children:c(r.text)}),e("div",{className:`flex h-5 w-5 items-center justify-center border-2 ${d?"rounded-md":"rounded-full"} ${b(r.id)}`,children:g(r.id)&&e("div",{className:(d?"h-2.5 w-2.5 rounded-sm":"h-2 w-2 rounded-full")+" bg-white"})})]},r.id))})]})}function b({article:r,label:n}){return t("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[t("div",{className:"mb-2 flex items-center gap-2",children:[e("span",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-teal-500 text-xs font-bold text-white",children:n}),e("h3",{className:"text-sm font-bold text-gray-900",children:r.name})]}),r.imageUrl&&e("div",{className:"mb-2",children:e(i,{src:r.imageUrl,alt:r.name,className:"max-h-48 w-full rounded-md object-contain"})}),e("div",{className:"article-content text-sm leading-relaxed text-gray-700",dangerouslySetInnerHTML:{__html:r.content}})]})}export function MoversChooseBestAnswerRenderer({partNumber:r,partName:n,questionCount:l,instruction:o,imageUrl:c,groupTitle:m,groupContent:g,articles:x,audioUrl:h,example:f,questions:w,answers:y,onAnswerChange:N,isReviewMode:v=!1}){const j=!!(h||c||x&&x.length>0||g||f);return t("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar relative",children:[t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-2",children:[e(s,{partNumber:r,questionCount:l,partName:n,isReviewMode:v}),e(a,{instruction:o,isReviewMode:v}),m&&!(x&&x.length>0)&&e("h2",{className:"text-center text-xl font-bold uppercase text-coral-500",children:m})]}),j&&t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-0 sticky top-0 z-20 bg-white/95 backdrop-blur pb-2 shadow-sm border-b border-gray-100",children:[h&&e(d,{audioSrc:h,compact:!0}),c&&e("div",{className:"flex items-center justify-center",children:e(i,{src:c,alt:"Shared illustration",className:"max-h-[250px] rounded-lg object-contain shadow-md"})}),x&&x.length>0?e("div",{className:"flex flex-col gap-3 max-h-[35vh] overflow-y-auto custom-scrollbar pr-2",children:x.map((t,r)=>e(b,{article:t,label:String.fromCharCode(65+r)},r))}):g?e("div",{className:"article-content rounded-lg bg-amber-50 p-3 sm:p-4 text-sm leading-relaxed text-gray-700 max-h-[35vh] overflow-y-auto pr-2 custom-scrollbar shadow-inner",dangerouslySetInnerHTML:{__html:g}}):null,f&&e(u,{example:f})]}),e("div",{className:"p-3 sm:pb-6 sm:px-6 "+(v?"grid grid-cols-1 md:grid-cols-2 gap-6":"flex flex-col gap-4"),children:w.map(t=>{const r=y[t.id],n=Array.isArray(r)?r:"string"==typeof r&&r?[r]:[];return e(p,{id:`question-${t.id}`,question:t,selectedOptionIds:n,onSelect:e=>{const r=t.multipleAnswers?n.includes(e)?n.filter(t=>t!==e):[...n,e]:e;N(t.id,r)},isReviewMode:v},t.id)})})]})}
1
+ "use client";import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{ZoomIn as n}from"lucide-react";import{useState as l}from"react";import{cn as o}from"../../../../../shared/lib/utils";import{ResolvedImage as i}from"../../../../../components/common/ResolvedImage";import s from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import a from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";function c(e){if("string"==typeof e)return e;if("number"==typeof e||"boolean"==typeof e)return String(e);if(Array.isArray(e))return e.map(e=>c(e)).filter(Boolean).join(" ");if(e&&"object"==typeof e){const t=e;if("string"==typeof t.content)return t.content;if("string"==typeof t.question)return t.question;if("string"==typeof t.text)return t.text;const r=Object.entries(t).filter(([e,t])=>/^\d+$/.test(e)&&"string"==typeof t).sort(([e],[t])=>Number(e)-Number(t)).map(([,e])=>e);if(r.length>0)return r.join("")}return""}function m({imageUrl:o}){const[s,a]=l(!1);return t(r,{children:[t("div",{className:"relative flex items-center justify-center",children:[e(i,{src:o,alt:"Question illustration",className:"max-h-[250px] cursor-pointer rounded-lg object-contain shadow-md",onClick:()=>a(!0)}),e("button",{type:"button",onClick:()=>a(!0),className:"absolute right-2 top-2 rounded-full bg-white/80 p-1.5 shadow-md hover:bg-white","aria-label":"Zoom image",children:e(n,{className:"h-4 w-4 text-gray-600"})})]}),s&&e("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/70",onClick:()=>a(!1),children:e(i,{src:o,alt:"Question illustration (zoomed)",className:"max-h-[90vh] max-w-[90vw] rounded-lg object-contain",showLoadingState:!1})})]})}function u({example:r}){return t("div",{className:"rounded-lg border-2 border-orange-300 bg-white p-4",children:[e("p",{className:"mb-3 text-sm font-semibold text-gray-700",children:"Example:"}),t("p",{className:"mb-3 text-[19px] font-bold text-[#001590]",children:[r.questionNumber,". ",c(r.questionText)]}),r.imageUrl&&e("div",{className:"mb-3",children:e(m,{imageUrl:r.imageUrl})}),"image"===r.optionType?e("div",{className:"grid gap-3 px-2",style:{gridTemplateColumns:`repeat(${r.options.length}, minmax(0, 1fr))`},children:r.options.map(n=>{const l=n.id===r.correctOptionId;return t("div",{className:"relative flex flex-col items-center gap-1 rounded-[16px] border-[3px] p-4 pt-6 mt-3 transition-colors "+(l?"border-green-500 bg-green-50":"border-blue-200 bg-white"),children:[e("div",{className:"absolute -top-[14px] left-1/2 -translate-x-1/2 bg-blue-600 text-white w-[28px] h-[28px] rounded-full flex items-center justify-center font-bold text-[15px] shadow-sm",children:n.label}),e(i,{src:n.text,alt:`Option ${n.label}`,className:"w-40 rounded object-contain mb-2"}),e("div",{className:o("w-7 h-7 rounded-full flex items-center justify-center border-[3px] transition-all mt-auto",l?"bg-green-500 border-green-500 text-white":"border-gray-300 bg-white text-transparent"),children:e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3.5",strokeLinecap:"round",strokeLinejoin:"round",children:e("polyline",{points:"20 6 9 17 4 12"})})})]},n.id)})}):e("div",{className:"flex flex-col gap-2 pl-2",children:r.options.map(n=>{const l=n.id===r.correctOptionId;return t("div",{className:"flex items-center gap-3 rounded-lg border-2 px-3 py-2 text-left transition-colors "+(l?"border-green-500 bg-green-50":"border-blue-200 bg-white"),children:[t("span",{className:"text-sm font-medium text-gray-600",children:[n.label,"."]}),e("span",{className:"flex-1 text-sm text-gray-700",children:c(n.text)}),e("div",{className:"flex h-5 w-5 items-center justify-center border-2 rounded-full "+(l?"border-green-500 bg-green-500":"border-gray-300 bg-white"),children:l&&e("div",{className:"h-2 w-2 rounded-full bg-white"})})]},n.id)})})]})}function p({question:r,selectedOptionIds:n,onSelect:l,isReviewMode:s,id:a}){const d=Boolean(r.multipleAnswers||r.correctOptionIds?.length),u=r.correctOptionIds?.length?r.correctOptionIds:r.correctOptionId?[r.correctOptionId]:[],p=e=>{const t=n.includes(e),r=u.includes(e);return s?r?"border-green-500 bg-green-50":t&&!r?"border-red-500 bg-red-50":"border-blue-200 bg-white":t?"border-blue-500 bg-[#f4f9ff]":"border-blue-200 bg-white hover:border-blue-300 hover:bg-blue-50/30"},b=e=>{const t=n.includes(e),r=u.includes(e);return s?r?"border-green-500 bg-green-500":t&&!r?"border-red-500 bg-red-500":"border-gray-300 bg-white":t?"border-blue-500 bg-blue-500":"border-gray-300 bg-white"},g=e=>s?n.includes(e)||u.includes(e):n.includes(e);return t("div",{id:a,className:"mb-6",children:[t("p",{className:"mb-3 text-[19px] font-bold text-[#001590]",children:[r.questionNumber,". ",c(r.questionText)]}),d&&e("p",{className:"mb-3 pl-2 text-sm font-medium text-blue-600",children:"Chọn nhiều đáp án"}),r.imageUrl&&e("div",{className:"mb-3",children:e(m,{imageUrl:r.imageUrl})}),"image"===r.optionType?e("div",{className:"grid gap-3 px-2",style:{gridTemplateColumns:`repeat(${r.options.length}, minmax(0, 1fr))`},children:r.options.map(r=>t("button",{type:"button",onClick:()=>!s&&l(r.id),disabled:s,className:`relative flex flex-col items-center gap-1 rounded-[16px] border-[3px] p-4 pt-6 mt-3 transition-colors ${p(r.id)} ${s?"cursor-default":"cursor-pointer"}`,children:[e("div",{className:"absolute -top-[14px] left-1/2 -translate-x-1/2 bg-blue-600 text-white w-[28px] h-[28px] rounded-full flex items-center justify-center font-bold text-[15px] shadow-sm",children:r.label}),e(i,{src:r.text,alt:`Option ${r.label}`,className:"w-35 rounded object-contain mb-2"}),e("div",{className:o("w-7 h-7 rounded-full flex items-center justify-center border-[3px] transition-all mt-auto",g(r.id)?"bg-blue-600 border-blue-600 text-white":"border-gray-300 bg-white text-transparent"),children:e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3.5",strokeLinecap:"round",strokeLinejoin:"round",children:e("polyline",{points:"20 6 9 17 4 12"})})})]},r.id))}):e("div",{className:"flex flex-col gap-2 pl-2",children:r.options.map(r=>t("button",{type:"button",onClick:()=>!s&&l(r.id),disabled:s,className:`flex items-center gap-3 rounded-lg border-2 px-3 py-2 text-left transition-colors ${p(r.id)} ${s?"cursor-default":"cursor-pointer"}`,children:[t("span",{className:"text-sm font-medium text-gray-600",children:[r.label,"."]}),e("span",{className:"flex-1 text-sm text-gray-700",children:c(r.text)}),e("div",{className:`flex h-5 w-5 items-center justify-center border-2 ${d?"rounded-md":"rounded-full"} ${b(r.id)}`,children:g(r.id)&&e("div",{className:(d?"h-2.5 w-2.5 rounded-sm":"h-2 w-2 rounded-full")+" bg-white"})})]},r.id))})]})}function b({article:r,label:n}){return t("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[t("div",{className:"mb-2 flex items-center gap-2",children:[e("span",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-teal-500 text-xs font-bold text-white",children:n}),e("h3",{className:"text-sm font-bold text-gray-900",children:r.name})]}),r.imageUrl&&e("div",{className:"mb-2",children:e(i,{src:r.imageUrl,alt:r.name,className:"max-h-48 w-full rounded-md object-contain"})}),e("div",{className:"article-content text-sm leading-relaxed text-gray-700",dangerouslySetInnerHTML:{__html:r.content}})]})}export function MoversChooseBestAnswerRenderer({partNumber:r,partName:n,questionCount:l,instruction:o,imageUrl:c,groupTitle:m,groupContent:g,articles:x,audioUrl:h,example:f,questions:w,answers:N,onAnswerChange:y,isReviewMode:v=!1}){const j=!!(h||c||x&&x.length>0||g);return t("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar relative",children:[t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-2",children:[e(s,{partNumber:r,questionCount:l,partName:n,isReviewMode:v}),e(a,{instruction:o,isReviewMode:v}),m&&!(x&&x.length>0)&&e("h2",{className:"text-center text-xl font-bold uppercase text-coral-500",children:m})]}),j&&t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-0 sticky top-0 z-20 bg-white/95 backdrop-blur pb-2 shadow-sm border-b border-gray-100",children:[h&&e(d,{audioSrc:h,compact:!0}),c&&e("div",{className:"flex items-center justify-center",children:e(i,{src:c,alt:"Shared illustration",className:"max-h-[250px] rounded-lg object-contain shadow-md"})}),x&&x.length>0?e("div",{className:"flex flex-col gap-3 max-h-[35vh] overflow-y-auto custom-scrollbar pr-2",children:x.map((t,r)=>e(b,{article:t,label:String.fromCharCode(65+r)},r))}):g?e("div",{className:"article-content rounded-lg bg-amber-50 p-3 sm:p-4 text-sm leading-relaxed text-gray-700 max-h-[35vh] overflow-y-auto pr-2 custom-scrollbar shadow-inner",dangerouslySetInnerHTML:{__html:g}}):null]}),f&&e("div",{className:"p-3 sm:px-6",children:e(u,{example:f})}),e("div",{className:"p-3 sm:pb-6 sm:px-6 "+(v?"grid grid-cols-1 md:grid-cols-2 gap-6":"flex flex-col gap-4"),children:w.map(t=>{const r=N[t.id],n=Array.isArray(r)?r:"string"==typeof r&&r?[r]:[];return e(p,{id:`question-${t.id}`,question:t,selectedOptionIds:n,onSelect:e=>{const r=t.multipleAnswers?n.includes(e)?n.filter(t=>t!==e):[...n,e]:e;y(t.id,r)},isReviewMode:v},t.id)})})]})}
@@ -1 +1 @@
1
- import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ResolvedImage as r}from"../../../../../components/common/ResolvedImage";import a from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import l from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import s from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";import n from"../../../../../components/themes/cambridge-yle/CambridgeYleExampleText";export function MoversListenAndWriteRenderer({partNumber:o,partName:i,questionCount:c,instruction:m,imageUrl:d,audioUrl:p,title:x,exampleText:b,exampleHighlight:g,questions:h,answers:u,onAnswerChange:f,isReviewMode:N=!1,showHints:v=!1,groupContent:w,groupTitle:y}){const C=e=>{if(N){const t=h.find(t=>t.id===e),r=u[e]?.trim().toLowerCase(),a=t?.correctAnswer?.trim().toLowerCase();return r===a?"#22c55e":"#ef4444"}return"#9ca3af"},A=!!(p||d||w||b);return t("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar relative",children:[t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-4",children:[e(a,{partNumber:o,questionCount:c,partName:i,isReviewMode:N}),e(l,{instruction:m,hasExample:!!b,isReviewMode:N})]}),A&&t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-0 sticky top-0 z-20 bg-white/95 backdrop-blur pb-2 shadow-sm border-b border-gray-100",children:[p&&e(s,{audioSrc:p,compact:!0}),t("div",{className:"max-h-[35vh] overflow-y-auto custom-scrollbar pr-2 flex flex-col gap-3",children:[d&&e("div",{className:"flex items-center justify-center",children:e(r,{src:d,alt:"Shared illustration",className:"max-h-[200px] rounded-lg object-contain shadow-md"})}),w&&t("div",{className:"rounded-lg bg-amber-50 p-3 text-sm leading-relaxed text-gray-700 shadow-inner",children:[y&&e("h3",{className:"mb-2 font-semibold text-gray-800",children:y}),w]})]}),b&&e(n,{text:b,highlightedWord:g||""})]}),t("div",{className:"flex flex-col gap-4 p-3 sm:gap-6 sm:pb-6 sm:px-6",children:[e("h2",{className:"text-center text-3xl font-bold uppercase text-coral-500",children:x}),e("div",{className:"flex flex-col gap-4",children:h.map(a=>{const l=a.isExample||!1,s=N||l,n=l&&a.correctAnswer?a.correctAnswer:u[a.id]||"";return t("div",{id:`question-${a.id}`,className:"flex flex-col gap-2 "+(l?"rounded-lg border-2 border-orange-200 bg-orange-50 p-2":""),children:[a.imageUrl&&e("div",{className:"flex items-center justify-center",children:e(r,{src:a.imageUrl,alt:`Question ${a.questionNumber} illustration`,className:"max-h-[150px] rounded-lg object-contain shadow-md"})}),t("div",{className:"flex items-baseline gap-2",children:[e("span",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-sm font-bold text-white "+(l?"bg-orange-400":"bg-blue-500"),children:a.questionNumber}),t("div",{className:"flex-1 text-base leading-relaxed text-gray-800",children:[a.textBefore&&e("span",{className:"mr-2",children:a.textBefore}),e("span",{className:"inline-block min-w-[120px]",style:{borderBottom:`2px solid ${l?"#fb923c":C(a.id)}`},children:e("input",{type:"text",value:n,onChange:e=>f(a.id,e.target.value),disabled:s,placeholder:"",className:"w-full bg-transparent text-center outline-none "+(l?"font-semibold text-orange-700 disabled:opacity-100":"disabled:opacity-60"),style:{border:"none",boxShadow:"none",appearance:"none",WebkitAppearance:"none"}})}),a.textAfter&&e("span",{className:"ml-2",children:a.textAfter}),l&&e("span",{className:"ml-2 rounded bg-orange-100 px-2 py-0.5 text-xs font-semibold text-orange-600",children:"Example"}),a.verbHint&&t("span",{className:"ml-2 italic text-gray-500",children:["(",a.verbHint,")"]}),v&&a.hints&&a.hints.length>0&&!N&&e("div",{className:"mt-2 flex flex-wrap gap-2",children:a.hints.map((t,r)=>e("button",{type:"button",onClick:()=>f(a.id,t),className:"rounded-full border border-blue-300 bg-blue-50 px-3 py-1 text-sm text-blue-700 transition-colors hover:bg-blue-100",children:t},r))}),N&&a.correctAnswer&&e("div",{className:"mt-1 text-sm",children:t("span",{className:"font-semibold text-green-600",children:["Đáp án đúng: ",a.correctAnswer]})})]})]})]},a.id)})})]})]})}
1
+ import{jsx as e,jsxs as r}from"react/jsx-runtime";import{ResolvedImage as t}from"../../../../../components/common/ResolvedImage";import l from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import a from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import s from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";import o from"../../../../../components/themes/cambridge-yle/CambridgeYleExampleText";export function MoversListenAndWriteRenderer({partNumber:i,partName:n,questionCount:d,instruction:m,imageUrl:c,audioUrl:b,title:x,exampleText:p,exampleHighlight:u,questions:h,answers:g,onAnswerChange:f,isReviewMode:w=!1,showHints:N=!1,groupContent:v,groupTitle:y}){const C=!!(b||c||v||p);return r("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar relative",children:[r("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-4",children:[e(l,{partNumber:i,questionCount:d,partName:n,isReviewMode:w}),e(a,{instruction:m,hasExample:!!p,isReviewMode:w})]}),C&&r("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-0 sticky top-0 z-20 bg-white/95 backdrop-blur pb-2 shadow-sm border-b border-gray-100",children:[b&&e(s,{audioSrc:b,compact:!0}),r("div",{className:"max-h-[35vh] overflow-y-auto custom-scrollbar pr-2 flex flex-col gap-3",children:[c&&e("div",{className:"flex items-center justify-center",children:e(t,{src:c,alt:"Shared illustration",className:"max-h-[200px] rounded-lg object-contain shadow-md"})}),v&&r("div",{className:"rounded-lg bg-amber-50 p-3 text-sm leading-relaxed text-gray-700 shadow-inner",children:[y&&e("h3",{className:"mb-2 font-semibold text-gray-800",children:y}),v]})]}),p&&e(o,{text:p,highlightedWord:u||""})]}),r("div",{className:"flex flex-col gap-4 p-3 sm:gap-6 sm:pb-6 sm:px-6",children:[x&&e("h2",{className:"text-center text-3xl font-bold uppercase text-coral-500",children:x}),e("div",{className:"flex flex-col gap-6 sm:gap-8",children:h.map(l=>{const a=l.isExample||!1,o=w||a,i=a&&l.correctAnswer?l.correctAnswer:g[l.id]||"",n=(g[l.id]||"").trim().toLowerCase(),d=(l.correctAnswer||"").trim().toLowerCase(),m=!!n,c=w&&!a&&m&&n===d,b=w&&!a&&m&&n!==d;return r("div",{id:`question-${l.id}`,className:"flex flex-col gap-4 rounded-2xl border p-4 sm:p-6 transition-all duration-200 hover:shadow-md "+(a?"border-amber-300 bg-amber-50/60 shadow-xs":"border-slate-200/80 bg-white shadow-xs hover:border-blue-300"),children:[r("div",{className:"flex items-center justify-between border-b border-slate-100 pb-3",children:[r("div",{className:"flex items-center gap-2",children:[e("span",{className:"inline-flex items-center justify-center rounded-full px-3.5 py-1 text-sm font-bold text-white shadow-xs "+(a?"bg-amber-500":w?c?"bg-emerald-500":b?"bg-rose-500":"bg-[#001590]":"bg-[#001590]"),children:a?"Ví dụ":`${l.questionNumber}.`}),a&&e("span",{className:"rounded-md bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700",children:"Example"})]}),w&&!a&&e("div",{children:e("span",c?{className:"inline-flex items-center gap-1 rounded-full bg-emerald-50 px-2.5 py-0.5 text-xs font-semibold text-emerald-700 border border-emerald-200",children:"✓ Đúng"}:b?{className:"inline-flex items-center gap-1 rounded-full bg-rose-50 px-2.5 py-0.5 text-xs font-semibold text-rose-700 border border-rose-200",children:"✕ Chưa đúng"}:{className:"inline-flex items-center gap-1 rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-medium text-slate-600",children:"Chưa trả lời"})})]}),l.audioUrl&&e("div",{className:"w-full my-1",children:e(s,{audioSrc:l.audioUrl,compact:!0,dense:!0})}),l.imageUrl&&e("div",{className:"flex items-center justify-center rounded-xl bg-slate-50/80 p-3 border border-slate-100 max-w-md mx-auto w-full",children:e(t,{src:l.imageUrl,alt:`Question ${l.questionNumber} illustration`,className:"max-h-[200px] sm:max-h-[220px] w-auto rounded-lg object-contain drop-shadow-xs"})}),r("div",{className:"flex flex-wrap items-baseline gap-2 text-base sm:text-lg leading-relaxed text-slate-800 pt-1",children:[l.textBefore&&e("span",{className:"font-medium",children:l.textBefore}),e("span",{className:"inline-block min-w-[140px] sm:min-w-[160px]",children:e("input",{type:"text",value:i,onChange:e=>f(l.id,e.target.value),disabled:o,placeholder:a?"":"...",className:`w-full rounded-lg border-2 px-3 py-1.5 text-center font-semibold outline-none transition-all ${a?"border-amber-300 bg-amber-100/50 text-amber-800 disabled:opacity-100":w?c?"border-emerald-400 bg-emerald-50 text-emerald-800":"border-rose-400 bg-rose-50 text-rose-800":"border-blue-200 bg-blue-50/30 text-blue-900 focus:border-blue-500 focus:bg-white focus:ring-4 focus:ring-blue-100"} ${o?"disabled:cursor-not-allowed disabled:opacity-75":""}`})}),l.textAfter&&e("span",{className:"font-medium",children:l.textAfter}),l.verbHint&&r("span",{className:"italic text-slate-500 font-normal",children:["(",l.verbHint,")"]})]}),N&&l.hints&&l.hints.length>0&&!w&&r("div",{className:"flex flex-wrap items-center gap-2 pt-2 border-t border-slate-100",children:[e("span",{className:"text-xs font-medium text-slate-500",children:"Gợi ý:"}),l.hints.map((r,t)=>e("button",{type:"button",onClick:()=>f(l.id,r),className:"rounded-full border border-blue-200 bg-blue-50 px-3 py-1 text-xs font-semibold text-blue-700 shadow-2xs transition-all hover:bg-blue-600 hover:text-white hover:border-blue-600 active:scale-95",children:r},t))]}),w&&!a&&l.correctAnswer&&e("div",{className:"mt-1 rounded-lg bg-emerald-50/80 p-2.5 text-sm border border-emerald-200/80",children:r("span",{className:"font-semibold text-emerald-800",children:["Đáp án đúng: ",e("span",{className:"font-bold underline",children:l.correctAnswer})]})})]},l.id)})})]})]})}
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t,useEffect as n,useMemo as a,useCallback as s,memo as l}from"react";import{ResolvedImage as i}from"../../../../../components/common/ResolvedImage";import o from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import c from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";import{cn as m}from"../../../../../shared/lib/utils";function u(e,r){const t=e.trim();return r?t:t.toLowerCase()}const p={},b=l(function({question:s,initialAnswers:l,onBlankChange:o,isReviewMode:d}){const c=t(null),p=t(o);p.current=o;const b=t(!1),g=t(l),x=a(()=>s.content.replace(/\{([\w\d]+)\}/g,(e,r)=>`\n <span class="structured-blank-wrapper" data-blank-wrapper="${r}">\n <input type="text" data-blank-key="${r}" placeholder="" class="structured-blank-input" />\n <span class="structured-blank-hint" data-blank-hint="${r}"></span>\n </span>\n `),[s.content]);return n(()=>{const e=c.current;if(!e)return;b.current||(e.innerHTML=x,b.current=!0);const r=e.querySelectorAll("input.structured-blank-input"),t=[];return r.forEach(r=>{const n=r.getAttribute("data-blank-key")||"",a=e.querySelector(`[data-blank-wrapper="${n}"]`),l=e.querySelector(`[data-blank-hint="${n}"]`),i=g.current[n]||"",o=s.answers[n]||"",c=i.trim().length>0,b=c&&u(i,s.caseSensitive)===u(o,s.caseSensitive),x=d&&!b&&o.trim().length>0;r.value=x?`${c?i:"(trống)"} → ${o}`:i,r.disabled=d,r.className=m("structured-blank-input inline-block min-w-[90px] max-w-[200px] rounded-t border-b-2 bg-slate-50/40 px-2 py-0.5 text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",d?b?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":c?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800 font-bold":"border-slate-400 text-slate-800 focus:border-teal-600"),a&&(a.className=m("structured-blank-wrapper inline-flex items-baseline align-baseline mx-1 my-0.5",d&&"mx-1 my-0.5")),l&&(l.textContent="",l.className="structured-blank-hint hidden");const h=e=>{const r=e.target;p.current(n,r.value)};r.addEventListener("input",h),t.push({el:r,handler:h})}),()=>{t.forEach(({el:e,handler:r})=>{e.removeEventListener("input",r)})}},[x,d]),r("div",{className:"space-y-3",children:[s.title&&e("h3",{className:"text-base font-semibold text-gray-800",children:s.title}),s.imageUrl&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200",children:e(i,{src:s.imageUrl,alt:s.title||"Question image",className:"max-h-48 w-auto object-contain"})})}),e("div",{ref:c,className:"structured-form-content rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 text-sm leading-relaxed text-gray-800 [&_.structured-blank-wrapper]:inline-flex [&_.structured-blank-wrapper]:align-baseline [&_.structured-blank-wrapper]:items-baseline [&_.structured-blank-hint]:max-w-[180px]"}),d&&r("div",{className:"space-y-2 rounded-lg border border-slate-200 bg-slate-50 p-3",children:[r("div",{className:"flex flex-wrap gap-2 text-[11px] font-medium",children:[r("span",{className:"inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-1 text-green-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-green-500"}),"Đúng"]}),r("span",{className:"inline-flex items-center gap-1 rounded-full bg-red-100 px-2 py-1 text-red-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-red-500"}),"Sai"]}),r("span",{className:"inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-1 text-amber-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-amber-500"}),"Chưa trả lời"]})]}),r("div",{children:[e("span",{className:"text-[10px] font-medium uppercase tracking-wider text-slate-500",children:"Đáp án đúng"}),e("div",{className:"mt-1 flex flex-wrap gap-1.5",children:Object.entries(s.answers).map(([e,t])=>r("span",{className:"rounded border border-green-300 bg-green-100 px-2 py-0.5 text-xs font-semibold text-green-800",children:[e,": ",t]},e))})]})]})]})});export function MoversWordFillStructuredFormRenderer({partNumber:n,partName:l,questionCount:m,instruction:u,audioUrl:g,groupTitle:x,groupContent:h,groupImageUrl:f,questions:w,answers:v,onAnswerChange:N,isReviewMode:y=!1}){const k=t(v);k.current=v;const C=t(N);C.current=N;const q=s((e,r,t)=>{const n=k.current[e]||{};C.current(e,{...n,[r]:t})},[]),j=a(()=>{const e={};return w.forEach(r=>{e[r.id]=(e,t)=>q(r.id,e,t)}),e},[w,q]);return r("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar",children:[r("div",{className:"flex flex-col gap-4 p-3 sm:px-6 sm:pt-2",children:[e(o,{partNumber:n,questionCount:m,partName:l,isReviewMode:y}),e(d,{instruction:u,isReviewMode:y}),x&&e("h2",{className:"text-center text-xl font-bold uppercase text-coral-500",children:x}),g&&e(c,{audioSrc:g,compact:!0}),f&&e("div",{className:"flex items-center justify-center",children:e(i,{src:f,alt:x||"Group image",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),h&&e("div",{className:"article-content rounded-lg bg-amber-50 p-4 text-sm leading-relaxed text-gray-700 max-h-[500px] overflow-y-auto pr-2 custom-scrollbar",dangerouslySetInnerHTML:{__html:h}})]}),w.length>0&&e("div",{className:"px-3 sm:px-6",children:e("hr",{className:"border-t border-slate-200/60 my-1"})}),e("div",{className:"grid grid-cols-1 gap-4 sm:gap-5 p-3 sm:px-6 sm:pb-6",children:w.map(t=>r("div",{id:`question-${t.id}`,className:"rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition-all hover:border-indigo-200 hover:shadow-md",children:[e("div",{className:"mb-2 flex items-center gap-2",children:e("span",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-xs font-bold text-white shadow-sm",children:t.questionNumber})}),e(b,{question:t,initialAnswers:v[t.id]||p,onBlankChange:j[t.id],isReviewMode:y})]},t.id))})]})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as r,useEffect as n,useMemo as a,useCallback as l,memo as s}from"react";import{ResolvedImage as i}from"../../../../../components/common/ResolvedImage";import o from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import c from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";import{cn as u}from"../../../../../shared/lib/utils";function m(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function p(e,t){return e.replace(/\{([\w\d]+)\}/g,(e,r)=>{const n=function(e,t){if(!e?.length)return null;const r=Number(t),n=Number.isFinite(r)&&r>=0&&r<e.length?e[r]:null;return null!=n&&"options"in n&&n.options?.length&&n.options.length>=2?n.options:null}(t,r);if(n){return`\n <span class="structured-blank-wrapper" data-blank-wrapper="${r}">\n <select data-blank-key="${r}" class="structured-blank-input structured-blank-select">${['<option value="">—</option>',...n.map(e=>`<option value="${m(e)}">${m(e)}</option>`)].join("")}</select>\n <span class="structured-blank-hint" data-blank-hint="${r}"></span>\n </span>\n `}return`\n <span class="structured-blank-wrapper" data-blank-wrapper="${r}">\n <input type="text" data-blank-key="${r}" placeholder="" class="structured-blank-input" />\n <span class="structured-blank-hint" data-blank-hint="${r}"></span>\n </span>\n `})}function b(e,t){const r=e.trim();return t?r:r.toLowerCase()}const g={},x=s(function({question:l,initialAnswers:s,onBlankChange:o,isReviewMode:d}){const c=r(null),m=r(o);m.current=o;const g=r(!1),x=r(s),h=a(()=>p(l.content,l.wordBank),[l.content,l.wordBank]);return n(()=>{const e=c.current;if(!e)return;g.current||(e.innerHTML=h,g.current=!0);const t=e.querySelectorAll("input.structured-blank-input, select.structured-blank-input"),r=[];return t.forEach(t=>{const n=t.getAttribute("data-blank-key")||"",a=e.querySelector(`[data-blank-wrapper="${n}"]`),s=e.querySelector(`[data-blank-hint="${n}"]`),i=x.current[n]||"",o=l.answers[n]||"",c=i.trim().length>0,p=c&&b(i,l.caseSensitive)===b(o,l.caseSensitive),g=d&&!p&&o.trim().length>0;t.value=g?`${c?i:"(trống)"} → ${o}`:i,t.disabled=d;const h="SELECT"===t.tagName,f=l.blankStyle||"word",w="letter"===f,y="phrase"===f;w&&!h&&"maxLength"in t&&(t.maxLength=Math.max(1,(o||" ").trim().length||1)),t.className=u("structured-blank-input inline-block bg-slate-50/40 text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",h?"min-w-[5.5rem] max-w-[10rem] rounded border px-2 py-1 text-center":w?"h-7 w-7 rounded border-2 px-0 text-center uppercase":y?"min-w-[9rem] max-w-[18rem] w-[14ch] rounded-t border-b-2 px-2 py-0.5 text-center":"min-w-[4.5rem] max-w-[7.5rem] w-[8ch] rounded-t border-b-2 px-1 py-0.5 text-center",d?p?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":c?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800 font-bold":"border-slate-400 text-slate-800 focus:border-teal-600"),h||w||!("size"in t)||(t.size=y?18:10),w&&(t.style.display="inline-block",t.style.width="1.75rem",t.style.height="1.75rem",t.style.minWidth="1.75rem",t.style.maxWidth="1.75rem",t.style.padding="0",t.style.margin="0 2px",t.style.borderWidth="2px",t.style.borderStyle="solid",t.style.borderRadius="4px",t.style.textAlign="center",t.style.fontWeight="700",t.style.textTransform="uppercase",t.style.boxSizing="border-box",t.style.verticalAlign="baseline"),a&&(a.className=u("structured-blank-wrapper inline-flex items-baseline align-baseline mx-1 my-0.5",d&&"mx-1 my-0.5")),s&&(s.textContent="",s.className="structured-blank-hint hidden");const k=e=>{const t=e.target;m.current(n,t.value)},v=h?"change":"input";t.addEventListener(v,k),r.push({el:t,handler:k,eventName:v})}),()=>{r.forEach(({el:e,handler:t,eventName:r})=>{e.removeEventListener(r,t)})}},[h,d]),t("div",{className:"space-y-3",children:[l.title&&e("h3",{className:"text-base font-semibold text-gray-800",children:l.title}),l.imageUrl&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200",children:e(i,{src:l.imageUrl,alt:l.title||"Question image",className:"max-h-48 w-auto object-contain"})})}),e("div",{ref:c,className:u("structured-form-content rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 text-sm leading-relaxed text-gray-800","[&_.structured-blank-wrapper]:inline-flex [&_.structured-blank-wrapper]:align-baseline [&_.structured-blank-wrapper]:items-baseline [&_.structured-blank-hint]:max-w-[180px]","[&_table.dialogue]:w-full [&_table.dialogue]:border-collapse [&_table.dialogue]:table-fixed","[&_table.dialogue_td.speaker]:w-[1%] [&_table.dialogue_td.speaker]:whitespace-nowrap","[&_table.dialogue_td.speaker]:align-top [&_table.dialogue_td.speaker]:pr-3","[&_table.dialogue_td.speaker]:font-semibold [&_table.dialogue_td.speaker]:text-slate-900","[&_table.dialogue_td]:align-top [&_table.dialogue_td]:py-1.5")}),d&&t("div",{className:"space-y-2 rounded-lg border border-slate-200 bg-slate-50 p-3",children:[t("div",{className:"flex flex-wrap gap-2 text-[11px] font-medium",children:[t("span",{className:"inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-1 text-green-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-green-500"}),"Đúng"]}),t("span",{className:"inline-flex items-center gap-1 rounded-full bg-red-100 px-2 py-1 text-red-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-red-500"}),"Sai"]}),t("span",{className:"inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-1 text-amber-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-amber-500"}),"Chưa trả lời"]})]}),t("div",{children:[e("span",{className:"text-[10px] font-medium uppercase tracking-wider text-slate-500",children:"Đáp án đúng"}),e("div",{className:"mt-1 flex flex-wrap gap-1.5",children:Object.entries(l.answers).map(([e,r])=>t("span",{className:"rounded border border-green-300 bg-green-100 px-2 py-0.5 text-xs font-semibold text-green-800",children:[e,": ",r]},e))})]})]})]})});export function MoversWordFillStructuredFormRenderer({partNumber:n,partName:s,questionCount:u,instruction:m,audioUrl:p,groupTitle:b,groupContent:h,groupImageUrl:f,questions:w,answers:y,onAnswerChange:k,isReviewMode:v=!1}){const N=r(y);N.current=y;const _=r(k);_.current=k;const $=l((e,t,r)=>{const n=N.current[e]||{};_.current(e,{...n,[t]:r})},[]),C=a(()=>{const e={};return w.forEach(t=>{e[t.id]=(e,r)=>$(t.id,e,r)}),e},[w,$]);return t("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar",children:[t("div",{className:"flex flex-col gap-4 p-3 sm:px-6 sm:pt-2",children:[e(o,{partNumber:n,questionCount:u,partName:s,isReviewMode:v}),e(d,{instruction:m,isReviewMode:v}),b&&e("h2",{className:"text-center text-xl font-bold uppercase text-coral-500",children:b}),p&&e(c,{audioSrc:p,compact:!0}),f&&e("div",{className:"flex items-center justify-center",children:e(i,{src:f,alt:b||"Group image",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),h&&e("div",{className:"article-content rounded-lg bg-amber-50 p-4 text-sm leading-relaxed text-gray-700 max-h-[500px] overflow-y-auto pr-2 custom-scrollbar",dangerouslySetInnerHTML:{__html:h}})]}),w.length>0&&e("div",{className:"px-3 sm:px-6",children:e("hr",{className:"border-t border-slate-200/60 my-1"})}),e("div",{className:"grid grid-cols-1 gap-4 sm:gap-5 p-3 sm:px-6 sm:pb-6",children:w.map(r=>t("div",{id:`question-${r.id}`,className:"rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition-all hover:border-indigo-200 hover:shadow-md",children:[e("div",{className:"mb-2 flex items-center gap-2",children:e("span",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-xs font-bold text-white shadow-sm",children:r.questionNumber})}),e(x,{question:r,initialAnswers:y[r.id]||g,onBlankChange:C[r.id],isReviewMode:v})]},r.id))})]})}
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{useMemo as s}from"react";import{ResolvedImage as r}from"../../../../../components/common/ResolvedImage";import l from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import{HtmlOrText as i}from"../../../../../shared/lib/html-content";export function MoversWritingRenderer({partNumber:n,partName:c,instruction:m,prompt:o,requirements:x,minWords:b,maxWords:p,answer:h,onAnswerChange:g,isReviewMode:u=!1,guideImages:f,teacherScore:N,maxScore:v,teacherFeedback:w,writingAssessment:y}){const F=s(()=>h&&h.trim()?h.trim().split(/\s+/).filter(Boolean).length:0,[h]);return t("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar relative",children:[t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-4",children:[e(l,{partNumber:n,questionCount:1,partName:c,isReviewMode:u}),m&&e(d,{instruction:m,isReviewMode:u})]}),e("div",{className:"flex flex-col gap-4 p-3 sm:px-6 sm:pt-0 sticky top-0 z-20 bg-white/95 backdrop-blur pb-2 shadow-sm border-b border-gray-100",children:t("div",{className:"flex flex-col gap-4",children:[e("div",{className:"rounded-2xl border border-slate-200 bg-white p-5 shadow-sm",children:t("div",{className:"flex flex-col gap-4 text-base leading-relaxed text-gray-800",children:[t("div",{className:"space-y-3",children:[e("p",{className:"text-lg font-semibold text-slate-900",children:"Writing task"}),e(i,{value:o,className:"text-base leading-relaxed text-gray-800"})]}),x&&x.length>0&&t("div",{className:"rounded-xl bg-slate-50 p-4",children:[e("p",{className:"text-sm font-bold uppercase tracking-wide text-slate-700",children:"Include"}),e("ul",{className:"mt-3 space-y-2",children:x.map((a,s)=>t("li",{className:"flex items-start gap-2 text-sm text-slate-700",children:[e("span",{className:"mt-1.5 h-2 w-2 rounded-full bg-blue-500"}),e(i,{value:a,as:"span"})]},s))})]}),(b||p)&&t("div",{className:"flex flex-wrap gap-2",children:[b&&t("div",{className:"inline-flex items-center rounded-full border border-blue-200 bg-blue-50 px-3 py-1 text-sm font-medium text-blue-700",children:["Minimum: ",b," words"]}),p&&t("div",{className:"inline-flex items-center rounded-full border border-slate-200 bg-white px-3 py-1 text-sm font-medium text-slate-600",children:["Suggested max: ",p," words"]})]})]})}),f&&f.length>0&&t("div",{className:"rounded-2xl border border-slate-200 bg-white p-4 shadow-sm",children:[t("div",{className:"mb-3 flex items-center justify-between gap-3",children:[e("p",{className:"text-sm font-semibold uppercase tracking-wide text-slate-600",children:"Story pictures"}),e("p",{className:"text-xs text-slate-400",children:"Read the sequence from left to right"})]}),e("div",{className:"grid gap-4 "+(1===f.length?"grid-cols-1":"grid-cols-1 sm:grid-cols-2"),children:f.map((a,s)=>t("div",{className:"rounded-xl border border-slate-200 bg-slate-50/80 p-2 shadow-sm",children:[e(r,{src:a,alt:`Picture ${s+1}`,className:"w-full rounded-lg bg-white object-contain "+(1===f.length?"max-h-[400px]":"aspect-[4/3]")}),e("div",{className:"pt-2 text-center text-[13px] font-medium text-slate-500",children:f.length>1?`Picture ${s+1}`:"Reference Image"})]},s))})]})]})}),t("div",{className:"flex flex-col gap-3 p-3 sm:px-6 sm:pb-6",children:[t("div",{className:"rounded-2xl border border-slate-200 bg-white shadow-sm",children:[t("div",{className:"flex items-center justify-between gap-3 border-b border-slate-100 px-5 py-3 bg-slate-50/50 rounded-t-2xl",children:[t("div",{children:[e("p",{className:"text-sm font-semibold uppercase tracking-wide text-slate-500",children:"Your answer"}),e("p",{className:"text-xs text-slate-400",children:"Write clearly and organise your ideas in order."})]}),t("div",{className:"rounded-full px-3 py-1 text-sm font-semibold "+(b&&F<b?"border border-amber-200 bg-amber-50 text-amber-700":"border border-emerald-200 bg-emerald-50 text-emerald-700"),children:[F," words"]})]}),t("div",{className:"p-4",children:[e("textarea",{id:"writing-answer",name:"writing-answer",value:h,onChange:e=>{const t=e.target.value;if(p){const e=Number(p);if((t.trim()?t.trim().split(/\s+/).filter(Boolean).length:0)>e&&t.length>=(h?.length||0))return}g(t)},disabled:u,placeholder:"Write your answer here...",className:"min-h-[340px] w-full resize-y rounded-xl border border-slate-200 bg-slate-50/50 p-4 text-base leading-8 text-gray-800 outline-none transition focus:border-blue-400 focus:bg-white focus:ring-2 focus:ring-blue-100 disabled:bg-gray-50 disabled:text-gray-700"}),t("div",{className:"mt-3 flex flex-wrap items-center justify-between gap-3 text-sm",children:[e("p",{className:"text-slate-500",children:b||p?t(a,{children:[b&&t("span",{children:["Minimum required: ",t("span",{className:"font-semibold text-slate-700",children:[b," words"]})]}),b&&p&&e("span",{className:"mx-2 text-slate-300",children:"|"}),p&&t("span",{children:["Maximum allowed: ",t("span",{className:"font-semibold text-slate-700",children:[p," words"]})]})]}):"Write a complete answer."}),t("p",{className:"font-medium "+(p&&F>=p?"text-red-500":"text-slate-500"),children:["Word count: ",F," ",p&&`/ ${p}`]})]})]})]}),u&&t("div",{className:"space-y-3",children:[y&&y.criteria?.length>0&&t("div",{className:"rounded-2xl border border-[#00AEEF]/30 bg-gradient-to-b from-[#F4F9FF] to-white p-4 space-y-3 shadow-xs",children:[t("div",{className:"flex flex-wrap items-center justify-between gap-2 border-b border-[#00AEEF]/20 pb-2.5",children:[t("div",{className:"flex items-center gap-2",children:[e("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-[#0054A6] text-xs font-bold text-white",children:"AI"}),t("span",{className:"font-bold text-[#0054A6] text-sm sm:text-base",children:["Đánh giá 4 tiêu chí IELTS Writing (","TASK_2"===y.taskType?"Task 2":"Task 1",")"]})]}),t("div",{className:"flex items-center gap-1.5 rounded-lg bg-[#EAF6FF] border border-[#00AEEF]/30 px-3 py-1",children:[e("span",{className:"text-xs font-semibold text-[#0054A6]",children:"Overall Band:"}),e("span",{className:"text-sm sm:text-base font-extrabold text-[#0054A6]",children:y.overallBand.toFixed(1)}),e("span",{className:"text-xs text-slate-400",children:"/ 9.0"})]})]}),e("div",{className:"grid gap-3 sm:grid-cols-2",children:y.criteria.map(a=>{const s=Math.max(0,Math.min(100,a.band/9*100)),r=a.band>=8?"bg-emerald-500":a.band>=6.5?"bg-[#00AEEF]":a.band>=5?"bg-amber-500":"bg-rose-500";return t("div",{className:"rounded-xl border border-slate-200 bg-white p-3 space-y-2 shadow-2xs",children:[t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("span",{className:"font-bold text-[#0054A6] bg-[#EAF6FF] px-2 py-0.5 rounded border border-[#00AEEF]/20 text-xs",children:a.code}),e("span",{className:"font-semibold text-slate-800 text-xs sm:text-sm",children:a.label})]}),t("span",{className:"font-extrabold text-[#0054A6] bg-slate-100 px-2 py-0.5 rounded text-xs sm:text-sm",children:["Band ",a.band.toFixed(1)]})]}),e("div",{className:"h-2 w-full rounded-full bg-slate-100 overflow-hidden",children:e("div",{className:`h-full rounded-full transition-all duration-500 ${r}`,style:{width:`${s}%`}})}),a.evidence&&e("p",{className:"text-xs text-slate-600 leading-relaxed pt-0.5",children:a.evidence})]},a.code)})}),(y.feedback?.vi||y.feedback?.en)&&t("div",{className:"rounded-xl bg-[#F4F9FF] border border-[#00AEEF]/20 p-3 text-xs sm:text-sm text-slate-700 space-y-1.5",children:[e("span",{className:"font-bold text-[#0054A6] block",children:"Nhận xét từ giám khảo AI:"}),y.feedback.vi&&e("p",{className:"leading-relaxed",children:y.feedback.vi}),y.feedback.en&&e("p",{className:"text-slate-500 italic border-t border-[#00AEEF]/10 pt-1.5 mt-1.5",children:y.feedback.en})]})]}),null!=N?t("div",{className:"rounded-2xl border border-green-200 bg-green-50 p-4",children:[e("div",{className:"mb-2 flex items-center gap-2",children:t("span",{className:"text-lg font-semibold text-green-700",children:["Điểm: ",N,"/",v]})}),w&&t("div",{className:"text-sm text-gray-700",children:[e("span",{className:"font-medium",children:"Nhận xét:"})," ",w]})]}):y?null:e("div",{className:"rounded-2xl border border-amber-200 bg-amber-50 p-4",children:t("div",{className:"flex items-center gap-2",children:[e("svg",{className:"h-5 w-5 shrink-0 text-amber-500",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"})}),e("span",{className:"text-sm font-medium text-amber-700",children:"Bài viết đang chờ giáo viên chấm điểm"})]})})]})]})]})}
1
+ "use client";import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{useMemo as s}from"react";import{ResolvedImage as r}from"../../../../../components/common/ResolvedImage";import l from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import{HtmlOrText as i}from"../../../../../shared/lib/html-content";export function MoversWritingRenderer({partNumber:n,partName:c,instruction:o,prompt:m,requirements:x,minWords:b,maxWords:p,answer:h,onAnswerChange:g,isReviewMode:u=!1,guideImages:f,teacherScore:N,maxScore:v,teacherFeedback:w,writingAssessment:y}){const F=s(()=>h&&h.trim()?h.trim().split(/\s+/).filter(Boolean).length:0,[h]);return t("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar relative",children:[t("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-4",children:[e(l,{partNumber:n,questionCount:1,partName:c,isReviewMode:u}),o&&e(d,{instruction:o,isReviewMode:u})]}),e("div",{className:"flex flex-col gap-4 p-3 sm:px-6 sm:pt-0 sticky top-0 z-20 bg-white/95 backdrop-blur pb-2 shadow-sm border-b border-gray-100",children:t("div",{className:"flex flex-col gap-4",children:[e("div",{className:"rounded-2xl border border-slate-200 bg-white p-5 shadow-sm",children:t("div",{className:"flex flex-col gap-4 text-base leading-relaxed text-gray-800",children:[t("div",{className:"space-y-3",children:[e("p",{className:"text-lg font-semibold text-slate-900",children:"Writing task"}),e(i,{value:m,className:"text-base leading-relaxed text-gray-800"})]}),x&&x.length>0&&t("div",{className:"rounded-xl bg-slate-50 p-4",children:[e("p",{className:"text-sm font-bold uppercase tracking-wide text-slate-700",children:"Include"}),e("ul",{className:"mt-3 space-y-2",children:x.map((a,s)=>t("li",{className:"flex items-start gap-2 text-sm text-slate-700",children:[e("span",{className:"mt-1.5 h-2 w-2 rounded-full bg-blue-500"}),e(i,{value:a,as:"span"})]},s))})]}),(b||p)&&t("div",{className:"flex flex-wrap gap-2",children:[b&&t("div",{className:"inline-flex items-center rounded-full border border-blue-200 bg-blue-50 px-3 py-1 text-sm font-medium text-blue-700",children:["Minimum: ",b," words"]}),p&&t("div",{className:"inline-flex items-center rounded-full border border-slate-200 bg-white px-3 py-1 text-sm font-medium text-slate-600",children:["Suggested max: ",p," words"]})]})]})}),f&&f.length>0&&t("div",{className:"rounded-2xl border border-slate-200 bg-white p-4 shadow-sm",children:[t("div",{className:"mb-3 flex items-center justify-between gap-3",children:[e("p",{className:"text-sm font-semibold uppercase tracking-wide text-slate-600",children:"Story pictures"}),e("p",{className:"text-xs text-slate-400",children:"Read the sequence from left to right"})]}),e("div",{className:"grid gap-4 "+(1===f.length?"grid-cols-1":"grid-cols-1 sm:grid-cols-2"),children:f.map((a,s)=>t("div",{className:"rounded-xl border border-slate-200 bg-slate-50/80 p-2 shadow-sm",children:[e(r,{src:a,alt:`Picture ${s+1}`,className:"w-full rounded-lg bg-white object-contain "+(1===f.length?"max-h-[400px]":"aspect-[4/3]")}),e("div",{className:"pt-2 text-center text-[13px] font-medium text-slate-500",children:f.length>1?`Picture ${s+1}`:"Reference Image"})]},s))})]})]})}),t("div",{className:"flex flex-col gap-3 p-3 sm:px-6 sm:pb-6",children:[t("div",{className:"rounded-2xl border border-slate-200 bg-white shadow-sm",children:[t("div",{className:"flex items-center justify-between gap-3 border-b border-slate-100 px-5 py-3 bg-slate-50/50 rounded-t-2xl",children:[t("div",{children:[e("p",{className:"text-sm font-semibold uppercase tracking-wide text-slate-500",children:"Your answer"}),e("p",{className:"text-xs text-slate-400",children:"Write clearly and organise your ideas in order."})]}),t("div",{className:"rounded-full px-3 py-1 text-sm font-semibold "+(b&&F<b?"border border-amber-200 bg-amber-50 text-amber-700":"border border-emerald-200 bg-emerald-50 text-emerald-700"),children:[F," words"]})]}),t("div",{className:"p-4",children:[e("textarea",{id:"writing-answer",name:"writing-answer",value:h,onChange:e=>{const t=e.target.value;if(p){const e=Number(p);if((t.trim()?t.trim().split(/\s+/).filter(Boolean).length:0)>e&&t.length>=(h?.length||0))return}g(t)},disabled:u,placeholder:"Write your answer here...",className:"min-h-[340px] w-full resize-y rounded-xl border border-slate-200 bg-slate-50/50 p-4 text-base leading-8 text-gray-800 outline-none transition focus:border-blue-400 focus:bg-white focus:ring-2 focus:ring-blue-100 disabled:bg-gray-50 disabled:text-gray-700"}),t("div",{className:"mt-3 flex flex-wrap items-center justify-between gap-3 text-sm",children:[e("p",{className:"text-slate-500",children:b||p?t(a,{children:[b&&t("span",{children:["Minimum required: ",t("span",{className:"font-semibold text-slate-700",children:[b," words"]})]}),b&&p&&e("span",{className:"mx-2 text-slate-300",children:"|"}),p&&t("span",{children:["Maximum allowed: ",t("span",{className:"font-semibold text-slate-700",children:[p," words"]})]})]}):"Write a complete answer."}),t("p",{className:"font-medium "+(p&&F>=p?"text-red-500":"text-slate-500"),children:["Word count: ",F," ",p&&`/ ${p}`]})]})]})]}),u&&t("div",{className:"space-y-3",children:[y&&y.criteria?.length>0&&(()=>{const a="ADULT_EMAIL"===y.taskType,s=a?10:9,r=a?"Đánh giá 4 tiêu chí Writing (Adult Placement)":`Đánh giá 4 tiêu chí IELTS Writing (${"TASK_2"===y.taskType?"Task 2":"Task 1"})`,l=a?"Total:":"Overall Band:",d=a?`${y.overallBand}`:y.overallBand.toFixed(1);return t("div",{className:"rounded-2xl border border-[#00AEEF]/30 bg-gradient-to-b from-[#F4F9FF] to-white p-4 space-y-3 shadow-xs",children:[t("div",{className:"flex flex-wrap items-center justify-between gap-2 border-b border-[#00AEEF]/20 pb-2.5",children:[t("div",{className:"flex items-center gap-2",children:[e("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-[#0054A6] text-xs font-bold text-white",children:"AI"}),e("span",{className:"font-bold text-[#0054A6] text-sm sm:text-base",children:r})]}),t("div",{className:"flex items-center gap-1.5 rounded-lg bg-[#EAF6FF] border border-[#00AEEF]/30 px-3 py-1",children:[e("span",{className:"text-xs font-semibold text-[#0054A6]",children:l}),e("span",{className:"text-sm sm:text-base font-extrabold text-[#0054A6]",children:d}),t("span",{className:"text-xs text-slate-400",children:["/ ",s]})]})]}),e("div",{className:"grid gap-3 sm:grid-cols-2",children:y.criteria.map(s=>{const r=s.maxPoints??(a?2:9),l=r>0?Math.max(0,Math.min(100,s.band/r*100)):0,d=l>=85?"bg-emerald-500":l>=65?"bg-[#00AEEF]":l>=45?"bg-amber-500":"bg-rose-500",i=a?`${s.band} / ${r}`:`Band ${s.band.toFixed(1)}`;return t("div",{className:"rounded-xl border border-slate-200 bg-white p-3 space-y-2 shadow-2xs",children:[t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("span",{className:"font-bold text-[#0054A6] bg-[#EAF6FF] px-2 py-0.5 rounded border border-[#00AEEF]/20 text-xs",children:s.code}),e("span",{className:"font-semibold text-slate-800 text-xs sm:text-sm",children:s.label})]}),e("span",{className:"font-extrabold text-[#0054A6] bg-slate-100 px-2 py-0.5 rounded text-xs sm:text-sm",children:i})]}),e("div",{className:"h-2 w-full rounded-full bg-slate-100 overflow-hidden",children:e("div",{className:`h-full rounded-full transition-all duration-500 ${d}`,style:{width:`${l}%`}})}),s.evidence&&e("p",{className:"text-xs text-slate-600 leading-relaxed pt-0.5",children:s.evidence})]},s.code)})}),(y.feedback?.vi||y.feedback?.en)&&t("div",{className:"rounded-xl bg-[#F4F9FF] border border-[#00AEEF]/20 p-3 text-xs sm:text-sm text-slate-700 space-y-1.5",children:[e("span",{className:"font-bold text-[#0054A6] block",children:"Nhận xét từ giám khảo AI:"}),y.feedback.vi&&e("p",{className:"leading-relaxed",children:y.feedback.vi}),y.feedback.en&&e("p",{className:"text-slate-500 italic border-t border-[#00AEEF]/10 pt-1.5 mt-1.5",children:y.feedback.en})]})]})})(),null!=N?t("div",{className:"rounded-2xl border border-green-200 bg-green-50 p-4",children:[e("div",{className:"mb-2 flex items-center gap-2",children:t("span",{className:"text-lg font-semibold text-green-700",children:["Điểm: ",N,"/",v]})}),w&&t("div",{className:"text-sm text-gray-700",children:[e("span",{className:"font-medium",children:"Nhận xét:"})," ",w]})]}):y?null:e("div",{className:"rounded-2xl border border-amber-200 bg-amber-50 p-4",children:t("div",{className:"flex items-center gap-2",children:[e("svg",{className:"h-5 w-5 shrink-0 text-amber-500",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"})}),e("span",{className:"text-sm font-medium text-amber-700",children:"Bài viết đang chờ giáo viên chấm điểm"})]})})]})]})]})}
@@ -11,6 +11,13 @@ export interface TemplateSection {
11
11
  viewMode?: string;
12
12
  wordBankConfig?: string[];
13
13
  }
14
+ export type WfsfBlankStyle = 'letter' | 'word' | 'phrase';
15
+ export type WfsfWordBankEntry = {
16
+ options: string[];
17
+ } | {
18
+ word: string;
19
+ isExample?: boolean;
20
+ } | null;
14
21
  export interface WordFillStructuredFormQuestion {
15
22
  id: string;
16
23
  content: string;
@@ -20,6 +27,9 @@ export interface WordFillStructuredFormQuestion {
20
27
  caseSensitive?: boolean;
21
28
  imageUrl?: string;
22
29
  questionNumber?: number;
30
+ blankStyle?: WfsfBlankStyle;
31
+ viewMode?: string;
32
+ wordBank?: WfsfWordBankEntry[];
23
33
  }
24
34
  export interface ExamTakingPart {
25
35
  id: string;
@@ -110,6 +120,8 @@ export declare function transformWordFillStructuredForm(questions: ExamTakingQue
110
120
  groupContent: string;
111
121
  groupImageUrl: string;
112
122
  questions: {
123
+ wordBank?: WfsfWordBankEntry[];
124
+ viewMode?: string;
113
125
  id: string;
114
126
  title: string;
115
127
  content: string;
@@ -117,5 +129,6 @@ export declare function transformWordFillStructuredForm(questions: ExamTakingQue
117
129
  caseSensitive: boolean;
118
130
  imageUrl: string;
119
131
  questionNumber: number;
132
+ blankStyle: WfsfBlankStyle;
120
133
  }[];
121
134
  };
@@ -1 +1 @@
1
- export function unwrap(t){return function(t){return"object"==typeof t&&null!==t&&"data"in t}(t)?t.data:t}export function unwrapAttempt(t){return"attempt"in t?t.attempt:t}export function groupQuestions(t){const n=t.examTemplate?.parts??[],e=new Map;for(const n of t.questions){const t=n.part_id??String(n.part_no??"default");e.set(t,[...e.get(t)??[],n])}return Array.from(e.entries()).map(([t,e])=>{const r=n.find(n=>String(n.id)===t);return{id:t,name:r?.name??`Part ${e[0]?.part_no??t}`,template:r,partId:t,partNo:e[0]?.part_no,instructions:r?.instructions,audioUrl:r?.audioUrl,questionType:r?.questionType??e[0]?.question_type,isGroup:r?.isGroup,group:r?.group,sections:r?.sections,viewMode:"string"==typeof r?.group?.viewMode?r.group.viewMode:void 0,startIndex:r?.startIndex,endIndex:r?.endIndex,questions:e.sort((t,n)=>t.question_number-n.question_number)}})}export function transformAnswerTheQuestion(t){return{instruction:n(t[0]?.content,"instruction"),imageUrl:n(t[0]?.content,"imageUrl"),title:n(t[0]?.content,"title"),groupContent:n(t[0]?.content,"groupContent"),questions:t.map(t=>({id:t.id,questionNumber:t.question_number,question:n(t.content,"question")||n(t.content,"text"),imageUrl:n(t.content,"imageUrl"),prefixText:n(t.content,"prefixText"),suffixText:n(t.content,"suffixText"),exampleAnswer:n(t.content,"exampleAnswer"),suggestedAnswers:r(t.content,"suggestedAnswers"),isExample:Boolean(t.is_example)}))}}export function transformMatchByWritingAnswer(t){return{instruction:n(t[0]?.content,"instruction"),groupImageUrl:n(t[0]?.content,"groupImageUrl"),options:o(t[0]?.content,"options").map(t=>({answer:String(t.answer??t.value??""),text:String(t.text??t.label??""),content:String(t.content??t.text??t.label??""),imageUrl:"string"==typeof t.imageUrl?t.imageUrl:void 0})),questions:t.map(t=>({id:t.id,questionNumber:t.question_number,question:n(t.content,"question")||n(t.content,"text"),imageUrl:n(t.content,"imageUrl"),exampleAnswer:n(t.content,"exampleAnswer"),isExample:Boolean(t.is_example)}))}}export function transformWordFillParagraph(t){const e=t[0];return{questionId:e?.id??"",paragraph:n(e?.content,"paragraph")||n(e?.content,"text"),title:n(e?.content,"title"),imageUrl:n(e?.content,"imageUrl"),wordBank:o(e?.content,"wordBank").map(t=>({word:String(t.word??t.text??""),isExample:Boolean(t.isExample)})),viewMode:n(e?.content,"viewMode"),instruction:n(e?.content,"instruction")}}export function transformWriteShortLetter(o){if(!o)return{prompt:"",requirements:[],minWords:void 0,maxWords:void 0,guideImages:[]};const i=t(o.content),s=t(i.instructions),u="string"==typeof s.mainInstruction?s.mainInstruction:"",a=Array.isArray(i.informations)?i.informations.filter(t=>"string"==typeof t):[],c=r(o.content,"guideImages"),p=e(o.content,"minWords"),m=e(o.content,"maxWords");if(u||a.length)return{prompt:u,requirements:a,minWords:p,maxWords:m,guideImages:c};return{prompt:(Array.isArray(i.contexts)?i.contexts:[]).map(t).filter(t=>"text"===t.type&&"string"==typeof t.text).map(t=>String(t.text)).join("\n")||n(o.content,"prompt")||n(o.content,"question"),requirements:r(o.content,"requirements"),minWords:p,maxWords:m,guideImages:c}}export function transformWordFillStructuredForm(e){return{instruction:n(e[0]?.content,"instruction"),groupTitle:n(e[0]?.content,"groupTitle"),groupContent:n(e[0]?.content,"groupContent"),groupImageUrl:n(e[0]?.content,"groupImageUrl"),questions:e.map(e=>({id:e.id,title:n(e.content,"title"),content:n(e.content,"content")||n(e.content,"html")||n(e.content,"text"),answers:i(e.content,"answers"),caseSensitive:Boolean(t(e.content).caseSensitive),imageUrl:n(e.content,"imageUrl"),questionNumber:e.question_number}))}}function t(t){return"object"==typeof t&&null!==t?t:{}}function n(n,e){const r=t(n)[e];return"string"==typeof r?r:""}function e(n,e){const r=t(n)[e];return"number"==typeof r?r:void 0}function r(n,e){const r=t(n)[e];return Array.isArray(r)?r.filter(t=>"string"==typeof t):[]}function o(n,e){const r=t(n)[e];return Array.isArray(r)?r.map(t):[]}function i(n,e){const r=t(n)[e],o=t(r);return Object.fromEntries(Object.entries(o).map(([t,n])=>[t,String(n??"")]))}
1
+ export function unwrap(t){return function(t){return"object"==typeof t&&null!==t&&"data"in t}(t)?t.data:t}export function unwrapAttempt(t){return"attempt"in t?t.attempt:t}export function groupQuestions(t){const n=t.examTemplate?.parts??[],e=new Map;for(const n of t.questions){const t=n.part_id??String(n.part_no??"default");e.set(t,[...e.get(t)??[],n])}return Array.from(e.entries()).map(([t,e])=>{const r=n.find(n=>String(n.id)===t);return{id:t,name:r?.name??`Part ${e[0]?.part_no??t}`,template:r,partId:t,partNo:e[0]?.part_no,instructions:r?.instructions,audioUrl:r?.audioUrl,questionType:r?.questionType??e[0]?.question_type,isGroup:r?.isGroup,group:r?.group,sections:r?.sections,viewMode:"string"==typeof r?.group?.viewMode?r.group.viewMode:void 0,startIndex:r?.startIndex,endIndex:r?.endIndex,questions:e.sort((t,n)=>t.question_number-n.question_number)}})}export function transformAnswerTheQuestion(t){return{instruction:n(t[0]?.content,"instruction"),imageUrl:n(t[0]?.content,"imageUrl"),title:n(t[0]?.content,"title"),groupContent:n(t[0]?.content,"groupContent"),questions:t.map(t=>({id:t.id,questionNumber:t.question_number,question:n(t.content,"question")||n(t.content,"text"),imageUrl:n(t.content,"imageUrl"),prefixText:n(t.content,"prefixText"),suffixText:n(t.content,"suffixText"),exampleAnswer:n(t.content,"exampleAnswer"),suggestedAnswers:r(t.content,"suggestedAnswers"),isExample:Boolean(t.is_example)}))}}export function transformMatchByWritingAnswer(t){return{instruction:n(t[0]?.content,"instruction"),groupImageUrl:n(t[0]?.content,"groupImageUrl"),options:o(t[0]?.content,"options").map(t=>({answer:String(t.answer??t.value??""),text:String(t.text??t.label??""),content:String(t.content??t.text??t.label??""),imageUrl:"string"==typeof t.imageUrl?t.imageUrl:void 0})),questions:t.map(t=>({id:t.id,questionNumber:t.question_number,question:n(t.content,"question")||n(t.content,"text"),imageUrl:n(t.content,"imageUrl"),exampleAnswer:n(t.content,"exampleAnswer"),isExample:Boolean(t.is_example)}))}}export function transformWordFillParagraph(t){const e=t[0];return{questionId:e?.id??"",paragraph:n(e?.content,"paragraph")||n(e?.content,"text"),title:n(e?.content,"title"),imageUrl:n(e?.content,"imageUrl"),wordBank:o(e?.content,"wordBank").map(t=>({word:String(t.word??t.text??""),isExample:Boolean(t.isExample)})),viewMode:n(e?.content,"viewMode"),instruction:n(e?.content,"instruction")}}export function transformWriteShortLetter(o){if(!o)return{prompt:"",requirements:[],minWords:void 0,maxWords:void 0,guideImages:[]};const i=t(o.content),s=t(i.instructions),a="string"==typeof s.mainInstruction?s.mainInstruction:"",u=Array.isArray(i.informations)?i.informations.filter(t=>"string"==typeof t):[],c=r(o.content,"guideImages"),p=e(o.content,"minWords"),m=e(o.content,"maxWords");if(a||u.length)return{prompt:a,requirements:u,minWords:p,maxWords:m,guideImages:c};return{prompt:(Array.isArray(i.contexts)?i.contexts:[]).map(t).filter(t=>"text"===t.type&&"string"==typeof t.text).map(t=>String(t.text)).join("\n")||n(o.content,"prompt")||n(o.content,"question"),requirements:r(o.content,"requirements"),minWords:p,maxWords:m,guideImages:c}}export function transformWordFillStructuredForm(e){const r=e[0],o=t=>{const n=String(t||"").toLowerCase();return"letter"===n||"box"===n?"letter":"phrase"===n||"sentence"===n?"phrase":"word"};return{instruction:n(r?.content,"instruction"),groupTitle:n(r?.content,"groupTitle"),groupContent:n(r?.content,"groupContent"),groupImageUrl:n(r?.content,"groupImageUrl"),questions:e.map(e=>{const r=e.content,s=e,a=s.correct_answer??s.answer,u=i(a,"answers"),c=Object.keys(u).length>0?u:i(r,"answers"),p=t(r),m=(t=>{if(!Array.isArray(t)||0===t.length)return;const n=t.map(t=>{if(null==t)return null;if("object"!=typeof t||Array.isArray(t))return null;const n=t;if(Array.isArray(n.options)){const t=n.options.map(t=>String(t??"").trim()).filter(t=>t.length>0);return t.length>=2?{options:t}:null}const e=String(n.word??"").trim();return e?n.isExample?{word:e,isExample:!0}:{word:e}:null});return n.some(t=>null!=t)?n:void 0})(p.wordBank),l=Boolean(m?.some(t=>null!=t&&"options"in t&&Array.isArray(t.options))),g=Boolean(m?.some(t=>null!=t&&"word"in t&&t.word)),d=n(r,"viewMode")||(l?"WITH_OPTIONS":g?"WITH_WORD_BANK":void 0);return{id:e.id,title:n(r,"title"),content:n(r,"content")||n(r,"html")||n(r,"text"),answers:c,caseSensitive:Boolean(t(a).caseSensitive??p.caseSensitive),imageUrl:n(r,"imageUrl"),questionNumber:e.question_number,blankStyle:o(n(r,"blankStyle")),...d?{viewMode:d}:{},...m?{wordBank:m}:{}}})}}function t(t){return"object"==typeof t&&null!==t?t:{}}function n(n,e){const r=t(n)[e];return"string"==typeof r?r:""}function e(n,e){const r=t(n)[e];return"number"==typeof r?r:void 0}function r(n,e){const r=t(n)[e];return Array.isArray(r)?r.filter(t=>"string"==typeof t):[]}function o(n,e){const r=t(n)[e];return Array.isArray(r)?r.map(t):[]}function i(n,e){const r=t(n)[e],o=t(r);return Object.fromEntries(Object.entries(o).map(([t,n])=>[t,String(n??"")]))}
@@ -10,6 +10,7 @@ export interface FillBlankQuestion {
10
10
  verbHint?: string;
11
11
  correctAnswer?: string;
12
12
  imageUrl?: string;
13
+ audioUrl?: string;
13
14
  hints?: string[];
14
15
  isExample?: boolean;
15
16
  }
@@ -123,6 +124,7 @@ export interface ChooseBestAnswerQuestion {
123
124
  correctOptionId?: string;
124
125
  correctOptionIds?: string[];
125
126
  imageUrl?: string;
127
+ audioUrl?: string;
126
128
  optionType?: 'text' | 'image';
127
129
  multipleAnswers?: boolean;
128
130
  explanation?: string;
@@ -134,6 +136,7 @@ export interface ChooseBestAnswerExample {
134
136
  correctOptionId: string;
135
137
  optionType?: 'text' | 'image';
136
138
  imageUrl?: string;
139
+ audioUrl?: string;
137
140
  }
138
141
  export interface MoversChooseBestAnswerRendererProps {
139
142
  partNumber: number;
@@ -140,7 +140,9 @@ export declare function transformWriteCorrectVerbForm(questions: ApiQuestion[]):
140
140
  title: string;
141
141
  instruction: string;
142
142
  };
143
- export declare function transformChooseCorrectAnswer(questions: ApiQuestion[]): {
143
+ export declare function transformChooseCorrectAnswer(questions: ApiQuestion[], optionsConfig?: {
144
+ shuffleOptions?: boolean;
145
+ }): {
144
146
  questions: ChooseBestAnswerQuestion[];
145
147
  example?: import('../types').ChooseBestAnswerExample;
146
148
  imageUrl?: string;
@@ -313,6 +315,13 @@ export declare function transformWriteSentences(questions: ApiQuestion[]): {
313
315
  title?: string;
314
316
  groupContent?: string;
315
317
  };
318
+ export type WfsfBlankStyle = 'letter' | 'word' | 'phrase';
319
+ export type WfsfWordBankEntry = {
320
+ options: string[];
321
+ } | {
322
+ word: string;
323
+ isExample?: boolean;
324
+ } | null;
316
325
  export interface WordFillStructuredFormQuestion {
317
326
  id: string;
318
327
  questionNumber: number;
@@ -321,6 +330,12 @@ export interface WordFillStructuredFormQuestion {
321
330
  content: string;
322
331
  answers: Record<string, string>;
323
332
  caseSensitive: boolean;
333
+ /** letter = square boxes; word / phrase = underline width */
334
+ blankStyle: WfsfBlankStyle;
335
+ /** WITH_OPTIONS when choice-inline options present (plan 0024). */
336
+ viewMode?: string;
337
+ /** Index i ↔ blank key "i" (or order of {n} appearance). */
338
+ wordBank?: WfsfWordBankEntry[];
324
339
  }
325
340
  export declare function transformWordFillStructuredForm(questions: ApiQuestion[]): {
326
341
  questions: WordFillStructuredFormQuestion[];