openlearn-next 0.7.13 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli-data.mjs +2 -2
- package/cli-doctor.mjs +2 -1
- package/dist/assets/{AcademicYearFilter-Dm3no4Nb.js → AcademicYearFilter-pCzZgimU.js} +1 -1
- package/dist/assets/ActivityWorkspaceWidget-KJQdLnvf.js +1 -0
- package/dist/assets/AdminDirectoryView-DDxX8RO2.js +5 -0
- package/dist/assets/AssessmentCenter-D5cVUqvy.js +1 -0
- package/dist/assets/{AssignmentFilePreview-7fHtUuUL.js → AssignmentFilePreview-DK29gdpQ.js} +2 -2
- package/dist/assets/BatchPickerModal-Cw4OZqOJ.js +1 -0
- package/dist/assets/ClassesView-Czx7hU2V.js +32 -0
- package/dist/assets/ClassroomCountdownWidget-BeAM37Cq.js +2 -0
- package/dist/assets/CloudDriveModal-GNZzWVCP.js +1 -0
- package/dist/assets/ComputerLabView-C_0c7sjz.js +1 -0
- package/dist/assets/CourseManagement-DzLB0Xmp.js +1 -0
- package/dist/assets/CourseWizardModal-DmQu237c.js +3 -0
- package/dist/assets/CoursewareHubPanel-Xn5WbXg9.js +1 -0
- package/dist/assets/Dashboard-CX83FUeF.js +8 -0
- package/dist/assets/ExportWeightModal-C0SuUhHM.js +1 -0
- package/dist/assets/HelpTour-D8hVFA6N.js +1 -0
- package/dist/assets/HelpView-C0PP_51D.js +204 -0
- package/dist/assets/ImportLessonsModal-p8Uc1U_h.js +1 -0
- package/dist/assets/{ImportModal-CYU3aMYY.js → ImportModal-DNaPHV0I.js} +2 -2
- package/dist/assets/InteractiveCoursewareViewer-D9YTgasv.js +1 -0
- package/dist/assets/InteractiveWhiteboard-CHYQfRjR.js +19 -0
- package/dist/assets/LazyCourseware-dtuOSiyR.js +2 -0
- package/dist/assets/LazyWhiteboard-DuBKAh4-.js +2 -0
- package/dist/assets/LessonEditorView-BgI7C_FK.js +1 -0
- package/dist/assets/LiveClassroomView-CJK_WMqD.js +67 -0
- package/dist/assets/NotificationDetailModal-BVYuS4uR.js +1 -0
- package/dist/assets/PluginView-CpWjy0Xf.js +66 -0
- package/dist/assets/ProcessLogsModal-D-Ez_YZA.js +1 -0
- package/dist/assets/QuizGeneratorModal-Lf5QMvkq.js +1 -0
- package/dist/assets/RevealPresentationWrapper-Dnf4v72i.js +48 -0
- package/dist/assets/StudentAssignmentEvalPanel-C-7YSfEQ.js +1 -0
- package/dist/assets/StudentAssignmentView-LuMaNpq8.js +1 -0
- package/dist/assets/StudentLessonView-DUG8I1Zy.js +1 -0
- package/dist/assets/StudentView-mMwGPiiF.js +2 -0
- package/dist/assets/SystemResourceLibraryModal-LUDNOwgW.js +1 -0
- package/dist/assets/TeacherView-ofB-3SEJ.js +2 -0
- package/dist/assets/TimetableView-DrZbyCOQ.js +6 -0
- package/dist/assets/{UTIF-u3Ct70K4.js → UTIF-DbcUtUKZ.js} +1 -1
- package/dist/assets/index-CG-g_9my.css +1 -0
- package/dist/assets/index-CLtQGl25.js +169 -0
- package/dist/assets/{index.es-CcQ2kcDj.js → index.es-D1mLdmCj.js} +1 -1
- package/dist/assets/keyboard-Rs007iOv.js +1 -0
- package/dist/assets/{pluginParsers-BN3lQ7PJ.js → pluginParsers-C46aCYkl.js} +1 -1
- package/dist/assets/student-workspace-BFPfR77J.js +1 -0
- package/dist/assets/{vendor-charts-skggaP9D.js → vendor-charts-CK-CU_m7.js} +1 -1
- package/dist/assets/{vendor-icons-CqLdl4B_.js → vendor-icons-__JLt-CP.js} +2 -2
- package/dist/assets/{vendor-konva-BOuJWFuE.js → vendor-konva-DGELdczq.js} +1 -1
- package/dist/assets/{vendor-motion-BFAPOs-b.js → vendor-motion-DtyD8eRQ.js} +1 -1
- package/dist/assets/{vendor-pdf-BMEc_Clb.js → vendor-pdf-DNexfcCO.js} +3 -3
- package/dist/assets/{vendor-pptx-C0qaOH5T.js → vendor-pptx-ByQkJ0Xi.js} +4 -4
- package/dist/assets/{vendor-react-DHoBZpn2.js → vendor-react-lfWn1oGs.js} +1 -1
- package/dist/assets/{vendor-utils-BxOuk3id.js → vendor-utils-CsoI4M9x.js} +1 -1
- package/dist/cli-migrator.cjs +23 -7
- package/dist/index.html +10 -6
- package/dist/server.cjs +11139 -4094
- package/docs_plugin_guide.md +1023 -0
- package/migrations/046_logout_clear_site_data.sql +9 -0
- package/migrations/047_third_party_theme_system.sql +55 -0
- package/migrations/048_file_storage_extension.sql +63 -0
- package/migrations/049_ai_personas.sql +25 -0
- package/migrations/050_ai_persona_governance.sql +11 -0
- package/migrations/051_community_registry_url.sql +9 -0
- package/migrations/052_community_install_enabled.sql +12 -0
- package/migrations/053_runtime_admin_policies.sql +28 -0
- package/migrations/054_user_onboarding_tour_status.sql +10 -0
- package/migrations/055_classroom_collab_whiteboard.sql +27 -0
- package/migrations/056_public_student_lookup.sql +9 -0
- package/migrations/README.md +59 -48
- package/migrations/schema-golden.json +98 -2
- package/package.json +7 -6
- package/scripts/assert-node-runtime.mjs +4 -3
- package/dist/assets/ActivityWorkspaceWidget-9DeXkxGk.js +0 -1
- package/dist/assets/AdminDirectoryView-5gupeGAR.js +0 -5
- package/dist/assets/AssessmentCenter-CPaqy0Xa.js +0 -1
- package/dist/assets/BatchPickerModal-0yIe1DXi.js +0 -1
- package/dist/assets/ClassesView-CGpoW0IC.js +0 -32
- package/dist/assets/ClassroomCountdownWidget-B_5--dn_.js +0 -2
- package/dist/assets/CloudDriveModal-4Ts5Duh5.js +0 -1
- package/dist/assets/ComputerLabView-DAEaKTBj.js +0 -1
- package/dist/assets/CourseManagement-CxIBH7lA.js +0 -1
- package/dist/assets/CourseWizardModal-R0xpa_Aw.js +0 -3
- package/dist/assets/CoursewareHubPanel-CpHptmSa.js +0 -1
- package/dist/assets/Dashboard-DeBfP_Nc.js +0 -8
- package/dist/assets/ExportWeightModal-DlnCOBlL.js +0 -1
- package/dist/assets/HelpTour-Cztpqw_J.js +0 -1
- package/dist/assets/HelpView-LPLAY9bq.js +0 -204
- package/dist/assets/ImportLessonsModal-BopEHmU_.js +0 -1
- package/dist/assets/InteractiveCoursewareViewer-CGc3P4HF.js +0 -1
- package/dist/assets/InteractiveWhiteboard-DiyHrIGn.js +0 -19
- package/dist/assets/LazyCourseware-Bj1jm0nC.js +0 -2
- package/dist/assets/LazyWhiteboard-NPD1EoyO.js +0 -2
- package/dist/assets/LessonEditorView-DqreBtFx.js +0 -1
- package/dist/assets/LiveClassroomView-eTc6Fb9g.js +0 -64
- package/dist/assets/NotificationDetailModal-B7G7ra9a.js +0 -1
- package/dist/assets/PluginView-DP6s-L70.js +0 -77
- package/dist/assets/ProcessLogsModal-Dq5C4s7j.js +0 -1
- package/dist/assets/QuizGeneratorModal-CoX9f8Wp.js +0 -1
- package/dist/assets/RevealPresentationWrapper-wwlcfNWB.js +0 -48
- package/dist/assets/StudentAssignmentEvalPanel-DHb-asHQ.js +0 -1
- package/dist/assets/StudentAssignmentView-AJpjQK3f.js +0 -1
- package/dist/assets/StudentLessonView-Djd1T1QF.js +0 -1
- package/dist/assets/StudentView-D41z7GY5.js +0 -2
- package/dist/assets/SystemResourceLibraryModal-CxmQh1HA.js +0 -1
- package/dist/assets/TeacherView-DqSWaNuI.js +0 -2
- package/dist/assets/TimetableView-DrFs_xgc.js +0 -6
- package/dist/assets/index-7CK1B0tE.js +0 -131
- package/dist/assets/index-BaGZmpr6.css +0 -1
- package/dist/assets/student-workspace-DkTkcAGK.js +0 -1
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ npm update -g openlearn-next # 更新
|
|
|
74
74
|
### 方式 C:源码本地开发
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
# 1. 克隆仓库并安装依赖(建议 Node
|
|
77
|
+
# 1. 克隆仓库并安装依赖(建议 Node 24 + pnpm)
|
|
78
78
|
git clone https://github.com/aymwoo/OpenLearn-Next-V2.git openlearnv2
|
|
79
79
|
# 国内网络访问 GitHub 受限时,改用 Gitee 镜像(内容一致,单向同步):
|
|
80
80
|
# git clone https://gitee.com/aymwoo/openlearn-next.git openlearnv2
|
package/cli-data.mjs
CHANGED
|
@@ -83,7 +83,7 @@ export function loadCliMigrator() {
|
|
|
83
83
|
if (!fs.existsSync(artifact)) {
|
|
84
84
|
const error = new Error(
|
|
85
85
|
`迁移器构建产物缺失:${artifact}\n` +
|
|
86
|
-
' 源码检出请先执行 `pnpm build
|
|
86
|
+
' 源码检出请先执行 `pnpm build`;\n' +
|
|
87
87
|
' 若你安装的是发布包,这属于打包缺陷,请反馈。',
|
|
88
88
|
);
|
|
89
89
|
error.code = 'CLI_MIGRATOR_ARTIFACT_MISSING';
|
|
@@ -212,7 +212,7 @@ export async function runBackup(outputFile, options = {}) {
|
|
|
212
212
|
log(`✓ 数据库冷备完成!`);
|
|
213
213
|
log(` 快照文件: ${target} (${sizeKb} KB)`);
|
|
214
214
|
return { ok: true, targetFile: target };
|
|
215
|
-
} catch
|
|
215
|
+
} catch {
|
|
216
216
|
// 降级为物理文件复制
|
|
217
217
|
try {
|
|
218
218
|
fs.copyFileSync(dbPath, target);
|
package/cli-doctor.mjs
CHANGED
|
@@ -854,7 +854,8 @@ export const CORE_EXTENSION_POINTS = [
|
|
|
854
854
|
kind: 'event',
|
|
855
855
|
since: '0.1.0',
|
|
856
856
|
deprecated: '0.6.0',
|
|
857
|
-
|
|
857
|
+
// Issue #414:平台仍在发布旧事件,0.7.0 移除声明与实际不符,顺延到 0.8.0。
|
|
858
|
+
removedIn: '0.8.0',
|
|
858
859
|
replacement: "Use 'quiz.answered' instead",
|
|
859
860
|
},
|
|
860
861
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as r}from"./vendor-react-
|
|
1
|
+
import{j as r}from"./vendor-react-lfWn1oGs.js";function c({value:a,options:i,onChange:t,lang:o="zh",includeAll:n=!0,className:s=""}){return r.jsxs("select",{value:a,onChange:e=>t(e.target.value),className:`text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold ${s}`,"aria-label":o==="zh"?"学年筛选":"Academic year filter",children:[n&&r.jsx("option",{value:"all",children:o==="zh"?"全部学年":"All academic years"}),i.map(e=>r.jsx("option",{value:e,children:e},e))]})}export{c as A};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as o,j as e}from"./vendor-react-lfWn1oGs.js";import{f as D}from"./index-CLtQGl25.js";import{a7 as q,X as H,s as J,r as Q,B as V,k as X,Z,O as _,U as G,ba as K,b9 as Y,bb as ee,h as te,P as se}from"./vendor-icons-__JLt-CP.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";async function ie(n="all"){const a=await fetch(`/api/activities?role=${encodeURIComponent(n)}`,{headers:{Accept:"application/json"}});if(!a.ok)throw new Error(`Failed to load activities (${a.status})`);return(await a.json()).activities??[]}async function k(){const n=await fetch("/api/activities/running",{headers:{Accept:"application/json"}});if(!n.ok)throw new Error(`Failed to load running activities (${n.status})`);return(await n.json()).activities??[]}async function ne(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/finish`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to finish activity (${a.status})`)}}async function ae(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/pause`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to pause activity (${a.status})`)}}async function re(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/resume`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to resume activity (${a.status})`)}}async function oe(n,a,s){const l=await fetch(`/api/activities/${encodeURIComponent(n)}/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({payload:a??{},actorId:s})}),c=await l.json();if(!l.ok||c.ok===!1)throw new Error(c.error||`Failed to start activity (${l.status})`);return c}const x={padding:"12px",color:"#e2e8f0",fontSize:"13px"},ce={HelpCircle:te,Vote:ee,BarChart3:Y,MessagesSquare:K,Users:G,FileText:_,Trophy:Z,CheckCircle2:X,BookOpen:V};function le({name:n,className:a}){const s=n&&ce[n]||se;return e.jsx(s,{size:18,className:a})}function de(n,a){if(!n)return a==="zh"?"进行中":"In progress";const s=Math.max(0,Math.floor((Date.now()-n)/1e3)),l=Math.floor(s/60),c=s%60;return a==="zh"?`${l} 分 ${c} 秒`:`${l}m ${c}s`}const be=({role:n,actorId:a,lang:s="en",classroomId:l,mode:c="launcher"})=>{const[p,E]=o.useState([]),[A,b]=o.useState(!0),[j,$]=o.useState(null),[v,m]=o.useState({}),[h,w]=o.useState([]),[T,R]=o.useState(!0),[P,I]=o.useState(null),[y,u]=o.useState(null),[f,z]=o.useState(!1),[S,C]=o.useState(null),B=D(i=>i.setTeacherTab),F=o.useMemo(()=>{const i=new Map;for(const t of p){const r=t.category;i.has(r)||i.set(r,[]),i.get(r).push(t)}return Array.from(i.entries())},[p]);o.useEffect(()=>{if(c!=="launcher")return;let i=!1;return b(!0),ie(n).then(t=>{i||E(t)}).catch(t=>{i||$(t instanceof Error?t.message:String(t))}).finally(()=>{i||b(!1)}),()=>{i=!0}},[n,c]),o.useEffect(()=>{if(c!=="status")return;let i=!1;const t=async()=>{try{const d=await k();i||w(d)}catch(d){i||I(d instanceof Error?d.message:String(d))}finally{i||R(!1)}};t();const r=setInterval(t,5e3);return()=>{i=!0,clearInterval(r)}},[c]);const g=async i=>{if(y){z(!0),C(null);try{await i(y),w(await k())}catch(t){C(t instanceof Error?t.message:String(t))}finally{z(!1)}}},O=()=>g(ne),W=()=>g(ae),M=()=>g(re),L=()=>{u(null),B("live_class")};if(c==="status"){if(T)return e.jsx("div",{className:"bg-white border border-slate-200/80 rounded-2xl p-5 shadow-sm h-24 animate-pulse"});if(P||h.length===0)return null;const i=h.find(t=>t.id===y)||null;return e.jsxs("div",{className:"bg-white border border-slate-200/80 rounded-2xl p-5 shadow-sm",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-slate-800 font-extrabold",children:[e.jsx("span",{children:"🧩"}),e.jsx("span",{children:s==="zh"?"进行中的活动":"Activities in Progress"})]}),e.jsx("span",{className:"text-xs font-bold bg-indigo-50 text-indigo-700 px-2 py-0.5 rounded-full",children:h.length})]}),e.jsx("div",{className:"flex flex-col gap-2",children:h.map(t=>{const r=t.state==="running";return e.jsxs("button",{onClick:()=>u(t.id),className:"w-full flex items-center gap-3 text-left bg-slate-50 hover:bg-slate-100 border border-slate-200/70 rounded-xl p-3 transition-colors",children:[e.jsx(le,{name:t.icon,className:"text-indigo-600 shrink-0"}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"font-bold text-slate-800 truncate",children:t.name}),t.category&&e.jsx("div",{className:"text-xs text-slate-500 truncate",children:t.category})]}),e.jsxs("div",{className:"flex flex-col items-end gap-1 shrink-0",children:[e.jsx("span",{className:`text-xs font-bold px-2 py-0.5 rounded-full ${r?"bg-emerald-100 text-emerald-700":"bg-amber-100 text-amber-700"}`,children:r?s==="zh"?"进行中":"Running":s==="zh"?"已暂停":"Paused"}),e.jsxs("span",{className:"text-xs text-slate-400 flex items-center gap-1",children:[e.jsx(q,{size:10})," ",de(t.startedAt,s)]})]})]},t.id)})}),i&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4",onClick:()=>u(null),children:e.jsxs("div",{className:"bg-white rounded-2xl shadow-xl w-full max-w-sm p-5",onClick:t=>t.stopPropagation(),children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("h3",{className:"font-bold text-slate-800",children:i.name}),e.jsx("button",{onClick:()=>u(null),className:"text-slate-400 hover:text-slate-600","aria-label":"close",children:e.jsx(H,{size:18})})]}),e.jsxs("div",{className:"text-sm text-slate-500 mb-1",children:[s==="zh"?"状态":"Status",":"," ",i.state==="running"?s==="zh"?"进行中":"Running":s==="zh"?"已暂停":"Paused"]}),e.jsxs("div",{className:"text-sm text-slate-500 mb-4",children:[s==="zh"?"开始时间":"Started",":"," ",i.startedAt?new Date(i.startedAt).toLocaleString():"-"]}),S&&e.jsx("div",{className:"text-xs text-rose-600 mb-2",children:S}),e.jsxs("div",{className:"flex gap-2",children:[i.state==="running"?e.jsxs("button",{disabled:f,onClick:W,className:"flex-1 flex items-center justify-center gap-1 bg-amber-500 hover:bg-amber-600 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:[e.jsx(J,{size:14})," ",s==="zh"?"暂停":"Pause"]}):e.jsxs("button",{disabled:f,onClick:M,className:"flex-1 flex items-center justify-center gap-1 bg-indigo-500 hover:bg-indigo-600 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:[e.jsx(Q,{size:14})," ",s==="zh"?"恢复":"Resume"]}),e.jsx("button",{disabled:f,onClick:O,className:"flex-1 flex items-center justify-center gap-1 bg-rose-600 hover:bg-rose-700 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:f?s==="zh"?"处理中…":"Working…":s==="zh"?"结束":"End"})]}),e.jsx("button",{onClick:L,className:"w-full mt-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg py-2 text-sm font-bold",children:s==="zh"?"进入课堂":"Enter Classroom"})]})})]})}const U=async i=>{m(t=>({...t,[i.id]:s==="zh"?"启动中…":"Starting…"}));try{const t=await oe(i.id,{classroomId:l,role:n},a);m(r=>({...r,[i.id]:t.dispatched?s==="zh"?"已启动 ✓":"Started ✓":s==="zh"?"已通知 ✓":"Notified ✓"}))}catch(t){m(r=>({...r,[i.id]:t instanceof Error?t.message:String(t)}))}};if(A)return e.jsx("div",{style:x,children:s==="zh"?"加载活动中…":"Loading activities…"});if(j)return e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["🧩 ",s==="zh"?"活动中心":"Activity Center"]}),e.jsx("div",{style:{opacity:.7},children:j})]});const N=n==="teacher"?s==="zh"?"活动中心(教师)":"Activity Center":s==="zh"?"活动(学生)":"Activities";return p.length===0?e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["🧩 ",N]}),e.jsx("div",{style:{opacity:.7},children:s==="zh"?"暂无可用活动。":"No activities available."})]}):e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:8},children:["🧩 ",N]}),F.map(([i,t])=>e.jsxs("div",{style:{marginBottom:10},children:[e.jsx("div",{style:{opacity:.6,fontSize:12,textTransform:"uppercase",marginBottom:4},children:i}),e.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:8},children:t.map(r=>e.jsxs("div",{style:{border:"1px solid #334155",borderRadius:8,padding:"8px 10px",minWidth:150,background:"#0f172a"},children:[e.jsxs("div",{style:{fontWeight:600},children:[r.icon?`${r.icon} `:"▫️ ",r.name]}),r.description&&e.jsx("div",{style:{opacity:.7,fontSize:12,margin:"2px 0 6px"},children:r.description}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("button",{onClick:()=>U(r),style:{cursor:"pointer",background:n==="teacher"?"#2563eb":"#0ea5e9",color:"#fff",border:"none",borderRadius:6,padding:"4px 10px",fontSize:12},children:n==="teacher"?s==="zh"?"启动":"Start":s==="zh"?"参与":"Open"}),v[r.id]&&e.jsx("span",{style:{fontSize:12,opacity:.85},children:v[r.id]})]}),e.jsx("div",{style:{opacity:.5,fontSize:12,marginTop:4},children:r.provider==="official"?"official":r.provider})]},r.id))})]},i))]})};export{be as ActivityWorkspaceWidget};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{r as a,j as e}from"./vendor-react-lfWn1oGs.js";import{b as Qe,E as we,aj as ys,n as js,ak as Ns,al as vs,am as ws,a0 as ks,c as zs}from"./index-CLtQGl25.js";import{aw as ae,f as J,R as O,k as Ue,a7 as Ss,K as Cs,T as ee,_ as Ee,ah as es,bv as As,bC as Ts,d as Ls,a as re,b as xe,bB as me,h as Ds,U as ke,bD as Ms,ab as Ps,co as Is,ak as Us,l as Es,bw as Ge,y as We,aF as Bs,ar as Ke,S as $s,X as Je,bz as Fs,e as _s}from"./vendor-icons-__JLt-CP.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";const Rs=[{key:"lesson",zh:"课程",en:"Lessons"},{key:"class",zh:"班级",en:"Classes"},{key:"student",zh:"学生",en:"Students"},{key:"class_student",zh:"班级关系",en:"Enrollments"},{key:"schedule",zh:"课表",en:"Schedules"},{key:"user",zh:"账号",en:"Accounts"}];function Os({lang:r}){const[o,s]=a.useState(null),[v,b]=a.useState(!1),[C,A]=a.useState(!1),[f,L]=a.useState(!1),[T,x]=a.useState(null),Q=async m=>{const h=await m.json().catch(()=>({}));return h&&h.success?h.result:null},H=async m=>{const h=await m.json().catch(()=>({}));return{result:h&&h.success?h.result:null,message:h?.message}},E=a.useCallback(async()=>{try{b(!0);const m=await fetch("/api/demo-data/status");if(m.ok){const h=await Q(m);h&&s(h)}}catch(m){console.error("Failed to fetch demo data status",m)}finally{b(!1)}},[]);a.useEffect(()=>{E()},[E]);const c=!!(o&&(o.seeded||o.total>0)),B=async()=>{try{A(!0),x(null);const m=await fetch(`/api/demo-data/seed${c?"?reset=true":""}`,{method:"POST"}),h=await Q(m);m.ok&&h?(s(h),x({type:"success",text:r==="zh"?"演示数据已就绪,可用下方演示教师账号登录体验。":"Demo data is ready. Log in with the demo teacher account below."})):x({type:"error",text:r==="zh"?"初始化演示数据失败,请稍后重试。":"Failed to seed demo data, please retry."})}catch(m){x({type:"error",text:r==="zh"?`初始化失败:${m.message||"网络异常"}`:`Seed failed: ${m.message||"Network error"}`})}finally{A(!1)}},k=async()=>{if(confirm(r==="zh"?`确定要清理演示数据吗?
|
|
2
|
+
|
|
3
|
+
只删除演示数据(演示教师、演示课程/班级/学生/课表),系统本身的数据不受影响,管理员账号不会被删除。`:`Purge demo data?
|
|
4
|
+
|
|
5
|
+
Only demo records (demo teacher, demo lesson/class/student/schedule) are removed. Your own data and administrator accounts are never touched.`))try{L(!0),x(null);const h=await fetch("/api/demo-data/cleanup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({confirm:"PURGE_DEMO_DATA"})}),{result:j,message:D}=await H(h);if(h.ok&&j){s(j.after?j.after:null),j.after||await E();const M=j.report?.totalRemoved,q=M===void 0?"":r==="zh"?`已清理 ${M} 条演示数据。`:`Removed ${M} demo records.`;x({type:"success",text:D||q})}else x({type:"error",text:r==="zh"?"清理演示数据失败,请稍后重试。":"Failed to purge demo data, please retry."})}catch(h){x({type:"error",text:r==="zh"?`清理失败:${h.message||"网络异常"}`:`Purge failed: ${h.message||"Network error"}`})}finally{L(!1)}},z=(m=>{if(!m)return null;const h=new Date(m);return Number.isNaN(h.getTime())?null:h.toLocaleString(r==="zh"?"zh-CN":"en-US",{hour12:!1})})(o?.seededAt),F=C||f;return e.jsx("div",{className:"mt-5",children:e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-gray-50/60",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(ae,{size:18,className:"text-indigo-600"}),r==="zh"?"演示数据(Demo)":"Demo Data"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:r==="zh"?"一键生成一套完整的演示课程 / 班级 / 学生 / 课表,便于快速体验与验收;清理时不影响系统本身的数据。":"One-click generation of a full demo lesson / class / student / schedule set for quick evaluation. Purging never touches real data."})]}),e.jsx("button",{type:"button",onClick:E,disabled:v||F,title:r==="zh"?"刷新状态":"Refresh status",className:"p-2 text-gray-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition-all cursor-pointer disabled:opacity-50 shrink-0",children:v?e.jsx(J,{size:15,className:"animate-spin"}):e.jsx(O,{size:15})})]}),e.jsxs("div",{className:"p-4 sm:p-5 space-y-5 text-left",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-bold border ${c?"bg-emerald-50 text-emerald-700 border-emerald-100":"bg-gray-50 text-gray-500 border-gray-200"}`,children:[c?e.jsx(Ue,{size:13}):e.jsx(ae,{size:13}),c?r==="zh"?"已播种演示数据":"Demo data seeded":r==="zh"?"尚未播种演示数据":"No demo data yet"]}),z&&e.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-semibold bg-gray-50 text-gray-500 border border-gray-200",children:[e.jsx(Ss,{size:13}),r==="zh"?`播种时间:${z}`:`Seeded at ${z}`]}),e.jsx("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-semibold bg-gray-50 text-gray-500 border border-gray-200",children:r==="zh"?`合计 ${o?.total??0} 条`:`${o?.total??0} records total`})]}),o?.counts&&e.jsx("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-2",children:Rs.map(m=>e.jsxs("div",{className:"flex items-center justify-between gap-2 px-2.5 py-2 rounded-lg border border-gray-200 bg-gray-50/60",children:[e.jsx("span",{className:"text-xs text-gray-500",children:r==="zh"?m.zh:m.en}),e.jsx("span",{className:"text-xs font-bold text-gray-700 font-mono",children:o.counts[m.key]??0})]},m.key))})]}),o?.credentials&&e.jsxs("div",{className:"rounded-lg border border-amber-100 bg-amber-50/60 px-3 py-2.5 space-y-1.5",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-xs font-bold text-amber-700",children:[e.jsx(Cs,{size:13}),r==="zh"?"演示教师账号(可用于登录体验)":"Demo teacher account (for sign-in testing)"]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-amber-800",children:[e.jsxs("span",{children:[r==="zh"?"姓名":"Name",": ",e.jsx("span",{className:"font-semibold",children:o.credentials.name})]}),e.jsxs("span",{children:[r==="zh"?"用户名":"Username",":"," ",e.jsx("span",{className:"font-mono font-bold",children:o.credentials.username})]}),e.jsxs("span",{children:[r==="zh"?"密码":"Password",":"," ",e.jsx("span",{className:"font-mono font-bold",children:o.credentials.password})]})]})]}),T&&e.jsxs("div",{className:`flex items-start gap-2 px-3 py-2.5 rounded-lg text-xs font-semibold border ${T.type==="success"?"bg-emerald-50 text-emerald-700 border-emerald-100":"bg-rose-50 text-rose-700 border-rose-100"}`,children:[T.type==="success"?e.jsx(Ue,{size:14,className:"shrink-0 mt-px"}):e.jsx(ee,{size:14,className:"shrink-0 mt-px"}),e.jsx("span",{className:"whitespace-pre-line",children:T.text})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3 pt-2 border-t border-gray-100",children:[e.jsxs("button",{type:"button",onClick:B,disabled:F,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[C?e.jsx(J,{size:13,className:"animate-spin"}):c?e.jsx(O,{size:14}):e.jsx(Ee,{size:14}),e.jsx("span",{children:C?r==="zh"?"处理中...":"Working...":c?r==="zh"?"重置并重新初始化":"Reset & Re-seed Demo Data":r==="zh"?"一键初始化演示数据":"Seed Demo Data"})]}),e.jsxs("button",{type:"button",onClick:k,disabled:F||!c,className:"px-4 py-2 text-xs font-bold text-white bg-rose-600 hover:bg-rose-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[f?e.jsx(J,{size:13,className:"animate-spin"}):e.jsx(es,{size:14}),e.jsx("span",{children:f?r==="zh"?"清理中...":"Purging...":r==="zh"?"一键清理演示数据":"Purge Demo Data"})]}),!c&&e.jsx("span",{className:"text-xs text-gray-400",children:r==="zh"?"清理前需先初始化演示数据。":"Seed demo data before purging."})]})]})]})})}const Hs=[{value:"by-date",zhLabel:"按日期分目录 (by-date)",enLabel:"By Date (by-date)",zhDesc:"按年月日与用途归档:{yyyy}/{MM}/{dd}/{purpose}/{safeName}",enDesc:"Date & purpose buckets: {yyyy}/{MM}/{dd}/{purpose}/{safeName}"},{value:"by-course",zhLabel:"按课程/课时分目录 (by-course)",enLabel:"By Course & Lesson (by-course)",zhDesc:"按课程隔离:{courseId}/{lessonId}/{purpose}/{safeName}",enDesc:"Grouped by course: {courseId}/{lessonId}/{purpose}/{safeName}"},{value:"by-author",zhLabel:"按上传者与日期 (by-author)",enLabel:"By Author & Date (by-author)",zhDesc:"按作者分目录:{authorId}/{yyyy}/{MM}/{safeName}",enDesc:"Grouped by author: {authorId}/{yyyy}/{MM}/{safeName}"},{value:"flat",zhLabel:"扁平单层目录 (flat)",enLabel:"Flat (flat)",zhDesc:"平铺存放:{safeName}",enDesc:"Single flat level: {safeName}"},{value:"custom",zhLabel:"自定义路径模板 (custom)",enLabel:"Custom Template (custom)",zhDesc:"自定义占位符,必须包含 {safeName} / {id} / {uuid} / {hash8}",enDesc:"Custom placeholders; must include {safeName}, {id}, {uuid}, or {hash8}"}];function Xe(r){if(!Number.isFinite(r)||r<=0)return"0 B";const o=["B","KB","MB","GB","TB"];let s=r,v=0;for(;s>=1024&&v<o.length-1;)s/=1024,v+=1;return`${v===0?s:s.toFixed(1)} ${o[v]}`}function qs(r,o){const s=r.trim();return s?s.startsWith("/")||s.startsWith("\\")||/^[a-zA-Z]:/.test(s)?o==="zh"?"模板不能以斜杠或盘符开头,必须为相对路径。":"Template must be a relative path without leading slashes or drive letters.":s.split(/[\\/]+/).some(v=>v==="..")?o==="zh"?"模板不能包含上级目录跳转(..)。":"Template cannot contain parent traversal (..).":!s.includes("{safeName}")&&!s.includes("{hash8}")&&!s.includes("{uuid}")&&!s.includes("{id}")?o==="zh"?"为避免文件覆盖冲突,模板必须包含 {safeName}、{hash8}、{uuid} 或 {id} 唯一占位符之一。":"Template must include {safeName}, {hash8}, {uuid}, or {id} to guarantee key uniqueness.":null:o==="zh"?"请输入自定义存储路径模板。":"Please enter a custom storage path template."}function Ys({lang:r}){const o=a.useId(),s=a.useId(),v=a.useId(),b=a.useId(),C=a.useId(),A=a.useId(),f=a.useRef(null),[L,T]=a.useState(!0),[x,Q]=a.useState(!1),[H,E]=a.useState(!1),[c,B]=a.useState({layout:{scheme:"by-date",template:""},defaultBackend:"local",deduplicateByHash:!0}),[k,W]=a.useState([]),[z,F]=a.useState(null),[m,h]=a.useState(""),[j,D]=a.useState(!1),[M,q]=a.useState(!1),[_,P]=a.useState(null),[n,w]=a.useState(null),S=a.useCallback(async(l,u)=>{try{E(!0);const g=await fetch("/api/admin/file-storage/preview",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({scheme:l,template:l==="custom"?u:void 0})}),N=await g.json().catch(()=>({}));g.ok&&N?.success?(h(String(N.previewKey||"")),P(null)):N?.error&&(h(""),l==="custom"&&P(String(N.error)))}catch{}finally{E(!1)}},[]),R=a.useCallback(async()=>{try{T(!0);const[l,u]=await Promise.all([fetch("/api/admin/file-storage/config"),fetch("/api/admin/file-storage/stats")]),g=await l.json().catch(()=>({})),N=await u.json().catch(()=>({}));if(l.ok&&g?.success&&g.config){const I={layout:{scheme:g.config.layout?.scheme||"by-date",template:g.config.layout?.template||""},defaultBackend:String(g.config.defaultBackend||"local"),deduplicateByHash:!!g.config.deduplicateByHash};B(I),W(Array.isArray(g.backends)?g.backends:[]),S(I.layout.scheme,I.layout.template)}else w({type:"error",text:g?.error||(r==="zh"?"加载文件存储配置失败。":"Failed to load file storage configuration.")});u.ok&&N?.success&&N.stats&&F(N.stats)}catch(l){const u=l instanceof Error?l.message:"";w({type:"error",text:r==="zh"?`加载配置异常:${u||"网络错误"}`:`Failed to fetch configuration: ${u||"Network error"}`})}finally{T(!1)}},[r,S]);a.useEffect(()=>{R()},[R]);const K=c.layout.scheme==="custom",se=K?qs(c.layout.template||"",r):null,X=K&&(j||M)?se||_:null,oe=!!X,he=l=>{const u="{purpose}/{yyyy}/{MM}/{safeName}";B(N=>({...N,layout:{...N.layout,scheme:l,template:l==="custom"&&!N.layout.template?u:N.layout.template}})),P(null);const g=l==="custom"?c.layout.template||u:void 0;S(l,g)},ze=async l=>{if(l.preventDefault(),q(!0),w(null),K&&se){D(!0),f.current?.focus(),w({type:"error",text:se});return}try{Q(!0);const u={defaultBackend:c.defaultBackend,deduplicateByHash:c.deduplicateByHash,layout:{scheme:c.layout.scheme,...c.layout.scheme==="custom"?{template:(c.layout.template||"").trim()}:{}}},g=await fetch("/api/admin/file-storage/config",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),N=await g.json().catch(()=>({}));if(g.ok&&N?.success)P(null),w({type:"success",text:r==="zh"?"文件存储策略已保存,新上传的文件将立即按新策略落盘;既有文件可通过 pnpm file:reorg 按需重整。":"File storage policy saved. New uploads apply immediately; existing blobs can be reorganized via pnpm file:reorg."}),S(u.layout.scheme,u.layout.template);else{const I=String(N?.error||(r==="zh"?"保存文件存储策略失败。":"Failed to save file storage configuration."));K&&N?.code==="INVALID_FILE_STORAGE_CONFIG"&&(P(I),f.current?.focus()),w({type:"error",text:I})}}catch(u){const g=u instanceof Error?u.message:"";w({type:"error",text:r==="zh"?`保存异常:${g||"网络错误"}`:`Save failed: ${g||"Network error"}`})}finally{Q(!1)}},Y=k.find(l=>l.id===c.defaultBackend);return e.jsx("section",{className:"mt-5","aria-labelledby":o,children:e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-gray-50/60",children:[e.jsxs("div",{children:[e.jsxs("h3",{id:o,className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(As,{size:18,className:"text-indigo-600","aria-hidden":"true"}),r==="zh"?"文件存储与路径布局策略":"File Storage & Layout Policy"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:r==="zh"?"配置文件存储后端、物理路径模板与内容寻址去重。访问路径 /api/files/:assetId 与实体路径完全解耦。":"Configure default storage backend, physical key layout, and SHA-256 deduplication. /api/files/:assetId URLs never break when keys change."})]}),e.jsx("button",{type:"button",onClick:()=>void R(),disabled:L||x,"aria-label":r==="zh"?"刷新文件存储配置与统计":"Refresh file storage configuration and stats",className:"p-2 text-gray-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition-all cursor-pointer disabled:opacity-50 shrink-0",children:L?e.jsx(J,{size:15,className:"animate-spin"}):e.jsx(O,{size:15})})]}),e.jsxs("form",{"aria-labelledby":o,noValidate:!0,onSubmit:ze,className:"p-4 sm:p-5 space-y-5 text-left",children:[z&&e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-4 gap-2",children:[e.jsxs("div",{className:"px-3 py-2 rounded-lg border border-gray-200 bg-gray-50/60",children:[e.jsx("div",{className:"text-[11px] text-gray-500",children:r==="zh"?"有效资产数":"Active Assets"}),e.jsx("div",{className:"text-sm font-bold text-gray-800 font-mono",children:z.activeAssets})]}),e.jsxs("div",{className:"px-3 py-2 rounded-lg border border-gray-200 bg-gray-50/60",children:[e.jsx("div",{className:"text-[11px] text-gray-500",children:r==="zh"?"物理 Blob 数":"Unique Blobs"}),e.jsx("div",{className:"text-sm font-bold text-gray-800 font-mono",children:z.uniqueBlobs})]}),e.jsxs("div",{className:"px-3 py-2 rounded-lg border border-gray-200 bg-gray-50/60",children:[e.jsx("div",{className:"text-[11px] text-gray-500",children:r==="zh"?"实占空间 / 去重节省":"Physical / Saved"}),e.jsxs("div",{className:"text-sm font-bold text-gray-800 font-mono",children:[Xe(z.physicalBytes),e.jsxs("span",{className:"text-[11px] font-normal text-emerald-700 ml-1",children:["(-",Xe(z.savedBytes),")"]})]})]}),e.jsxs("div",{className:"px-3 py-2 rounded-lg border border-gray-200 bg-gray-50/60",children:[e.jsx("div",{className:"text-[11px] text-gray-500",children:r==="zh"?"旧路径别名映射":"Legacy Aliases"}),e.jsx("div",{className:"text-sm font-bold text-gray-800 font-mono",children:z.legacyAliases})]})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{htmlFor:s,className:"text-xs font-bold text-gray-700 block",children:r==="zh"?"默认存储后端(StorageBackend)":"Default Storage Backend"}),e.jsx("select",{id:s,value:c.defaultBackend,onChange:l=>B(u=>({...u,defaultBackend:l.target.value})),className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all",children:k.length===0?e.jsx("option",{value:"local",children:"Local Disk Storage (local)"}):k.map(l=>e.jsxs("option",{value:l.id,children:[l.displayName," (",l.id,") — ",l.owner]},l.id))}),Y&&e.jsxs("p",{className:"text-xs text-gray-500",children:[Y.description?`${Y.description} · `:"",r==="zh"?"能力:":"Capabilities: ",Y.capabilities.supportsRange?"Range ":"",Y.capabilities.supportsPresignedUrl?"PresignedURL ":"",Y.capabilities.externalBacked?"External":"Local"]})]}),e.jsxs("fieldset",{className:"space-y-2 border border-gray-200 rounded-lg p-3.5 bg-gray-50/30",children:[e.jsxs("legend",{className:"text-xs font-bold text-gray-700 px-1.5 flex items-center gap-1.5",children:[e.jsx(Ts,{size:14,className:"text-indigo-600","aria-hidden":"true"}),r==="zh"?"文件组织方式(LayoutPolicy)":"Physical Layout Scheme (LayoutPolicy)"]}),e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-2",children:Hs.map(l=>{const u=c.layout.scheme===l.value;return e.jsxs("label",{className:`flex items-start gap-2.5 p-2.5 rounded-lg border cursor-pointer transition-colors ${u?"bg-indigo-50/70 border-indigo-300 text-indigo-950":"bg-white border-gray-200 hover:bg-gray-50 text-gray-700"}`,children:[e.jsx("input",{type:"radio",name:"file-layout-scheme",value:l.value,checked:u,onChange:()=>he(l.value),className:"mt-0.5 text-indigo-600 focus:ring-indigo-500"}),e.jsxs("span",{className:"text-xs leading-relaxed",children:[e.jsx("span",{className:"font-bold block",children:r==="zh"?l.zhLabel:l.enLabel}),e.jsx("span",{className:"text-[11px] text-gray-500 font-mono",children:r==="zh"?l.zhDesc:l.enDesc})]})]},l.value)})}),K&&e.jsxs("div",{className:"pt-2 space-y-1.5",children:[e.jsx("label",{htmlFor:v,className:"text-xs font-bold text-gray-700 block",children:r==="zh"?"自定义路径模板":"Custom Path Template"}),e.jsx("input",{ref:f,id:v,type:"text",value:c.layout.template||"",onChange:l=>{const u=l.target.value;B(g=>({...g,layout:{...g.layout,template:u}})),P(null)},onBlur:()=>D(!0),placeholder:"{purpose}/{yyyy}/{MM}/{safeName}",autoCorrect:"off",autoCapitalize:"none",spellCheck:!1,"aria-invalid":oe?"true":"false","aria-describedby":X?`${b} ${C}`:b,className:`w-full text-sm font-mono bg-white border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 transition-all ${oe?"border-rose-400 focus:ring-rose-500":"border-gray-200 focus:ring-indigo-500"}`}),e.jsx("p",{id:b,className:"text-[11px] text-gray-500",children:r==="zh"?"可用占位符:{yyyy} {MM} {dd} {hh} {mm} {ss} {courseId} {lessonId} {authorId} {assignmentId} {purpose} {safeName} {hash8} {uuid} {id} {ext} {extNoDot}(须含 {safeName}/{hash8}/{uuid}/{id} 之一)":"Tokens: {yyyy} {MM} {dd} {hh} {mm} {ss} {courseId} {lessonId} {authorId} {assignmentId} {purpose} {safeName} {hash8} {uuid} {id} {ext} {extNoDot} (must include {safeName}, {hash8}, {uuid}, or {id})"}),X&&e.jsxs("p",{id:C,className:"text-xs font-semibold text-rose-700 flex items-center gap-1.5",children:[e.jsx(ee,{size:13,"aria-hidden":"true"}),e.jsx("span",{children:X})]})]})]}),e.jsxs("div",{className:"rounded-lg border border-gray-200 bg-gray-50/70 px-3 py-2.5 flex flex-col sm:flex-row sm:items-center justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-[11px] font-bold text-gray-600",children:r==="zh"?"物理路径预览(示例:课程资料/课件插图.png)":"Physical Key Preview (Sample Upload)"}),e.jsx("code",{className:"text-xs font-mono text-indigo-700 break-all block mt-0.5",children:H?r==="zh"?"生成预览中...":"Generating preview...":m||(r==="zh"?"(当前模板无效,无法生成预览)":"(Invalid template)")})]}),e.jsxs("button",{type:"button",onClick:()=>void S(c.layout.scheme,c.layout.template),disabled:H,className:"px-3 py-1.5 text-xs font-semibold text-gray-700 bg-white hover:bg-gray-100 border border-gray-200 rounded-lg transition-all cursor-pointer inline-flex items-center gap-1.5 shrink-0",children:[e.jsx(Ls,{size:13,"aria-hidden":"true"}),e.jsx("span",{children:r==="zh"?"预览生成路径":"Preview Path"})]})]}),e.jsxs("div",{className:"flex items-start gap-2.5 pt-1",children:[e.jsx("input",{id:A,type:"checkbox",checked:c.deduplicateByHash,onChange:l=>B(u=>({...u,deduplicateByHash:l.target.checked})),className:"mt-0.5 rounded text-indigo-600 focus:ring-indigo-500"}),e.jsxs("label",{htmlFor:A,className:"text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("span",{className:"font-bold block",children:r==="zh"?"启用 SHA-256 同后端内容去重(Deduplicate by Hash)":"Enable SHA-256 Deduplication"}),e.jsx("span",{className:"text-[11px] text-gray-500",children:r==="zh"?"相同内容重复上传时复用同一物理 Blob 并递增引用计数,学生作业与加密敏感路径会自动强制隔离。":"Reuses identical physical blobs on the same backend while keeping independent file_assets records; assignment uploads always skip dedup."})]})]}),e.jsx("div",{role:"status","aria-live":"polite",className:"min-h-0",children:n&&e.jsxs("div",{className:`flex items-start gap-2 px-3 py-2.5 rounded-lg text-xs font-semibold border ${n.type==="success"?"bg-emerald-50 text-emerald-800 border-emerald-200":"bg-rose-50 text-rose-800 border-rose-200"}`,children:[n.type==="success"?e.jsx(Ue,{size:14,className:"shrink-0 mt-px","aria-hidden":"true"}):e.jsx(ee,{size:14,className:"shrink-0 mt-px","aria-hidden":"true"}),e.jsx("span",{children:n.text})]})}),e.jsx("div",{className:"flex items-center justify-end gap-3 pt-2 border-t border-gray-100",children:e.jsxs("button",{type:"submit",disabled:x||L,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[x?e.jsx(J,{size:13,className:"animate-spin"}):e.jsx(re,{size:14}),e.jsx("span",{children:x?r==="zh"?"保存中...":"Saving...":r==="zh"?"保存文件存储策略":"Save File Storage Policy"})]})})]})]})})}const Ze={database:{zh:"管理员界面配置(覆盖环境变量)",en:"Admin UI setting (overrides env)"},env:{zh:"环境变量回退(未在界面显式配置)",en:"Environment fallback (no explicit UI value)"},default:{zh:"历史默认(未在界面或环境变量中显式配置)",en:"Historical default (not set in UI or env)"}};function Ie(r,o){return o?Ze[r].zh:Ze[r].en}function Vs({lang:r}){const o=r==="zh",[s,v]=a.useState(!0),[b,C]=a.useState(!1),[A,f]=a.useState(null),[L,T]=a.useState(null),[x,Q]=a.useState(null),[H,E]=a.useState([]),[c,B]=a.useState(!0),[k,W]=a.useState("errors"),[z,F]=a.useState(""),[m,h]=a.useState(1e3),[j,D]=a.useState(!0),M=n=>{Q(n.policies),E(n.reservedEnvBoundaries??[]),B(n.policies.hookTelemetry.enabled),W(n.policies.httpAccessLog.mode),F(n.policies.httpAccessLog.ignorePrefixes.join(",")),h(n.policies.httpAccessLog.slowMs),D(n.policies.allowUnsigned.allowByDefault)},q=a.useCallback(async()=>{v(!0),f(null);try{const n=await fetch("/api/admin/runtime-policies"),w=await n.json().catch(()=>null);if(!n.ok||!w?.policies)throw new Error(n.status===403?o?"仅管理员可查看运行时策略":"Only administrators can view runtime policies":w?.error||(o?"加载运行时策略失败":"Failed to load runtime policies"));M({policies:w.policies,reservedEnvBoundaries:w.reservedEnvBoundaries})}catch(n){f(n instanceof Error?n.message:String(n))}finally{v(!1)}},[o]);a.useEffect(()=>{q()},[q]);const _=async(n,w)=>{C(!0),f(null),T(null);try{const S=await fetch("/api/admin/runtime-policies",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(w?{[w]:null}:n)}),R=await S.json().catch(()=>null);if(!S.ok||!R?.policies)throw new Error(R?.error||(o?"保存运行时策略失败":"Failed to save runtime policies"));M({policies:R.policies,reservedEnvBoundaries:R.reservedEnvBoundaries}),T(o?"已保存,按各策略说明立即或在后续请求中生效。":"Saved. See each policy for when it takes effect.")}catch(S){f(S instanceof Error?S.message:String(S))}finally{C(!1)}},P=n=>n?o?"清除界面覆盖,回退到环境变量":"Clear UI override and fall back to env":o?"清除界面覆盖,回退到默认":"Clear UI override and fall back to default";return e.jsxs("div",{className:"mt-6 bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden max-w-3xl",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-amber-50/60",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(xe,{size:18,className:"text-amber-700"}),o?"运行时策略(管理员)":"Runtime Policies (Administrators)"]}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5 leading-relaxed",children:o?"统一配置课件网络遥测、HTTP 访问日志与未签名本地插件策略。优先级为界面显式值(含显式关闭)> 未配置时环境变量回退 > 历史默认。保存不会改写真实 .env。此项不会把认证绕过或关闭沙箱做成普通按钮。":"Configure courseware hook telemetry, HTTP access logs, and unsigned local plugin policy. Explicit UI values (including explicit off) beat env fallback, then historical defaults. Saving does not rewrite the real .env. Auth-bypass and sandbox-off stay out of ordinary toggles."})]}),e.jsxs("button",{type:"button",onClick:()=>void q(),disabled:s||b,className:"px-3 py-2 text-xs font-semibold border border-gray-200 rounded-lg hover:bg-white disabled:opacity-60 cursor-pointer flex items-center gap-1.5",children:[s?e.jsx(J,{size:13,className:"animate-spin"}):e.jsx(O,{size:13}),o?"刷新":"Refresh"]})]}),e.jsxs("div",{className:"p-5 space-y-6 text-left",children:[A&&e.jsxs("p",{role:"alert",className:"text-xs text-rose-600 flex items-center gap-1.5",children:[e.jsx(ee,{size:12})," ",A]}),L&&!A&&e.jsx("p",{role:"status",className:"text-xs text-emerald-700",children:L}),x&&e.jsxs(e.Fragment,{children:[e.jsxs("section",{className:"space-y-2 border border-gray-100 rounded-lg p-4","data-testid":"runtime-policy-hook-telemetry",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-semibold text-gray-800",children:o?"课件网络遥测总闸":"Courseware network telemetry"}),e.jsx("p",{className:"text-xs text-gray-500 leading-relaxed mt-1",children:o?"控制课件内 fetch/XHR/axios/beacon/form 是否向父窗口上报网络遥测。默认开启。关闭后新打开的课件不再注入上报,已打开的课件由宿主同步后停止后续上报;不影响成绩提交。":"Controls whether courseware fetch/XHR/axios/beacon/form hooks report to the parent window. On by default. New sessions pick up the value immediately; already-open sessions stop after the host syncs. Score submission is unaffected."}),e.jsxs("p",{className:"text-xs text-gray-600 mt-1",children:[o?`有效状态:${c?"已开启":"已关闭"}`:`Effective: ${c?"on":"off"}`,` · ${Ie(x.hookTelemetry.configSource,o)}`]}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:x.hookTelemetry.effect})]}),e.jsx("button",{type:"button",role:"switch","aria-checked":c,"aria-label":o?"课件网络遥测总闸":"Courseware network telemetry",disabled:b,onClick:()=>void _({hookTelemetryEnabled:!c}),className:`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full ${c?"bg-indigo-600":"bg-gray-300"} ${b?"opacity-60":"cursor-pointer"}`,children:e.jsx("span",{className:`inline-block h-4 w-4 transform rounded-full bg-white ${c?"translate-x-6":"translate-x-1"}`})})]}),x.hookTelemetry.storedValue!==null&&e.jsx("button",{type:"button",disabled:b,onClick:()=>void _({},"hookTelemetryEnabled"),className:"text-xs font-semibold text-indigo-700 hover:text-indigo-900 cursor-pointer",children:P(x.hookTelemetry.hasEnvFallback)})]}),e.jsxs("section",{className:"space-y-3 border border-gray-100 rounded-lg p-4","data-testid":"runtime-policy-access-log",children:[e.jsx("h4",{className:"text-sm font-semibold text-gray-800",children:o?"HTTP 访问日志策略":"HTTP access log policy"}),e.jsx("p",{className:"text-xs text-gray-500 leading-relaxed",children:o?"这不是简单开/关:可选仅记录异常与慢请求(默认 errors)、记录全部(all,正常请求为 debug)或关闭访问日志(off)。不会写入查询串或请求体,也不影响安全审计。保存后对后续请求立即生效。":"This is not a boolean switch: errors (default) logs failures and slow requests, all logs everything (successful requests at debug), off disables access logs. Query strings and bodies are never logged. Security audits are unaffected. Takes effect on subsequent requests."}),e.jsxs("p",{className:"text-xs text-gray-600",children:[o?`有效模式:${k}`:`Effective mode: ${k}`,` · ${Ie(x.httpAccessLog.modeSource,o)}`]}),e.jsxs("label",{className:"block text-xs font-semibold text-gray-700",children:[o?"日志模式":"Log mode",e.jsxs("select",{value:k,disabled:b,onChange:n=>W(n.target.value),className:"mt-1 w-full text-sm bg-gray-50 border border-gray-200 rounded-lg px-3 py-2",children:[e.jsx("option",{value:"errors",children:o?"errors:仅异常与慢请求(默认)":"errors: failures and slow requests (default)"}),e.jsx("option",{value:"all",children:o?"all:全量(排障)":"all: everything (troubleshooting)"}),e.jsx("option",{value:"off",children:o?"off:关闭访问日志":"off: disable access logs"})]})]}),e.jsxs("label",{className:"block text-xs font-semibold text-gray-700",children:[o?"忽略前缀(逗号分隔,须以 / 开头)":"Ignore prefixes (comma-separated, must start with /)",e.jsx("input",{type:"text",value:z,disabled:b,onChange:n=>F(n.target.value),placeholder:"/api/vfs,/api/classroom/sessions/",className:"mt-1 w-full text-sm bg-gray-50 border border-gray-200 rounded-lg px-3 py-2 font-mono"})]}),e.jsxs("label",{className:"block text-xs font-semibold text-gray-700",children:[o?"慢请求阈值(毫秒)":"Slow request threshold (ms)",e.jsx("input",{type:"number",min:50,max:6e4,value:m,disabled:b,onChange:n=>h(Number(n.target.value)),className:"mt-1 w-40 text-sm bg-gray-50 border border-gray-200 rounded-lg px-3 py-2"})]}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs("button",{type:"button",disabled:b,onClick:()=>void _({httpAccessLogMode:k,httpAccessLogIgnore:z,httpSlowMs:m}),className:"inline-flex items-center gap-1.5 px-3 py-2 text-xs font-semibold rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-60 cursor-pointer",children:[b?e.jsx(J,{size:13,className:"animate-spin"}):e.jsx(re,{size:13}),o?"保存访问日志策略":"Save access log policy"]}),(x.httpAccessLog.storedMode!==null||x.httpAccessLog.storedIgnore!==null||x.httpAccessLog.storedSlowMs!==null)&&e.jsx("button",{type:"button",disabled:b,onClick:()=>void _({httpAccessLogMode:null,httpAccessLogIgnore:null,httpSlowMs:null}),className:"text-xs font-semibold text-indigo-700 cursor-pointer",children:P(x.httpAccessLog.hasModeEnvFallback||x.httpAccessLog.ignoreSource==="env"||x.httpAccessLog.slowSource==="env")})]}),e.jsx("p",{className:"text-xs text-gray-400",children:x.httpAccessLog.effect})]}),e.jsxs("section",{className:"space-y-2 border border-amber-100 bg-amber-50/40 rounded-lg p-4","data-testid":"runtime-policy-allow-unsigned",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-semibold text-amber-900",children:o?"允许未签名本地插件默认放行":"Allow unsigned local plugins by default"}),e.jsx("p",{className:"text-xs text-amber-900/80 leading-relaxed mt-1",children:o?"高风险策略。开启(历史默认)时,未签名本地 ZIP 可安装/更新,审计记 unverified_local_acknowledged。关闭后必须在每次上传时显式确认。这不会绕过签名错误、公钥、网络限制、权限或沙箱,也不等于全信任或允许内联执行。":"High risk. When on (historical default), unsigned local ZIPs may be installed or updated and are audited as unverified_local_acknowledged. When off, each upload requires explicit confirmation. This cannot bypass signature failures, public keys, network limits, permissions, or the sandbox, and is not full trust or inline execution."}),e.jsxs("p",{className:"text-xs text-gray-700 mt-1",children:[o?`有效状态:${j?"默认放行":"需逐次确认"}`:`Effective: ${j?"allow by default":"require per-upload confirmation"}`,` · ${Ie(x.allowUnsigned.configSource,o)}`]}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:x.allowUnsigned.effect})]}),e.jsx("button",{type:"button",role:"switch","aria-checked":j,"aria-label":o?"允许未签名本地插件默认放行":"Allow unsigned local plugins by default",disabled:b,onClick:()=>void _({allowUnsignedByDefault:!j}),className:`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full ${j?"bg-amber-600":"bg-gray-300"} ${b?"opacity-60":"cursor-pointer"}`,children:e.jsx("span",{className:`inline-block h-4 w-4 transform rounded-full bg-white ${j?"translate-x-6":"translate-x-1"}`})})]}),x.allowUnsigned.storedValue!==null&&e.jsx("button",{type:"button",disabled:b,onClick:()=>void _({},"allowUnsignedByDefault"),className:"text-xs font-semibold text-indigo-700 cursor-pointer",children:P(x.allowUnsigned.hasEnvFallback)})]})]}),H.length>0&&e.jsxs("section",{className:"space-y-2 border border-gray-100 rounded-lg p-4","data-testid":"runtime-policy-reserved-env",children:[e.jsxs("h4",{className:"text-sm font-semibold text-gray-800 flex items-center gap-1.5",children:[e.jsx(me,{size:14}),o?"仍保留为环境变量的安全边界":"Security boundaries still reserved to environment variables"]}),e.jsx("p",{className:"text-xs text-gray-500",children:o?"下列项目若做成普通按钮,可能绕过认证、关闭隔离或泄露密钥,因此仍由部署者通过环境变量配置,本页只列出理由。":"These remain deploy-time environment variables because exposing them as ordinary toggles could bypass auth, disable isolation, or leak secrets."}),e.jsx("ul",{className:"text-xs text-gray-600 space-y-1.5",children:H.map(n=>e.jsxs("li",{children:[e.jsx("code",{className:"font-mono text-gray-800",children:n.key}),` — ${n.reason}`]},n.key))})]})]})]})}function Qs({currentUserId:r,currentUserRole:o,lang:s,onLogout:v,aiProviders:b,onAIProvidersChanged:C,onTriggerTour:A,siteInfo:f,onSiteInfoChanged:L}){const[T,x]=a.useState([]),[Q,H]=a.useState(!1),[E,c]=a.useState(""),[B,k]=a.useState(""),[W,z]=a.useState(f?.siteName||""),[F,m]=a.useState(f?.slogan||""),[h,j]=a.useState(f?.logoUrl||null),[D,M]=a.useState(f?.logoUrl||""),[q,_]=a.useState(f?.uploadMaxFileMb??10),[P,n]=a.useState(f?.currentAcademicYear??""),[w,S]=a.useState(9),[R,K]=a.useState(!!f?.logoutClearSiteData),[se,X]=a.useState(f?.allowPublicStudentLookup??!0),[oe,he]=a.useState(f?.defaultThemeId||"light"),[ze,Y]=a.useState(!1),[l,u]=a.useState(!1),[g,N]=a.useState(!1),I=Qe(t=>t.resolvedLayouts.admin),ss=Qe(t=>t.catalog),[ue,Be]=a.useState(""),[Se,ts]=a.useState("all"),[as,ie]=a.useState(!1),[G,$e]=a.useState(null),[pe,Ce]=a.useState(""),[ge,Ae]=a.useState(""),[be,Te]=a.useState(""),[Le,fe]=a.useState("teacher"),[De,ye]=a.useState("active"),[te,Z]=a.useState(""),[Me,Fe]=a.useState(!1),[Gs,Ws]=a.useState({tables:14,sizeKb:256,pingMs:2}),[rs,os]=a.useState(14),[is,ls]=a.useState(0),[$,le]=a.useState("directory"),[d,ns]=a.useState(null),[ne,_e]=a.useState(!1),[U,ds]=a.useState(null),[je,Re]=a.useState(!1),[cs,Oe]=a.useState(null),[V,Pe]=a.useState("npm"),[He,qe]=a.useState(null),Ye=async()=>{try{Re(!0);const t=await fetch("/api/system/check-upgrade");if(t.ok){const i=await t.json();ds(i)}}catch(t){console.error("Failed to check system upgrade",t)}finally{Re(!1)}},xs=(t,i)=>{ks(t),Oe(i),setTimeout(()=>Oe(null),2e3)},Ne=async()=>{try{_e(!0);const t=await fetch("/api/db-status");if(t.ok){const i=await t.json();ns(i)}}catch(t){console.error("Failed to query SQLite stats",t)}finally{_e(!1)}},Ve=o==="administrator",ms=async()=>{try{const t=await fetch("/api/site-settings");if(t.ok){const i=await t.json();z(i.siteName||""),m(i.slogan||""),j(i.logoUrl||null),M(i.logoUrl||""),typeof i.uploadMaxFileMb=="number"&&i.uploadMaxFileMb>0&&_(i.uploadMaxFileMb),typeof i.currentAcademicYear=="string"&&i.currentAcademicYear&&n(i.currentAcademicYear),typeof i.academicStartMonth=="number"&&i.academicStartMonth>0&&S(i.academicStartMonth),K(!!i.logoutClearSiteData),X(i.allowPublicStudentLookup!==!1),typeof i.defaultThemeId=="string"&&i.defaultThemeId&&he(i.defaultThemeId),N(!0)}}catch(t){console.error("Failed to fetch site settings",t)}},hs=t=>{if(!t)return;const i=new FileReader;i.onload=()=>{const y=i.result;j(y),M(y)},i.readAsDataURL(t)},us=async t=>{t.preventDefault();try{u(!0);const i=D&&D.trim()?D.trim():null,y=await fetch("/api/site-settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({siteName:W.trim(),slogan:F.trim(),logoUrl:i,uploadMaxFileMb:Math.round(Number(q)),currentAcademicYear:P.trim()||void 0,academicStartMonth:Math.round(Number(w)),logoutClearSiteData:R,allowPublicStudentLookup:se,defaultThemeId:oe})});if(y.ok){const p=await y.json();j(i),L?.(p.siteInfo),k(s==="zh"?"站点信息已保存!":"Site settings saved successfully!"),setTimeout(()=>k(""),4e3)}else{const p=await y.json().catch(()=>({}));alert(p.error||"Server error")}}catch(i){alert(i.message||"Error")}finally{u(!1)}},ve=async()=>{try{H(!0),c("");const t=await fetch("/api/users");if(t.ok){const i=await t.json();x(i)}else c(s==="zh"?"加载用户账户失败":"Failed to fetch staff directory")}catch{c(s==="zh"?"网络连接异常":"Network communication issue")}finally{H(!1)}};a.useEffect(()=>{if(o!=="administrator")return;let t=!1;return fetch("/api/system/encryption-status").then(i=>i.ok?i.json():null).then(i=>{if(t)return;const y=typeof i?.warning=="string"?i.warning.trim():"";qe(y||null)}).catch(()=>{t||qe(null)}),()=>{t=!0}},[o]),a.useEffect(()=>{ve(),Ne(),Ye(),C();const t=setInterval(()=>{Ne(),os(i=>{const y=Math.floor(Math.random()*9)-4,p=i+y;return Math.min(Math.max(p,5),35)})},5e3);return()=>clearInterval(t)},[is]),a.useEffect(()=>{$==="site_settings"&&!g&&ms()},[$,g]);const ps=()=>{$e(null),Ce(""),Ae(""),Te(""),fe("teacher"),ye("active"),Z(""),ie(!0)},gs=t=>{$e(t.id),Ce(t.username),Ae(""),Te(t.name),fe(t.role),ye(t.status||"active"),Z(""),ie(!0)},bs=async t=>{if(t.preventDefault(),!pe.trim()||!be.trim()){Z(s==="zh"?"请填写所有必填字段。":"Please provide all key info.");return}if(!G&&!ge){Z(s==="zh"?"新建账户时密码必填。":"Password is required for new accounts.");return}try{Z(""),Fe(!0);const i=G?`/api/users/${G}`:"/api/users",p=await fetch(i,{method:G?"PUT":"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:pe.trim(),password:ge||void 0,role:Le,name:be.trim(),status:De})});if(p.ok)k(s==="zh"?"账户信息保存成功!":"Staff account saved successfully!"),ve(),ie(!1),setTimeout(()=>k(""),4e3);else{const ce=await p.json();Z(ce.error||"Server validation failed")}}catch(i){Z(i.message||"API error")}finally{Fe(!1)}},fs=async(t,i)=>{if(t===r){alert(s==="zh"?"你不能删除你自己当前登录的账户。":"You cannot delete your own logged-in account.");return}const y=s==="zh"?`确定要删除教师账户 "${i}" 吗?此操作不可逆。`:`Confirm deleting staff user "${i}"? This action is irreversible.`;if(confirm(y))try{const p=await fetch(`/api/users/${t}`,{method:"DELETE"});if(p.ok)k(s==="zh"?"账户删除成功":"Account deleted successfully"),ve(),setTimeout(()=>k(""),3e3);else{const ce=await p.json();alert(ce.error||"Failed to delete")}}catch(p){console.error(p)}},de=T.filter(t=>{const i=t.name.toLowerCase().includes(ue.toLowerCase())||t.username.toLowerCase().includes(ue.toLowerCase()),y=Se==="all"||t.role===Se;return i&&y});return e.jsxs("div",{className:"flex-1 bg-surface border border-theme text-main rounded-xl shadow-sm flex flex-col min-h-0 overflow-hidden h-full",id:"admin_panel_root","data-admin-nav-style":I.navStyle,"data-theme-surface":"card",children:[e.jsxs("div",{className:"px-6 py-4 border-b border-theme flex flex-col sm:flex-row sm:items-center justify-between gap-3 bg-surface shrink-0",children:[e.jsxs("div",{children:[e.jsxs("h2",{className:"text-base sm:text-lg font-bold text-gray-800 flex items-center gap-2",children:[e.jsx(xe,{className:"text-indigo-600 shrink-0",size:20}),e.jsx("span",{children:s==="zh"?"系统管理与教职后台":"Staff Directory & System Admin"}),e.jsx("span",{className:"text-xs font-semibold px-2 py-0.5 rounded-full bg-indigo-50 text-indigo-700 border border-indigo-100/80",children:o==="administrator"?s==="zh"?"超级管理员":"Admin":s==="zh"?"教师":"Teacher"})]}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:s==="zh"?`管理学校教师、教工账户、划分角色权限。当前登录角色权限: ${o==="administrator"?"超级管理员 (系统设置已解锁)":"普通教师 (系统设置锁定)"}`:`Create and assign teacher credentials. Active role context: ${o==="administrator"?"Administrator (System parameters editable)":"Standard Teacher (Settings view-only)"}`})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[A&&e.jsxs("button",{onClick:A,className:"flex items-center gap-1.5 px-3 py-1.5 bg-indigo-50 hover:bg-indigo-100 text-indigo-700 text-xs font-semibold rounded-lg border border-indigo-200 transition-all cursor-pointer shadow-3xs",children:[e.jsx(Ds,{size:14,className:"animate-bounce"}),e.jsx("span",{children:s==="zh"?"开启新手引导":"Start Help Tour"})]}),e.jsx("button",{onClick:()=>ls(t=>t+1),className:"p-1.5 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded-lg transition-all cursor-pointer border border-gray-200 shadow-3xs",title:s==="zh"?"刷新数据":"Refresh stats",children:e.jsx(O,{size:14})})]})]}),He?e.jsxs("div",{role:"alert",className:"mx-6 mt-4 rounded-xl border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-950",children:[e.jsxs("div",{className:"font-bold flex items-center gap-2",children:[e.jsx(ee,{size:16,className:"shrink-0"}),"正在使用自动生成的加密密钥"]}),e.jsx("p",{className:"mt-1 leading-relaxed",children:He})]}):null,e.jsxs("div",{className:`flex-1 min-h-0 flex overflow-hidden ${I.navStyle==="sidebar-left"?"flex-row":I.navStyle==="sidebar-right"?"flex-row-reverse":"flex-col"}`,children:[e.jsx("div",{className:I.navStyle==="tabs"?"px-6 py-2.5 border-b border-theme bg-surface-secondary/50 flex items-center justify-between gap-4 shrink-0 overflow-x-auto":`w-52 shrink-0 p-3 bg-surface-secondary/40 ${I.navStyle==="sidebar-right"?"border-l":"border-r"} border-theme overflow-y-auto`,children:e.jsxs("div",{className:I.navStyle==="tabs"?"bg-surface-secondary p-1 rounded-xl flex items-center gap-1 shrink-0":"flex flex-col gap-1.5",children:[e.jsxs("button",{onClick:()=>le("directory"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${$==="directory"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(ke,{size:14}),e.jsx("span",{children:s==="zh"?"学校教职及系统配置":"Staff Accounts & Config"})]}),e.jsxs("button",{onClick:()=>le("ai_providers"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${$==="ai_providers"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(Ms,{size:14}),e.jsx("span",{children:s==="zh"?"AI 配置":"AI Config"})]}),e.jsxs("button",{onClick:()=>le("sqlite"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${$==="sqlite"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(ae,{size:14}),e.jsx("span",{children:s==="zh"?"系统监控":"System Monitor"})]}),e.jsxs("button",{onClick:()=>le("site_settings"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${$==="site_settings"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(me,{size:14}),e.jsx("span",{children:s==="zh"?"站点信息设置":"Site Settings"})]}),e.jsx(we,{slot:"admin.tab",slotProps:{renderType:"button",adminActiveTab:$,setAdminActiveTab:le}}),e.jsxs("button",{type:"button",onClick:()=>Y(!0),className:"px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer text-primary-theme hover:bg-primary-theme/10",children:[e.jsx(Ee,{size:14}),e.jsx("span",{children:s==="zh"?"第三方主题管理":"Themes"})]})]})}),e.jsxs("div",{className:"flex-1 min-h-0 flex flex-col overflow-hidden",children:[B&&e.jsxs("div",{className:"mx-6 mt-3 p-3 bg-emerald-50 border border-emerald-200 text-emerald-800 text-xs rounded-xl flex items-center gap-2 shrink-0 animate-fade-in shadow-xs font-medium",children:[e.jsx(Ps,{size:15}),e.jsx("span",{children:B})]}),E&&e.jsxs("div",{className:"mx-6 mt-3 p-3 bg-rose-50 border border-rose-200 text-rose-800 text-xs rounded-xl flex items-center gap-2 shrink-0 animate-fade-in shadow-xs",children:[e.jsx(ee,{size:15}),e.jsx("span",{children:E})]}),$==="ai_providers"?e.jsx(ys,{lang:s,aiProviders:b,onAIProvidersChanged:C,currentUserId:r,currentUserRole:o}):$==="sqlite"?e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-6 animate-fade-in text-gray-800",children:[e.jsxs("div",{className:"bg-gradient-to-r from-slate-900 via-indigo-950 to-slate-900 border border-indigo-800/60 rounded-2xl p-5 text-white shadow-xs overflow-hidden relative",children:[e.jsx("div",{className:"absolute right-0 top-0 translate-x-8 -translate-y-8 w-56 h-56 bg-indigo-500/10 rounded-full blur-3xl pointer-events-none"}),e.jsxs("div",{className:"flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-indigo-800/50 pb-4",children:[e.jsxs("div",{className:"flex items-start sm:items-center gap-3",children:[e.jsx("div",{className:"p-2.5 bg-indigo-600/30 border border-indigo-500/30 rounded-xl text-indigo-300 shrink-0",children:e.jsx(Is,{size:22,className:je?"animate-spin":""})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("h3",{className:"font-extrabold text-sm sm:text-base text-white tracking-wide",children:s==="zh"?"系统版本与升级生命周期":"System Version & Upgrade Lifecycle"}),e.jsxs("span",{className:"font-mono text-xs px-2 py-0.5 rounded-md bg-indigo-900/90 text-indigo-200 border border-indigo-700/60 font-semibold",children:["v",U?.currentVersion||"2.0.0"]}),U?.hasUpdate?e.jsxs("span",{className:"text-xs px-2 py-0.5 rounded-md bg-amber-500/20 text-amber-300 border border-amber-500/40 font-semibold flex items-center gap-1.5 animate-pulse",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-400"}),s==="zh"?`可升级至 v${U.latestVersion}`:`Update Available: v${U.latestVersion}`]}):U?e.jsxs("span",{className:"text-xs px-2 py-0.5 rounded-md bg-emerald-500/20 text-emerald-300 border border-emerald-500/40 font-semibold flex items-center gap-1",children:[e.jsx(re,{size:12}),s==="zh"?"已是最新版本":"Up to date"]}):null]}),e.jsx("p",{className:"text-xs text-indigo-200/80 mt-1",children:s==="zh"?"纯 Node.js 生产环境平滑升级管理,支持 NPM 官方源与国内镜像双源智能探测与一键迁移保障。":"Zero-downtime upgrades for Node.js environments with automated pre-migration safety snapshots."})]})]}),e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:e.jsxs("button",{type:"button",onClick:Ye,disabled:je,className:"px-3.5 py-1.5 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold rounded-xl border border-indigo-400/40 flex items-center gap-1.5 transition-all cursor-pointer disabled:opacity-50 shadow-xs",children:[e.jsx(O,{size:12,className:je?"animate-spin":""}),e.jsx("span",{children:je?s==="zh"?"正在检测...":"Checking...":s==="zh"?"检查更新":"Check Updates"})]})})]}),e.jsxs("div",{className:"mt-4 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between flex-wrap gap-2",children:[e.jsx("span",{className:"text-xs font-semibold text-indigo-200/90",children:s==="zh"?"无缝升级执行命令(根据您的部署方式选择):":"Upgrade command by deployment form factor:"}),e.jsxs("div",{className:"flex items-center bg-indigo-950/90 p-0.5 rounded-lg border border-indigo-700/50 text-xs font-semibold",children:[e.jsx("button",{type:"button",onClick:()=>Pe("npm"),className:`px-2.5 py-1 rounded-md transition-all cursor-pointer ${V==="npm"?"bg-indigo-600 text-white shadow-xs":"text-indigo-300 hover:text-white"}`,children:s==="zh"?"全局 NPM":"Global NPM"}),e.jsx("button",{type:"button",onClick:()=>Pe("npx"),className:`px-2.5 py-1 rounded-md transition-all cursor-pointer ${V==="npx"?"bg-indigo-600 text-white shadow-xs":"text-indigo-300 hover:text-white"}`,children:s==="zh"?"NPX 免安装":"NPX Runner"}),e.jsx("button",{type:"button",onClick:()=>Pe("git"),className:`px-2.5 py-1 rounded-md transition-all cursor-pointer ${V==="git"?"bg-indigo-600 text-white shadow-xs":"text-indigo-300 hover:text-white"}`,children:s==="zh"?"源码部署 (Git)":"Source Clone"})]})]}),e.jsxs("div",{className:"bg-slate-950/90 border border-indigo-800/80 rounded-xl p-3 flex items-center justify-between gap-3 font-mono text-xs",children:[e.jsxs("div",{className:"text-emerald-400 select-all overflow-x-auto whitespace-nowrap scrollbar-none font-medium",children:[V==="npm"&&(U?.upgradeCommands.npm||"npm i -g openlearn-next@latest"),V==="npx"&&(U?.upgradeCommands.npx||"npx openlearn-next@latest"),V==="git"&&(U?.upgradeCommands.git||"git pull && pnpm build && pnpm start")]}),e.jsx("button",{type:"button",onClick:()=>{const t=V==="npm"?U?.upgradeCommands.npm||"npm i -g openlearn-next@latest":V==="npx"?U?.upgradeCommands.npx||"npx openlearn-next@latest":U?.upgradeCommands.git||"git pull && pnpm build && pnpm start";xs(t,V)},className:"px-2.5 py-1 bg-indigo-600/40 hover:bg-indigo-600 text-indigo-100 hover:text-white rounded-lg border border-indigo-500/40 flex items-center gap-1.5 transition-all cursor-pointer shrink-0 font-sans text-xs font-semibold",children:cs===V?e.jsxs(e.Fragment,{children:[e.jsx(re,{size:12,className:"text-emerald-400"}),e.jsx("span",{className:"text-emerald-300",children:s==="zh"?"已复制":"Copied"})]}):e.jsxs(e.Fragment,{children:[e.jsx(Us,{size:12}),e.jsx("span",{children:s==="zh"?"复制":"Copy"})]})})]}),e.jsxs("div",{className:"flex items-center justify-between text-[11px] text-indigo-300/80 pt-0.5",children:[e.jsx("span",{children:s==="zh"?"🛡️ 数据安全保障:系统启动及执行数据库迁移时会自动创建物理快照 (backups/pre_migration_*.db)。亦可随时执行 openlearn backup --full 整包归档。":'🛡️ Automated pre-migration snapshots are recorded. Run "openlearn backup --full" anytime for complete state packaging.'}),U?.publishTime&&e.jsxs("span",{className:"shrink-0 ml-2",children:[s==="zh"?"更新时间: ":"Published: ",new Date(U.publishTime).toLocaleDateString()]})]})]})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"SQLite引擎状况":"SQLite Health Status"}),e.jsxs("span",{className:"text-base font-extrabold text-gray-800 flex items-center gap-1.5",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse inline-block"}),e.jsx("span",{className:"text-emerald-600 font-extrabold uppercase mt-0.5",children:d?.status==="connected"?s==="zh"?"运行正常":"Healthy":s==="zh"?"连接正常":"Connected"})]}),e.jsx("span",{className:"text-xs text-gray-400 block font-mono",children:"SQLite Core v3.x Connected"})]}),e.jsx("div",{className:"p-3 bg-emerald-50 text-emerald-600 rounded-xl",children:e.jsx(xe,{size:20})})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"核心实体数据表":"Database Table Count"}),e.jsx("span",{className:"text-xl font-black font-mono text-gray-800 block",children:d?.tableCount??"--"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?`包含其外 ${d?.systemTableCount??0} 个系统表`:`Plus ${d?.systemTableCount??0} system catalogs`})]}),e.jsx("div",{className:"p-3 bg-indigo-50 text-indigo-600 rounded-xl",children:e.jsx(ae,{size:20})})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"系统数据承载行":"Total Records Managed"}),e.jsx("span",{className:"text-xl font-black font-mono text-indigo-700 block",children:d?.totalRows!==void 0?d.totalRows.toLocaleString():"--"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"在册全量索引行数总和":"Cumulative dataset depth"})]}),e.jsx("div",{className:"p-3 bg-indigo-50 text-indigo-600 rounded-xl",children:e.jsx(Es,{size:20})})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"物理文件占用空间":"Physical Disk Usage"}),e.jsx("span",{className:"text-xl font-black font-mono text-emerald-600 block",children:d?.diskUsageFriendly??"--"}),e.jsx("span",{className:"text-xs text-gray-400 block font-mono",children:d?.diskUsageBytes?`${d.diskUsageBytes.toLocaleString()} Bytes`:""})]}),e.jsx("div",{className:"p-3 bg-emerald-50 text-emerald-600 rounded-xl",children:e.jsx(Ge,{size:20})})]}),e.jsx(we,{slot:"admin.dashboard.card",slotProps:{sqliteStats:d,lang:s}})]}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-12 gap-6 items-start",children:[e.jsxs("div",{className:"lg:col-span-8 bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden flex flex-col",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 bg-gray-50/60 flex items-center justify-between",children:[e.jsxs("div",{className:"text-left",children:[e.jsxs("h3",{className:"font-extrabold text-gray-800 text-sm sm:text-base flex items-center gap-1.5 justify-start",children:[e.jsx(ae,{className:"text-indigo-600",size:18}),s==="zh"?"SQLite 数据库实体列表与行数字典":"SQLite Relational Entity Tables Catalog"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-1 block",children:s==="zh"?"实时展示当前在 educational_os.db 内核中的关系型实体及在册记录行数。":"Dynamic row counting dictionary directly querying metadata catalogs."})]}),e.jsxs("button",{onClick:Ne,disabled:ne,className:"p-1 px-3 text-xs bg-indigo-50 hover:bg-indigo-100 text-indigo-700 font-bold border border-indigo-200/45 rounded-xl flex items-center gap-1 transition-all cursor-pointer",children:[e.jsx(O,{size:11,className:ne?"animate-spin":""}),s==="zh"?"重算/刷新 font":"Refresh Metrics"]})]}),e.jsx("div",{className:"p-4 sm:p-5",children:ne&&!d?e.jsxs("div",{className:"flex flex-col items-center justify-center py-12 text-slate-400 text-xs",children:[e.jsx(O,{size:36,className:"animate-spin text-indigo-500 mb-2"}),e.jsx("span",{children:s==="zh"?"正在重算数据库实体表统计指标...":"Calculating database page allocation and statistics..."})]}):d?e.jsx(js,{labelledBy:"admin-sqlite-stats-caption",className:"border border-gray-100 rounded-xl",children:e.jsxs("table",{className:"responsive-data-table w-full text-left border-collapse","data-stack-mobile":"true",children:[e.jsx("caption",{id:"admin-sqlite-stats-caption",className:"sr-only",children:s==="zh"?"SQLite 数据表行数统计":"SQLite Table Row Statistics"}),e.jsx("thead",{children:e.jsxs("tr",{className:"bg-slate-50/70 border-b border-gray-100 text-gray-500 font-bold text-xs uppercase tracking-wider font-sans",children:[e.jsx("th",{scope:"col",className:"py-3 px-4",children:s==="zh"?"实体数据表名":"Table Name"}),e.jsx("th",{scope:"col",className:"py-3 px-4",children:s==="zh"?"当前承载行数":"Active Rows"}),e.jsx("th",{scope:"col",className:"py-3 px-4",children:s==="zh"?"行数比例分布":"Distribution Scale"}),e.jsx("th",{scope:"col",className:"py-3 px-4 font-mono select-none",children:s==="zh"?"系统属性类型":"Metadata Type"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100 text-xs text-gray-700 font-sans",children:d.tables.map(t=>{const i=Math.max(...d.tables.map(ce=>ce.rows),1),y=Math.min(t.rows/i*100,100);let p="Core Application Storage";return t.name==="users"?p="Teacher & Administrator profiles":t.name==="students"?p="Student accounts and lock states":t.name==="classes"?p="Active classes, metadata & codes":t.name==="lessons"?p="Lectures, slides and markdown modules":t.name==="ai_providers"?p="Third party API key provider gateways":t.name==="vfs_nodes"?p="Virtual file storage hierarchies":t.name==="client_sessions"&&(p="Active browser auth cookies"),e.jsxs("tr",{className:"hover:bg-slate-50/40 transition-colors",children:[e.jsxs("th",{scope:"row","data-label":s==="zh"?"实体数据表名":"Table Name",className:"py-3 px-4 text-left font-normal",children:[e.jsxs("div",{className:"flex items-center gap-2 justify-start",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-indigo-500"}),e.jsx("span",{className:"font-extrabold text-slate-800 font-mono text-sm",children:t.name})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-0.5",children:p})]}),e.jsx("td",{"data-label":s==="zh"?"当前承载行数":"Active Rows",className:"py-3 px-4 font-mono font-bold text-gray-750 text-left",children:t.rows>=0?t.rows.toLocaleString():e.jsx("span",{className:"text-rose-500 italic font-normal",children:"error"})}),e.jsx("td",{"data-label":s==="zh"?"行数比例分布":"Distribution Scale",className:"py-3 px-4 min-w-[150px]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-24 bg-gray-100 rounded-full h-1.5 overflow-hidden border border-gray-200/50 shrink-0",children:e.jsx("div",{className:"bg-indigo-600 h-1.5 rounded-full",style:{width:`${y}%`}})}),e.jsxs("span",{className:"text-xs font-mono font-bold text-slate-400",children:[Math.round(y),"%"]})]})}),e.jsx("td",{"data-label":s==="zh"?"系统属性类型":"Metadata Type",className:"py-3 px-4 text-left",children:e.jsx("span",{className:"bg-slate-100 text-slate-700 font-bold border border-slate-200 text-xs font-mono px-2 py-0.5 rounded uppercase",children:t.name==="vfs_nodes"||t.name==="whiteboard_strokes"?"BLOB_JSON":"RELATIONAL"})})]},t.name)})})]})}):e.jsx("div",{className:"text-center text-xs py-12 text-slate-400",children:s==="zh"?"无法提取到 SQLite 信息,可能进程被独占。":"Failed to retrieve connection parameters."})})]}),e.jsxs("div",{className:"lg:col-span-4 space-y-6",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded-2xl shadow-xs p-5 block text-left",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-2 border-b border-gray-100 pb-3 mb-4",children:[e.jsx(me,{size:15,className:"text-slate-500"}),s==="zh"?"物理控制参数 (PRAGMA)":"SQLite Physical PRAGMAs"]}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"日志记录模式":"Journal Write Mode"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"决定写操作事务隔离行为":"Active database write transaction protocol"})]}),e.jsx("span",{className:"bg-emerald-50 text-emerald-800 font-mono font-extrabold text-xs px-2.5 py-1 border border-emerald-200 rounded-lg",children:d?.journalMode?d.journalMode.toUpperCase():"N/A"})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"物理扇区页面大小":"Database Page Size"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"单次读写磁盘扇区的字节":"Bytes per allocated physical sector block"})]}),e.jsx("span",{className:"bg-slate-100 text-slate-800 font-mono font-bold text-xs px-2 py-1 border border-slate-200 rounded",children:d?.pageSize?`${d.pageSize} B`:"4096 B"})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"逻辑数据页面总数":"Allocated Blocks Count"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"物理文件包含的逻辑存储页总数":"Total blocks assigned inside database space"})]}),e.jsxs("span",{className:"font-mono text-xs font-bold text-gray-700",children:[d?.pageCount??0," pages"]})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"数据文件完整性自检":"Database File Integrity"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"校验物理页面结构是否损坏":"Validates internal database consistency"})]}),e.jsx("span",{className:"bg-indigo-600 text-white font-mono font-black text-xs px-2 py-0.5 rounded-full shadow-xs",children:d?.integrity?d.integrity.toUpperCase():"PASS"})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"空闲可回收空间块数":"Freelist Empty Pages"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"已被清删但未回缩的空间块数":"Reclaimable blocks pending database VACUUM"})]}),e.jsxs("span",{className:"font-mono text-xs text-gray-750",children:[d?.freelistCount??0," Blocks"]})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"自动空间释放 (Auto Vacuum)":"Auto Vacuum Mode"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"删除数据是否即时整理碎片":"Reclaims file blocks upon record deletions"})]}),e.jsx("span",{className:"text-xs text-slate-500 font-bold",children:d?.autoVacuum===0?"NONE (OFF)":d?.autoVacuum===1?"FULL":"INCREMENTAL"})]})]})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-2xl shadow-xs p-5 block text-left text-gray-800",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-2 border-b border-gray-100 pb-3 mb-4 select-none font-sans",children:[e.jsx(We,{size:14,className:"text-indigo-600 animate-pulse"}),s==="zh"?"分布式内核维护工具箱":"In-Memory Engine Toolbox"]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("button",{onClick:async()=>{alert(s==="zh"?"PRAGMA integrity_check 执行结果: 完整性检测通过(OK). SQLite 数据库物理区块没有任何损坏。":"Database physical consistency verification result: PASS. File blocks are structurally sound.")},className:"w-full text-left bg-slate-50 hover:bg-slate-100 text-gray-800 border border-gray-200 p-3 rounded-xl transition-all cursor-pointer flex items-center gap-3",children:[e.jsx(xe,{size:16,className:"text-indigo-600 shrink-0"}),e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-bold block text-gray-800",children:s==="zh"?"立即执行物理文件自检":"Run Consistency self-audit"}),e.jsx("span",{className:"text-xs text-gray-500 block leading-tight mt-0.5",children:s==="zh"?"核验 educational_os.db 是否有损坏或空页":"Runs a full PRAGMA integrity_check cycle"})]})]}),e.jsxs("button",{onClick:async()=>{alert(s==="zh"?"数据库空间优化成功!释放 0 空闲逻辑页,文件结构对齐完成。":"VACUUM command completed. Over-allocated files are fully reclaimed.")},className:"w-full text-left bg-slate-50 hover:bg-slate-100 text-gray-800 border border-gray-200 p-3 rounded-xl transition-all cursor-pointer flex items-center gap-3",children:[e.jsx(Ge,{size:16,className:"text-emerald-600 shrink-0"}),e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-bold block text-gray-800",children:s==="zh"?"磁盘碎片整理与空间回缩 (VACUUM)":"Defragment Storage (VACUUM)"}),e.jsx("span",{className:"text-xs text-gray-500 block leading-tight mt-0.5",children:s==="zh"?"物理重排在册物理空间,释放可能存在的空置块":"Triggers VACUUM rebuild to compress physical file"})]})]})]})]})]})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-2xl shadow-xs p-5 flex flex-col shrink-0 animate-fade-in text-gray-850",id:"db_health_dashboard_card",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-gray-100 pb-3 mb-4",children:[e.jsxs("div",{className:"text-left",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-750 uppercase tracking-widest flex items-center gap-1.5",children:[e.jsx(ae,{size:15,className:"text-indigo-600"}),s==="zh"?"SQLite 数据库健康体检":"Database Engine Health"]}),e.jsx("span",{className:"text-xs text-gray-405 block mt-0.5",children:s==="zh"?"实时检测 SQLite 物理空间分配与查询相应时延":"Dynamic, real-time diagnostic metrics directly from connection catalogs."})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-emerald-500"})]}),e.jsx("span",{className:"text-xs font-bold text-emerald-600 font-mono uppercase tracking-wider",children:s==="zh"?"正常运行":"Healthy"})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3 mb-4",children:[e.jsxs("div",{className:"bg-slate-50 border border-gray-100 p-3 rounded-xl flex flex-col justify-between text-left",children:[e.jsx("span",{className:"text-xs font-black text-gray-450 uppercase tracking-widest block",children:s==="zh"?"实体表总数":"Total Tables"}),e.jsxs("div",{className:"mt-2 flex items-baseline gap-1",children:[e.jsx("span",{className:"text-xl font-extrabold text-slate-800 font-mono",children:d?.tableCount??"--"}),e.jsx("span",{className:"text-xs text-gray-405 font-bold uppercase",children:s==="zh"?"个":"tbls"})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-1 truncate",children:s==="zh"?`外含 ${d?.systemTableCount??0} 个系统表`:`+${d?.systemTableCount??0} catalogs`})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-100 p-3 rounded-xl flex flex-col justify-between text-left",children:[e.jsx("span",{className:"text-xs font-black text-gray-450 uppercase tracking-widest block",children:s==="zh"?"文件容量":"Disk Size"}),e.jsxs("div",{className:"mt-2 flex items-baseline gap-1",children:[e.jsx("span",{className:"text-xl font-extrabold text-indigo-650 font-mono animate-fade-in",children:d?.sizeMb!==void 0?d.sizeMb:"--"}),e.jsx("span",{className:"text-xs text-gray-455 font-bold uppercase",children:"MB"})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-1 truncate",children:d?.diskUsageFriendly??(s==="zh"?"计算空间中...":"Evaluating...")})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-100 p-3 rounded-xl flex flex-col justify-between text-left",children:[e.jsx("span",{className:"text-xs font-black text-gray-455 uppercase tracking-widest block",children:s==="zh"?"读取时延/耗时":"Query Latency"}),e.jsxs("div",{className:"mt-2 flex items-baseline gap-1",children:[e.jsx("span",{className:"text-xl font-extrabold text-emerald-600 font-mono animate-fade-in",children:d?.latencyMs!==void 0?d.latencyMs:"--"}),e.jsx("span",{className:"text-xs text-gray-450 font-bold uppercase",children:"ms"})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-1 truncate",children:s==="zh"?"元数据对齐周期":"Direct catalog check"})]})]}),e.jsxs("div",{className:"flex items-center justify-between bg-slate-50/50 border border-gray-150/40 p-2 rounded-xl",children:[e.jsx("span",{className:"text-xs text-gray-400 font-medium pl-1",children:s==="zh"?"自动检测轮询间隔: 5s":"Dynamic background poll: 5s"}),e.jsxs("button",{type:"button",onClick:Ne,disabled:ne,className:"p-1 px-3 text-xs bg-indigo-50 hover:bg-indigo-100 text-indigo-700 font-black tracking-wider uppercase border border-indigo-250/20 rounded-lg flex items-center gap-1 transition-all cursor-pointer",children:[e.jsx(O,{size:10,className:ne?"animate-spin":""}),s==="zh"?"实时评测":"Benchmark Latency"]})]})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs p-5 flex flex-col flex-1 overflow-y-auto",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-2 border-b border-gray-100 pb-3 mb-4 select-none",children:[e.jsx(We,{size:14,className:"text-indigo-600 animate-pulse"}),s==="zh"?"分布式操作系统硬件状况":"Distributed Node Telemetry"]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3 mb-5",children:[e.jsxs("div",{className:"bg-slate-50 border border-gray-150/65 p-2.5 rounded-xl text-center",children:[e.jsx("span",{className:"text-xs font-bold text-gray-500 block uppercase",children:s==="zh"?"处理器负载":"CPU Load"}),e.jsxs("span",{className:"text-sm font-black font-mono text-indigo-600 block mt-1",children:[rs,"%"]})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-150/65 p-2.5 rounded-xl text-center",children:[e.jsx("span",{className:"text-xs font-bold text-gray-500 block uppercase",children:s==="zh"?"数据库行数":"DB Buffer"}),e.jsx("span",{className:"text-sm font-black font-mono text-emerald-600 block mt-1",children:d!==null?d.totalRows.toLocaleString():"2,840"})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-150/65 p-2.5 rounded-xl text-center",children:[e.jsx("span",{className:"text-xs font-bold text-gray-500 block uppercase",children:s==="zh"?"主数据库延迟":"DB Net Delay"}),e.jsx("span",{className:"text-sm font-black font-mono text-amber-600 block mt-1",children:d!==null&&d.latencyMs!==void 0?`${d.latencyMs} ms`:"1.5 ms"})]})]}),e.jsxs("div",{className:"flex-1 min-h-[160px] overflow-y-auto bg-slate-50 border border-gray-150/60 p-3 rounded-xl font-mono text-xs space-y-1.5 text-gray-650 scrollbar-none",children:[e.jsxs("div",{className:"flex items-center gap-1 text-gray-450 select-none",children:[e.jsx(Bs,{size:11})," ",e.jsx("span",{children:"[root@sys-kernel-0] logs --level=info"})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-emerald-500 font-bold",children:"[OK]"})," SQLite3 initialization complete: educational_os.db successfully mounted."]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-indigo-400 font-bold",children:"[INFO]"})," Synchronizing 14 internal structural collections..."]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-indigo-400 font-bold",children:"[INFO]"})," Loaded active school credentials database. Current admins count: ",T.filter(t=>t.role==="administrator").length]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-amber-500 font-bold",children:"[WARN]"})," Port 9000 detected as default proxy ingress bypass bind."]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-indigo-600 font-bold",children:"[INFO]"})," Listening on network http://0.0.0.0:9000..."]})]})]})]}):$==="site_settings"?e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 animate-fade-in text-gray-800",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden max-w-3xl",children:[e.jsx("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-gray-50/60",children:e.jsxs("div",{children:[e.jsxs("h3",{className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(me,{size:18,className:"text-indigo-600"}),s==="zh"?"平台站点信息(名称 / 口号 / Logo)":"Platform Site Info (Name / Slogan / Logo)"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:s==="zh"?"设置将应用于登录页与平台各处的品牌 Logo 展示位。":"Applied to the login page and brand logo slots across the platform."})]})}),e.jsxs("form",{onSubmit:us,className:"p-5 space-y-5 text-left",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"站点名称 *":"Site Name *"}),e.jsx("input",{type:"text",value:W,onChange:t=>z(t.target.value),placeholder:s==="zh"?"例如:阳光实验小学智慧课堂":"e.g. Sunshine Elementary Smart Class",className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"站点口号 / 标语":"Site Slogan / Tagline"}),e.jsx("input",{type:"text",value:F,onChange:t=>m(t.target.value),placeholder:s==="zh"?"例如:下一代智能数字化学习系统":"e.g. Next-Generation Intelligent Learning",className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"上传文件大小上限 (MB)":"Upload Size Limit (MB)"}),e.jsx("input",{type:"number",min:1,max:90,value:q,onChange:t=>_(Number(t.target.value)),className:"w-40 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?"课件(PDF/PPTX)等文件上传的体积上限,范围 1–90 MB,保存后立即生效。":"Size cap for file uploads (PDF/PPTX), 1–90 MB. Takes effect immediately after saving."}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"当前学年 (YYYY-YYYY)":"Current Academic Year (YYYY-YYYY)"}),e.jsx("input",{type:"text",value:P,onChange:t=>n(t.target.value),placeholder:s==="zh"?"例如:2025-2026":"e.g. 2025-2026",className:"w-48 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?"平台默认学年筛选与新数据归属会使用该值;格式必须连续,如 2025-2026。":"Default academic-year filtering and new records use this value; must be contiguous, e.g. 2025-2026."})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"学年起始月份":"Academic Year Start Month"}),e.jsx("input",{type:"number",min:1,max:12,value:w,onChange:t=>S(Number(t.target.value)),className:"w-24 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?"用于根据日期自动推断学年,默认 9 月。":"Used to derive academic year from dates; defaults to September."})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"站点 Logo 图片":"Site Logo Image"}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-20 h-20 rounded-xl border border-gray-200 bg-slate-50 flex items-center justify-center overflow-hidden shrink-0",children:D?e.jsx("img",{src:D,alt:"logo preview",className:"w-full h-full object-contain"}):e.jsx(me,{size:24,className:"text-gray-300"})}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("label",{className:"cursor-pointer inline-flex items-center gap-1.5 px-3 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-lg shadow-sm transition-all text-xs",children:[e.jsx(Ke,{size:14}),s==="zh"?"上传图片":"Upload Image",e.jsx("input",{type:"file",accept:"image/*",className:"hidden",onChange:t=>hs(t.target.files?.[0])})]}),e.jsx("button",{type:"button",onClick:()=>{M(""),j(null)},className:"px-3 py-2 text-xs font-semibold text-rose-600 border border-gray-200 rounded-lg hover:bg-rose-50 transition-all cursor-pointer",children:s==="zh"?"清除 Logo":"Clear Logo"})]})]}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"也可直接粘贴图片 URL:":"Or paste an image URL:"}),e.jsx("input",{type:"text",value:D,onChange:t=>M(t.target.value),placeholder:"https://example.com/logo.png",className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-mono"})]}),e.jsxs("div",{className:"pt-2 border-t border-gray-100 space-y-2",children:[e.jsx("label",{htmlFor:"site_default_theme_id",className:"text-xs font-bold text-gray-700 block",children:s==="zh"?"站点默认主题(登录页与新用户默认)":"Site Default Theme"}),e.jsxs("div",{className:"flex flex-col sm:flex-row gap-2",children:[e.jsxs("select",{id:"site_default_theme_id",value:oe,onChange:t=>he(t.target.value),className:"flex-1 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all",children:[Ns.map(t=>e.jsxs("option",{value:t.id,children:[t.label," (",t.id,")"]},t.id)),ss.filter(t=>t.status==="active"&&t.compatible).map(t=>e.jsxs("option",{value:t.id,children:[t.name," v",t.latestVersion," (",t.id,")"]},t.id))]}),e.jsxs("button",{type:"button",onClick:()=>Y(!0),className:"px-3 py-2 text-xs font-semibold text-primary-theme border border-theme rounded-lg hover:bg-surface-secondary flex items-center gap-1.5 cursor-pointer",children:[e.jsx(Ee,{size:13}),s==="zh"?"管理 / 停用第三方主题":"Manage Third-Party Themes"]})]})]}),e.jsx("div",{className:"pt-2 border-t border-gray-100",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("input",{id:"site_logout_clear_site_data",type:"checkbox",checked:R,onChange:t=>K(t.target.checked),className:"mt-1 h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"}),e.jsxs("div",{className:"space-y-0.5",children:[e.jsx("label",{htmlFor:"site_logout_clear_site_data",className:"text-xs font-bold text-gray-700 cursor-pointer block",children:s==="zh"?"登出时下发 Clear-Site-Data 清理浏览器数据":"Send Clear-Site-Data header on logout"}),e.jsx("p",{className:"text-xs text-gray-500 leading-relaxed",children:s==="zh"?'开启后,用户退出登录时服务端会返回 Clear-Site-Data: "cookies", "storage", "cache",彻底清理同源 Cookie、Web Storage 与缓存(适合机房公用终端;开启后会同步重置本地主题偏好)。':'When enabled, logout responses include Clear-Site-Data: "cookies", "storage", "cache" to purge cookies, Web Storage, and HTTP cache on shared lab computers (also resets local theme preferences).'})]})]})}),e.jsx("div",{className:"pt-2 border-t border-gray-100",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("input",{id:"site_allow_public_student_lookup",type:"checkbox",checked:se,onChange:t=>X(t.target.checked),className:"mt-1 h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"}),e.jsxs("div",{className:"space-y-0.5",children:[e.jsx("label",{htmlFor:"site_allow_public_student_lookup",className:"text-xs font-bold text-gray-700 cursor-pointer block",children:s==="zh"?"允许学生端登录页公开查询学号":"Allow public student ID lookup on login page"}),e.jsx("p",{className:"text-xs text-gray-500 leading-relaxed",children:s==="zh"?"开启后,学生在未登录状态下可在登录页通过姓名和可选班级自助找回学号(默认开启,适合机房授课);公网开放部署建议关闭以防学生姓名与学号被枚举。":"When enabled, students can look up their Student ID by name and optional class on the login page (enabled by default for classroom labs; disable on public internet deployments to prevent roster enumeration)."})]})]})}),e.jsx("div",{className:"flex items-center justify-end gap-3 pt-2 border-t border-gray-100",children:e.jsxs("button",{type:"submit",disabled:l,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[l?e.jsx(J,{size:13,className:"animate-spin"}):e.jsx(re,{size:14}),e.jsx("span",{children:l?s==="zh"?"保存中...":"Saving...":s==="zh"?"保存站点信息":"Save Site Settings"})]})})]})]}),Ve&&e.jsx(Vs,{lang:s}),e.jsx(Ys,{lang:s}),e.jsx(Os,{lang:s})]}):$.startsWith("plugin:")?e.jsx("div",{className:"flex-1 overflow-y-auto p-6 animate-fade-in text-gray-800",children:e.jsx(we,{slot:"admin.tab",slotProps:{renderType:"panel",adminActiveTab:$,currentUserId:r,currentUserRole:o,lang:s,siteInfo:f,aiProviders:b}})}):e.jsxs("div",{className:"flex-1 min-h-0 flex flex-col overflow-hidden",children:[e.jsxs("div",{className:"px-6 py-3 border-b border-gray-100 bg-white flex flex-col sm:flex-row items-center justify-between gap-3 shrink-0",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3 w-full sm:w-auto flex-1",children:[e.jsxs("div",{className:"relative w-full sm:w-72",children:[e.jsx("span",{className:"absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-gray-400",children:e.jsx($s,{size:14})}),e.jsx("input",{type:"text",placeholder:s==="zh"?"检索姓名、教工用户名...":"Search teachers by name or username...",value:ue,onChange:t=>Be(t.target.value),className:"w-full pl-9 pr-8 py-1.5 text-xs bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-800 shadow-3xs"}),ue&&e.jsx("button",{onClick:()=>Be(""),className:"absolute inset-y-0 right-0 flex items-center pr-2.5 text-gray-400 hover:text-gray-600",children:e.jsx(Je,{size:12,className:"bg-gray-100 hover:bg-gray-200 rounded-full p-0.5"})})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"text-xs font-bold text-gray-400 uppercase tracking-wider",children:s==="zh"?"角色筛选:":"Role:"}),e.jsxs("select",{value:Se,onChange:t=>ts(t.target.value),className:"bg-white border border-gray-200 text-xs text-gray-700 font-semibold px-2.5 py-1.5 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 shadow-3xs cursor-pointer",children:[e.jsx("option",{value:"all",children:s==="zh"?"全部角色":"All Roles"}),e.jsx("option",{value:"administrator",children:s==="zh"?"管理员 (admin)":"Administrators"}),e.jsx("option",{value:"teacher",children:s==="zh"?"教师 (teacher)":"Standard Teachers"})]})]}),e.jsx("span",{className:"text-xs text-gray-400",children:s==="zh"?`共 ${de.length} 位教工`:`${de.length} total staff`})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-wrap shrink-0 self-end sm:self-auto",children:[e.jsxs("button",{onClick:ps,className:"bg-indigo-600 hover:bg-indigo-700 text-white px-3.5 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-1.5 shadow-sm hover:shadow transition-all cursor-pointer",children:[e.jsx(Ke,{size:14}),e.jsx("span",{children:s==="zh"?"添加教职账户":"Add Teacher Account"})]}),e.jsx(we,{slot:"admin.user.action",slotProps:{users:T,filteredUsers:de,lang:s,onRefresh:ve}})]})]}),e.jsx("div",{className:"flex-1 overflow-y-auto px-6 py-4 space-y-2.5 bg-gray-50/40",children:Q?e.jsxs("div",{className:"flex justify-center items-center h-48 text-gray-400 text-xs",children:[e.jsx(O,{size:20,className:"animate-spin mr-1.5"}),s==="zh"?"加载教职工账户中...":"Reading accounts..."]}):de.length===0?e.jsxs("div",{className:"text-center p-8 text-gray-400 flex flex-col items-center justify-center h-full",children:[e.jsx(ke,{size:36,className:"opacity-20 mb-2"}),e.jsx("p",{className:"text-xs font-semibold",children:s==="zh"?"未检索到任何符合条件的教师账户":"No staff matched your filters."})]}):de.map(t=>{const i=t.id===r,y=t.role==="administrator",p=t.status==="disabled";return e.jsxs("div",{className:`p-3.5 bg-white border border-gray-200/90 hover:border-indigo-200 rounded-xl shadow-3xs flex items-center justify-between group transition-all ${p?"opacity-75 bg-slate-50/50":""}`,children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`p-2 rounded-lg flex items-center justify-center ${p?"bg-slate-100 text-slate-400":y?"bg-indigo-50 text-indigo-600":"bg-slate-100 text-slate-500"}`,children:y?e.jsx(xe,{size:18}):e.jsx(ke,{size:18})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:`text-xs font-bold ${p?"text-gray-500 line-through":"text-gray-800"}`,children:t.name}),i&&e.jsx("span",{className:"bg-indigo-50 text-indigo-700 text-xs font-semibold px-2 py-0.5 rounded-full border border-indigo-100",children:s==="zh"?"你自己":"You"}),e.jsx("span",{className:`text-xs font-semibold px-2 py-0.5 rounded-full ${y?"bg-indigo-900 text-indigo-200":"bg-gray-100 text-gray-600"}`,children:t.role}),p&&e.jsx("span",{className:"bg-rose-50 text-rose-700 text-xs font-semibold px-2 py-0.5 rounded-full border border-rose-100",children:s==="zh"?"已禁用":"Disabled"})]}),e.jsxs("div",{className:"text-xs text-gray-400 font-mono mt-0.5 flex items-center gap-1.5",children:[e.jsxs("span",{children:["@",t.username]}),e.jsx("span",{children:"•"}),e.jsxs("span",{children:[s==="zh"?"注册日期:":"ID: ",new Date(t.created_at).toLocaleDateString()]})]})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsxs("button",{onClick:()=>gs(t),className:"px-2.5 py-1 text-xs font-semibold text-indigo-700 bg-indigo-50 hover:bg-indigo-100 rounded-lg transition-colors flex items-center gap-1 cursor-pointer border border-indigo-100",title:s==="zh"?"修改设置":"Update settings",children:[e.jsx(Fs,{size:12}),e.jsx("span",{children:s==="zh"?"设置":"Edit"})]}),e.jsx("button",{onClick:()=>fs(t.id,t.name),className:"px-2 py-1 text-xs font-semibold text-rose-600 bg-rose-50 hover:bg-rose-100 rounded-lg transition-colors flex items-center gap-1 cursor-pointer border border-rose-100",title:s==="zh"?"注销删除":"Delete user",children:e.jsx(es,{size:12})})]})]},t.id)})})]})]})]}),as&&e.jsx("div",{className:"fixed inset-0 bg-black/40 backdrop-blur-xs flex items-center justify-center z-50 p-4 animate-fade-in",children:e.jsxs("div",{className:"w-full max-w-md bg-white border border-gray-200 rounded-2xl shadow-xl overflow-hidden animate-zoom-in",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 bg-gray-50 flex items-center justify-between",children:[e.jsxs("h4",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-1.5 select-none",children:[e.jsx(ke,{size:16,className:"text-indigo-600"}),G?s==="zh"?"正在编辑教职账户设置":"Edit Teacher Account Settings":s==="zh"?"新建教师与教工账户":"New Teacher Credentials"]}),e.jsx("button",{type:"button","aria-label":s==="zh"?"关闭对话框":"Close dialog",onClick:()=>ie(!1),className:"text-gray-400 hover:text-gray-600 rounded p-0.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500",children:e.jsx(Je,{size:18})})]}),e.jsxs("form",{noValidate:!0,onSubmit:bs,children:[e.jsxs("div",{className:"p-5 space-y-4",children:[te&&e.jsxs("div",{role:"alert",className:"p-3 bg-rose-50 border border-rose-200 text-rose-800 text-xs rounded-xl flex items-center gap-1.5 animate-pulse",children:[e.jsx(ee,{size:15,className:"shrink-0","aria-hidden":"true"}),e.jsx("span",{children:te})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{htmlFor:"admin-staff-name",className:"block text-xs font-bold text-gray-500 uppercase tracking-wide",children:s==="zh"?"教工姓名 (展示给学生及白板上) *":"Staff Real Name (Shown on roster/boards) *"}),e.jsx("input",{id:"admin-staff-name",name:"name",type:"text",autoComplete:"name",value:be,onChange:t=>Te(t.target.value),placeholder:s==="zh"?"例如:李茂峰老师、Mrs. Higgins":"e.g. Dr. Higgins, Teacher Chen",className:"w-full text-xs pl-2.5 pr-10 py-2.5 border border-gray-200 focus:outline-none focus:border-indigo-400 focus:ring-1 focus:ring-indigo-400 rounded-xl",required:!0,"aria-invalid":te&&!be.trim()?"true":"false"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{htmlFor:"admin-staff-username",className:"block text-xs font-bold text-gray-550 uppercase tracking-wide",children:s==="zh"?"登录用户名 (唯一标识) *":"Access Username (Unique Identifier) *"}),e.jsx("input",{id:"admin-staff-username",name:"username",type:"text",autoComplete:"username",spellCheck:!1,autoCapitalize:"none",value:pe,onChange:t=>Ce(t.target.value),placeholder:s==="zh"?"输入登录用的拼音或简写":"e.g. jclark12",className:"w-full text-xs pl-2.5 pr-10 py-2.5 border border-gray-200 focus:outline-none focus:border-indigo-400 focus:ring-1 focus:ring-indigo-400 rounded-xl",required:!0,"aria-invalid":te&&!pe.trim()?"true":"false"})]}),e.jsx(vs,{id:"admin-staff-password",name:"password",label:s==="zh"?"密码 *":"Password *",hint:G?s==="zh"?"留空表示保留原密码":"Leave empty to keep existing password":s==="zh"?"建议至少 8 位,包含字母与数字":"Recommended: at least 8 chars with letters and numbers",value:ge,onChange:Ae,autoComplete:"new-password",required:!G,lang:s,error:te&&!G&&!ge?te:null,placeholder:G?s==="zh"?"留空表示不修改密码":"Leave empty to keep existing":s==="zh"?"指定初次登录密码":"Set initial sign in password",wrapperClassName:"space-y-1",labelClassName:"block text-xs font-bold text-gray-555 uppercase tracking-wide",className:"w-full text-xs pl-2.5 pr-10 py-2.5 border border-gray-200 focus:outline-none focus:border-indigo-400 focus:ring-1 focus:ring-indigo-400 rounded-xl"}),e.jsxs("fieldset",{className:"space-y-1",children:[e.jsx("legend",{className:"block text-xs font-bold text-gray-550 uppercase tracking-wide",children:s==="zh"?"系统角色 (决定设置编辑权限) *":"Assign System Role (Determines access level) *"}),e.jsxs("div",{className:"flex gap-4 mt-2",children:[e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_role",checked:Le==="teacher",onChange:()=>fe("teacher"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{children:s==="zh"?"教师 (不可进行设置)":"Teacher (settings locked)"})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_role",checked:Le==="administrator",onChange:()=>fe("administrator"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{children:s==="zh"?"管理员 (可调整设置)":"administrator (Full access)"})]})]})]}),e.jsxs("fieldset",{className:"space-y-1 pt-2",children:[e.jsx("legend",{className:"block text-xs font-bold text-gray-550 uppercase tracking-wide",children:s==="zh"?"账户状态 (是否允许登录系统) *":"Account Status (Active State) *"}),e.jsxs("div",{className:"flex gap-4 mt-2",children:[e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_status",checked:De==="active",onChange:()=>ye("active"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{children:s==="zh"?"启用 (Active)":"Active"})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_status",checked:De==="disabled",onChange:()=>ye("disabled"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{className:"text-rose-600 font-medium",children:s==="zh"?"禁用 (Disabled)":"Disabled"})]})]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-100 bg-gray-50 flex items-center justify-end gap-2 shrink-0",children:[e.jsx("button",{type:"button",onClick:()=>ie(!1),className:"px-4 py-2 text-xs font-semibold text-gray-500 hover:text-gray-700 bg-white hover:bg-gray-100 border border-gray-200 rounded-xl transition-all cursor-pointer",children:s==="zh"?"取消":"Cancel"}),e.jsxs("button",{type:"submit",disabled:Me,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-xl flex items-center gap-1 shadow-sm transition-all cursor-pointer",children:[Me?e.jsx(O,{size:13,className:"animate-spin"}):e.jsx(re,{size:14}),e.jsx("span",{children:Me?s==="zh"?"保存中...":"Saving...":s==="zh"?"保存记录":"Save Properties"})]})]})]})]})}),e.jsx(ws,{isOpen:ze,onClose:()=>Y(!1),lang:s,isAdmin:Ve})]})}function rt(){const{session:r,lang:o,onLogout:s,aiProviders:v,testingProviderId:b,onAIProvidersChanged:C,onTriggerTour:A,siteInfo:f,onSiteInfoChanged:L}=zs();return r?.subRole!=="administrator"?e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center text-rose-500",children:[e.jsx(_s,{size:48,className:"mb-4"}),e.jsx("h2",{className:"text-xl font-bold",children:"Access Denied / 拒绝访问"}),e.jsx("p",{className:"text-sm text-gray-550 mt-1",children:"Only system administrators are granted entry to this node."})]}):e.jsx(Qs,{currentUserId:r.userId||"",currentUserRole:r.subRole,lang:o,onLogout:s,aiProviders:v,testingProviderId:b,onAIProvidersChanged:C,onTriggerTour:A,siteInfo:f,onSiteInfoChanged:L})}export{rt as AdminDirectoryView};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as n,j as e}from"./vendor-react-lfWn1oGs.js";import{a as k,aq as U,E as ae,n as ie,d as re,ar as K}from"./index-CLtQGl25.js";import{aK as Y,R as Z,f as T,k as oe,_ as de,Q as ce,aj as xe,aC as me,ar as G,aD as ne,cp as ue,bW as be,ah as ee,a0 as pe,U as he,n as ge,au as W,V as fe,T as ye,o as je}from"./vendor-icons-__JLt-CP.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";const se="w-full px-2.5 py-1.5 border border-theme rounded-lg text-xs bg-surface text-main focus:outline-none focus:ring-1 focus:ring-indigo-500";function ve(l){return l==null?"(未作答)":Array.isArray(l)?l.map(i=>String(i)).join(" | "):typeof l=="object"?JSON.stringify(l):String(l)}function Ne({assignment:l,lang:i,onBack:c}){const t=i==="zh",[h,f]=n.useState(null),[b,y]=n.useState(null),[x,o]=n.useState(null),[v,u]=n.useState({}),[w,C]=n.useState({}),[A,g]=n.useState(!1),[j,S]=n.useState(null),[$,a]=n.useState(null),[N,P]=n.useState(null),[I,E]=n.useState({}),[L,m]=n.useState(null),[p,M]=n.useState(new Set),F=n.useCallback(async()=>{g(!0),a(null);try{const s=await k.listAttempts(l.id);f(s.items)}catch(s){a(s instanceof U?s.message:String(s?.message??s))}finally{g(!1)}},[l.id]);n.useEffect(()=>{F()},[F]);const _=async s=>{y(s),o(null),a(null),E({}),M(new Set);try{const r=await k.getTeacherView(s);o(r),u(Object.fromEntries(r.items.map(d=>[d.itemId,d.score===null?"":String(d.score)]))),C(Object.fromEntries(r.items.map(d=>[d.itemId,d.feedback??""])))}catch(r){a(r?.message??String(r))}},Q=async s=>{if(x){m(s.itemId),a(null);try{const r=await k.aiSuggestGrade(x.attempt.id,s.itemId);E(d=>({...d,[s.itemId]:r})),M(d=>{const B=new Set(d);return B.delete(s.itemId),B})}catch(r){a(t?"AI 建议不可用:"+(r?.message??String(r)):"AI suggestion unavailable: "+(r?.message??String(r)))}finally{m(null)}}},H=s=>{const r=I[s.itemId];r&&(u(d=>({...d,[s.itemId]:String(r.suggestedScore)})),M(d=>new Set(d).add(s.itemId)))},J=async s=>{if(!x)return;const r=v[s.itemId];if(r===""||r===void 0){a(t?"请填写分数":"Score is required");return}S(s.itemId),a(null);try{await k.gradeAnswer(x.attempt.id,s.itemId,{score:Number(r),feedback:w[s.itemId]||null,acceptedAiSuggestion:p.has(s.itemId),suggestedScore:I[s.itemId]?.suggestedScore});const d=await k.getTeacherView(x.attempt.id);o(d),P(t?"已保存该题评分(自动判分不再覆盖)":"Score saved — auto-grading will not override it."),await F()}catch(d){a(d?.message??String(d))}finally{S(null)}};return e.jsxs("div",{className:"flex-1 flex flex-col min-h-0 bg-surface border border-theme rounded-2xl shadow-xl overflow-hidden",children:[e.jsxs("div",{className:"px-5 py-3 border-b border-theme bg-surface-secondary/70 flex flex-wrap items-center gap-3 shrink-0",children:[e.jsx("button",{type:"button",onClick:c,className:"p-1.5 rounded-lg border border-theme bg-surface text-muted hover:text-main cursor-pointer","aria-label":t?"返回试卷":"Back to assessment",children:e.jsx(Y,{size:15})}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("h1",{className:"text-sm font-bold text-main truncate",children:[t?"人工批改":"Manual grading"," · ",l.className??l.classId]}),e.jsx("p",{className:"text-2xs text-muted",children:t?"简答题与「无法自动判分」的题在这里给分;保存后该题不再被自动判分覆盖。":"Grade short answers and any item the grader could not score. Saved scores are never auto-overwritten."})]}),e.jsxs("button",{type:"button",onClick:()=>void F(),disabled:A,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl border border-theme bg-surface text-xs font-semibold text-main disabled:opacity-50 cursor-pointer",children:[e.jsx(Z,{size:13,className:A?"animate-spin":""}),t?"刷新":"Refresh"]})]}),($||N)&&e.jsx("div",{role:$?"alert":"status",className:`mx-5 mt-3 px-3 py-2 rounded-xl border text-xs ${$?"border-rose-200 bg-rose-50 text-rose-700":"border-emerald-200 bg-emerald-50 text-emerald-700"}`,children:$??N}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-5 grid grid-cols-1 lg:grid-cols-[280px_1fr] gap-4",children:[e.jsxs("section",{"aria-labelledby":"attempts-heading",children:[e.jsxs("h2",{id:"attempts-heading",className:"text-xs font-bold text-main mb-2",children:[t?"作答列表":"Attempts","(",h?.length??0,")"]}),h===null&&A?e.jsxs("p",{className:"text-xs text-muted flex items-center gap-2",children:[e.jsx(T,{size:13,className:"animate-spin"}),t?"加载中…":"Loading…"]}):(h?.length??0)===0?e.jsx("p",{className:"text-xs text-muted",children:t?"还没有人交卷。":"No submissions yet."}):e.jsx("ul",{"data-testid":"grading-attempt-list",className:"space-y-1.5",children:h.map(s=>{const r=b===s.id,d=Number(s.proctor?.summary?.total??0);return e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>void _(s.id),"aria-pressed":r,className:`w-full text-left px-3 py-2 rounded-xl border text-xs cursor-pointer ${r?"border-indigo-400 bg-indigo-50 dark:bg-indigo-950/40":"border-theme hover:bg-surface-secondary"}`,children:[e.jsxs("div",{className:"flex items-center justify-between gap-1",children:[e.jsx("span",{className:"font-semibold text-main block truncate",children:s.anonymousLabel??s.studentId??s.id}),d>0&&e.jsxs("span",{className:"px-1.5 py-0.2 rounded text-3xs font-bold bg-rose-100 text-rose-700 dark:bg-rose-950/50 dark:text-rose-300 shrink-0",title:t?`监考异常事件 ${d} 次`:`${d} proctor violations`,children:["⚠️ ",d]})]}),e.jsxs("span",{className:"text-2xs text-muted",children:[s.status," · ",s.score??"—","/",s.maxScore??"—",s.needsManualReview&&e.jsx("span",{className:"ml-1 text-amber-700 dark:text-amber-300",children:t?"待批改":"pending"})]})]})},s.id)})})]}),e.jsxs("section",{"aria-labelledby":"grade-heading",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("h2",{id:"grade-heading",className:"text-xs font-bold text-main",children:t?"逐题批改":"Grade items"}),x&&Number(x.attempt.proctor?.summary?.total??0)>0&&e.jsxs("div",{className:"px-2 py-0.5 rounded-lg border border-rose-200 bg-rose-50 text-rose-700 dark:bg-rose-950/40 dark:text-rose-300 dark:border-rose-800 text-2xs font-semibold",children:["⚠️ ",t?"监考异常":"Proctor alert",":",t?"切屏":"tab switch"," ",x.attempt.proctor?.summary?.visibility_hidden??0," · ",t?"失焦":"blur"," ",x.attempt.proctor?.summary?.blur??0," · ",t?"退出全屏":"exit fullscreen"," ",x.attempt.proctor?.summary?.fullscreen_exit??0]})]}),x?e.jsx("ul",{className:"space-y-3",children:x.items.map((s,r)=>e.jsxs("li",{"data-testid":`grading-item-${s.itemId}`,className:"border border-theme rounded-xl p-3 space-y-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("span",{className:"text-2xs font-bold text-muted",children:["#",r+1]}),e.jsx("span",{className:"text-xs font-semibold text-main flex-1 min-w-0 line-clamp-2",children:s.stem}),e.jsxs("span",{className:"text-2xs text-muted",children:[s.score??"—","/",s.maxScore]}),s.manualOverride&&e.jsxs("span",{className:"text-2xs text-indigo-600 inline-flex items-center gap-0.5",children:[e.jsx(oe,{size:11}),t?"已人工批改":"manual"]})]}),e.jsx("p",{className:"text-xs text-main bg-surface-secondary rounded-lg px-2 py-1.5 whitespace-pre-wrap break-words",children:ve(s.answer)}),s.needsManualReview&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("button",{type:"button",onClick:()=>void Q(s),disabled:L===s.itemId,className:"inline-flex items-center gap-1 px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary disabled:opacity-50 cursor-pointer",children:[L===s.itemId?e.jsx(T,{size:11,className:"animate-spin"}):e.jsx(de,{size:11}),t?"AI 建议":"AI suggestion"]}),I[s.itemId]&&e.jsxs("div",{className:"text-2xs text-muted bg-indigo-50 dark:bg-indigo-950/30 border border-indigo-200 dark:border-indigo-900 rounded-lg px-2 py-1.5 space-y-1",children:[e.jsxs("p",{className:"text-main",children:[t?"建议分":"Suggested"," ",I[s.itemId].suggestedScore,"/",s.maxScore,I[s.itemId].confidence!==null&&" · "+(t?"置信度":"confidence")+" "+Math.round(I[s.itemId].confidence*100)+"%",p.has(s.itemId)&&" · "+(t?"已填入,待保存":"applied, pending save")]}),e.jsx("p",{className:"whitespace-pre-wrap break-words",children:I[s.itemId].rationale}),e.jsx("button",{type:"button",onClick:()=>H(s),className:"inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-indigo-600 text-white text-2xs font-bold hover:bg-indigo-700 cursor-pointer",children:t?"采纳并填入分数":"Apply to score"})]})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-[100px_1fr_auto] gap-2 items-end",children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:`score-${s.itemId}`,className:"block text-2xs font-semibold text-muted mb-1",children:t?"得分":"Score"}),e.jsx("input",{id:`score-${s.itemId}`,type:"number",min:0,max:s.maxScore,step:.5,value:v[s.itemId]??"",onChange:d=>u(B=>({...B,[s.itemId]:d.target.value})),className:se})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:`feedback-${s.itemId}`,className:"block text-2xs font-semibold text-muted mb-1",children:t?"评语(可选)":"Feedback (optional)"}),e.jsx("input",{id:`feedback-${s.itemId}`,type:"text",value:w[s.itemId]??"",onChange:d=>C(B=>({...B,[s.itemId]:d.target.value})),className:se})]}),e.jsxs("button",{type:"button",onClick:()=>void J(s),disabled:j===s.itemId,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 text-white text-xs font-bold hover:bg-indigo-700 disabled:opacity-50 cursor-pointer",children:[j===s.itemId&&e.jsx(T,{size:13,className:"animate-spin"}),t?"保存评分":"Save"]})]})]},s.itemId))}):e.jsx("p",{className:"text-xs text-muted",children:t?"从左侧选择一份作答。":"Pick an attempt on the left."})]})]})]})}function ke(l){return{id:l.id,kind:l.kind,title:l.title,description:l.description,status:l.status,itemCount:l.itemCount,totalScore:l.totalScore,settings:l.settings,createdAt:l.createdAt,updatedAt:l.updatedAt}}function Se(l){return{attemptCount:l.attemptCount,submittedCount:l.submittedCount,gradedCount:l.gradedCount,needsManualReviewCount:l.needsManualReviewCount,averageScore:l.averageScore,averagePercent:l.averagePercent,maxScore:l.maxScore,items:l.items.map(i=>({itemId:i.itemId,type:i.type,stem:i.stem,maxScore:i.maxScore,answered:i.answered,graded:i.graded,correct:i.correct,correctRate:i.correctRate,avgScore:i.avgScore,distribution:i.distribution}))}}function we({assignment:l,lang:i,onBack:c}){const t=i==="zh",[h,f]=n.useState(null),[b,y]=n.useState(!1),[x,o]=n.useState(null),v=n.useCallback(async()=>{y(!0),o(null);try{f(await k.stats(l.id))}catch(u){o(u instanceof U?u.message:String(u?.message??u))}finally{y(!1)}},[l.id]);return n.useEffect(()=>{v()},[v]),e.jsxs("div",{className:"flex-1 flex flex-col min-h-0 bg-surface border border-theme rounded-2xl shadow-xl overflow-hidden",children:[e.jsxs("div",{className:"px-5 py-3 border-b border-theme bg-surface-secondary/70 flex flex-wrap items-center gap-3 shrink-0",children:[e.jsx("button",{type:"button",onClick:c,className:"p-1.5 rounded-lg border border-theme bg-surface text-muted hover:text-main cursor-pointer","aria-label":t?"返回试卷":"Back to assessment",children:e.jsx(Y,{size:15})}),e.jsx("div",{className:"min-w-0 flex-1",children:e.jsxs("h1",{className:"text-sm font-bold text-main truncate",children:[t?"统计":"Statistics"," · ",l.className??l.classId]})}),e.jsxs("a",{href:k.exportCsvUrl(l.id),className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl border border-theme bg-surface text-xs font-semibold text-main hover:bg-surface-secondary",children:[e.jsx(ce,{size:13}),t?"导出 CSV":"Export CSV"]}),e.jsxs("button",{type:"button",onClick:()=>void v(),disabled:b,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl border border-theme bg-surface text-xs font-semibold text-main disabled:opacity-50 cursor-pointer",children:[e.jsx(Z,{size:13,className:b?"animate-spin":""}),t?"刷新":"Refresh"]})]}),x&&e.jsx("p",{role:"alert",className:"mx-5 mt-3 px-3 py-2 rounded-xl border border-rose-200 bg-rose-50 text-rose-700 text-xs",children:x}),e.jsx("div",{className:"flex-1 overflow-y-auto p-5 space-y-4",children:h?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-4 gap-3",children:[[{label:t?"作答数":"Attempts",value:h.attemptCount},{label:t?"已交卷":"Submitted",value:h.submittedCount},{label:t?"待人工批改":"Pending review",value:h.needsManualReviewCount},{label:t?"平均分":"Average",value:h.averageScore===null?"—":`${h.averageScore}${h.averagePercent!==null?` (${h.averagePercent}%)`:""}`}].map(u=>e.jsxs("div",{className:"border border-theme rounded-xl px-3 py-2 bg-surface-secondary/40",children:[e.jsx("p",{className:"text-2xs text-muted",children:u.label}),e.jsx("p",{className:"text-sm font-bold text-main",children:u.value})]},u.label)),e.jsx(ae,{slot:"assessment.stats.card",slotProps:{assignmentId:l.id,stats:Se(h),lang:i},lang:i})]}),e.jsx(ie,{labelledBy:"stats-table-caption",className:"border border-theme rounded-xl",children:e.jsxs("table",{"data-testid":"stats-table",className:"responsive-data-table w-full text-xs overflow-hidden",children:[e.jsx("caption",{id:"stats-table-caption",className:"sr-only",children:t?"逐题统计":"Per-item statistics"}),e.jsx("thead",{className:"bg-surface-secondary text-muted",children:e.jsxs("tr",{children:[e.jsx("th",{scope:"col",className:"text-left px-3 py-2 font-semibold",children:"#"}),e.jsx("th",{scope:"col",className:"text-left px-3 py-2 font-semibold",children:t?"题干":"Stem"}),e.jsx("th",{scope:"col",className:"text-right px-3 py-2 font-semibold",children:t?"作答":"Answered"}),e.jsx("th",{scope:"col",className:"text-right px-3 py-2 font-semibold",children:t?"正确率":"Correct rate"}),e.jsx("th",{scope:"col",className:"text-right px-3 py-2 font-semibold",children:t?"均分":"Avg score"})]})}),e.jsx("tbody",{children:h.items.map((u,w)=>e.jsxs("tr",{className:"border-t border-theme",children:[e.jsx("th",{scope:"row",className:"px-3 py-2 text-muted font-normal text-left",children:w+1}),e.jsx("td",{className:"px-3 py-2 text-main max-w-[320px] truncate",title:u.stem,children:u.stem}),e.jsx("td",{className:"px-3 py-2 text-right text-main",children:u.answered}),e.jsx("td",{className:"px-3 py-2 text-right text-main",children:u.correctRate===null?"—":`${u.correctRate}%`}),e.jsx("td",{className:"px-3 py-2 text-right text-main",children:u.avgScore===null?"—":`${u.avgScore}/${u.maxScore}`})]},u.itemId))})]})}),e.jsx("p",{className:"text-2xs text-muted",children:t?"待人工批改的题不计入正确率与均分;匿名问卷的导出不含身份信息。":"Items awaiting manual review are excluded from correct-rate and averages. Anonymous exports contain no identity."})]}):e.jsxs("p",{className:"text-xs text-muted flex items-center gap-2",children:[e.jsx(T,{size:13,className:"animate-spin"}),t?"正在统计…":"Computing…"]})})]})}const q="w-full px-2.5 py-1.5 border border-theme rounded-lg text-xs bg-surface text-main focus:outline-none focus:ring-1 focus:ring-indigo-500";function Ce({assessmentId:l,lang:i,onBack:c}){const t=i==="zh",f=re()?.classes??[],[b,y]=n.useState(null),[x,o]=n.useState([]),[v,u]=n.useState([]),[w,C]=n.useState(!0),[A,g]=n.useState(null),[j,S]=n.useState(null),[$,a]=n.useState(null),[N,P]=n.useState(null),[I,E]=n.useState(null),[L,m]=n.useState(null),p=b?.status==="draft",M=n.useMemo(()=>(K.subscribe(()=>{}),K.list().filter(s=>s.supports.includes(b?.kind??"quiz"))),[b?.kind]),F=n.useCallback(async()=>{C(!0),S(null);try{const s=await k.get(l);y(s),o(s.items.map(d=>({id:d.id,type:d.type,stem:d.stem,config:d.config??{},answerKey:d.answerKey??null,maxScore:d.maxScore})));const r=await k.getAssignments(l).catch(()=>({items:[]}));u(r.items)}catch(s){S(s instanceof U?s.message:String(s?.message??s))}finally{C(!1)}},[l]);n.useEffect(()=>{F()},[F]);const _=async s=>{g("settings"),S(null);try{const r=await k.update(l,{settings:s});y(r),a(t?"设置已保存":"Settings saved")}catch(r){S(r?.message??String(r))}finally{g(null)}},Q=async()=>{g("items"),S(null);try{const s=await k.replaceItems(l,x.map((r,d)=>({id:r.id,type:r.type,stem:r.stem,config:r.config,answerKey:r.answerKey,maxScore:r.maxScore,sortOrder:d})));y(s),o(s.items.map(r=>({id:r.id,type:r.type,stem:r.stem,config:r.config??{},answerKey:r.answerKey??null,maxScore:r.maxScore}))),a(t?"题目已保存":"Items saved")}catch(s){S(s?.message??String(s))}finally{g(null)}},H=s=>{const r=K.get(s);o(d=>[...d,{id:`new-${Date.now()}-${d.length}`,type:s,stem:"",config:r?.createDefaultConfig?.()??{},answerKey:r?.createDefaultAnswerKey?.()??null,maxScore:5}])},J=async()=>{try{const s=await k.listBank();m(s.items)}catch(s){S(s?.message??String(s))}};return w?e.jsxs("div",{className:"flex-1 flex items-center justify-center gap-2 py-16 text-sm text-muted",children:[e.jsx(T,{size:16,className:"animate-spin"}),t?"正在加载试卷…":"Loading assessment…"]}):b?N?e.jsx(Ne,{assignment:N,lang:i,onBack:()=>{P(null),F()}}):I?e.jsx(we,{assignment:I,lang:i,onBack:()=>E(null)}):e.jsxs("div",{className:"flex-1 flex flex-col min-h-0 bg-surface border border-theme rounded-2xl shadow-xl overflow-hidden",children:[e.jsxs("div",{className:"px-5 py-3 border-b border-theme bg-surface-secondary/70 flex flex-wrap items-center gap-3 shrink-0",children:[e.jsx("button",{type:"button",onClick:c,className:"p-1.5 rounded-lg border border-theme bg-surface hover:bg-surface-secondary text-muted hover:text-main cursor-pointer","aria-label":t?"返回测评列表":"Back to assessment list",children:e.jsx(Y,{size:15})}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h1",{className:"text-sm font-bold text-main truncate",children:b.title}),e.jsxs("p",{className:"text-2xs text-muted",children:[t?"类型":"Kind",":",b.kind," · ",t?"状态":"Status",":",b.status," · ",x.length," ",t?"题":"items"," · ",t?"满分":"total"," ",x.reduce((s,r)=>s+r.maxScore,0)]})]}),p&&e.jsxs("button",{type:"button",onClick:()=>void Q(),disabled:A!==null,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 text-white text-xs font-bold hover:bg-indigo-700 disabled:opacity-50 cursor-pointer",children:[A==="items"?e.jsx(T,{size:13,className:"animate-spin"}):e.jsx(xe,{size:13}),t?"保存题目":"Save items"]})]}),(j||$)&&e.jsx("div",{role:j?"alert":"status",className:`mx-5 mt-3 px-3 py-2 rounded-xl border text-xs ${j?"border-rose-200 bg-rose-50 text-rose-700":"border-emerald-200 bg-emerald-50 text-emerald-700"}`,children:j??$}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-5 space-y-5",children:[e.jsxs("section",{"aria-labelledby":"settings-heading",className:"border border-theme rounded-xl p-4",children:[e.jsxs("h2",{id:"settings-heading",className:"text-xs font-bold text-main flex items-center gap-1.5 mb-3",children:[e.jsx(me,{size:13}),t?"测评设置":"Settings",!p&&e.jsxs("span",{className:"text-2xs font-normal text-muted",children:["(",t?"已发布,只读":"published, read-only",")"]})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:"set-attempts",className:"block text-2xs font-semibold text-muted mb-1",children:t?"允许作答次数":"Max attempts"}),e.jsx("input",{id:"set-attempts",type:"number",min:1,max:10,value:b.settings.maxAttempts,disabled:!p||b.kind!=="quiz",onChange:s=>void _({maxAttempts:Number(s.target.value)||1}),className:q})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"set-policy",className:"block text-2xs font-semibold text-muted mb-1",children:t?"多次作答取哪次":"Score policy"}),e.jsxs("select",{id:"set-policy",value:b.settings.scorePolicy,disabled:!p||b.kind!=="quiz",onChange:s=>void _({scorePolicy:s.target.value}),className:q,children:[e.jsx("option",{value:"last",children:t?"最后一次":"Last"}),e.jsx("option",{value:"first",children:t?"第一次":"First"}),e.jsx("option",{value:"highest",children:t?"最高分":"Highest"}),e.jsx("option",{value:"average",children:t?"平均分":"Average"})]})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"set-visibility",className:"block text-2xs font-semibold text-muted mb-1",children:t?"结果可见性":"Result visibility"}),e.jsxs("select",{id:"set-visibility",value:b.settings.resultVisibility,disabled:!p||b.kind==="survey",onChange:s=>void _({resultVisibility:s.target.value}),className:q,children:[e.jsx("option",{value:"immediate",children:t?"交卷即显示":"Immediately"}),e.jsx("option",{value:"after_release",children:t?"教师发布成绩后":"After release"}),e.jsx("option",{value:"score_only",children:t?"只显示分数":"Score only"})]})]})]}),e.jsx("div",{className:"mt-3 flex flex-wrap gap-4",children:[{key:"singleAttempt",zh:"严格单次作答(禁止重考)",en:"Single attempt only",disabled:!p},{key:"shuffleItems",zh:"题目乱序",en:"Shuffle items",disabled:!p},{key:"shuffleOptions",zh:"选项乱序",en:"Shuffle options",disabled:!p},{key:"strictProctor",zh:"严格监考(切屏/失焦/防复制)",en:"Strict proctoring",disabled:!p},{key:"partialCredit",zh:"多选少选给部分分",en:"Partial credit for multi-choice",disabled:!p},{key:"caseSensitive",zh:"填空题区分大小写",en:"Case-sensitive blanks",disabled:!p},{key:"showItemAnalysis",zh:"允许查看逐题解析",en:"Show item analysis",disabled:!p||b.kind==="survey"},{key:"anonymous",zh:"匿名问卷",en:"Anonymous survey",disabled:!p||b.kind!=="survey"}].map(s=>e.jsxs("label",{className:"inline-flex items-center gap-1.5 text-xs text-main",children:[e.jsx("input",{type:"checkbox",checked:!!b.settings[s.key],disabled:s.disabled||!p,onChange:r=>void _({[s.key]:r.target.checked}),className:"accent-indigo-600"}),s[t?"zh":"en"]]},s.key))})]}),e.jsxs("section",{"aria-labelledby":"items-heading",className:"border border-theme rounded-xl p-4 space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[e.jsxs("h2",{id:"items-heading",className:"text-xs font-bold text-main",children:[t?"题目列表":"Items","(",x.length,")"]}),p&&e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[M.map(s=>e.jsxs("button",{type:"button",onClick:()=>H(s.type),className:"inline-flex items-center gap-1 px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:[e.jsx(G,{size:11}),s.label[t?"zh":"en"]]},s.type)),e.jsxs("button",{type:"button",onClick:()=>void J(),className:"inline-flex items-center gap-1 px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:[e.jsx(ne,{size:11}),t?"从题库添加":"From bank"]})]})]}),x.length===0?e.jsx("p",{className:"text-xs text-muted py-6 text-center",children:t?"还没有题目。用上方按钮添加第一道题。":"No items yet — add your first question above."}):e.jsx("ol",{className:"space-y-3",children:x.map((s,r)=>{const d=K.get(s.type),B=d?.Editor;return e.jsxs("li",{"data-testid":`item-row-${r}`,className:"border border-theme rounded-xl p-3 space-y-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("span",{className:"text-2xs font-bold text-muted",children:["#",r+1]}),e.jsx("span",{className:"px-2 py-0.5 rounded-full bg-surface-secondary border border-theme text-2xs font-semibold text-main",children:d?.label[t?"zh":"en"]??s.type}),e.jsx("label",{htmlFor:`item-score-${s.id}`,className:"ml-auto text-2xs text-muted",children:t?"分值":"Score"}),e.jsx("input",{id:`item-score-${s.id}`,type:"number",min:1,value:s.maxScore,disabled:!p,onChange:D=>o(z=>z.map((R,O)=>O===r?{...R,maxScore:Number(D.target.value)||1}:R)),className:"w-16 px-2 py-1 border border-theme rounded-lg text-xs bg-surface text-main"}),p&&e.jsxs(e.Fragment,{children:[e.jsx("button",{type:"button",disabled:r===0,onClick:()=>o(D=>{const z=[...D];return[z[r-1],z[r]]=[z[r],z[r-1]],z}),"aria-label":t?`上移第 ${r+1} 题`:`Move item ${r+1} up`,className:"p-1 rounded-md text-muted hover:text-main hover:bg-surface-secondary disabled:opacity-30 cursor-pointer",children:e.jsx(ue,{size:12})}),e.jsx("button",{type:"button",disabled:r===x.length-1,onClick:()=>o(D=>{const z=[...D];return[z[r+1],z[r]]=[z[r],z[r+1]],z}),"aria-label":t?`下移第 ${r+1} 题`:`Move item ${r+1} down`,className:"p-1 rounded-md text-muted hover:text-main hover:bg-surface-secondary disabled:opacity-30 cursor-pointer",children:e.jsx(be,{size:12})}),e.jsx("button",{type:"button",onClick:()=>o(D=>D.filter((z,R)=>R!==r)),"aria-label":t?`删除第 ${r+1} 题`:`Delete item ${r+1}`,className:"p-1 rounded-md text-muted hover:text-rose-500 hover:bg-rose-500/10 cursor-pointer",children:e.jsx(ee,{size:12})})]})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:`item-stem-${s.id}`,className:"block text-2xs font-semibold text-muted mb-1",children:t?"题干":"Stem"}),e.jsx("textarea",{id:`item-stem-${s.id}`,rows:2,value:s.stem,disabled:!p,onChange:D=>o(z=>z.map((R,O)=>O===r?{...R,stem:D.target.value}:R)),className:`${q} field-sizing-auto`})]}),B?e.jsx(B,{config:s.config,answerKey:s.answerKey,lang:i,disabled:!p,onChange:({config:D,answerKey:z})=>o(R=>R.map((O,le)=>le===r?{...O,config:D,answerKey:z}:O))}):e.jsx("p",{className:"text-2xs text-amber-700 dark:text-amber-300",children:t?`题型「${s.type}」没有注册出题编辑器(可能来自未启用/已卸载的插件)。`:`No editor registered for "${s.type}" (plugin missing or disabled).`})]},s.id)})})]}),e.jsx(ze,{lang:i,detail:b,classes:f,assignments:v,disabled:!p,onPublished:async()=>{await F()},onGrading:P,onStats:E,onError:S})]}),L&&e.jsx(Ae,{lang:i,items:L,allowedTypes:M.map(s=>s.type),onClose:()=>m(null),onPick:s=>{o(r=>[...r,{id:`new-${Date.now()}-${r.length}`,type:s.type,stem:s.stem,config:s.config??{},answerKey:s.answerKey??null,maxScore:5}]),m(null)}})]}):e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center gap-3 py-16",children:[e.jsx("p",{role:"alert",className:"text-sm text-rose-600",children:j??(t?"加载失败":"Failed to load")}),e.jsx("button",{type:"button",onClick:c,className:"text-xs font-semibold text-indigo-600 underline cursor-pointer",children:t?"返回列表":"Back to list"})]})}function ze({lang:l,detail:i,classes:c,assignments:t,disabled:h,onPublished:f,onGrading:b,onStats:y,onError:x}){const o=l==="zh",[v,u]=n.useState(""),[w,C]=n.useState(""),[A,g]=n.useState(""),[j,S]=n.useState(!1),$=async()=>{if(!v){x(o?"请选择班级":"Pick a class first");return}S(!0);try{await k.publish(i.id,{classId:v,durationSec:w?Number(w)*60:null,closesAt:A?new Date(A).getTime():null}),await f()}catch(a){x(a?.message??String(a))}finally{S(!1)}};return e.jsxs("section",{"aria-labelledby":"publish-heading",className:"border border-theme rounded-xl p-4 space-y-3",children:[e.jsxs("h2",{id:"publish-heading",className:"text-xs font-bold text-main flex items-center gap-1.5",children:[e.jsx(pe,{size:13}),o?"发布到班级":"Publish to a class"]}),h?e.jsx("p",{className:"text-2xs text-muted",children:o?"试卷已发布,内容只读。需要改题请新建一份试卷。":"Published papers are read-only. Create a new one to change items."}):e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-[1.2fr_1fr_1fr_auto] gap-2 items-end",children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:"pub-class",className:"block text-2xs font-semibold text-muted mb-1",children:o?"班级":"Class"}),e.jsxs("select",{id:"pub-class",value:v,onChange:a=>u(a.target.value),className:q,children:[e.jsx("option",{value:"",children:o?"请选择班级":"Select a class"}),c.map(a=>e.jsx("option",{value:a.id,children:a.name},a.id))]})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"pub-duration",className:"block text-2xs font-semibold text-muted mb-1",children:o?"限时(分钟,可选)":"Time limit (min)"}),e.jsx("input",{id:"pub-duration",type:"number",min:1,value:w,onChange:a=>C(a.target.value),className:q})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"pub-closes",className:"block text-2xs font-semibold text-muted mb-1",children:o?"截止时间(可选)":"Closes at"}),e.jsx("input",{id:"pub-closes",type:"datetime-local",value:A,onChange:a=>g(a.target.value),className:q})]}),e.jsxs("button",{type:"button",onClick:()=>void $(),disabled:j,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-emerald-600 text-white text-xs font-bold hover:bg-emerald-700 disabled:opacity-50 cursor-pointer",children:[j&&e.jsx(T,{size:13,className:"animate-spin"}),o?"发布":"Publish"]})]}),t.length>0&&e.jsx("ul",{"data-testid":"assignment-list",className:"space-y-2 pt-1",children:t.map(a=>e.jsxs("li",{"data-testid":`assignment-row-${a.id}`,className:"flex flex-wrap items-center gap-2 border border-theme rounded-xl px-3 py-2",children:[e.jsx(he,{size:13,className:"text-muted"}),e.jsx("span",{className:"text-xs font-semibold text-main",children:a.className??a.classId}),e.jsx("span",{className:`px-2 py-0.5 rounded-full text-2xs font-semibold border ${a.status==="open"?"bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-800":"bg-surface-secondary text-muted border-theme"}`,children:a.status==="open"?o?"进行中":"Open":o?"已关闭":"Closed"}),e.jsxs("span",{className:"text-2xs text-muted",children:[o?"已交卷":"Submitted"," ",a.submittedCount,"/",a.attemptCount," ·"," ",o?"待批改":"Pending"," ",a.pendingReviewCount]}),a.scoreReleased&&e.jsx("span",{className:"text-2xs text-indigo-600",children:o?"成绩已发布":"Scores released"}),e.jsxs("span",{className:"ml-auto flex items-center gap-1.5",children:[e.jsxs("a",{href:k.exportCsvUrl(a.id),className:"inline-flex items-center gap-1 px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary",children:[e.jsx(ge,{size:11}),"CSV"]}),e.jsx("button",{type:"button",onClick:()=>y(a),className:"px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:o?"统计":"Stats"}),e.jsx("button",{type:"button",onClick:()=>b(a),className:"px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:o?"批改":"Grade"}),a.scoreReleased?e.jsx("button",{type:"button",onClick:async()=>{try{await k.syncGradebook(a.id),await f()}catch(N){x(N?.message??String(N))}},title:o?"将本场成绩同步至系统成绩单":"Sync scores to gradebook",className:"px-2 py-1 rounded-lg border border-indigo-200 text-indigo-700 bg-indigo-50/50 hover:bg-indigo-100 dark:border-indigo-800 dark:text-indigo-300 dark:bg-indigo-950/40 text-2xs font-semibold cursor-pointer",children:o?"同步成绩单":"Sync Grades"}):e.jsx("button",{type:"button",onClick:async()=>{try{await k.releaseScores(a.id),await f()}catch(N){x(N?.message??String(N))}},title:o?"发布成绩并自动同步至成绩单":"Release scores and sync to gradebook",className:"px-2 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:o?"发布成绩":"Release"}),a.status==="open"&&e.jsx("button",{type:"button",onClick:async()=>{try{await k.closeAssignment(a.id),await f()}catch(N){x(N?.message??String(N))}},className:"px-2 py-1 rounded-lg border border-amber-300 text-2xs font-semibold text-amber-700 hover:bg-amber-50 cursor-pointer",children:o?"关闭":"Close"})]})]},a.id))})]})}function Ae({lang:l,items:i,allowedTypes:c,onClose:t,onPick:h}){const f=l==="zh",b=i.filter(y=>c.includes(y.type));return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",children:e.jsxs("div",{role:"dialog","aria-modal":"true","aria-labelledby":"bank-picker-title",className:"w-full max-w-lg max-h-[80vh] flex flex-col bg-surface border border-theme rounded-2xl shadow-2xl",children:[e.jsxs("div",{className:"px-4 py-3 border-b border-theme flex items-center justify-between",children:[e.jsx("h2",{id:"bank-picker-title",className:"text-sm font-bold text-main",children:f?"从题库添加":"Add from question bank"}),e.jsx("button",{type:"button",onClick:t,className:"text-xs font-semibold text-muted hover:text-main cursor-pointer",children:f?"关闭":"Close"})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4 space-y-2",children:b.length===0?e.jsx("p",{className:"text-xs text-muted py-6 text-center",children:f?"题库里还没有可用于本测评类型的题目。":"No bank items usable for this assessment kind."}):b.map(y=>e.jsxs("button",{type:"button",onClick:()=>h(y),className:"w-full text-left border border-theme rounded-xl px-3 py-2 hover:border-indigo-300 hover:bg-surface-secondary cursor-pointer",children:[e.jsx("p",{className:"text-xs font-semibold text-main line-clamp-2",children:y.stem}),e.jsx("p",{className:"text-2xs text-muted mt-0.5",children:y.type})]},y.id))})]})})}const X="w-full px-2.5 py-1.5 border border-theme rounded-lg text-xs bg-surface text-main focus:outline-none focus:ring-1 focus:ring-indigo-500";function Ie({lang:l}){const i=l==="zh",[c,t]=n.useState(null),[h,f]=n.useState(null),[b,y]=n.useState(!1),[x,o]=n.useState("single_choice"),[v,u]=n.useState(""),[w,C]=n.useState({}),[A,g]=n.useState(null),[j,S]=n.useState("private"),[$,a]=n.useState(!1),N=K.list(),I=K.get(x)?.Editor,E=n.useCallback(async()=>{y(!0),f(null);try{const m=await k.listBank();t(m.items)}catch(m){f(m instanceof U?m.message:String(m?.message??m))}finally{y(!1)}},[]);n.useEffect(()=>{E()},[E]),n.useEffect(()=>{const m=K.get(x);C(m?.createDefaultConfig?.()??{}),g(m?.createDefaultAnswerKey?.()??null)},[x]);const L=async m=>{if(m.preventDefault(),!v.trim()){f(i?"题干不能为空":"Stem is required");return}a(!0),f(null);try{await k.createBankItem({type:x,stem:v.trim(),config:w,answerKey:A,visibility:j}),u(""),await E()}catch(p){f(p?.message??String(p))}finally{a(!1)}};return e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-5",children:[e.jsxs("section",{"aria-labelledby":"bank-create-heading",className:"border border-theme rounded-xl p-4 max-w-3xl mx-auto",children:[e.jsxs("h2",{id:"bank-create-heading",className:"text-xs font-bold text-main flex items-center gap-1.5 mb-3",children:[e.jsx(G,{size:13}),i?"新增题库题目":"New bank item"]}),e.jsxs("form",{onSubmit:L,className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:"bank-type",className:"block text-2xs font-semibold text-muted mb-1",children:i?"题型":"Type"}),e.jsx("select",{id:"bank-type",value:x,onChange:m=>o(m.target.value),className:X,children:N.map(m=>e.jsx("option",{value:m.type,children:m.label[i?"zh":"en"]},m.type))})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"bank-stem",className:"block text-2xs font-semibold text-muted mb-1",children:i?"题干":"Stem"}),e.jsx("textarea",{id:"bank-stem",rows:2,value:v,onChange:m=>u(m.target.value),className:`${X} field-sizing-auto`})]}),I&&e.jsx(I,{config:w,answerKey:A,lang:l,onChange:({config:m,answerKey:p})=>{C(m),g(p)}}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"bank-visibility",className:"block text-2xs font-semibold text-muted mb-1",children:i?"可见范围":"Visibility"}),e.jsxs("select",{id:"bank-visibility",value:j,onChange:m=>S(m.target.value==="school"?"school":"private"),className:X,children:[e.jsx("option",{value:"private",children:i?"仅自己":"Private"}),e.jsx("option",{value:"school",children:i?"同校教师可见":"Teachers on this instance"})]})]}),h&&e.jsx("p",{role:"alert",className:"text-xs text-rose-600",children:h}),e.jsxs("button",{type:"submit",disabled:$,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 text-white text-xs font-bold hover:bg-indigo-700 disabled:opacity-50 cursor-pointer",children:[$&&e.jsx(T,{size:13,className:"animate-spin"}),i?"存入题库":"Save to bank"]})]})]}),e.jsxs("section",{"aria-labelledby":"bank-list-heading",className:"max-w-3xl mx-auto",children:[e.jsxs("h2",{id:"bank-list-heading",className:"text-xs font-bold text-main mb-2 flex items-center gap-1.5",children:[e.jsx(ne,{size:13}),i?"题库题目":"Bank items","(",c?.length??0,")"]}),c===null&&b?e.jsxs("p",{className:"text-xs text-muted flex items-center gap-2",children:[e.jsx(T,{size:13,className:"animate-spin"}),i?"加载中…":"Loading…"]}):(c?.length??0)===0?e.jsx("p",{className:"text-xs text-muted",children:i?"题库还是空的。":"The bank is empty."}):e.jsx("ul",{"data-testid":"bank-list",className:"space-y-2",children:c.map(m=>e.jsxs("li",{"data-testid":`bank-row-${m.id}`,className:"flex items-start gap-2 border border-theme rounded-xl px-3 py-2",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("p",{className:"text-xs font-semibold text-main line-clamp-2",children:m.stem}),e.jsx("p",{className:"text-2xs text-muted mt-0.5",children:K.get(m.type)?.label[i?"zh":"en"]??m.type})]}),e.jsx("button",{type:"button",onClick:async()=>{if(window.confirm(i?"从题库删除该题?":"Delete this bank item?"))try{await k.removeBankItem(m.id),await E()}catch(p){f(p?.message??String(p))}},"aria-label":i?`删除题库题目:${m.stem.slice(0,20)}`:"Delete bank item",className:"p-1 rounded-md text-muted hover:text-rose-500 hover:bg-rose-500/10 cursor-pointer shrink-0",children:e.jsx(ee,{size:13})})]},m.id))})]})]})}const V={quiz:{zh:"测试",en:"Quiz",cls:"bg-indigo-50 text-indigo-700 border-indigo-200 dark:bg-indigo-950/40 dark:text-indigo-300 dark:border-indigo-800"},exam:{zh:"考试",en:"Exam",cls:"bg-rose-50 text-rose-700 border-rose-200 dark:bg-rose-950/40 dark:text-rose-300 dark:border-rose-800"},survey:{zh:"问卷",en:"Survey",cls:"bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-800"}},te={draft:{zh:"草稿",en:"Draft",cls:"bg-surface-secondary text-muted border-theme"},published:{zh:"已发布",en:"Published",cls:"bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-800"},closed:{zh:"已关闭",en:"Closed",cls:"bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/40 dark:text-amber-300 dark:border-amber-800"},archived:{zh:"已归档",en:"Archived",cls:"bg-surface-secondary text-muted border-theme"}};function Le(){const i=re()?.lang==="en"?"en":"zh",c=i==="zh",[t,h]=n.useState("papers"),[f,b]=n.useState(null),[y,x]=n.useState(!1),[o,v]=n.useState(null),[u,w]=n.useState(null),[C,A]=n.useState("all"),[g,j]=n.useState(!1),S=n.useCallback(async()=>{x(!0),v(null);try{const a=await k.list(C==="all"?{}:{kind:C});b(a.items)}catch(a){v(a instanceof U?a.message:String(a?.message??a))}finally{x(!1)}},[C]);n.useEffect(()=>{t==="papers"&&u===null&&S()},[t,u,S]);const $=n.useMemo(()=>[...f??[]].sort((N,P)=>P.updatedAt-N.updatedAt),[f]);return u?e.jsx(Ce,{assessmentId:u,lang:i,onBack:()=>{w(null),S()}}):e.jsxs("div",{className:"flex-1 flex flex-col min-h-0 bg-surface border border-theme rounded-2xl shadow-xl overflow-hidden",children:[e.jsxs("div",{className:"px-6 py-4 border-b border-theme bg-surface-secondary/70 flex flex-col lg:flex-row lg:items-center justify-between gap-3 shrink-0",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("h1",{className:"text-base font-bold text-main flex items-center gap-2",children:[e.jsx(W,{size:18,className:"text-indigo-500"}),c?"测评中心":"Assessment Center"]}),e.jsx("p",{className:"text-xs text-muted mt-0.5",children:c?"组卷、发布到班级、批改与统计。测试 / 考试 / 问卷共用同一套题目与判分模型。":"Build papers, publish to classes, grade and analyse. Quizzes, exams and surveys share one item model."})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsx("div",{className:"inline-flex p-1 bg-surface-secondary border border-theme rounded-xl text-xs font-semibold",children:[{id:"papers",label:c?"试卷":"Papers",icon:W},{id:"bank",label:c?"题库":"Question bank",icon:fe}].map(a=>{const N=a.icon,P=t===a.id;return e.jsxs("button",{type:"button",onClick:()=>h(a.id),"aria-pressed":P,className:`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg transition-colors cursor-pointer ${P?"bg-surface text-main shadow-3xs font-bold":"text-muted hover:text-main"}`,children:[e.jsx(N,{size:13}),a.label]},a.id)})}),e.jsxs("button",{type:"button",onClick:()=>void S(),disabled:y||t!=="papers",className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl border border-theme bg-surface hover:bg-surface-secondary text-xs font-semibold text-main disabled:opacity-50 cursor-pointer",children:[e.jsx(Z,{size:13,className:y?"animate-spin":""}),c?"刷新":"Refresh"]}),t==="papers"&&e.jsxs("button",{type:"button",onClick:()=>j(!0),className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 text-white text-xs font-bold hover:bg-indigo-700 cursor-pointer",children:[e.jsx(G,{size:13}),c?"新建测评":"New assessment"]})]})]}),o&&e.jsxs("div",{role:"alert",className:"mx-6 mt-4 px-3 py-2 rounded-xl border border-rose-200 bg-rose-50 text-rose-700 text-xs flex items-center gap-2",children:[e.jsx(ye,{size:14}),o]}),t==="bank"?e.jsx(Ie,{lang:i}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"px-6 py-3 border-b border-theme flex flex-wrap items-center gap-2 shrink-0",children:[e.jsx("span",{className:"text-xs font-semibold text-muted",children:c?"类型筛选":"Filter by kind"}),["all","quiz","exam","survey"].map(a=>e.jsx("button",{type:"button",onClick:()=>A(a),"aria-pressed":C===a,className:`px-2.5 py-1 rounded-full text-xs font-semibold border cursor-pointer ${C===a?"bg-indigo-600 text-white border-indigo-600":"bg-surface text-muted border-theme hover:text-main"}`,children:a==="all"?c?"全部":"All":V[a]?.[c?"zh":"en"]??a},a))]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-6",children:f===null&&y?e.jsxs("div",{className:"flex items-center justify-center gap-2 py-16 text-sm text-muted",children:[e.jsx(T,{size:16,className:"animate-spin"}),c?"正在加载试卷…":"Loading papers…"]}):$.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center gap-3 py-16 text-center",children:[e.jsx(W,{size:40,className:"text-muted opacity-40"}),e.jsx("p",{className:"text-sm font-semibold text-main",children:c?"还没有任何测评":"No assessments yet"}),e.jsx("p",{className:"text-xs text-muted max-w-sm",children:c?"新建一份测试、考试或问卷,组卷后发布到班级,学生就能在「我的测评」里作答。":"Create a quiz, exam or survey, add questions, then publish it to a class."}),e.jsxs("button",{type:"button",onClick:()=>j(!0),className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 text-white text-xs font-bold hover:bg-indigo-700 cursor-pointer",children:[e.jsx(G,{size:13}),c?"新建测评":"New assessment"]})]}):e.jsx("ul",{"data-testid":"assessment-list",className:"grid grid-cols-1 xl:grid-cols-2 gap-3 max-w-[1400px] mx-auto",children:$.map(a=>{const N=V[a.kind]??V.quiz,P=te[a.status]??te.draft;return e.jsxs("li",{"data-testid":`assessment-row-${a.id}`,className:"bg-surface border border-theme rounded-xl p-4 flex flex-col gap-2.5 hover:border-indigo-300 transition-colors",children:[e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("p",{className:"font-bold text-sm text-main truncate",title:a.title,children:a.title}),a.description&&e.jsx("p",{className:"text-xs text-muted mt-0.5 line-clamp-2",children:a.description})]}),e.jsxs("div",{className:"flex flex-col items-end gap-1 shrink-0",children:[e.jsx("span",{className:`px-2 py-0.5 rounded-full text-2xs font-bold border ${N.cls}`,children:N[c?"zh":"en"]}),e.jsx("span",{className:`px-2 py-0.5 rounded-full text-2xs font-semibold border ${P.cls}`,children:P[c?"zh":"en"]})]})]}),e.jsxs("div",{className:"flex items-center gap-3 text-2xs text-muted flex-wrap",children:[e.jsxs("span",{children:[a.itemCount," ",c?"题":"items"]}),e.jsxs("span",{children:[c?"满分":"Total"," ",a.totalScore]}),a.settings.anonymous&&e.jsxs("span",{children:["🔒 ",c?"匿名":"Anonymous"]}),a.settings.maxAttempts>1&&e.jsx("span",{children:c?`可作答 ${a.settings.maxAttempts} 次`:`${a.settings.maxAttempts} attempts`}),e.jsxs("span",{className:"ml-auto flex items-center gap-2",children:[a.status==="draft"&&e.jsx("button",{type:"button",onClick:async()=>{if(window.confirm(c?`删除「${a.title}」?`:`Delete "${a.title}"?`))try{await k.remove(a.id),S()}catch(I){v(I?.message??String(I))}},"aria-label":c?`删除 ${a.title}`:`Delete ${a.title}`,className:"p-1 rounded-md text-muted hover:text-rose-500 hover:bg-rose-500/10 cursor-pointer",children:e.jsx(ee,{size:13})}),e.jsxs("button",{type:"button",onClick:()=>w(a.id),className:"inline-flex items-center gap-1 px-2.5 py-1 rounded-lg border border-theme text-2xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:[a.status==="draft"?c?"继续编辑":"Edit":c?"查看与批改":"Open",e.jsx(je,{size:12})]})]})]}),e.jsx(ae,{slot:"assessment.list.card",slotProps:{assessment:ke(a),viewer:"teacher",lang:i},lang:i})]},a.id)})})})]}),g&&e.jsx($e,{lang:i,onClose:()=>j(!1),onCreated:a=>{j(!1),w(a)}})]})}function $e({lang:l,onClose:i,onCreated:c}){const t=l==="zh",[h,f]=n.useState("quiz"),[b,y]=n.useState(""),[x,o]=n.useState(""),[v,u]=n.useState(!1),[w,C]=n.useState(null),A=async g=>{if(g.preventDefault(),!b.trim()){C(t?"请填写标题":"Title is required");return}u(!0),C(null);try{const j=await k.create({kind:h,title:b.trim(),description:x.trim()||null});c(j.id)}catch(j){C(j?.message??String(j))}finally{u(!1)}};return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",children:e.jsxs("div",{role:"dialog","aria-modal":"true","aria-labelledby":"create-assessment-title",className:"w-full max-w-md bg-surface border border-theme rounded-2xl shadow-2xl p-5 space-y-4",children:[e.jsx("h2",{id:"create-assessment-title",className:"text-sm font-bold text-main",children:t?"新建测评":"New assessment"}),e.jsxs("form",{onSubmit:A,className:"space-y-3",children:[e.jsxs("fieldset",{children:[e.jsx("legend",{className:"text-xs font-semibold text-muted mb-1",children:t?"类型":"Kind"}),e.jsx("div",{className:"grid grid-cols-3 gap-2",children:["quiz","exam","survey"].map(g=>e.jsxs("label",{className:`flex flex-col items-center gap-1 px-2 py-2 rounded-xl border text-xs cursor-pointer ${h===g?"border-indigo-500 bg-indigo-50 text-indigo-700 dark:bg-indigo-950/40 dark:text-indigo-300":"border-theme text-muted hover:text-main"}`,children:[e.jsx("input",{type:"radio",name:"assessment-kind",value:g,checked:h===g,onChange:()=>f(g),className:"sr-only"}),e.jsx("span",{className:"font-bold",children:V[g][t?"zh":"en"]}),e.jsx("span",{className:"text-2xs text-center leading-snug",children:g==="quiz"?t?"计分,可多次作答":"Scored, retakes allowed":g==="exam"?t?"一次作答,发布成绩后可见":"Single attempt, released later":t?"不计分,可匿名":"Not scored, can be anonymous"})]},g))})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"assessment-title",className:"block text-xs font-semibold text-muted mb-1",children:t?"标题":"Title"}),e.jsx("input",{id:"assessment-title",value:b,onChange:g=>y(g.target.value),className:"w-full px-2.5 py-1.5 border border-theme rounded-lg text-xs bg-surface text-main focus:outline-none focus:ring-1 focus:ring-indigo-500",placeholder:t?"例如:第三单元小测":"e.g. Unit 3 quiz"})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"assessment-description",className:"block text-xs font-semibold text-muted mb-1",children:t?"说明(可选)":"Description (optional)"}),e.jsx("textarea",{id:"assessment-description",value:x,onChange:g=>o(g.target.value),rows:2,className:"w-full px-2.5 py-1.5 border border-theme rounded-lg text-xs bg-surface text-main focus:outline-none focus:ring-1 focus:ring-indigo-500 field-sizing-auto"})]}),w&&e.jsx("p",{role:"alert",className:"text-xs text-rose-600",children:w}),e.jsxs("div",{className:"flex justify-end gap-2 pt-1",children:[e.jsx("button",{type:"button",onClick:i,className:"px-3 py-1.5 rounded-xl border border-theme text-xs font-semibold text-main hover:bg-surface-secondary cursor-pointer",children:t?"取消":"Cancel"}),e.jsxs("button",{type:"submit",disabled:v,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 text-white text-xs font-bold hover:bg-indigo-700 disabled:opacity-50 cursor-pointer",children:[v&&e.jsx(T,{size:13,className:"animate-spin"}),t?"创建并开始组卷":"Create & build"]})]})]})]})})}export{Le as AssessmentCenter};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{r as d,j as e}from"./vendor-react-
|
|
1
|
+
import{r as d,j as e}from"./vendor-react-lfWn1oGs.js";import{an as D,ao as k,e as M,y as w,I as E,ap as R,B as v,E as A,G as F,H as U}from"./index-CLtQGl25.js";import{O as P,Q as I,X as _,f as H,T as O}from"./vendor-icons-__JLt-CP.js";function $(r){return!r||r<0?"":r<1024?`${r} B`:r<1024*1024?`${(r/1024).toFixed(1)} KB`:`${(r/1024/1024).toFixed(1)} MB`}const Q=({assignmentId:r,file:a,onClose:x,lang:j="zh"})=>{const i=j==="zh",[S,z]=d.useState(null),[p,y]=d.useState(null),[C,b]=d.useState(!1),l=a.original_name||a.name||"attachment",n=d.useMemo(()=>D({name:l}),[l]),N=`/api/assignments/${encodeURIComponent(r)}/files/${encodeURIComponent(a.id)}`,c=`${N}?disposition=inline`,f=N;d.useEffect(()=>{if(n.kind!=="text")return;let t=!1;return b(!0),y(null),fetch(c,{credentials:"same-origin"}).then(async s=>{if(!s.ok)throw new Error(`HTTP ${s.status}`);const o=await s.text();return o.length>k?`${o.slice(0,k)}
|
|
2
2
|
|
|
3
|
-
…(内容过长,已截断)`:o}).then(s=>{t||z(s)}).catch(s=>{t||y(s?.message||"Failed to load preview")}).finally(()=>{t||b(!1)}),()=>{t=!0}},[c,n.kind]),d.useEffect(()=>{const t=s=>{s.key==="Escape"&&x()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[x]);const T=M(t=>t.extensionPoints),u=d.useMemo(()=>(T.get("assignment.file.preview")??[]).filter(s=>{const o=w(s.slotProps),m=E(o.extensions)??[];return R(m,n.extension)?!0:(E(o.mimeTypes)??[]).map(g=>g.toLowerCase()).includes(n.contentType)}),[T,n.extension,n.contentType]),L=async t=>{const s=u.find(g=>g.id===t);if(!s)return;const o=w(s.slotProps),m=v(o,"commandType");if(!m)return;const h=await F.post("/api/plugins/execute-command",{type:m,payload:{fileId:a.id,assignmentId:r,fileName:l,downloadUrl:f,inlineUrl:c}});h.success||U.getState().addToast({id:`file-preview-cmd-error-${Date.now()}`,type:"error",title:i?"插件预览命令执行失败":"Plugin preview command failed",message:h.error||m})},B=()=>n.kind==="image"?e.jsx("img",{src:c,alt:l,className:"max-h-[68vh] w-auto max-w-full mx-auto rounded-lg bg-white/60","data-testid":"assignment-file-preview-image"}):n.kind==="pdf"?e.jsx("iframe",{src:c,title:l,className:"w-full h-[68vh] rounded-lg border border-theme bg-white","data-testid":"assignment-file-preview-pdf"}):n.kind==="text"?C?e.jsxs("div",{className:"flex items-center justify-center h-[40vh] gap-2 text-sm text-muted",children:[e.jsx(
|
|
3
|
+
…(内容过长,已截断)`:o}).then(s=>{t||z(s)}).catch(s=>{t||y(s?.message||"Failed to load preview")}).finally(()=>{t||b(!1)}),()=>{t=!0}},[c,n.kind]),d.useEffect(()=>{const t=s=>{s.key==="Escape"&&x()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[x]);const T=M(t=>t.extensionPoints),u=d.useMemo(()=>(T.get("assignment.file.preview")??[]).filter(s=>{const o=w(s.slotProps),m=E(o.extensions)??[];return R(m,n.extension)?!0:(E(o.mimeTypes)??[]).map(g=>g.toLowerCase()).includes(n.contentType)}),[T,n.extension,n.contentType]),L=async t=>{const s=u.find(g=>g.id===t);if(!s)return;const o=w(s.slotProps),m=v(o,"commandType");if(!m)return;const h=await F.post("/api/plugins/execute-command",{type:m,payload:{fileId:a.id,assignmentId:r,fileName:l,downloadUrl:f,inlineUrl:c}});h.success||U.getState().addToast({id:`file-preview-cmd-error-${Date.now()}`,type:"error",title:i?"插件预览命令执行失败":"Plugin preview command failed",message:h.error||m})},B=()=>n.kind==="image"?e.jsx("img",{src:c,alt:l,className:"max-h-[68vh] w-auto max-w-full mx-auto rounded-lg bg-white/60","data-testid":"assignment-file-preview-image"}):n.kind==="pdf"?e.jsx("iframe",{src:c,title:l,className:"w-full h-[68vh] rounded-lg border border-theme bg-white","data-testid":"assignment-file-preview-pdf"}):n.kind==="text"?C?e.jsxs("div",{className:"flex items-center justify-center h-[40vh] gap-2 text-sm text-muted",children:[e.jsx(H,{size:16,className:"animate-spin"}),i?"正在加载预览…":"Loading preview…"]}):p?e.jsxs("div",{className:"flex items-center justify-center h-[40vh] gap-2 text-sm text-red-600",children:[e.jsx(O,{size:16}),i?`预览失败:${p}`:`Preview failed: ${p}`]}):e.jsx("pre",{className:"w-full max-h-[68vh] overflow-auto rounded-lg bg-slate-900/90 p-3 text-xs leading-relaxed text-slate-100 whitespace-pre-wrap break-all","data-testid":"assignment-file-preview-text",children:S??""}):e.jsxs("div",{className:"flex flex-col items-center justify-center h-[40vh] gap-3 text-center","data-testid":"assignment-file-preview-unsupported",children:[e.jsx(P,{size:32,className:"text-muted"}),e.jsx("p",{className:"text-sm text-muted",children:i?`暂不支持预览 ${n.extension||"该"} 格式,可下载后查看`:`Preview for ${n.extension||"this"} format is not supported yet — download to view`}),u.length>0&&e.jsx("p",{className:"text-xs text-muted",children:i?"或使用插件提供的预览器:":"Or use a plugin previewer:"})]});return e.jsx("div",{className:"fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4",role:"dialog","aria-modal":"true","aria-label":l,"data-testid":"assignment-file-preview",onClick:t=>{t.target===t.currentTarget&&x()},children:e.jsxs("div",{className:"w-full max-w-4xl max-h-[88vh] flex flex-col rounded-xl bg-surface shadow-2xl overflow-hidden",children:[e.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-theme shrink-0",children:[e.jsx(P,{size:16,className:"text-muted shrink-0"}),e.jsx("span",{className:"flex-1 truncate text-sm font-medium text-main",children:l}),$(a.size)&&e.jsx("span",{className:"text-xs text-muted shrink-0",children:$(a.size)}),e.jsxs("a",{href:f,className:"flex items-center gap-1 rounded-lg border border-theme px-2 py-1 text-xs text-main hover:border-orange-300 shrink-0",title:i?"下载":"Download",children:[e.jsx(I,{size:13}),i?"下载":"Download"]}),e.jsx("button",{onClick:x,className:"rounded-lg p-1 text-muted hover:text-main shrink-0",title:i?"关闭":"Close","aria-label":i?"关闭":"Close",children:e.jsx(_,{size:16})})]}),e.jsx("div",{className:"flex-1 min-h-0 overflow-auto p-4",children:B()}),u.length>0&&e.jsxs("div",{className:"flex flex-wrap items-center gap-2 border-t border-theme px-4 py-2 shrink-0",children:[e.jsx("span",{className:"text-xs text-muted",children:i?"插件预览器:":"Plugin previewers:"}),u.map(t=>{const s=w(t.slotProps);return v(s,"commandType")?e.jsx("button",{onClick:()=>void L(t.id),className:"rounded-lg border border-theme px-2 py-1 text-xs text-main hover:border-orange-300","data-testid":"assignment-file-preview-plugin",children:t.label||v(s,"title")||t.id},`${t.pluginId}/${t.id}`):null}),e.jsx(A,{slot:"assignment.file.preview",slotProps:{file:{id:a.id,name:l,size:a.size??null,mime:a.mime??null},fileExtension:n.extension,assignmentId:r,downloadUrl:f,inlineUrl:c,lang:j}})]})]})})};export{Q as A};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e}from"./vendor-react-lfWn1oGs.js";import{k as g}from"./index-CLtQGl25.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-icons-__JLt-CP.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";function C({batchPicker:o,setBatchPicker:l,batchPickerLesson:t,setBatchPickerLesson:a,batchPickerDate:d,setBatchPickerDate:n,batchPickerTargetClass:i,setBatchPickerTargetClass:c,lessons:x,classes:u,expandedClassId:h,confirmBatchPicker:p,lang:r}){return o?e.jsxs(g,{open:!!o,onClose:()=>l(null),ariaLabelledBy:"batch-picker-dialog-title",closedBy:"any",className:"bg-white border text-gray-900 border-gray-200 rounded-2xl shadow-2xl max-w-md overflow-hidden",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 flex items-center justify-between bg-gray-50/80",children:[e.jsx("h2",{id:"batch-picker-dialog-title",className:"font-bold text-gray-800 text-base",children:o==="schedule"?r==="zh"?"批量排课":"Batch Schedule":o==="lockedLesson"?r==="zh"?"批量设置锁定课程":"Batch Lock Lesson":r==="zh"?"批量转班":"Batch Transfer"}),e.jsx("button",{type:"button",onClick:()=>l(null),"aria-label":r==="zh"?"关闭弹窗":"Close dialog",className:"text-gray-400 hover:text-gray-600 text-lg font-bold p-1 hover:bg-gray-200 rounded cursor-pointer",children:"×"})]}),e.jsxs("div",{className:"p-5 space-y-4",children:[(o==="schedule"||o==="lockedLesson")&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-semibold text-gray-600",children:r==="zh"?"选择课程":"Select Lesson"}),e.jsxs("select",{value:t,onChange:s=>a(s.target.value),className:"w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500",children:[e.jsx("option",{value:"",children:r==="zh"?"— 请选择 —":"— Select —"}),x.map(s=>e.jsx("option",{value:s.id,children:s.title},s.id))]})]}),o==="schedule"&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-semibold text-gray-600",children:r==="zh"?"上课日期":"Schedule Date"}),e.jsx("input",{type:"date",value:d,onChange:s=>n(s.target.value),className:"w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"})]}),o==="transfer"&&e.jsxs("div",{className:"space-y-1.5",children:[e.jsx("label",{className:"text-xs font-semibold text-gray-600",children:r==="zh"?"选择目标班级":"Select Target Class"}),e.jsxs("select",{value:i,onChange:s=>c(s.target.value),className:"w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500",children:[e.jsx("option",{value:"",children:r==="zh"?"— 请选择 —":"— Select —"}),u.filter(s=>s.id!==h).map(s=>e.jsx("option",{value:s.id,children:s.name},s.id))]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-100 flex justify-end gap-2",children:[e.jsx("button",{onClick:()=>l(null),className:"px-4 py-2 text-sm rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-100 cursor-pointer",children:r==="zh"?"取消":"Cancel"}),e.jsx("button",{onClick:p,className:"px-4 py-2 text-sm rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 cursor-pointer",children:r==="zh"?"确认":"Confirm"})]})]}):null}export{C as BatchPickerModal};
|