@tinyweb_dev/oe-exam-sdk 0.1.24 → 0.1.26

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.
@@ -1,12 +1,20 @@
1
1
  /** WFSF blank input width/shape (content.blankStyle). */
2
2
  export type WfsfBlankStyle = 'letter' | 'word' | 'phrase';
3
- /** Plan 0024 WITH_OPTIONS: { options }. Plan 0025 WITH_WORD_BANK: { word }. null = free-type slot (OPTIONS only). */
4
- export type WfsfWordBankEntry = {
5
- options: string[];
6
- } | {
3
+ /**
4
+ * WORD_FILL_STRUCTURED_FORM creator / client contracts.
5
+ * wordBank + viewMode: plan 0024 (WITH_OPTIONS) + 0025 (WITH_WORD_BANK).
6
+ */
7
+ export type WfsfViewMode = 'WITH_OPTIONS' | 'WITH_WORD_BANK';
8
+ /** Shared suggestion box (plan 0025). */
9
+ export type WfsfSharedWordBankEntry = {
7
10
  word: string;
8
11
  isExample?: boolean;
12
+ };
13
+ /** Per-blank dropdown options (plan 0024). `null` = free-type slot. */
14
+ export type WfsfOptionsWordBankEntry = {
15
+ options: string[];
9
16
  } | null;
17
+ export type WfsfWordBankEntry = WfsfSharedWordBankEntry | WfsfOptionsWordBankEntry;
10
18
  export interface WordFillStructuredFormData {
11
19
  title?: string;
12
20
  imageUrl?: string;
@@ -26,6 +34,8 @@ export interface WordFillStructuredFormData {
26
34
  /** OPTIONS: index i ↔ {i}; WORD_BANK: shared list of { word }. */
27
35
  wordBank?: WfsfWordBankEntry[];
28
36
  points?: number;
37
+ /** Student rubric / page instruction (shown in client header). */
38
+ instruction?: string;
29
39
  explanation?: string;
30
40
  caseSensitive?: boolean;
31
41
  groupTitle?: string;
@@ -46,17 +56,15 @@ export interface WordFillStructuredFormCreatorProps {
46
56
  triggerValidation: () => Promise<boolean>;
47
57
  getFormData: () => WordFillStructuredFormData;
48
58
  } | null>;
49
- /** Group config from exam template (e.g. [{ number: 1, questionCount: 5 }]) */
50
59
  groups?: Array<{
51
60
  number: number;
52
61
  questionCount: number;
53
62
  }>;
54
- /** Zero-based index of this question within the current part */
55
63
  questionIndexInPart?: number;
56
- /** Part ID for shared group state across questions in the same group */
57
64
  partId?: string;
58
- /** Group type from exam template (e.g. "BASIC", "DOCUMENT") */
59
65
  groupType?: string;
66
+ /** Template / host viewMode override (Pearson edit dialog). */
67
+ viewMode?: WfsfViewMode | 'DEFAULT' | 'CONVERSATION' | 'EMAIL' | null;
60
68
  }
61
69
  export interface WordFillStructuredFormClientProps {
62
70
  questionData: WordFillStructuredFormData;
@@ -64,7 +72,8 @@ export interface WordFillStructuredFormClientProps {
64
72
  userAnswer?: Record<string, string>;
65
73
  onAnswerChange?: (questionNumber: string, value: string) => void;
66
74
  autoFillCorrectAnswers?: boolean;
67
- /** Student-facing rubric (Pearson instruction maps here). */
75
+ /** Page rubric prefer over hard-coded type label in header. */
76
+ instruction?: string;
68
77
  explanation?: string;
69
78
  /** Exercise / question number shown above instruction (e.g. Bài 1). */
70
79
  questionNumber?: number;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import{useState as s,useEffect as a,useMemo as l}from"react";import{Check as n,X as i,CircleHelp as d,Lightbulb as o,ClipboardList as c,Users as m,BookOpen as g}from"lucide-react";import{usePresignedFileUrl as h}from"../../../../shared/lib/hooks";import{cn as b}from"../../../../shared/lib/utils";function p({src:r,alt:t,className:s}){const{previewUrl:a,isLoading:l}=h(r);return l?e("div",{className:"flex h-16 w-16 items-center justify-center",children:e("div",{className:"h-4 w-4 animate-spin rounded-full border-2 border-gray-200 border-t-blue-500"})}):e("img",{src:a||r,alt:t,className:b("max-h-20 w-auto object-contain rounded-md",s),onError:e=>{const r=e.target;r.onerror=null,r.src="/images/placeholder-image.svg"}})}export function ChooseTheCorrectAnswerClient({questionData:x,onSubmit:u,isReviewMode:f=!1,userAnswer:N,questionConfig:w}){const v=Boolean(x.multipleAnswers||Array.isArray(x.correctAnswer)),y=l(()=>Array.isArray(x.correctAnswer)?x.correctAnswer:-1===x.correctAnswer?[]:[x.correctAnswer],[x.correctAnswer]),j=l(()=>void 0===N?[]:Array.isArray(N)?N:-1===N?[]:[N],[N]),[C,k]=s(j),{previewUrl:A,isLoading:U}=h(x.imageUrl||"");a(()=>{k(j)},[j]);const S=C.length>0,T=f&&C.length===y.length&&C.every(e=>y.includes(e)),$=e=>0===e.length?"(Chưa trả lời)":e.map(e=>{const r=String.fromCharCode(65+e);return"image"===x.optionType?`Đáp án ${r}`:`${r}. ${x.options[e]}`}).join(", "),q=!w||0===w.length||w.includes("question"),I=!w||0===w.length||w.includes("imageUrl")||w.includes("image");console.log("questionConfig",w),console.log("showImage",I);const B=!w||0===w.length||w.includes("options"),G=x.imageUrl?.trim()?x.imageUrl:"",L=Array.isArray(x.articles)&&x.articles.length>0;return r("div",{className:"space-y-4",children:[x.isGroup&&(x.groupTitle||x.groupSubtitle||x.groupContent||x.groupImageUrl||L)&&r("div",{className:"relative overflow-hidden rounded-2xl border border-indigo-100 bg-white shadow-sm ring-1 ring-black/5 transition-all",children:[e("div",{className:"absolute inset-0 bg-gradient-to-br from-indigo-50/80 via-white to-blue-50/30 opacity-70 pointer-events-none"}),e("div",{className:"absolute left-0 top-0 bottom-0 w-1.5 bg-gradient-to-b from-indigo-500 to-blue-600 rounded-l-2xl"}),r("div",{className:"relative p-6 sm:p-7",children:[r("div",{className:"flex items-start gap-4 mb-5 border-b border-indigo-50/60 pb-4",children:[e("div",{className:"flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-100 to-blue-100 shadow-inner ring-1 ring-indigo-200/50",children:e(g,{className:"h-5 w-5 text-indigo-600"})}),r("div",{className:"flex-1 pt-1",children:[e("div",{className:"flex items-center gap-2 mb-1",children:x.groupNumber&&r("span",{className:"inline-flex items-center rounded-md bg-indigo-50 px-2 py-1 text-xs font-semibold text-indigo-700 ring-1 ring-inset ring-indigo-700/10",children:["Bài đọc ",x.groupNumber]})}),x.groupTitle&&e("h4",{className:"text-xl font-bold text-slate-800 leading-tight tracking-tight",children:x.groupTitle}),x.groupSubtitle&&e("p",{className:"mt-1 text-sm text-slate-500 leading-snug",children:x.groupSubtitle})]})]}),L&&e("div",{className:"flex flex-col gap-4",children:x.articles.map((t,s)=>r("div",{className:"group relative overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm transition-all hover:shadow-md hover:border-indigo-200",children:[r("div",{className:"flex items-center gap-2 border-b border-slate-100 bg-gradient-to-r from-indigo-50/60 to-blue-50/40 px-4 py-2.5",children:[e("div",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-blue-600 text-xs font-bold text-white shadow-sm",children:String.fromCharCode(65+s)}),e("h5",{className:"flex-1 truncate text-sm font-bold text-slate-800",children:t.name||`Bài viết ${s+1}`})]}),r("div",{className:"flex flex-col gap-3 p-4 sm:flex-row sm:items-start",children:[t.imageUrl&&e("div",{className:"flex flex-shrink-0 items-center justify-center rounded-md bg-slate-50 p-2 sm:w-40",children:e(p,{src:t.imageUrl,alt:t.name||`Article ${s+1}`,className:"max-h-40 w-auto rounded-md object-contain"})}),t.content&&e("div",{className:"prose prose-sm prose-slate max-w-none flex-1 text-[14px] leading-relaxed text-slate-700",children:t.content.split("\n").map((r,t)=>e("p",{className:"mb-2 last:mb-0",children:r},t))})]})]},s))}),!L&&r("div",{className:"space-y-6 text-slate-700",children:[x.groupContent&&e("div",{className:"prose prose-slate prose-p:leading-relaxed prose-p:mb-3 max-w-none text-[15px]",dangerouslySetInnerHTML:{__html:x.groupContent.replace(/\{(\d+)\}/g,(e,r)=>`<span style="display:inline-block;min-width:100px;padding:1px 5px;margin:0 5px;background:#eef2ff;border-bottom:2px solid #6366f1;color:#4f46e5;font-weight:700;font-size:0.85em;text-align:center;vertical-align:baseline;letter-spacing:2px">———(${Number(r)+1})———</span>`).split("\n").map(e=>`<p class="last:mb-0">${e}</p>`).join("")}}),x.groupImageUrl&&r("div",{className:"mt-5 relative overflow-hidden rounded-xl border border-slate-200/60 bg-slate-50 shadow-sm transition-all hover:shadow-md",children:[e("div",{className:"flex items-center justify-between border-b border-slate-200/60 bg-slate-100/50 px-4 py-2",children:r("div",{className:"flex items-center gap-1.5",children:[e("div",{className:"h-2.5 w-2.5 rounded-full bg-slate-300"}),e("div",{className:"h-2.5 w-2.5 rounded-full bg-slate-300"}),e("div",{className:"h-2.5 w-2.5 rounded-full bg-slate-300"}),e("span",{className:"ml-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500",children:"Tài liệu đính kèm"})]})}),e("div",{className:"flex items-center justify-center p-4 min-h-[160px]",children:e(p,{src:x.groupImageUrl,alt:x.groupTitle||"Group Image Attachment",className:"max-h-72 w-auto object-contain drop-shadow-sm rounded-lg"})})]})]})]})]}),r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm",children:e(d,{className:"h-4 w-4"})}),e("span",{className:"text-sm font-medium text-gray-700",children:v?"Chọn các đáp án đúng":"Chọn đáp án đúng"})]}),f&&S&&r("div",{className:b("flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",T?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e(T?n:i,{className:"h-3.5 w-3.5"}),T?"Đúng":"Sai"]})]}),r("div",{className:"p-5 space-y-5",children:[(x.askerName||x.answererName)&&r("div",{className:"flex items-center gap-4 rounded-lg border border-purple-100 bg-gradient-to-r from-purple-50 to-violet-50 px-4 py-3",children:[e(m,{className:"h-4 w-4 flex-shrink-0 text-purple-500"}),r("div",{className:"flex items-center gap-4 text-sm",children:[x.askerName&&r("div",{className:"flex items-center gap-1.5",children:[e("span",{className:"font-medium text-gray-500",children:"Người hỏi:"}),e("span",{className:"font-semibold text-purple-700",children:x.askerName})]}),x.askerName&&x.answererName&&e("div",{className:"h-4 w-px bg-purple-200"}),x.answererName&&r("div",{className:"flex items-center gap-1.5",children:[e("span",{className:"font-medium text-gray-500",children:"Người trả lời:"}),e("span",{className:"font-semibold text-purple-700",children:x.answererName})]})]})]}),q&&e("div",{className:"rounded-lg bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-100",children:e("h3",{className:"text-base font-semibold text-gray-800 leading-relaxed",children:x.question||"Câu hỏi chưa được nhập"})}),I&&(A||G)&&r("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100",children:[e("div",{className:"border-b border-gray-100 px-3 py-1.5",children:r("div",{className:"flex items-center gap-1.5",children:[e("div",{className:"h-2 w-2 rounded-full bg-red-400"}),e("div",{className:"h-2 w-2 rounded-full bg-yellow-400"}),e("div",{className:"h-2 w-2 rounded-full bg-green-400"}),e("span",{className:"ml-2 text-xs font-medium text-gray-500",children:"Hình ảnh"})]})}),e("div",{className:"flex items-center justify-center p-4",children:U?e("div",{className:"flex h-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500"})}):e("img",{src:A||G,alt:"Question illustration",className:"max-h-56 w-auto object-contain rounded-md",onError:e=>{const r=e.target;r.onerror=null,r.src="/images/placeholder-image.svg"}})})]}),B&&r("div",{className:"space-y-2.5",children:[e("span",{className:"text-xs font-medium text-gray-500 uppercase tracking-wider",children:v?"Chọn một hoặc nhiều đáp án":"Chọn đáp án"}),x.options.map((t,s)=>{const a=C.includes(s),l=(d=s,!!f&&y.includes(d));var d;const o=String.fromCharCode(65+s);return r("button",{type:"button",onClick:()=>(e=>{if(f)return;const r=v?C.includes(e)?C.filter(r=>r!==e):[...C,e].sort((e,r)=>e-r):[e];k(r),u?.(v?r:r[0])})(s),disabled:f,className:b("group flex w-full items-center gap-3 rounded-lg border-2 p-3.5 text-left transition-all",f?l?"border-green-400 bg-green-50":a?"border-red-400 bg-red-50":"border-gray-200 bg-gray-50":a?"border-blue-500 bg-blue-50 shadow-sm ring-2 ring-blue-100":"border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm"),children:[e("div",{className:b("flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold transition-all",f?l?"bg-green-500 text-white":a?"bg-red-500 text-white":"bg-gray-200 text-gray-600":a?"bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm":"bg-gray-100 text-gray-600 group-hover:bg-gray-200"),children:o}),"image"===x.optionType?e("div",{className:"flex-1 flex items-center justify-center py-1",children:e(p,{src:t,alt:`Đáp án ${o}`})}):e("span",{className:b("flex-1 text-sm font-medium",f&&l?"text-green-800":f&&a&&!l?"text-red-800":"text-gray-700"),children:t}),f&&(l||a)&&e("div",{className:b("flex h-6 w-6 items-center justify-center rounded-full",l?"bg-green-500":"bg-red-500"),children:e(l?n:i,{className:"h-4 w-4 text-white"})})]},s)})]}),!f&&!S&&r("div",{className:"rounded-lg border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-3 flex items-center gap-2",children:[e("div",{className:"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white",children:e("span",{className:"text-xs font-bold",children:"!"})}),e("span",{className:"text-sm text-amber-800",children:v?"Vui lòng chọn ít nhất một đáp án":"Vui lòng chọn một đáp án"})]})]})]}),f&&x.explanation&&r("div",{className:"overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-br from-blue-50 to-indigo-50 shadow-sm",children:[r("div",{className:"flex items-center gap-2 border-b border-blue-100 bg-white/50 px-4 py-3",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm",children:e(o,{className:"h-4 w-4"})}),e("h4",{className:"text-sm font-semibold text-gray-800",children:"Giải thích"})]}),e("div",{className:"p-4",children:e("p",{className:"text-sm text-gray-700 leading-relaxed",children:x.explanation})})]}),f&&r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-4 py-3",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-slate-500 to-gray-600 text-white shadow-sm",children:e(c,{className:"h-4 w-4"})}),e("h4",{className:"text-sm font-semibold text-gray-800",children:"Tổng kết"})]}),r("div",{className:"p-4 space-y-3",children:[r("div",{className:"flex items-center justify-between rounded-lg bg-gray-50 p-3",children:[e("span",{className:"text-sm text-gray-600",children:"Đáp án của bạn:"}),e("span",{className:b("text-sm font-semibold",T?"text-green-600":S?"text-red-600":"text-gray-500"),children:$(C)})]}),r("div",{className:"flex items-center justify-between rounded-lg bg-green-50 p-3 border border-green-100",children:[e("span",{className:"text-sm text-gray-600",children:"Đáp án đúng:"}),e("span",{className:"text-sm font-semibold text-green-600",children:$(y)})]}),r("div",{className:b("flex items-center justify-between rounded-lg p-3 border",T?"bg-green-50 border-green-200":"bg-red-50 border-red-200"),children:[e("span",{className:"text-sm text-gray-600",children:"Kết quả:"}),e("div",{className:b("flex items-center gap-1.5 text-sm font-semibold",T?"text-green-600":"text-red-600"),children:r(t,T?{children:[e(n,{className:"h-4 w-4"}),e("span",{children:"Chính xác"})]}:{children:[e(i,{className:"h-4 w-4"}),e("span",{children:"Sai"})]})})]})]})]})]})}
1
+ "use client";import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import{useState as s,useEffect as a,useMemo as n}from"react";import{Check as l,X as i,CircleHelp as d,Lightbulb as o,ClipboardList as c,Users as m,BookOpen as g}from"lucide-react";import{usePresignedFileUrl as h}from"../../../../shared/lib/hooks";import{cn as b}from"../../../../shared/lib/utils";import{normalizeChooseAnswerCorrectAnswer as p}from"./normalize";function u({src:r,alt:t,className:s}){const{previewUrl:a,isLoading:n}=h(r);return n?e("div",{className:"flex h-16 w-16 items-center justify-center",children:e("div",{className:"h-4 w-4 animate-spin rounded-full border-2 border-gray-200 border-t-blue-500"})}):e("img",{src:a||r,alt:t,className:b("max-h-20 w-auto object-contain rounded-md",s),onError:e=>{const r=e.target;r.onerror=null,r.src="/images/placeholder-image.svg"}})}export function ChooseTheCorrectAnswerClient({questionData:x,onSubmit:f,isReviewMode:N=!1,userAnswer:w,questionConfig:v}){const y=Boolean(x.multipleAnswers||Array.isArray(x.correctAnswer)),j=n(()=>{const e=p(x.correctAnswer,x.options);return Array.isArray(e)?e:-1===e?[]:[e]},[x.correctAnswer,x.options]),C=n(()=>{if(null==w)return[];const e=p(w,x.options);return Array.isArray(e)?e:-1===e?[]:[e]},[w,x.options]),[k,A]=s(C),{previewUrl:U,isLoading:S}=h(x.imageUrl||"");a(()=>{A(C)},[C]);const T=k.length>0,$=N&&k.length===j.length&&k.every(e=>j.includes(e)),q=e=>0===e.length?"(Chưa trả lời)":e.map(e=>{const r=String.fromCharCode(65+e);return"image"===x.optionType?`Đáp án ${r}`:`${r}. ${x.options[e]}`}).join(", "),I=!v||0===v.length||v.includes("question"),B=!v||0===v.length||v.includes("imageUrl")||v.includes("image");console.log("questionConfig",v),console.log("showImage",B);const G=!v||0===v.length||v.includes("options"),L=x.imageUrl?.trim()?x.imageUrl:"",z=Array.isArray(x.articles)&&x.articles.length>0;return r("div",{className:"space-y-4",children:[x.isGroup&&(x.groupTitle||x.groupSubtitle||x.groupContent||x.groupImageUrl||z)&&r("div",{className:"relative overflow-hidden rounded-2xl border border-indigo-100 bg-white shadow-sm ring-1 ring-black/5 transition-all",children:[e("div",{className:"absolute inset-0 bg-gradient-to-br from-indigo-50/80 via-white to-blue-50/30 opacity-70 pointer-events-none"}),e("div",{className:"absolute left-0 top-0 bottom-0 w-1.5 bg-gradient-to-b from-indigo-500 to-blue-600 rounded-l-2xl"}),r("div",{className:"relative p-6 sm:p-7",children:[r("div",{className:"flex items-start gap-4 mb-5 border-b border-indigo-50/60 pb-4",children:[e("div",{className:"flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-100 to-blue-100 shadow-inner ring-1 ring-indigo-200/50",children:e(g,{className:"h-5 w-5 text-indigo-600"})}),r("div",{className:"flex-1 pt-1",children:[e("div",{className:"flex items-center gap-2 mb-1",children:x.groupNumber&&r("span",{className:"inline-flex items-center rounded-md bg-indigo-50 px-2 py-1 text-xs font-semibold text-indigo-700 ring-1 ring-inset ring-indigo-700/10",children:["Bài đọc ",x.groupNumber]})}),x.groupTitle&&e("h4",{className:"text-xl font-bold text-slate-800 leading-tight tracking-tight",children:x.groupTitle}),x.groupSubtitle&&e("p",{className:"mt-1 text-sm text-slate-500 leading-snug",children:x.groupSubtitle})]})]}),z&&e("div",{className:"flex flex-col gap-4",children:x.articles.map((t,s)=>r("div",{className:"group relative overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm transition-all hover:shadow-md hover:border-indigo-200",children:[r("div",{className:"flex items-center gap-2 border-b border-slate-100 bg-gradient-to-r from-indigo-50/60 to-blue-50/40 px-4 py-2.5",children:[e("div",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-blue-600 text-xs font-bold text-white shadow-sm",children:String.fromCharCode(65+s)}),e("h5",{className:"flex-1 truncate text-sm font-bold text-slate-800",children:t.name||`Bài viết ${s+1}`})]}),r("div",{className:"flex flex-col gap-3 p-4 sm:flex-row sm:items-start",children:[t.imageUrl&&e("div",{className:"flex flex-shrink-0 items-center justify-center rounded-md bg-slate-50 p-2 sm:w-40",children:e(u,{src:t.imageUrl,alt:t.name||`Article ${s+1}`,className:"max-h-40 w-auto rounded-md object-contain"})}),t.content&&e("div",{className:"prose prose-sm prose-slate max-w-none flex-1 text-[14px] leading-relaxed text-slate-700",children:t.content.split("\n").map((r,t)=>e("p",{className:"mb-2 last:mb-0",children:r},t))})]})]},s))}),!z&&r("div",{className:"space-y-6 text-slate-700",children:[x.groupContent&&e("div",{className:"prose prose-slate prose-p:leading-relaxed prose-p:mb-3 max-w-none text-[15px]",dangerouslySetInnerHTML:{__html:x.groupContent.replace(/\{(\d+)\}/g,(e,r)=>`<span style="display:inline-block;min-width:100px;padding:1px 5px;margin:0 5px;background:#eef2ff;border-bottom:2px solid #6366f1;color:#4f46e5;font-weight:700;font-size:0.85em;text-align:center;vertical-align:baseline;letter-spacing:2px">———(${Number(r)+1})———</span>`).split("\n").map(e=>`<p class="last:mb-0">${e}</p>`).join("")}}),x.groupImageUrl&&r("div",{className:"mt-5 relative overflow-hidden rounded-xl border border-slate-200/60 bg-slate-50 shadow-sm transition-all hover:shadow-md",children:[e("div",{className:"flex items-center justify-between border-b border-slate-200/60 bg-slate-100/50 px-4 py-2",children:r("div",{className:"flex items-center gap-1.5",children:[e("div",{className:"h-2.5 w-2.5 rounded-full bg-slate-300"}),e("div",{className:"h-2.5 w-2.5 rounded-full bg-slate-300"}),e("div",{className:"h-2.5 w-2.5 rounded-full bg-slate-300"}),e("span",{className:"ml-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500",children:"Tài liệu đính kèm"})]})}),e("div",{className:"flex items-center justify-center p-4 min-h-[160px]",children:e(u,{src:x.groupImageUrl,alt:x.groupTitle||"Group Image Attachment",className:"max-h-72 w-auto object-contain drop-shadow-sm rounded-lg"})})]})]})]})]}),r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm",children:e(d,{className:"h-4 w-4"})}),e("span",{className:"text-sm font-medium text-gray-700",children:y?"Chọn các đáp án đúng":"Chọn đáp án đúng"})]}),N&&T&&r("div",{className:b("flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",$?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e($?l:i,{className:"h-3.5 w-3.5"}),$?"Đúng":"Sai"]})]}),r("div",{className:"p-5 space-y-5",children:[(x.askerName||x.answererName)&&r("div",{className:"flex items-center gap-4 rounded-lg border border-purple-100 bg-gradient-to-r from-purple-50 to-violet-50 px-4 py-3",children:[e(m,{className:"h-4 w-4 flex-shrink-0 text-purple-500"}),r("div",{className:"flex items-center gap-4 text-sm",children:[x.askerName&&r("div",{className:"flex items-center gap-1.5",children:[e("span",{className:"font-medium text-gray-500",children:"Người hỏi:"}),e("span",{className:"font-semibold text-purple-700",children:x.askerName})]}),x.askerName&&x.answererName&&e("div",{className:"h-4 w-px bg-purple-200"}),x.answererName&&r("div",{className:"flex items-center gap-1.5",children:[e("span",{className:"font-medium text-gray-500",children:"Người trả lời:"}),e("span",{className:"font-semibold text-purple-700",children:x.answererName})]})]})]}),I&&e("div",{className:"rounded-lg bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-100",children:e("h3",{className:"text-base font-semibold text-gray-800 leading-relaxed",children:x.question||"Câu hỏi chưa được nhập"})}),B&&(U||L)&&r("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100",children:[e("div",{className:"border-b border-gray-100 px-3 py-1.5",children:r("div",{className:"flex items-center gap-1.5",children:[e("div",{className:"h-2 w-2 rounded-full bg-red-400"}),e("div",{className:"h-2 w-2 rounded-full bg-yellow-400"}),e("div",{className:"h-2 w-2 rounded-full bg-green-400"}),e("span",{className:"ml-2 text-xs font-medium text-gray-500",children:"Hình ảnh"})]})}),e("div",{className:"flex items-center justify-center p-4",children:S?e("div",{className:"flex h-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500"})}):e("img",{src:U||L,alt:"Question illustration",className:"max-h-56 w-auto object-contain rounded-md",onError:e=>{const r=e.target;r.onerror=null,r.src="/images/placeholder-image.svg"}})})]}),G&&r("div",{className:"space-y-2.5",children:[e("span",{className:"text-xs font-medium text-gray-500 uppercase tracking-wider",children:y?"Chọn một hoặc nhiều đáp án":"Chọn đáp án"}),x.options.map((t,s)=>{const a=k.includes(s),n=(d=s,!!N&&j.includes(d));var d;const o=String.fromCharCode(65+s);return r("button",{type:"button",onClick:()=>(e=>{if(N)return;const r=y?k.includes(e)?k.filter(r=>r!==e):[...k,e].sort((e,r)=>e-r):[e];A(r),f?.(y?r:r[0])})(s),disabled:N,className:b("group flex w-full items-center gap-3 rounded-lg border-2 p-3.5 text-left transition-all",N?n?"border-green-400 bg-green-50":a?"border-red-400 bg-red-50":"border-gray-200 bg-gray-50":a?"border-blue-500 bg-blue-50 shadow-sm ring-2 ring-blue-100":"border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm"),children:[e("div",{className:b("flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold transition-all",N?n?"bg-green-500 text-white":a?"bg-red-500 text-white":"bg-gray-200 text-gray-600":a?"bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm":"bg-gray-100 text-gray-600 group-hover:bg-gray-200"),children:o}),"image"===x.optionType?e("div",{className:"flex-1 flex items-center justify-center py-1",children:e(u,{src:t,alt:`Đáp án ${o}`})}):e("span",{className:b("flex-1 text-sm font-medium",N&&n?"text-green-800":N&&a&&!n?"text-red-800":"text-gray-700"),children:t}),N&&(n||a)&&e("div",{className:b("flex h-6 w-6 items-center justify-center rounded-full",n?"bg-green-500":"bg-red-500"),children:e(n?l:i,{className:"h-4 w-4 text-white"})})]},s)})]}),!N&&!T&&r("div",{className:"rounded-lg border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-3 flex items-center gap-2",children:[e("div",{className:"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white",children:e("span",{className:"text-xs font-bold",children:"!"})}),e("span",{className:"text-sm text-amber-800",children:y?"Vui lòng chọn ít nhất một đáp án":"Vui lòng chọn một đáp án"})]})]})]}),N&&x.explanation&&r("div",{className:"overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-br from-blue-50 to-indigo-50 shadow-sm",children:[r("div",{className:"flex items-center gap-2 border-b border-blue-100 bg-white/50 px-4 py-3",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm",children:e(o,{className:"h-4 w-4"})}),e("h4",{className:"text-sm font-semibold text-gray-800",children:"Giải thích"})]}),e("div",{className:"p-4",children:e("p",{className:"text-sm text-gray-700 leading-relaxed",children:x.explanation})})]}),N&&r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-4 py-3",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-slate-500 to-gray-600 text-white shadow-sm",children:e(c,{className:"h-4 w-4"})}),e("h4",{className:"text-sm font-semibold text-gray-800",children:"Tổng kết"})]}),r("div",{className:"p-4 space-y-3",children:[r("div",{className:"flex items-center justify-between rounded-lg bg-gray-50 p-3",children:[e("span",{className:"text-sm text-gray-600",children:"Đáp án của bạn:"}),e("span",{className:b("text-sm font-semibold",$?"text-green-600":T?"text-red-600":"text-gray-500"),children:q(k)})]}),r("div",{className:"flex items-center justify-between rounded-lg bg-green-50 p-3 border border-green-100",children:[e("span",{className:"text-sm text-gray-600",children:"Đáp án đúng:"}),e("span",{className:"text-sm font-semibold text-green-600",children:q(j)})]}),r("div",{className:b("flex items-center justify-between rounded-lg p-3 border",$?"bg-green-50 border-green-200":"bg-red-50 border-red-200"),children:[e("span",{className:"text-sm text-gray-600",children:"Kết quả:"}),e("div",{className:b("flex items-center gap-1.5 text-sm font-semibold",$?"text-green-600":"text-red-600"),children:r(t,$?{children:[e(l,{className:"h-4 w-4"}),e("span",{children:"Chính xác"})]}:{children:[e(i,{className:"h-4 w-4"}),e("span",{children:"Sai"})]})})]})]})]})]})}
@@ -1 +1 @@
1
- export const transformChooseTheCorrectAnswer=e=>{const r=e.answer;if(!r?.options||void 0===r.correctAnswer)return{apiContent:{},apiCorrectAnswer:{}};const t={question:r.question||"",optionType:r.optionType||"text",options:r.options,...r.multipleAnswers?{multipleAnswers:!0}:{}};r.imageUrl&&(t.imageUrl=r.imageUrl),r.askerName&&(t.askerName=r.askerName),r.answererName&&(t.answererName=r.answererName);const o=!!(r.groupTitle||r.groupSubtitle||r.groupContent||r.groupImageUrl||r.articles&&r.articles.length>0)?{title:r.groupTitle||"",subtitle:r.groupSubtitle||"",content:r.groupContent||"",imageUrl:r.groupImageUrl||"",...r.articles?{articles:r.articles}:{}}:void 0;return{apiContent:t,apiCorrectAnswer:{answer:r.correctAnswer,...r.multipleAnswers?{multipleAnswers:!0}:{}},isExample:r.isExample||!1,basicGroupData:o,groupNumber:r.groupNumber||1}};
1
+ export const transformChooseTheCorrectAnswer=e=>{const r=e.answer;if(!r?.options||void 0===r.correctAnswer)return{apiContent:{},apiCorrectAnswer:{}};const t={question:r.question||"",optionType:r.optionType||"text",options:r.options,...r.multipleAnswers?{multipleAnswers:!0}:{}};r.imageUrl&&(t.imageUrl=r.imageUrl),r.askerName&&(t.askerName=r.askerName),r.answererName&&(t.answererName=r.answererName);const o=!!(r.groupTitle||r.groupSubtitle||r.groupContent||r.groupImageUrl||r.articles&&r.articles.length>0)?{title:r.groupTitle||"",subtitle:r.groupSubtitle||"",content:r.groupContent||"",imageUrl:r.groupImageUrl||"",...r.articles?{articles:r.articles}:{}}:void 0,i=(e,r=[])=>{if("number"==typeof e)return r[e]&&"object"==typeof r[e]&&r[e].id?r[e].id:`opt-${e+1}`;if("string"==typeof e){if(e.startsWith("opt-")||isNaN(Number(e)))return e;const t=parseInt(e,10);if(!isNaN(t))return r[t]&&"object"==typeof r[t]&&r[t].id?r[t].id:`opt-${t+1}`}return String(e??"")};return{apiContent:t,apiCorrectAnswer:{answer:((e,r=[])=>Array.isArray(e)?e.map(e=>i(e,r)):i(e,r))(r.correctAnswer,r.options),...r.multipleAnswers?{multipleAnswers:!0}:{}},isExample:r.isExample||!1,basicGroupData:o,groupNumber:r.groupNumber||1}};
@@ -0,0 +1,14 @@
1
+ import type { WfsfOptionsWordBankEntry, WfsfSharedWordBankEntry } from '../../_shared/types/word-fill-structured-form.type';
2
+ import type { WfsfBankMode } from './wfsf-word-bank.utils';
3
+ export type WfsfWordBankEditorProps = {
4
+ mode: WfsfBankMode;
5
+ onModeChange: (mode: WfsfBankMode) => void;
6
+ sharedBank: WfsfSharedWordBankEntry[];
7
+ onSharedBankChange: (next: WfsfSharedWordBankEntry[]) => void;
8
+ optionsBank: WfsfOptionsWordBankEntry[];
9
+ onOptionsBankChange: (next: WfsfOptionsWordBankEntry[]) => void;
10
+ blankKeys: string[];
11
+ answers: Record<string, string>;
12
+ onAnswerChange: (key: string, value: string) => void;
13
+ };
14
+ export declare function WfsfWordBankEditor({ mode, onModeChange, sharedBank, onSharedBankChange, optionsBank, onOptionsBankChange, blankKeys, answers, onAnswerChange, }: WfsfWordBankEditorProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{CheckCircle2 as r,Library as n,ListChecks as a,Plus as s,Trash2 as l}from"lucide-react";import{Button as i}from"../../../../components/ui/button";import{Card as o,CardContent as d,CardHeader as c,CardTitle as h}from"../../../../components/ui/card";import{Checkbox as m}from"../../../../components/ui/checkbox";import{Input as g}from"../../../../components/ui/input";import{Label as p}from"../../../../components/ui/label";const x=[{id:"FREE_TYPE",label:"Tự gõ",hint:"Không word bank"},{id:"WITH_WORD_BANK",label:"Word Bank chung",hint:"Hộp gợi ý shared"},{id:"WITH_OPTIONS",label:"Options từng ô",hint:"Dropdown theo blank"}];export function WfsfWordBankEditor({mode:b,onModeChange:u,sharedBank:f,onSharedBankChange:N,optionsBank:y,onOptionsBankChange:w,blankKeys:v,answers:k,onAnswerChange:C}){const j=()=>{N([...f,{word:""}])};return t("div",{className:"space-y-4",children:[t(o,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-indigo-400 to-purple-400"}),e(c,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100",children:e(n,{className:"h-3.5 w-3.5 text-indigo-600"})}),t("div",{children:[e(h,{className:"text-base font-semibold text-gray-800",children:"Word Bank / gợi ý đáp án"}),e("p",{className:"mt-0.5 text-xs text-gray-500",children:"Chọn chế độ hiển thị cho học sinh khi làm bài (WFSF plan 0024/0025)"})]})]})}),e(d,{className:"space-y-3 px-4 pb-4",children:e("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-3",children:x.map(r=>{const n=b===r.id;return t("button",{type:"button",onClick:()=>u(r.id),className:"rounded-xl border-2 px-3 py-2.5 text-left transition-all "+(n?"border-indigo-400 bg-indigo-50 shadow-sm":"border-gray-200 bg-white hover:border-indigo-200"),children:[e("span",{className:"block text-sm font-semibold "+(n?"text-indigo-800":"text-gray-700"),children:r.label}),e("span",{className:"mt-0.5 block text-[11px] text-gray-500",children:r.hint})]},r.id)})})})]}),"WITH_WORD_BANK"===b&&t(o,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-purple-400 to-pink-400"}),e(c,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-purple-100",children:e(n,{className:"h-3.5 w-3.5 text-purple-600"})}),t(h,{className:"text-base font-semibold text-gray-800",children:["Word Bank (",f.filter(e=>e.word.trim()).length," từ)"]})]}),t(i,{type:"button",variant:"outline",size:"sm",onClick:j,className:"gap-1.5 border-purple-200 text-purple-600 hover:bg-purple-50",children:[e(s,{className:"h-3.5 w-3.5"}),"Thêm từ"]})]})}),t(d,{className:"space-y-3 px-4 pb-4",children:[t("p",{className:"text-xs text-gray-500",children:["Danh sách gợi ý (đáp án + từ nhiễu). Không bắt buộc khớp 1:1 với số chỗ trống. Đánh dấu ",e("strong",{children:"Example"})," nếu từ là mẫu."]}),0===f.length?t("div",{className:"flex h-24 flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-gray-200 bg-gray-50",children:[e("p",{className:"text-sm text-gray-400",children:"Chưa có từ nào"}),t(i,{type:"button",variant:"outline",size:"sm",onClick:j,children:[e(s,{className:"mr-1 h-3.5 w-3.5"}),"Thêm từ"]})]}):e("div",{className:"grid grid-cols-1 gap-3 md:grid-cols-2",children:f.map((r,n)=>t("div",{className:"rounded-lg border p-3 "+(r.isExample?"border-amber-300 bg-amber-50/50":"border-gray-200 bg-gray-50/50"),children:[t("div",{className:"mb-2 flex items-center justify-between",children:[e("span",{className:"flex h-7 w-7 items-center justify-center rounded-lg text-xs font-bold text-white "+(r.isExample?"bg-gradient-to-br from-amber-400 to-orange-500":"bg-gradient-to-br from-purple-500 to-pink-600"),children:n+1}),t("div",{className:"flex items-center gap-2",children:[t("div",{className:"flex items-center gap-1.5",children:[e(m,{id:`wfsf-ex-${n}`,checked:!!r.isExample,onCheckedChange:e=>((e,t)=>{const r=[...f];r[e]={...r[e],isExample:t||void 0},N(r)})(n,!0===e)}),e(p,{htmlFor:`wfsf-ex-${n}`,className:"cursor-pointer text-xs text-gray-600",children:"Example"})]}),e(i,{type:"button",variant:"ghost",size:"sm",onClick:()=>(e=>{N(f.filter((t,r)=>r!==e))})(n),className:"h-7 w-7 p-0 text-gray-400 hover:bg-red-50 hover:text-red-500",children:e(l,{className:"h-3.5 w-3.5"})})]})]}),e(g,{value:r.word,onChange:e=>((e,t)=>{const r=[...f];r[e]={...r[e],word:t},N(r)})(n,e.target.value),placeholder:"Nhập từ...",className:r.isExample?"bg-amber-50":"border-gray-200"})]},n))})]})]}),"WITH_OPTIONS"===b&&t(o,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-400 to-cyan-400"}),e(c,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(a,{className:"h-3.5 w-3.5 text-teal-600"})}),t(h,{className:"text-base font-semibold text-gray-800",children:["Lựa chọn theo chỗ trống (",v.length,")"]})]})}),e(d,{className:"space-y-4 px-4 pb-4",children:0===v.length?t("p",{className:"rounded-lg border-2 border-dashed border-gray-200 p-6 text-center text-sm text-gray-500",children:["Chưa có chỗ trống. Thêm ","{0}",", ","{1}","… trong nội dung trước."]}):v.map((n,a)=>{const o=y[a]??null,d=null==o,c=k[n]||"";return t("div",{className:"rounded-xl border-2 p-4 "+(d?"border-dashed border-gray-200 bg-gray-50/40":"border-gray-200 bg-white"),children:[t("div",{className:"mb-3 flex flex-wrap items-center justify-between gap-2",children:[t("div",{className:"flex items-center gap-2",children:[e("span",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 text-sm font-bold text-white",children:n}),t("span",{className:"text-sm font-semibold text-gray-700",children:["Chỗ trống ",e("code",{className:"text-teal-700",children:`{${n}}`})]}),c&&t("span",{className:"inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700",children:[e(r,{className:"h-3 w-3"}),c]})]}),t("label",{className:"flex items-center gap-2 text-xs text-gray-600",children:[e(m,{checked:d,onCheckedChange:e=>((e,t)=>{const r=[...y];r[e]=t?null:{options:["",""]},w(r)})(a,!0===e)}),"Free-type (không options)"]})]}),!d&&o&&t("div",{className:"space-y-2",children:[o.options.map((r,s)=>t("div",{className:"flex items-center gap-2",children:[e("button",{type:"button",title:"Chọn làm đáp án đúng",onClick:()=>r.trim()&&C(n,r.trim()),className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-xs font-bold "+(c===r.trim()&&r.trim()?"bg-green-500 text-white":"bg-gray-100 text-gray-500 hover:bg-green-100 hover:text-green-600"),children:String.fromCharCode(65+s)}),e(g,{value:r,onChange:e=>((e,t,r)=>{const n=[...y],a=n[e];if(null==a)return;const s=[...a.options];s[t]=r,n[e]={options:s},w(n)})(a,s,e.target.value),placeholder:`Lựa chọn ${String.fromCharCode(65+s)}`,className:"flex-1 border-gray-200 text-sm"}),o.options.length>2&&e(i,{type:"button",variant:"ghost",size:"sm",onClick:()=>((e,t)=>{const r=[...y],n=r[e];if(null==n||n.options.length<=2)return;const a=n.options.filter((e,r)=>r!==t);r[e]={options:a},w(r);const s=v[e],l=n.options[t];s&&k[s]===l&&C(s,"")})(a,s),className:"h-8 w-8 p-0 text-red-400 hover:bg-red-50 hover:text-red-600",children:e(l,{className:"h-3.5 w-3.5"})})]},s)),t(i,{type:"button",variant:"ghost",size:"sm",onClick:()=>(e=>{const t=[...y],r=t[e];null!=r&&(t[e]={options:[...r.options,""]},w(t))})(a),className:"mt-1 gap-1 text-xs text-teal-600 hover:bg-teal-50",children:[e(s,{className:"h-3 w-3"}),"Thêm lựa chọn"]})]})]},n)})})]})]})}
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
2
  import type { WordFillStructuredFormClientProps } from '../../_shared/types/word-fill-structured-form.type';
3
- export declare function WordFillStructuredFormClient({ questionData, isReviewMode, userAnswer, onAnswerChange, autoFillCorrectAnswers, explanation, questionNumber, }: WordFillStructuredFormClientProps): React.JSX.Element;
3
+ export declare function WordFillStructuredFormClient({ questionData, isReviewMode, userAnswer, onAnswerChange, autoFillCorrectAnswers, instruction, questionNumber, explanation, }: WordFillStructuredFormClientProps): React.JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import r,{useState as l,useEffect as n,useMemo as a,useCallback as i}from"react";import{Check as o,X as s}from"lucide-react";import{usePresignedFileUrl as d}from"../../../../shared/lib/hooks";import{cn as c}from"../../../../shared/lib/utils";export function WordFillStructuredFormClient({questionData:m,isReviewMode:p=!1,userAnswer:u,onAnswerChange:g,autoFillCorrectAnswers:b=!1,explanation:f,questionNumber:h}){const{title:x="",imageUrl:w,content:y="",answers:v={},blankStyle:k,caseSensitive:N=!1,wordBank:_}=m,A=function(e){const t=String(e||"").toLowerCase();return"letter"===t||"box"===t?"letter":"phrase"===t||"sentence"===t?"phrase":"word"}(k),C=function(e){if(!Array.isArray(e)||0===e.length)return;const t=e.map(e=>{if(null==e)return null;if("object"!=typeof e||Array.isArray(e))return null;const t=e;if(Array.isArray(t.options)){const e=t.options.map(e=>String(e??"").trim()).filter(e=>e.length>0);return e.length>=2?{options:e}:null}const r=String(t.word??"").trim();return r?t.isExample?{word:r,isExample:!0}:{word:r}:null});return t.some(e=>null!=e)?t:void 0}(_),S=a(()=>function(e){if(!e?.length)return[];const t=[];for(const r of e)if(r&&"word"in r&&"string"==typeof r.word&&r.word){const e="isExample"in r&&Boolean(r.isExample);t.push(e?{word:r.word,isExample:!0}:{word:r.word})}return t}(C),[C]),$=String(m.viewMode||""),B=S.length>0&&("WITH_WORD_BANK"===$||!C?.some(e=>null!=e&&"options"in e&&Array.isArray(e.options))),[E,W]=l(()=>u?{...u}:b?{...v}:{});n(()=>{u&&W({...u})},[u]);const{previewUrl:L,isLoading:j}=d(w||""),T=a(()=>{const e=new Set,t=/\{([\w\d]+)\}/g;let r;for(;null!==(r=t.exec(y));)e.add(r[1]);return Array.from(e).sort((e,t)=>{const r=parseInt(e),l=parseInt(t);return isNaN(r)||isNaN(l)?e.localeCompare(t):r-l})},[y]),F=T.every(e=>E[e]?.trim()),O=p&&T.every(e=>{const t=(E[e]||"").trim(),r=(v[e]||"").trim();return N?t===r:t.toLowerCase()===r.toLowerCase()}),z=String(f||m.explanation||"").trim(),D="number"==typeof h&&h>0?`Bài ${h}`:"",H=i((e,t)=>{p||(W(r=>({...r,[e]:t})),g?.(e,t))},[p,g]),M=i((r,l)=>{const n=E[r]||"",a=v[r]||"",i=N?n.trim():n.trim().toLowerCase(),d=N?a.trim():a.trim().toLowerCase(),m=p&&i===d,u=p&&!!n&&i!==d,g="letter"===A,b="phrase"===A,f=g?1:void 0,h=function(e,t,r){if(!e?.length)return null;const l=Number(t);let n;if(Number.isFinite(l)&&l>=0&&l<e.length)n=e[l];else{const l=r.indexOf(t);l>=0&&l<e.length&&(n=e[l])}return null!=n&&"options"in n&&n.options?.length?n.options:null}(C,r,T),x=Boolean(h&&h.length>=2),w=g?{display:"inline-block",width:"1.625rem",height:"1.625rem",minWidth:"1.625rem",maxWidth:"1.625rem",padding:0,margin:"0 1px",borderWidth:2,borderStyle:"solid",borderColor:p?m?"#10b981":u?"#f43f5e":"#f59e0b":"#94a3b8",borderRadius:4,textAlign:"center",fontSize:"0.8125rem",fontWeight:700,lineHeight:"1.375rem",textTransform:"uppercase",backgroundColor:p?m?"#ecfdf5":u?"#fff1f2":"#fffbeb":"#ffffff",color:p?m?"#065f46":u?"#9f1239":"#92400e":"#1e293b",verticalAlign:"middle",outline:"none",boxSizing:"border-box"}:void 0,y=p?m?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":u?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800":"border-slate-400 text-slate-800 focus:border-teal-600";return t("span",{className:c("relative inline-flex items-center",g?"mx-px":"mx-1 align-baseline"),style:g?{display:"inline-flex",verticalAlign:"middle",whiteSpace:"nowrap",flexShrink:0}:void 0,children:[!g&&e("span",{className:"absolute -top-4 left-1 text-[10px] font-semibold text-gray-400",children:r}),x&&h?t("select",{value:n,disabled:p,onChange:e=>H(r,e.target.value),"aria-label":`Blank ${r}`,className:c("inline-block min-w-[7rem] max-w-[14rem] rounded border bg-white px-2 py-1 text-sm font-semibold outline-none transition-all align-baseline",y,!p&&"hover:border-teal-500 focus:border-teal-600 focus:ring-1 focus:ring-teal-200"),children:[e("option",{value:"",children:"—"}),h.map(t=>e("option",{value:t,children:t},t))]}):e("input",{type:"text",value:n,onChange:e=>{const t=e.target.value,l=g?t.slice(-1).replace(/\s/g,""):t;H(r,l)},disabled:p,placeholder:"",maxLength:f,inputMode:g?"text":void 0,autoComplete:"off",spellCheck:!1,"aria-label":`Blank ${r}`,style:w,className:c(!g&&"inline-block bg-slate-50/40 text-center text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",!g&&(b?"min-w-[9rem] max-w-[18rem] w-[14ch] rounded-t border-b-2 px-2 py-0.5":"min-w-[4.5rem] max-w-[7.5rem] w-[8ch] rounded-t border-b-2 px-1 py-0.5"),!g&&y),size:g?1:b?18:10}),p&&n&&(m?e(o,{className:"ml-0.5 h-3.5 w-3.5 shrink-0 text-green-600"}):e(s,{className:"ml-0.5 h-3.5 w-3.5 shrink-0 text-red-600"}))]},l)},[v,A,N,T,E,H,p,C]),I=a(()=>{if("undefined"==typeof window)return y;const t=(new DOMParser).parseFromString(y||"<p></p>","text/html"),l=/\{([\w\d]+)\}/g,n="letter"===A,a=(t,i)=>{if(t.nodeType===Node.TEXT_NODE)return((t,a)=>{if(!t)return null;const i=[];let o,s=0;for(l.lastIndex=0;null!==(o=l.exec(t));)o.index>s&&i.push({kind:"text",value:t.slice(s,o.index)}),i.push({kind:"blank",key:o[1],index:o.index}),s=o.index+o[0].length;if(s<t.length&&i.push({kind:"text",value:t.slice(s)}),!i.length)return null;if(!n){const t=i.map((e,t)=>"text"===e.kind?e.value:M(e.key,`${a}-blank-${e.index}-${t}`));return 1===t.length?t[0]:e(r.Fragment,{children:t},a)}const d=[];let c=[],m=0;const p=()=>{c.length&&(d.push(e("span",{style:{display:"inline",whiteSpace:"nowrap",verticalAlign:"baseline"},children:c},`${a}-w${m}`)),m+=1,c=[])};for(let e=0;e<i.length;e+=1){const t=i[e];if("blank"===t.kind){c.push(M(t.key,`${a}-blank-${t.index}-${e}`));continue}const r=t.value.split(/(\s+)/);for(let e=0;e<r.length;e+=1){const t=r[e];t&&(/^\s+$/.test(t)?(p(),d.push(t)):c.push(t))}}return p(),d.length?1===d.length?d[0]:e(r.Fragment,{children:d},a):null})(t.textContent||"",i);if(t.nodeType!==Node.ELEMENT_NODE)return null;const o=t,s=o.tagName.toLowerCase(),d={key:i};let c="",m={};Array.from(o.attributes).forEach(e=>{var t;if("class"!==e.name)return"style"===e.name?(t=e.value,void(m=t.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,t)=>{const[r,l]=t.split(":").map(e=>e.trim());return r&&l?(e[r.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())]=l,e):e},{}))):void(d[e.name]=e.value);c=e.value});const p=c.split(/\s+/).filter(Boolean);p.includes("wfsf-block")&&(m={display:"block",margin:"0 0 1.25rem 0",padding:"0.35rem 0 1rem 0",borderBottom:"1px solid #e2e8f0",...m}),p.includes("wfsf-item-no")&&(m={fontWeight:700,fontSize:"1rem",lineHeight:1.4,margin:"0 0 0.4rem 0",color:"#0f172a",...m}),"table"===s&&p.includes("dialogue")&&(m={width:"100%",borderCollapse:"collapse",tableLayout:"auto",...m}),"td"===s&&p.includes("speaker")?m={width:"1%",whiteSpace:"nowrap",verticalAlign:"top",paddingRight:"12px",paddingTop:"6px",paddingBottom:"6px",fontWeight:600,color:"#0f172a",...m}:"td"===s&&!p.includes("speaker")&&o.closest?.("table.dialogue")&&(m={verticalAlign:"top",paddingTop:"6px",paddingBottom:"6px",width:"auto",...m}),c&&(d.className=c),Object.keys(m).length>0&&(d.style=m);const u=Array.from(o.childNodes).map((e,t)=>a(e,`${i}-${t}`)).filter(e=>null!==e);return r.createElement(s,d,...u)};return Array.from(t.body.childNodes).map((e,t)=>a(e,`root-${t}`)).filter(e=>null!==e)},[A,y,M]);return e("div",{className:"space-y-4",children:t("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[(D||z||p&&F)&&e("div",{className:"border-b border-gray-100 bg-white px-5 py-4",children:t("div",{className:"flex items-start justify-between gap-3",children:[t("div",{className:"min-w-0 space-y-1.5",children:[D?e("h2",{className:"text-lg font-bold tracking-tight text-slate-900",children:D}):null,z?e("p",{className:"text-sm leading-relaxed whitespace-pre-line text-slate-700",children:z}):null]}),p&&F?t("div",{className:c("flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",O?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e(O?o:s,{className:"h-3.5 w-3.5"}),O?"Đúng":"Sai"]}):null]})}),t("div",{className:"space-y-5 p-5",children:[x?e("h3",{className:"text-center text-base font-semibold text-gray-900",children:x}):null,(L||w)&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gray-50",children:j?e("div",{className:"flex h-48 w-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-teal-500"})}):e("img",{src:L||w,alt:x||D||"Hình minh họa",className:"max-h-56 w-auto object-contain p-2"})})}),B?t("div",{className:"word-bank-shared space-y-2",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-gray-500",children:"Word Bank"}),e("div",{className:"flex flex-wrap gap-2",children:S.map((t,r)=>e("span",{className:c("rounded-lg border-2 px-3 py-1.5 text-sm font-semibold",t.isExample?"border-amber-300 bg-amber-50 text-amber-800":"border-sky-200 bg-sky-50 text-sky-900"),children:t.word},`wb-${r}-${t.word}`))})]}):null,e("div",{className:c("structured-form-content space-y-2 rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5 text-base leading-relaxed text-gray-800","[&_table.dialogue]:w-full [&_table.dialogue]:border-collapse [&_table.dialogue]:table-fixed","[&_table.dialogue_td.speaker]:w-[1%] [&_table.dialogue_td.speaker]:whitespace-nowrap","[&_table.dialogue_td.speaker]:align-top [&_table.dialogue_td.speaker]:pr-3","[&_table.dialogue_td.speaker]:font-semibold [&_table.dialogue_td.speaker]:text-slate-900","[&_table.dialogue_td]:align-top [&_table.dialogue_td]:py-1.5 [&_table.dialogue_td]:leading-relaxed","[&_table.dialogue_td:not(.speaker)]:w-auto","[&_table:not(.dialogue)]:w-full [&_table:not(.dialogue)]:border-collapse"),style:"letter"===A?{wordBreak:"normal",overflowWrap:"break-word",lineHeight:1.7}:void 0,children:I}),p&&!O&&t("div",{className:"rounded-lg border border-green-200 bg-green-50 p-3",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-green-600",children:"Đáp án đúng"}),e("div",{className:"mt-2 flex flex-wrap gap-2",children:T.map(e=>t("span",{className:"rounded-lg border border-green-300 bg-green-100 px-3 py-1.5 text-sm font-semibold text-green-800",children:[`{${e}}`,": ",v[e]]},e))})]})]})]})})}
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import t,{useState as a,useEffect as l,useMemo as n,useCallback as s}from"react";import{Check as o,X as i,FileText as d,Library as m}from"lucide-react";import{usePresignedFileUrl as c}from"../../../../shared/lib/hooks";import{cn as b}from"../../../../shared/lib/utils";import{detectWfsfBankMode as p,isOptionsWordEntry as u,normalizeSharedWordBank as g}from"./wfsf-word-bank.utils";export function WordFillStructuredFormClient({questionData:x,isReviewMode:h=!1,userAnswer:f,onAnswerChange:w,autoFillCorrectAnswers:N=!1,instruction:y,questionNumber:v,explanation:k}){const{title:C="",imageUrl:S,content:E="",answers:A={},caseSensitive:T=!1,viewMode:j,wordBank:O=[],instruction:I}=x,[L,$]=a(()=>f?{...f}:N?{...A}:{}),[W,_]=a(null);l(()=>{f&&$({...f})},[f]);const B=n(()=>p(j,O),[j,O]),D=n(()=>"WITH_WORD_BANK"===B?g(O):[],[B,O]),F=n(()=>{const e=new Set;return Object.values(L).forEach(r=>{const t=String(r||"").trim().toLowerCase();t&&e.add(t)}),e},[L]),M=n(()=>{const e=new Map;return"WITH_OPTIONS"===B&&Array.isArray(O)?(O.forEach((r,t)=>{u(r)&&e.set(String(t),r.options.map(e=>String(e??"")))}),e):e},[B,O]),{previewUrl:H,isLoading:P}=c(S||""),R=n(()=>{const e=new Set,r=/\{([\w\d]+)\}/g;let t;for(;null!==(t=r.exec(E));)e.add(t[1]);return Array.from(e).sort((e,r)=>{const t=parseInt(e),a=parseInt(r);return isNaN(t)||isNaN(a)?e.localeCompare(r):t-a})},[E]),U=R.every(e=>L[e]?.trim()),q=h&&R.every(e=>{const r=(L[e]||"").trim(),t=(A[e]||"").trim();return T?r===t:r.toLowerCase()===t.toLowerCase()}),z=String(k||x.explanation||"").trim(),K=String(y||I||z||"").trim()||"Điền từ vào biểu mẫu có cấu trúc",G=z&&z!==K?z:"",X=s((e,r)=>{h||($(t=>({...t,[e]:r})),w?.(e,r))},[h,w]),J=s((e,r)=>{h||($(t=>({...t,[e]:r})),w?.(e,r),_(null))},[h,w]),Q=s((t,a)=>{const l=L[t]||"",n=A[t]||"",s=T?l.trim():l.trim().toLowerCase(),d=T?n.trim():n.trim().toLowerCase(),m=h&&s===d,c=h&&!!l&&s!==d,p=M.get(t);if("WITH_OPTIONS"===B&&p&&p.length>=2){const n=W===t;return r("span",{className:"relative mx-1 inline-flex flex-col items-center align-baseline",children:[r("button",{type:"button",onClick:()=>!h&&_(n?null:t),disabled:h,className:b("inline-flex min-w-[90px] items-center justify-center rounded border-2 border-dashed px-2 py-0.5 text-center text-sm font-semibold transition-all",h?m?"border-emerald-500 bg-emerald-50 text-emerald-800":c?"border-rose-500 bg-rose-50 text-rose-800":"border-amber-500 bg-amber-50 text-amber-800":"border-teal-400 bg-teal-50/50 text-teal-800 hover:bg-teal-50"),children:[l||`{${t}}`,h&&l&&(m?e(o,{className:"ml-0.5 h-3.5 w-3.5 text-green-600"}):e(i,{className:"ml-0.5 h-3.5 w-3.5 text-red-600"}))]}),n&&!h&&e("div",{className:"absolute left-1/2 top-full z-10 mt-1 flex -translate-x-1/2 flex-wrap gap-1 rounded-lg border border-teal-200 bg-white p-1.5 shadow-lg",children:p.map(r=>e("button",{type:"button",onClick:()=>J(t,r),className:b("rounded-md border px-2 py-1 text-xs font-medium transition-colors",l===r?"border-teal-500 bg-teal-100 text-teal-800":"border-gray-200 bg-white text-gray-700 hover:border-teal-300 hover:bg-teal-50"),children:r},r))})]},a)}return r("span",{className:"relative mx-1 inline-flex items-baseline align-baseline",children:[e("span",{className:"absolute -top-4 left-1 text-[10px] font-semibold text-gray-400",children:t}),e("input",{type:"text",value:l,onChange:e=>X(t,e.target.value),disabled:h,placeholder:"",className:b("inline-block min-w-[90px] max-w-[200px] rounded-t border-b-2 bg-slate-50/40 px-2 py-0.5 text-center text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",h?m?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":c?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800":"border-slate-400 text-slate-800 focus:border-teal-600")}),h&&l&&(m?e(o,{className:"ml-0.5 h-3.5 w-3.5 text-green-600"}):e(i,{className:"ml-0.5 h-3.5 w-3.5 text-red-600"}))]},a)},[A,B,T,W,L,X,J,h,M]),V=n(()=>{if("undefined"==typeof window)return E;const r=(new DOMParser).parseFromString(E||"<p></p>","text/html"),a=/\{([\w\d]+)\}/g,l=(r,n)=>{if(r.nodeType===Node.TEXT_NODE){const l=r.textContent||"",s=[];let o,i=0;for(a.lastIndex=0;null!==(o=a.exec(l));)o.index>i&&s.push(l.slice(i,o.index)),s.push(Q(o[1],`${n}-blank-${o.index}`)),i=o.index+o[0].length;return i<l.length&&s.push(l.slice(i)),0===s.length?null:1===s.length?s[0]:e(t.Fragment,{children:s},n)}if(r.nodeType!==Node.ELEMENT_NODE)return null;const s=r,o=s.tagName.toLowerCase(),i={key:n};Array.from(s.attributes).forEach(e=>{"class"!==e.name?"style"!==e.name?i[e.name]=e.value:i.style=e.value.split(";").map(e=>e.trim()).filter(Boolean).reduce((e,r)=>{const[t,a]=r.split(":").map(e=>e.trim());return t&&a?(e[t.replace(/-([a-z])/g,(e,r)=>r.toUpperCase())]=a,e):e},{}):i.className=e.value});const d=Array.from(s.childNodes).map((e,r)=>l(e,`${n}-${r}`)).filter(e=>null!==e);return t.createElement(o,i,...d)};return Array.from(r.body.childNodes).map((e,r)=>l(e,`root-${r}`)).filter(e=>null!==e)},[E,Q]);return e("div",{className:"space-y-4",children:r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center justify-between gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:[r("div",{className:"flex min-w-0 items-start gap-3",children:[e("div",{className:"mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 text-white shadow-sm",children:e(d,{className:"h-4 w-4"})}),r("div",{className:"min-w-0",children:["number"==typeof v&&v>0?r("p",{className:"text-[11px] font-semibold uppercase tracking-wider text-teal-700",children:["Bài ",v]}):null,e("span",{className:"block text-sm font-medium leading-snug text-gray-800",children:K})]})]}),h&&U&&r("div",{className:b("flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",q?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e(q?o:i,{className:"h-3.5 w-3.5"}),q?"Đúng":"Sai"]})]}),r("div",{className:"space-y-5 p-5",children:[C&&e("h2",{className:"text-center text-xl font-bold text-gray-900",children:C}),(H||S)&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gray-50",children:P?e("div",{className:"flex h-48 w-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-teal-500"})}):e("img",{src:H||S,alt:C,className:"max-h-56 w-auto object-contain p-2"})})}),"WITH_WORD_BANK"===B&&D.length>0&&r("div",{className:"rounded-lg border border-purple-200 bg-gradient-to-r from-purple-50 to-pink-50 p-3",children:[r("div",{className:"mb-2 flex items-center gap-2",children:[e(m,{className:"h-4 w-4 text-purple-600"}),e("span",{className:"text-xs font-semibold uppercase tracking-wider text-purple-700",children:"Word Bank"})]}),e("div",{className:"flex flex-wrap gap-2",children:D.map((e,t)=>{const a=F.has(e.word.trim().toLowerCase());return r("span",{className:b("rounded-full border px-3 py-1 text-sm font-medium",e.isExample?"border-amber-300 bg-amber-100 text-amber-800":a?"border-purple-200 bg-white/60 text-purple-400 line-through":"border-purple-300 bg-white text-purple-800"),children:[e.word,e.isExample?" (ex)":""]},`${e.word}-${t}`)})})]}),e("div",{className:"structured-form-content space-y-2 rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5 text-base leading-loose text-gray-800",children:V}),h&&!q&&r("div",{className:"rounded-lg border border-green-200 bg-green-50 p-3",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-green-600",children:"Đáp án đúng"}),e("div",{className:"mt-2 flex flex-wrap gap-2",children:R.map(e=>r("span",{className:"rounded-lg border border-green-300 bg-green-100 px-3 py-1.5 text-sm font-semibold text-green-800",children:[`{${e}}`,": ",A[e]]},e))})]}),h&&G&&r("div",{className:"rounded-lg border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-3",children:[e("span",{className:"text-xs font-medium uppercase tracking-wider text-blue-600",children:"Giải thích"}),e("p",{className:"mt-1 text-sm text-blue-800",children:G})]})]})]})})}
@@ -1,2 +1,2 @@
1
1
  import type { WordFillStructuredFormCreatorProps } from '../../_shared/types/word-fill-structured-form.type';
2
- export declare function WordFillStructuredFormCreator({ initialData, onSave, onCancel, onChange, onUnsavedChangesChange: _onUnsavedChangesChange, validationRef, externalErrors, groupConfig, groupType, groups, questionIndexInPart, partId, }: WordFillStructuredFormCreatorProps): import("react").JSX.Element;
2
+ export declare function WordFillStructuredFormCreator({ initialData, onSave, onCancel, onChange, onUnsavedChangesChange: _onUnsavedChangesChange, validationRef, externalErrors, groupConfig, groupType, groups, questionIndexInPart, partId, viewMode: viewModeProp, }: WordFillStructuredFormCreatorProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as n,useEffect as r,useRef as a,useMemo as i,useCallback as l}from"react";import{useEditor as s,EditorContent as c}from"@tiptap/react";import o from"@tiptap/starter-kit";import d from"@tiptap/extension-text-align";import{Table as m,TableRow as h,TableCell as g,TableHeader as u}from"@tiptap/extension-table";import{List as p,ListOrdered as x,Bold as b,Italic as f,AlignLeft as N,AlignCenter as v,AlignRight as w,TableIcon as y,Plus as C,Minus as k,Trash2 as S,BookOpen as A,Eye as I,Pencil as U,CheckCircle2 as T,PenLine as j,Info as G,MessageSquare as H,ImageIcon as B}from"lucide-react";import{Button as D}from"../../../../components/ui/button";import{Label as R}from"../../../../components/ui/label";import{Input as O}from"../../../../components/ui/input";import{Textarea as P}from"../../../../components/ui/textarea";import{Switch as F}from"../../../../components/ui/switch";import{PointsInput as L}from"../../../../components/ui/points-input";import{Card as E,CardContent as z,CardHeader as J,CardTitle as $}from"../../../../components/ui/card";import{FileUpload as q}from"../../../../components/ui/file-upload";import{ImagePreview as X}from"../../../../components/ui/image-preview";import{WordFillStructuredFormClient as M}from"./WordFillStructuredFormClient";import{useDebouncedCallback as Q,usePresignedFileUrl as V}from"../../../../shared/lib/hooks";import{BasicQuestionGroupCard as K}from"../../groups/BasicQuestionGroupCard";import{DocumentGroupCard as W}from"../../groups/DocumentGroupCard";import{useBasicQuestionGroup as _,getGroupNumberForIndex as Y}from"../../_shared/hooks/useBasicQuestionGroup";const Z=({onClick:t,active:n,disabled:r,title:a,children:i,className:l=""})=>e("button",{type:"button",onClick:t,disabled:r,title:a,className:`inline-flex h-7 min-w-7 items-center justify-center rounded px-1.5 text-sm transition-colors disabled:opacity-30\n ${n?"bg-teal-100 text-teal-700":"text-gray-500 hover:bg-gray-100 hover:text-gray-800"}\n ${l}`,children:i}),ee=()=>e("div",{className:"mx-1 h-4 w-px bg-gray-200"}),te=({editor:n})=>n?t("div",{className:"flex flex-wrap items-center gap-0.5 border-b border-gray-100 bg-gray-50/70 px-2 py-1.5 rounded-t-lg",children:[e(Z,{onClick:()=>n.chain().focus().toggleBold().run(),active:n.isActive("bold"),title:"Đậm",children:e(b,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().toggleItalic().run(),active:n.isActive("italic"),title:"Nghiêng",children:e(f,{className:"h-3.5 w-3.5"})}),e(ee,{}),e(Z,{onClick:()=>n.chain().focus().toggleHeading({level:2}).run(),active:n.isActive("heading",{level:2}),className:"px-2 text-xs font-bold",children:"H2"}),e(Z,{onClick:()=>n.chain().focus().toggleHeading({level:3}).run(),active:n.isActive("heading",{level:3}),className:"px-2 text-xs font-bold",children:"H3"}),e(Z,{onClick:()=>n.chain().focus().toggleHeading({level:4}).run(),active:n.isActive("heading",{level:4}),className:"px-2 text-xs font-bold",children:"H4"}),e(ee,{}),e(Z,{onClick:()=>n.chain().focus().toggleBulletList().run(),active:n.isActive("bulletList"),title:"Danh sách",children:e(p,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().toggleOrderedList().run(),active:n.isActive("orderedList"),title:"Danh sách có số",children:e(x,{className:"h-3.5 w-3.5"})}),e(ee,{}),e(Z,{onClick:()=>n.chain().focus().setTextAlign("left").run(),active:n.isActive({textAlign:"left"}),title:"Trái",children:e(N,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().setTextAlign("center").run(),active:n.isActive({textAlign:"center"}),title:"Giữa",children:e(v,{className:"h-3.5 w-3.5"})}),e(Z,{onClick:()=>n.chain().focus().setTextAlign("right").run(),active:n.isActive({textAlign:"right"}),title:"Phải",children:e(w,{className:"h-3.5 w-3.5"})}),e(ee,{}),t(Z,{onClick:()=>n.chain().focus().insertTable({rows:5,cols:2,withHeaderRow:!1}).run(),title:"Chèn bảng 2 cột × 5 hàng",className:"gap-1 px-2 text-xs",children:[e(y,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Bảng 2 cột"})]}),t(Z,{onClick:()=>n.chain().focus().addRowAfter().run(),disabled:!n.can().addRowAfter(),title:"Thêm hàng",className:"gap-1 px-2 text-xs",children:[e(C,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(Z,{onClick:()=>n.chain().focus().deleteRow().run(),disabled:!n.can().deleteRow(),title:"Xóa hàng",className:"gap-1 px-2 text-xs",children:[e(k,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(Z,{onClick:()=>n.chain().focus().deleteTable().run(),disabled:!n.can().deleteTable(),title:"Xóa bảng",className:"gap-1 px-2 text-xs text-red-400 hover:text-red-600",children:[e(S,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Bảng"})]})]}):null;export function WordFillStructuredFormCreator({initialData:p,onSave:b,onCancel:f,onChange:N,onUnsavedChangesChange:v,validationRef:w,externalErrors:y,groupConfig:C,groupType:k,groups:S,questionIndexInPart:Z=0,partId:ee=""}){const[ne,re]=n(!1),[ae,ie]=n(p?.points??1),[le,se]=n(p?.caseSensitive??!1),[ce,oe]=n(p?.explanation||""),[de,me]=n(""),{isGrouped:he,isFirstInGroup:ge,currentGroupNumber:ue,groupTitle:pe,groupSubtitle:xe,groupContent:be,imageUrl:fe,setGroupTitle:Ne,setGroupSubtitle:ve,setGroupContent:we,setImageUrl:ye}=_({partId:ee,groups:S,questionIndexInPart:Z,initialData:{title:p?.groupTitle||"",subtitle:p?.groupSubtitle||"",content:p?.groupContent||"",imageUrl:p?.groupImageUrl||""}}),Ce=(k||"BASIC").toUpperCase(),ke=!(C&&C.length>0)||C.includes("content"),Se=ue||Y(Z,S),[Ae,Ie]=n(p?.title||""),[Ue,Te]=n(p?.imageUrl||""),je=Ae,Ge=Ue,He=Ie,Be=Te,{previewUrl:De}=V(Ge),Re=de||De||"",Oe=p?.content||"<p></p>",[Pe,Fe]=n(Oe),[Le,Ee]=n(p?.answers||{}),ze=a(""),Je=a(p),$e=a(!1),qe=a(Pe),Xe=a(N);r(()=>{Xe.current=N},[N]);const Me=i(()=>[o.configure({heading:{levels:[2,3,4]}}),d.configure({types:["heading","paragraph"]}),m.configure({resizable:!0}),h,g,u],[]),Qe=i(()=>({attributes:{class:"tiptap-editor prose prose-sm max-w-none p-4 focus:outline-none min-h-[400px]"}}),[]),Ve=l(({editor:e})=>{const t=e.getHTML();t!==qe.current&&(qe.current=t,Fe(t))},[]),Ke=s({immediatelyRender:!1,extensions:Me,content:Pe,editorProps:Qe,onUpdate:Ve});r(()=>{qe.current=Pe},[Pe]),r(()=>{if(!(JSON.stringify(Je.current)!==JSON.stringify(p)))return;if(Je.current=p,!p?.content||"<p></p>"===p.content)return;$e.current=!0,Ie(p?.title||""),Te(p?.imageUrl||""),ie(p?.points??1),se(p?.caseSensitive??!1),oe(p?.explanation||""),Ee(p?.answers||{});const e=p.content;e!==qe.current&&(qe.current=e,Fe(e),Ke&&!Ke.isDestroyed&&Ke.commands.setContent(e)),setTimeout(()=>{$e.current=!1},150)},[p,Ke]);const We=Q(e=>{Xe.current?.(e)},300),_e=JSON.stringify(Le),Ye=i(()=>{const e=new Set,t=/\{([\w\d]+)\}/g;let n;for(;null!==(n=t.exec(Pe));)e.add(n[1]);return Array.from(e).sort((e,t)=>{const n=parseInt(e),r=parseInt(t);return isNaN(n)||isNaN(r)?e.localeCompare(t):n-r})},[Pe]);return r(()=>{Ee(e=>{let t=!1;const n={...e};return Object.keys(n).forEach(e=>{Ye.includes(e)||(delete n[e],t=!0)}),Ye.forEach(e=>{e in n||(n[e]="",t=!0)}),t?n:e})},[Ye]),r(()=>{const e={title:je,imageUrl:Ge,content:Pe,answers:Le,points:ae,caseSensitive:le,explanation:ce,groupTitle:pe,groupSubtitle:xe,groupContent:be,groupImageUrl:fe,groupNumber:Se};if($e.current)return void(ze.current=JSON.stringify(e));const t=JSON.stringify(e);t!==ze.current&&(ze.current=t,We(e))},[je,Ge,Pe,_e,ae,le,ce,pe,xe,be,fe,Se]),r(()=>{w&&(w.current={triggerValidation:async()=>{if(!Pe||"<p></p>"===Pe)return!1;if(0===Ye.length)return!1;for(const e of Ye)if(!Le[e]||""===Le[e].trim())return!1;return!0},getFormData:()=>({title:je,imageUrl:Ge,content:Pe,answers:Le,points:ae,caseSensitive:le,explanation:ce,groupTitle:pe,groupSubtitle:xe,groupContent:be,groupImageUrl:fe,groupNumber:Se})})},[w,je,Ge,Pe,Le,ae,le,ce,Ye,pe,xe,be,fe,Se]),t("div",ne?{className:"space-y-4",children:[e(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:e(J,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 shadow-sm",children:e(I,{className:"h-4 w-4 text-white"})}),t("div",{children:[e($,{className:"text-base font-bold text-gray-900",children:"Điền từ – Xem trước"}),e("p",{className:"text-xs text-gray-500",children:"Chế độ xem trước với đáp án đúng đã được điền sẵn"})]})]}),t(D,{variant:"outline",size:"sm",onClick:()=>re(!1),className:"gap-2 border-teal-200 text-teal-700 hover:bg-teal-50",children:[e(U,{className:"h-4 w-4"}),"Quay lại Edit"]})]})})}),e(M,{questionData:{title:je,imageUrl:Ge,content:Pe,answers:Le,points:ae,caseSensitive:le},isReviewMode:!0,userAnswer:Le})]}:{className:"space-y-4",children:[he&&"BASIC"===Ce&&e(K,{groupNumber:Se,title:pe,subtitle:xe,content:be,imageUrl:fe,onTitleChange:Ne,onSubtitleChange:ve,onContentChange:we,onImageChange:ye,groupConfig:k?C:void 0,isReadOnly:!ge,uploadIdPrefix:`word-fill-structured-${ee??"group"}`}),he&&"DOCUMENT"===Ce&&e(W,{groupNumber:Se,title:pe,subtitle:xe,content:be,imageUrl:fe,onTitleChange:Ne,onSubtitleChange:ve,onContentChange:we,onImageChange:ye,groupConfig:k?C:void 0,isReadOnly:!ge,uploadIdPrefix:`word-fill-structured-${ee??"document"}`}),t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-500 via-cyan-500 to-emerald-500"}),e(J,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(j,{className:"h-4 w-4 text-teal-600"})}),t("div",{children:[e($,{className:"text-base font-bold text-gray-900",children:"Điền từ vào biểu mẫu có cấu trúc"}),t("p",{className:"text-xs text-gray-500",children:["Nhập nội dung, đặt chỗ trống bằng ","{0}"," ","{1}","… rồi điền đáp án bên dưới"]})]})]}),t(D,{variant:"outline",size:"sm",onClick:()=>re(!0),disabled:0===Ye.length||Ye.some(e=>!Le[e]?.trim()),className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(I,{className:"h-4 w-4"}),"Xem trước"]})]})}),t(z,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"flex flex-wrap items-center gap-3",children:[e(L,{value:ae,onChange:e=>ie(Number(e.target.value)||1)}),t("div",{role:"button",tabIndex:0,onClick:()=>se(!le),onKeyDown:e=>{" "!==e.key&&"Enter"!==e.key||se(!le)},style:{paddingBlock:"calc(var(--spacing) * 1)"},className:"inline-flex cursor-pointer items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none "+(le?"border-teal-300 bg-teal-50 text-teal-700":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"),children:[e(F,{checked:le,onCheckedChange:se,className:le?"data-[state=checked]:bg-teal-500 pointer-events-none":"pointer-events-none",tabIndex:-1}),"Phân biệt Hoa / thường"]})]}),t("div",{className:"space-y-2",children:[t(R,{htmlFor:"wfsf-title",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(H,{className:"h-4 w-4 text-teal-500"}),"Tiêu đề",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(O,{id:"wfsf-title",value:je,onChange:e=>He(e.target.value),placeholder:"Nhập tiêu đề câu hỏi...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100"})]}),t("div",{className:"space-y-2",children:[t(R,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(B,{className:"h-4 w-4 text-teal-500"}),"Hình ảnh",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(q,{id:"wfsf-image",label:"",accept:"image/*",value:Ge||"",onChange:e=>Be(e),onPresignedUrlChange:me,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),Re&&e(X,{src:Re,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),y&&y.length>0&&t("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),t("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:y.map((t,n)=>e("li",{children:t},n))})]})]})]})]}),ke&&t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e(J,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(x,{className:"h-3.5 w-3.5 text-teal-600"})}),e($,{className:"text-base font-semibold text-gray-800",children:"Nội dung cấu trúc"})]}),Ye.length>0&&t("span",{className:"inline-flex items-center rounded-full border border-teal-200 bg-teal-50 px-2.5 py-0.5 text-xs font-semibold text-teal-700",children:[Ye.length," chỗ trống"]})]})}),t(z,{className:"space-y-3 px-4 pb-4",children:[t("div",{className:"overflow-hidden rounded-xl border border-gray-200 transition-all focus-within:border-teal-300 focus-within:ring-2 focus-within:ring-teal-100",children:[e(te,{editor:Ke}),e(c,{editor:Ke})]}),t("div",{className:"flex items-start gap-2 rounded-lg border border-teal-100 bg-teal-50/60 px-3 py-2.5",children:[e(G,{className:"mt-0.5 h-4 w-4 shrink-0 text-teal-500"}),t("p",{className:"text-xs text-teal-700",children:["Nhập ",e("strong",{children:"{0}"}),", ",e("strong",{children:"{1}"}),", ",e("strong",{children:"{2}"}),"… vào văn bản để tạo ô trống. Đáp án sẽ tự động xuất hiện bên dưới để bạn nhập."]})]})]})]}),Ye.length>0&&t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e(J,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(T,{className:"h-3.5 w-3.5 text-teal-600"})}),t($,{className:"text-base font-semibold text-gray-800",children:["Đáp án ",t("span",{className:"text-xs font-normal text-gray-500",children:["(",Ye.length," chỗ trống)"]})]})]})}),t(z,{className:"px-4 pb-4",children:[e("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",children:Ye.map(n=>{const r=!Le[n]||""===Le[n].trim();return t("div",{className:"rounded-xl border-2 p-3 transition-all duration-200 "+(r?"border-gray-200 bg-white":"border-teal-200 bg-gradient-to-br from-teal-50 to-cyan-50 shadow-sm shadow-teal-100"),children:[t(R,{className:"mb-1.5 flex items-center justify-between text-sm font-semibold text-gray-700",children:[t("span",{children:["Chỗ trống ",e("code",{className:"rounded bg-gray-100 px-1 text-teal-700",children:`{${n}}`})]}),r&&e("span",{className:"text-[10px] font-normal italic text-red-500",children:"*Bắt buộc"})]}),e(O,{value:Le[n]||"",onChange:e=>Ee(t=>({...t,[n]:e.target.value})),placeholder:"Đáp án đúng...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100 "+(r?"":"border-teal-200")}),!r&&t("span",{className:"mt-1.5 inline-flex items-center gap-1 rounded-full bg-teal-100 px-2 py-0.5 text-[10px] font-semibold text-teal-700",children:[e(T,{className:"h-3 w-3"}),"Đã nhập"]})]},n)})}),Ye.some(e=>!Le[e]?.trim())&&t("div",{className:"mt-4 flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-amber-100",children:e("span",{className:"text-lg",children:"⚠️"})}),t("div",{className:"text-sm text-amber-800",children:[e("p",{className:"font-semibold",children:"Còn chỗ trống chưa có đáp án"}),t("p",{className:"mt-0.5 text-amber-700",children:["Vui lòng điền đáp án đúng cho tất cả các ô được đánh dấu ",e("span",{className:"text-red-500 font-medium",children:"*Bắt buộc"}),"."]})]})]})]})]}),t(E,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(J,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(A,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e($,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"mt-0.5 text-xs text-gray-500",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(z,{className:"px-4 pb-4",children:e(P,{value:ce,onChange:e=>oe(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] resize-none border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200"})})]})]})}
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as n,useEffect as r,useRef as a,useMemo as i,useCallback as s}from"react";import{useEditor as l,EditorContent as o}from"@tiptap/react";import c from"@tiptap/starter-kit";import d from"@tiptap/extension-text-align";import m from"@tiptap/extension-underline";import{Table as h,TableRow as g,TableCell as u,TableHeader as p}from"@tiptap/extension-table";import{List as f,ListOrdered as x,Bold as b,Italic as N,Underline as w,Strikethrough as v,AlignLeft as y,AlignCenter as C,AlignRight as k,TableIcon as I,Plus as S,Minus as T,Trash2 as O,Undo2 as A,Redo2 as B,BookOpen as H,Eye as U,Pencil as j,CheckCircle2 as W,PenLine as D,Info as P,MessageSquare as R,ImageIcon as _}from"lucide-react";import{Button as G}from"../../../../components/ui/button";import{Label as J}from"../../../../components/ui/label";import{Input as F}from"../../../../components/ui/input";import{Textarea as L}from"../../../../components/ui/textarea";import{Switch as E}from"../../../../components/ui/switch";import{PointsInput as K}from"../../../../components/ui/points-input";import{Card as M,CardContent as z,CardHeader as $,CardTitle as q}from"../../../../components/ui/card";import{FileUpload as V}from"../../../../components/ui/file-upload";import{ImagePreview as X}from"../../../../components/ui/image-preview";import{WordFillStructuredFormClient as Q}from"./WordFillStructuredFormClient";import{WfsfWordBankEditor as Y}from"./WfsfWordBankEditor";import{useDebouncedCallback as Z,usePresignedFileUrl as ee}from"../../../../shared/lib/hooks";import{BasicQuestionGroupCard as te}from"../../groups/BasicQuestionGroupCard";import{DocumentGroupCard as ne}from"../../groups/DocumentGroupCard";import{useBasicQuestionGroup as re,getGroupNumberForIndex as ae}from"../../_shared/hooks/useBasicQuestionGroup";import{bankPayloadForMode as ie,detectWfsfBankMode as se,emptySharedBank as le,normalizeOptionsWordBank as oe,normalizeSharedWordBank as ce}from"./wfsf-word-bank.utils";const de=({onClick:t,active:n,disabled:r,title:a,children:i,className:s=""})=>e("button",{type:"button",onClick:t,disabled:r,title:a,className:`inline-flex h-7 min-w-7 items-center justify-center rounded px-1.5 text-sm transition-colors disabled:opacity-30\n ${n?"bg-teal-100 text-teal-700":"text-gray-500 hover:bg-gray-100 hover:text-gray-800"}\n ${s}`,children:i}),me=()=>e("div",{className:"mx-1 h-4 w-px bg-gray-200"}),he=({editor:n})=>n?t("div",{className:"flex flex-wrap items-center gap-0.5 border-b border-gray-100 bg-gray-50/70 px-2 py-1.5 rounded-t-lg",children:[e(de,{onClick:()=>n.chain().focus().undo().run(),disabled:!n.can().chain().focus().undo().run(),title:"Hoàn tác",children:e(A,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().redo().run(),disabled:!n.can().chain().focus().redo().run(),title:"Làm lại",children:e(B,{className:"h-3.5 w-3.5"})}),e(me,{}),e(de,{onClick:()=>n.chain().focus().toggleBold().run(),active:n.isActive("bold"),title:"Đậm",children:e(b,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleItalic().run(),active:n.isActive("italic"),title:"Nghiêng",children:e(N,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleUnderline().run(),active:n.isActive("underline"),title:"Gạch dưới",children:e(w,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleStrike().run(),active:n.isActive("strike"),title:"Gạch ngang",children:e(v,{className:"h-3.5 w-3.5"})}),e(me,{}),e(de,{onClick:()=>n.chain().focus().toggleHeading({level:2}).run(),active:n.isActive("heading",{level:2}),className:"px-2 text-xs font-bold",children:"H2"}),e(de,{onClick:()=>n.chain().focus().toggleHeading({level:3}).run(),active:n.isActive("heading",{level:3}),className:"px-2 text-xs font-bold",children:"H3"}),e(de,{onClick:()=>n.chain().focus().toggleHeading({level:4}).run(),active:n.isActive("heading",{level:4}),className:"px-2 text-xs font-bold",children:"H4"}),e(me,{}),e(de,{onClick:()=>n.chain().focus().toggleBulletList().run(),active:n.isActive("bulletList"),title:"Danh sách",children:e(f,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().toggleOrderedList().run(),active:n.isActive("orderedList"),title:"Danh sách có số",children:e(x,{className:"h-3.5 w-3.5"})}),e(me,{}),e(de,{onClick:()=>n.chain().focus().setTextAlign("left").run(),active:n.isActive({textAlign:"left"}),title:"Trái",children:e(y,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().setTextAlign("center").run(),active:n.isActive({textAlign:"center"}),title:"Giữa",children:e(C,{className:"h-3.5 w-3.5"})}),e(de,{onClick:()=>n.chain().focus().setTextAlign("right").run(),active:n.isActive({textAlign:"right"}),title:"Phải",children:e(k,{className:"h-3.5 w-3.5"})}),e(me,{}),t(de,{onClick:()=>n.chain().focus().insertTable({rows:5,cols:2,withHeaderRow:!1}).run(),title:"Chèn bảng 2 cột × 5 hàng",className:"gap-1 px-2 text-xs",children:[e(I,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Bảng 2 cột"})]}),t(de,{onClick:()=>n.chain().focus().addRowAfter().run(),disabled:!n.can().addRowAfter(),title:"Thêm hàng",className:"gap-1 px-2 text-xs",children:[e(S,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(de,{onClick:()=>n.chain().focus().deleteRow().run(),disabled:!n.can().deleteRow(),title:"Xóa hàng",className:"gap-1 px-2 text-xs",children:[e(T,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Hàng"})]}),t(de,{onClick:()=>n.chain().focus().deleteTable().run(),disabled:!n.can().deleteTable(),title:"Xóa bảng",className:"gap-1 px-2 text-xs text-red-400 hover:text-red-600",children:[e(O,{className:"h-3 w-3"}),e("span",{className:"hidden sm:inline",children:"Bảng"})]})]}):null;export function WordFillStructuredFormCreator({initialData:f,onSave:b,onCancel:N,onChange:w,onUnsavedChangesChange:v,validationRef:y,externalErrors:C,groupConfig:k,groupType:I,groups:S,questionIndexInPart:T=0,partId:O="",viewMode:A}){const[B,de]=n(!1),[me,ge]=n(f?.points??1),[ue,pe]=n(f?.caseSensitive??!1),[fe,xe]=n(f?.instruction||f?.explanation||""),[be,Ne]=n(f?.explanation||""),[we,ve]=n(""),[ye,Ce]=n(f?.blankStyle),ke=se(f?.viewMode??A,f?.wordBank),[Ie,Se]=n(ke),[Te,Oe]=n(()=>"WITH_WORD_BANK"===ke?ce(f?.wordBank):[]),[Ae,Be]=n(()=>"WITH_OPTIONS"===ke?oe(f?.wordBank,Object.keys(f?.answers||{})):[]),{isGrouped:He,isFirstInGroup:Ue,currentGroupNumber:je,groupTitle:We,groupSubtitle:De,groupContent:Pe,imageUrl:Re,setGroupTitle:_e,setGroupSubtitle:Ge,setGroupContent:Je,setImageUrl:Fe}=re({partId:O,groups:S,questionIndexInPart:T,initialData:{title:f?.groupTitle||"",subtitle:f?.groupSubtitle||"",content:f?.groupContent||"",imageUrl:f?.groupImageUrl||""}}),Le=(I||"BASIC").toUpperCase(),Ee=!(k&&k.length>0)||k.includes("content"),Ke=je||ae(T,S),[Me,ze]=n(f?.title||""),[$e,qe]=n(f?.imageUrl||""),Ve=Me,Xe=$e,Qe=ze,Ye=qe,{previewUrl:Ze}=ee(Xe),et=we||Ze||"",tt=f?.content||"<p></p>",[nt,rt]=n(tt),[at,it]=n(f?.answers||{}),st=a(""),lt=a(f),ot=a(!1),ct=a(nt),dt=a(w);r(()=>{dt.current=w},[w]);const mt=i(()=>[c.configure({heading:{levels:[2,3,4]}}),m,d.configure({types:["heading","paragraph"]}),h.configure({resizable:!0}),g,u,p],[]),ht=i(()=>({attributes:{class:"tiptap-editor prose prose-sm max-w-none p-4 focus:outline-none min-h-[400px]"}}),[]),gt=s(({editor:e})=>{const t=e.getHTML();t!==ct.current&&(ct.current=t,rt(t))},[]),ut=l({immediatelyRender:!1,extensions:mt,content:nt,editorProps:ht,onUpdate:gt});r(()=>{ct.current=nt},[nt]),r(()=>{if(!(JSON.stringify(lt.current)!==JSON.stringify(f)))return;if(lt.current=f,!f?.content||"<p></p>"===f.content)return;ot.current=!0,ze(f?.title||""),qe(f?.imageUrl||""),ge(f?.points??1),pe(f?.caseSensitive??!1),xe(f?.instruction||f?.explanation||"");{const e=String(f?.instruction||f?.explanation||"").trim(),t=String(f?.explanation||"").trim();Ne(t&&t!==e?t:"")}it(f?.answers||{}),Ce(f?.blankStyle);const e=se(f?.viewMode??A,f?.wordBank);Se(e),"WITH_WORD_BANK"===e?Oe(ce(f?.wordBank)):"WITH_OPTIONS"===e?Be(oe(f?.wordBank,Object.keys(f?.answers||{}))):(Oe([]),Be([]));const t=f.content;t!==ct.current&&(ct.current=t,rt(t),ut&&!ut.isDestroyed&&ut.commands.setContent(t)),setTimeout(()=>{ot.current=!1},150)},[f,ut,A]);const pt=Z(e=>{dt.current?.(e)},300),ft=JSON.stringify(at),xt=i(()=>{const e=new Set,t=/\{([\w\d]+)\}/g;let n;for(;null!==(n=t.exec(nt));)e.add(n[1]);return Array.from(e).sort((e,t)=>{const n=parseInt(e),r=parseInt(t);return isNaN(n)||isNaN(r)?e.localeCompare(t):n-r})},[nt]);r(()=>{it(e=>{let t=!1;const n={...e};return Object.keys(n).forEach(e=>{xt.includes(e)||(delete n[e],t=!0)}),xt.forEach(e=>{e in n||(n[e]="",t=!0)}),t?n:e})},[xt]),r(()=>{"WITH_OPTIONS"===Ie&&Be(e=>{const t=oe(e,xt);return 0===e.length&&xt.length>0?xt.map(()=>null):JSON.stringify(e)===JSON.stringify(t)?e:t})},[Ie,xt]);const bt=JSON.stringify(Te),Nt=JSON.stringify(Ae),wt=s(()=>{const e=ie(Ie,Te,Ae);return{title:Ve,imageUrl:Xe,content:nt,answers:at,points:me,caseSensitive:ue,instruction:fe,explanation:be,blankStyle:ye,...e,groupTitle:We,groupSubtitle:De,groupContent:Pe,groupImageUrl:Re,groupNumber:Ke}},[Ie,Te,Ae,Ve,Xe,nt,at,me,ue,fe,be,ye,We,De,Pe,Re,Ke]),vt=s(e=>{Se(e),"WITH_WORD_BANK"===e?Oe(e=>e.length>0?e:le()):"WITH_OPTIONS"===e&&Be(e=>e.length===xt.length?e:e.length>0?oe(e,xt):xt.map(()=>({options:["",""]})))},[xt]);return r(()=>{const e=wt();if(ot.current)return void(st.current=JSON.stringify(e));const t=JSON.stringify(e);t!==st.current&&(st.current=t,pt(e))},[wt,ft,bt,Nt,Ie,ye]),r(()=>{y&&(y.current={triggerValidation:async()=>{if(!nt||"<p></p>"===nt)return!1;if(0===xt.length)return!1;for(const e of xt)if(!at[e]||""===at[e].trim())return!1;if("WITH_WORD_BANK"===Ie){if(Te.map(e=>e.word.trim()).filter(Boolean).length<1)return!1}if("WITH_OPTIONS"===Ie)for(let e=0;e<xt.length;e+=1){const t=Ae[e];if(null==t)continue;const n=t.options.map(e=>e.trim()).filter(Boolean);if(n.length<2)return!1;const r=xt[e],a=(at[r]||"").trim();if(a&&!n.includes(a))return!1}return!0},getFormData:()=>wt()})},[y,wt,nt,xt,at,Ie,Te,Ae]),t("div",B?{className:"space-y-4",children:[e(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:e($,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 shadow-sm",children:e(U,{className:"h-4 w-4 text-white"})}),t("div",{children:[e(q,{className:"text-base font-bold text-gray-900",children:"Điền từ – Xem trước"}),e("p",{className:"text-xs text-gray-500",children:"Chế độ xem trước với đáp án đúng đã được điền sẵn"})]})]}),t(G,{variant:"outline",size:"sm",onClick:()=>de(!1),className:"gap-2 border-teal-200 text-teal-700 hover:bg-teal-50",children:[e(j,{className:"h-4 w-4"}),"Quay lại Edit"]})]})})}),e(Q,{questionData:wt(),isReviewMode:!0,userAnswer:at,instruction:fe})]}:{className:"space-y-4",children:[He&&"BASIC"===Le&&e(te,{groupNumber:Ke,title:We,subtitle:De,content:Pe,imageUrl:Re,onTitleChange:_e,onSubtitleChange:Ge,onContentChange:Je,onImageChange:Fe,groupConfig:I?k:void 0,isReadOnly:!Ue,uploadIdPrefix:`word-fill-structured-${O??"group"}`}),He&&"DOCUMENT"===Le&&e(ne,{groupNumber:Ke,title:We,subtitle:De,content:Pe,imageUrl:Re,onTitleChange:_e,onSubtitleChange:Ge,onContentChange:Je,onImageChange:Fe,groupConfig:I?k:void 0,isReadOnly:!Ue,uploadIdPrefix:`word-fill-structured-${O??"document"}`}),t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-500 via-cyan-500 to-emerald-500"}),e($,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(D,{className:"h-4 w-4 text-teal-600"})}),t("div",{children:[e(q,{className:"text-base font-bold text-gray-900",children:"Điền từ vào biểu mẫu có cấu trúc"}),t("p",{className:"text-xs text-gray-500",children:["Nhập nội dung, đặt chỗ trống bằng ","{0}"," ","{1}","… rồi điền đáp án bên dưới"]})]})]}),t(G,{variant:"outline",size:"sm",onClick:()=>de(!0),disabled:0===xt.length||xt.some(e=>!at[e]?.trim()),className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(U,{className:"h-4 w-4"}),"Xem trước"]})]})}),t(z,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"space-y-2",children:[t(J,{htmlFor:"wfsf-instruction",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(P,{className:"h-4 w-4 text-teal-500"}),"Instruction",e("span",{className:"text-sm font-normal text-gray-500",children:"(hướng dẫn bài tập)"})]}),e(L,{id:"wfsf-instruction",value:fe,onChange:e=>xe(e.target.value),placeholder:"VD: Complete the words in the sentences. Use the words in the box.",rows:2,className:"min-h-[64px] resize-y border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100"})]}),t("div",{className:"flex flex-wrap items-center gap-3",children:[e(K,{value:me,onChange:e=>ge(Number(e.target.value)||1)}),t("div",{role:"button",tabIndex:0,onClick:()=>pe(!ue),onKeyDown:e=>{" "!==e.key&&"Enter"!==e.key||pe(!ue)},style:{paddingBlock:"calc(var(--spacing) * 1)"},className:"inline-flex cursor-pointer items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none "+(ue?"border-teal-300 bg-teal-50 text-teal-700":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"),children:[e(E,{checked:ue,onCheckedChange:pe,className:ue?"data-[state=checked]:bg-teal-500 pointer-events-none":"pointer-events-none",tabIndex:-1}),"Phân biệt Hoa / thường"]})]}),t("div",{className:"space-y-2",children:[t(J,{htmlFor:"wfsf-title",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(R,{className:"h-4 w-4 text-teal-500"}),"Tiêu đề",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(F,{id:"wfsf-title",value:Ve,onChange:e=>Qe(e.target.value),placeholder:"Nhập tiêu đề câu hỏi...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100"})]}),t("div",{className:"space-y-2",children:[t(J,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(_,{className:"h-4 w-4 text-teal-500"}),"Hình ảnh",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(V,{id:"wfsf-image",label:"",accept:"image/*",value:Xe||"",onChange:e=>Ye(e),onPresignedUrlChange:ve,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),et&&e(X,{src:et,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),C&&C.length>0&&t("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),t("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:C.map((t,n)=>e("li",{children:t},n))})]})]})]})]}),Ee&&t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e($,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(x,{className:"h-3.5 w-3.5 text-teal-600"})}),e(q,{className:"text-base font-semibold text-gray-800",children:"Nội dung cấu trúc"})]}),xt.length>0&&t("span",{className:"inline-flex items-center rounded-full border border-teal-200 bg-teal-50 px-2.5 py-0.5 text-xs font-semibold text-teal-700",children:[xt.length," chỗ trống"]})]})}),t(z,{className:"space-y-3 px-4 pb-4",children:[t("div",{className:"overflow-hidden rounded-xl border border-gray-200 transition-all focus-within:border-teal-300 focus-within:ring-2 focus-within:ring-teal-100",children:[e(he,{editor:ut}),e(o,{editor:ut})]}),t("div",{className:"flex items-start gap-2 rounded-lg border border-teal-100 bg-teal-50/60 px-3 py-2.5",children:[e(P,{className:"mt-0.5 h-4 w-4 shrink-0 text-teal-500"}),t("p",{className:"text-xs text-teal-700",children:["Nhập ",e("strong",{children:"{0}"}),", ",e("strong",{children:"{1}"}),", ",e("strong",{children:"{2}"}),"… vào văn bản để tạo ô trống. Đáp án sẽ tự động xuất hiện bên dưới để bạn nhập."]})]})]})]}),e(Y,{mode:Ie,onModeChange:vt,sharedBank:Te,onSharedBankChange:Oe,optionsBank:Ae,onOptionsBankChange:Be,blankKeys:xt,answers:at,onAnswerChange:(e,t)=>it(n=>({...n,[e]:t}))}),xt.length>0&&"WITH_OPTIONS"!==Ie&&t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e($,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(W,{className:"h-3.5 w-3.5 text-teal-600"})}),t(q,{className:"text-base font-semibold text-gray-800",children:["Đáp án ",t("span",{className:"text-xs font-normal text-gray-500",children:["(",xt.length," chỗ trống)"]})]})]})}),t(z,{className:"px-4 pb-4",children:[e("div",{className:"grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4",children:xt.map(n=>{const r=!at[n]||""===at[n].trim();return t("div",{className:"rounded-xl border-2 p-3 transition-all duration-200 "+(r?"border-gray-200 bg-white":"border-teal-200 bg-gradient-to-br from-teal-50 to-cyan-50 shadow-sm shadow-teal-100"),children:[t(J,{className:"mb-1.5 flex items-center justify-between text-sm font-semibold text-gray-700",children:[t("span",{children:["Chỗ trống ",e("code",{className:"rounded bg-gray-100 px-1 text-teal-700",children:`{${n}}`})]}),r&&e("span",{className:"text-[10px] font-normal italic text-red-500",children:"*Bắt buộc"})]}),e(F,{value:at[n]||"",onChange:e=>it(t=>({...t,[n]:e.target.value})),placeholder:"Đáp án đúng...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-100 "+(r?"":"border-teal-200")}),!r&&t("span",{className:"mt-1.5 inline-flex items-center gap-1 rounded-full bg-teal-100 px-2 py-0.5 text-[10px] font-semibold text-teal-700",children:[e(W,{className:"h-3 w-3"}),"Đã nhập"]})]},n)})}),xt.some(e=>!at[e]?.trim())&&t("div",{className:"mt-4 flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-amber-100",children:e("span",{className:"text-lg",children:"⚠️"})}),t("div",{className:"text-sm text-amber-800",children:[e("p",{className:"font-semibold",children:"Còn chỗ trống chưa có đáp án"}),t("p",{className:"mt-0.5 text-amber-700",children:["Vui lòng điền đáp án đúng cho tất cả các ô được đánh dấu ",e("span",{className:"text-red-500 font-medium",children:"*Bắt buộc"}),"."]})]})]})]})]}),t(M,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e($,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(H,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e(q,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"mt-0.5 text-xs text-gray-500",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(z,{className:"px-4 pb-4",children:e(L,{value:be,onChange:e=>Ne(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] resize-none border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200"})})]})]})}
@@ -1 +1 @@
1
- import{WordFillStructuredFormCreator as t}from"./WordFillStructuredFormCreator";export const wordFillStructuredFormRegistration={component:t,transformInitialData:t=>{const e=t?.content||{},n=t?.answer||{},o=t?.correctAnswer||n?.correctAnswer||{},a=t?.total_points?parseFloat(t.total_points):n?.points||t?.points||1,i=t?.explanation||n?.explanation||"";let s=e?.content||n?.content||"";const r=e?.blocks||n?.blocks;!s&&r?.length&&(s=r.map(t=>(t.heading?`<h3>${t.heading}</h3>`:"")+(t.content||"")).join(""));const l=t?.questionGroup?.payload;return{title:e?.title||n?.title||"",imageUrl:e?.imageUrl||n?.imageUrl||"",content:s,answers:o?.answers||n?.answers||{},caseSensitive:o?.caseSensitive??n?.caseSensitive??!1,explanation:i,points:a,groupTitle:l?.title||n?.groupTitle||"",groupSubtitle:l?.subtitle||n?.groupSubtitle||"",groupContent:l?.content||n?.groupContent||"",groupImageUrl:l?.imageUrl||n?.groupImageUrl||""}},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef,groupConfig:t.groupConfig,groupType:t.groupType,groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
1
+ import{WordFillStructuredFormCreator as t}from"./WordFillStructuredFormCreator";import{detectWfsfBankMode as e,normalizeOptionsWordBank as n,normalizeSharedWordBank as o}from"./wfsf-word-bank.utils";export const wordFillStructuredFormRegistration={component:t,transformInitialData:t=>{const i=t?.content||{},a=t?.answer||{},r=t?.correctAnswer||a?.correctAnswer||{},s=t?.total_points?parseFloat(t.total_points):a?.points||t?.points||1,l=String(a?.instruction||t?.instruction||t?.explanation||""),p=String(a?.explanation||"");let u=i?.content||a?.content||"";const c=i?.blocks||a?.blocks;!u&&c?.length&&(u=c.map(t=>(t.heading?`<h3>${t.heading}</h3>`:"")+(t.content||"")).join(""));const g=i?.wordBank??a?.wordBank,d=e(i?.viewMode??a?.viewMode,g),w=function(t){const e=new Set,n=/\{([\w\d]+)\}/g;let o;for(;null!==(o=n.exec(t));)e.add(o[1]);return Array.from(e).sort((t,e)=>{const n=parseInt(t,10),o=parseInt(e,10);return Number.isNaN(n)||Number.isNaN(o)?t.localeCompare(e):n-o})}(String(u||""));let f=g,m="FREE_TYPE"===d?void 0:d;"WITH_WORD_BANK"===d?(f=o(g),m="WITH_WORD_BANK"):"WITH_OPTIONS"===d?(f=n(g,w),m="WITH_OPTIONS"):f=void 0;const v=t?.questionGroup?.payload;return{title:i?.title||a?.title||"",imageUrl:i?.imageUrl||a?.imageUrl||"",content:u,answers:r?.answers||a?.answers||{},caseSensitive:r?.caseSensitive??a?.caseSensitive??!1,blankStyle:i?.blankStyle??a?.blankStyle,viewMode:m,wordBank:f,instruction:l,explanation:p,points:s,groupTitle:v?.title||a?.groupTitle||"",groupSubtitle:v?.subtitle||a?.groupSubtitle||"",groupContent:v?.content||a?.groupContent||"",groupImageUrl:v?.imageUrl||a?.groupImageUrl||""}},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef,groupConfig:t.groupConfig,groupType:t.groupType,groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId,viewMode:t.viewMode}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
@@ -2,9 +2,12 @@ import type { TransformHandler } from '../../../../shared/lib/utils/question-tra
2
2
  /**
3
3
  * Transform WORD_FILL_STRUCTURED_FORM creator data → API format
4
4
  *
5
- * Creator (question.answer): { title, imageUrl, content, answers, caseSensitive, points, explanation }
5
+ * Creator (question.answer): {
6
+ * title, imageUrl, content, answers, caseSensitive, points, explanation,
7
+ * blankStyle?, viewMode?, wordBank?
8
+ * }
6
9
  * API:
7
- * content: { title, imageUrl, content }
10
+ * content: { title, imageUrl, content, blankStyle?, viewMode?, wordBank? }
8
11
  * correctAnswer: { answers, caseSensitive }
9
12
  */
10
13
  export declare const transformWordFillStructuredForm: TransformHandler;
@@ -1 +1 @@
1
- export const transformWordFillStructuredForm=e=>{const t=e.answer;if(!t?.content||!t?.answers)return{apiContent:{},apiCorrectAnswer:{}};const r=!!(t.groupTitle||t.groupSubtitle||t.groupContent||t.groupImageUrl),n={title:t.title||"",imageUrl:t.imageUrl||"",content:t.content};t.blankStyle&&(n.blankStyle=t.blankStyle),t.viewMode&&(n.viewMode=t.viewMode),Array.isArray(t.wordBank)&&t.wordBank.length>0&&(n.wordBank=t.wordBank);return{apiContent:n,apiCorrectAnswer:{answers:t.answers,caseSensitive:t.caseSensitive??!1},basicGroupData:r?{title:t.groupTitle||"",subtitle:t.groupSubtitle||"",content:t.groupContent||"",imageUrl:t.groupImageUrl||""}:void 0,groupNumber:t.groupNumber||1}};
1
+ export const transformWordFillStructuredForm=e=>{const r=e.answer;if(!r?.content||!r?.answers)return{apiContent:{},apiCorrectAnswer:{}};const t=!!(r.groupTitle||r.groupSubtitle||r.groupContent||r.groupImageUrl),n={title:r.title||"",imageUrl:r.imageUrl||"",content:r.content};r.blankStyle&&(n.blankStyle=r.blankStyle),r.viewMode&&(n.viewMode=r.viewMode),Array.isArray(r.wordBank)&&r.wordBank.length>0&&(n.wordBank=r.wordBank),"letter"!==r.blankStyle&&"word"!==r.blankStyle&&"phrase"!==r.blankStyle||(n.blankStyle=r.blankStyle),"WITH_OPTIONS"!==r.viewMode&&"WITH_WORD_BANK"!==r.viewMode||(n.viewMode=r.viewMode),Array.isArray(r.wordBank)&&(n.wordBank=r.wordBank);return{apiContent:n,apiCorrectAnswer:{answers:r.answers,caseSensitive:r.caseSensitive??!1},basicGroupData:t?{title:r.groupTitle||"",subtitle:r.groupSubtitle||"",content:r.groupContent||"",imageUrl:r.groupImageUrl||""}:void 0,groupNumber:r.groupNumber||1}};
@@ -0,0 +1,24 @@
1
+ import type { WfsfOptionsWordBankEntry, WfsfSharedWordBankEntry, WfsfViewMode, WfsfWordBankEntry } from '../../_shared/types/word-fill-structured-form.type';
2
+ export type WfsfBankMode = 'FREE_TYPE' | WfsfViewMode;
3
+ export declare function isSharedWordEntry(entry: unknown): entry is WfsfSharedWordBankEntry;
4
+ export declare function isOptionsWordEntry(entry: unknown): entry is {
5
+ options: string[];
6
+ };
7
+ /** Detect mode from explicit viewMode and/or wordBank shape. */
8
+ export declare function detectWfsfBankMode(viewMode: unknown, wordBank: unknown): WfsfBankMode;
9
+ export declare function normalizeSharedWordBank(wordBank: unknown): WfsfSharedWordBankEntry[];
10
+ /**
11
+ * Align options bank length to blank count (numeric keys preferred).
12
+ * Non-numeric blank keys still get sequential slots by sorted order.
13
+ */
14
+ export declare function normalizeOptionsWordBank(wordBank: unknown, blankKeys: string[]): WfsfOptionsWordBankEntry[];
15
+ export declare function emptyOptionsBank(blankCount: number): WfsfOptionsWordBankEntry[];
16
+ export declare function emptySharedBank(): WfsfSharedWordBankEntry[];
17
+ /**
18
+ * Payload slice for save / onChange.
19
+ * FREE_TYPE sets viewMode: null so host can clear preserved bank fields.
20
+ */
21
+ export declare function bankPayloadForMode(mode: WfsfBankMode, sharedBank: WfsfSharedWordBankEntry[], optionsBank: WfsfOptionsWordBankEntry[]): {
22
+ viewMode: WfsfViewMode | null;
23
+ wordBank?: WfsfWordBankEntry[];
24
+ };
@@ -0,0 +1 @@
1
+ export function isSharedWordEntry(r){return!!r&&"object"==typeof r&&!Array.isArray(r)&&"word"in r&&!("options"in r)}export function isOptionsWordEntry(r){return!!r&&"object"==typeof r&&!Array.isArray(r)&&Array.isArray(r.options)}export function detectWfsfBankMode(r,n){if("WITH_OPTIONS"===r)return"WITH_OPTIONS";if("WITH_WORD_BANK"===r)return"WITH_WORD_BANK";if(!Array.isArray(n)||0===n.length)return"FREE_TYPE";const t=n.find(r=>null!=r);return isOptionsWordEntry(t)?"WITH_OPTIONS":isSharedWordEntry(t)?"WITH_WORD_BANK":n.some(r=>isOptionsWordEntry(r))?"WITH_OPTIONS":"FREE_TYPE"}export function normalizeSharedWordBank(r){return Array.isArray(r)?r.filter(isSharedWordEntry).map(r=>({word:String(r.word??""),...r.isExample?{isExample:!0}:{}})):[]}export function normalizeOptionsWordBank(r,n){const t=Array.isArray(r)?r:[],o=n.length,e=[];for(let r=0;r<o;r+=1){const o=n[r],i=parseInt(o,10),a=t[Number.isNaN(i)?r:i]??t[r]??null;null!=a?isOptionsWordEntry(a)&&a.options.length>=1?e.push({options:a.options.map(r=>String(r??""))}):e.push(null):e.push(null)}return e}export function emptyOptionsBank(r){return Array.from({length:r},()=>({options:["",""]}))}export function emptySharedBank(){return[{word:""}]}export function bankPayloadForMode(r,n,t){if("WITH_WORD_BANK"===r){return{viewMode:"WITH_WORD_BANK",wordBank:n.map(r=>({word:r.word.trim(),...r.isExample?{isExample:!0}:{}})).filter(r=>r.word.length>0)}}if("WITH_OPTIONS"===r){return{viewMode:"WITH_OPTIONS",wordBank:t.map(r=>{if(null==r)return null;const n=r.options.map(r=>r.trim()).filter(Boolean);return n.length<2?null:{options:n}})}}return{viewMode:null}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Reusable OceanEdu question components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "build:alias": "tsc-alias -p tsconfig.build.json",
23
23
  "build:ts": "npm run build:compile && npm run build:assets && npm run build:alias",
24
24
  "build:assets": "for d in cambridge-yle english-certification summer-sky; do [ -d src/components/themes/$d/assets ] && cp -R src/components/themes/$d/assets dist/components/themes/$d/ || true; done; [ -d src/components/shared/audio/assets ] && cp -R src/components/shared/audio/assets dist/components/shared/audio/ || true; [ -d src/components/results/report/assets ] && mkdir -p dist/components/results/report && cp -R src/components/results/report/assets dist/components/results/report/ || true; (cd src && find . -name '*.module.css' -exec cp --parents {} ../dist/ \\;); cp src/global.d.ts dist/global.d.ts",
25
- "minify:js": "find dist -name '*.js' -type f -exec sh -c 'for f do terser \"$f\" --compress directives=false --mangle --module --comments false --output \"$f\"; done' sh {} +",
25
+ "minify:js": "find dist -name '*.js' -type f -exec sh -c 'for f do npx terser \"$f\" --compress directives=false --mangle --module --comments false --output \"$f\"; done' sh {} +",
26
26
  "build": "npm run clean && npm run build:ts && npm run minify:js",
27
27
  "pack:local": "npm pack",
28
28
  "release": "npm run release:patch",
@@ -101,6 +101,11 @@
101
101
  },
102
102
  "devDependencies": {
103
103
  "@types/node": "^25.9.3",
104
+ "@types/react": "^19.2.18",
105
+ "@types/react-dom": "^19.2.4",
106
+ "next": "^16.3.0",
107
+ "react": "^19.2.8",
108
+ "react-dom": "^19.2.8",
104
109
  "terser": "^5.48.0",
105
110
  "tsc-alias": "^1.8.17",
106
111
  "typescript": "~5.9.2"
@@ -137,6 +142,7 @@
137
142
  "@tiptap/extension-table-header": "^3.22.3",
138
143
  "@tiptap/extension-table-row": "^3.22.3",
139
144
  "@tiptap/extension-text-align": "^3.22.3",
145
+ "@tiptap/extension-underline": "^3.27.0",
140
146
  "@tiptap/pm": "^3.22.3",
141
147
  "@tiptap/react": "^3.22.3",
142
148
  "@tiptap/starter-kit": "^3.22.3",