openlearn-next 0.7.7 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/assets/{ActivityWorkspaceWidget-BLJAc5g8.js → ActivityWorkspaceWidget-BXS1IamH.js} +1 -1
  2. package/dist/assets/{AdminDirectoryView-D6sMmobg.js → AdminDirectoryView-479SfYyD.js} +2 -2
  3. package/dist/assets/{AssessmentCenter-3Cu5DE67.js → AssessmentCenter-CJ-pchOQ.js} +1 -1
  4. package/dist/assets/{AssignmentFilePreview-DvBdEpB3.js → AssignmentFilePreview-wjnrKjsp.js} +2 -2
  5. package/dist/assets/{BatchPickerModal-D1kEgc4L.js → BatchPickerModal-JvoD-1pF.js} +1 -1
  6. package/dist/assets/{ClassesView-DpC-D2y5.js → ClassesView-CB8q2kW0.js} +12 -12
  7. package/dist/assets/ClassroomCountdownWidget-BqzBpAvi.js +2 -0
  8. package/dist/assets/{CloudDriveModal-drWgNbEq.js → CloudDriveModal-DHxOnL4m.js} +1 -1
  9. package/dist/assets/ComputerLabView-BEDe7WiU.js +1 -0
  10. package/dist/assets/{CourseManagement-DuVTm_hD.js → CourseManagement-BjxQC6sq.js} +1 -1
  11. package/dist/assets/{CourseWizardModal-BvQ4d1tN.js → CourseWizardModal-D3UPFtT_.js} +2 -2
  12. package/dist/assets/{CoursewareHubPanel-Ct9hTAOu.js → CoursewareHubPanel-CqJqcZGs.js} +1 -1
  13. package/dist/assets/{Dashboard-D3yj1oZj.js → Dashboard-C3BaEHIG.js} +2 -2
  14. package/dist/assets/{ExportWeightModal-CnbJuOav.js → ExportWeightModal-iF0pVevG.js} +1 -1
  15. package/dist/assets/{HelpTour-COz9zyR9.js → HelpTour-6ChRqWnB.js} +1 -1
  16. package/dist/assets/{HelpView-PmIyAO8_.js → HelpView-D3iYhzBz.js} +2 -2
  17. package/dist/assets/ImportLessonsModal-BOwj7ezj.js +1 -0
  18. package/dist/assets/{ImportModal-CCzFqKY0.js → ImportModal-DAHGdI9h.js} +1 -1
  19. package/dist/assets/{InteractiveCoursewareViewer-bgyDq7MF.js → InteractiveCoursewareViewer-DX9xnk6j.js} +1 -1
  20. package/dist/assets/InteractiveWhiteboard-41mi9keH.js +65 -0
  21. package/dist/assets/{LazyCourseware-Bt31zjJT.js → LazyCourseware-DKyUHXaE.js} +2 -2
  22. package/dist/assets/{LazyWhiteboard-Da0K2ZR1.js → LazyWhiteboard-fXS2P44c.js} +2 -2
  23. package/dist/assets/{LessonEditorView-DT3M9p_4.js → LessonEditorView-Dx-hOKtt.js} +1 -1
  24. package/dist/assets/LiveClassroomView-m0hKdcVK.js +61 -0
  25. package/dist/assets/{NotificationDetailModal-BgqbD3Gh.js → NotificationDetailModal-C2tg_IoJ.js} +1 -1
  26. package/dist/assets/PluginView-BPRUpCjV.js +77 -0
  27. package/dist/assets/{ProcessLogsModal-zO4lMuUa.js → ProcessLogsModal-CULHzXp7.js} +1 -1
  28. package/dist/assets/{QuizGeneratorModal-D60-HJGk.js → QuizGeneratorModal-CYxngpW9.js} +1 -1
  29. package/dist/assets/{StudentAssignmentEvalPanel-C336iZSv.js → StudentAssignmentEvalPanel-Dfe4IOs5.js} +1 -1
  30. package/dist/assets/{StudentAssignmentView--JlShlDQ.js → StudentAssignmentView-IfRo3yJk.js} +1 -1
  31. package/dist/assets/StudentLessonView-Dw2uxBhq.js +1 -0
  32. package/dist/assets/{StudentView-Cjq1QOvV.js → StudentView-DVYUSrVU.js} +2 -2
  33. package/dist/assets/{SystemResourceLibraryModal-pPdl2NHf.js → SystemResourceLibraryModal-BhbpfIBt.js} +1 -1
  34. package/dist/assets/{TeacherView-dLr1v6tY.js → TeacherView-CZjIejet.js} +2 -2
  35. package/dist/assets/{TimetableView-CVAD4LYw.js → TimetableView-CupOIfEy.js} +1 -1
  36. package/dist/assets/{UTIF-p6fKAG_V.js → UTIF-BtQhea94.js} +1 -1
  37. package/dist/assets/index-BDFyco9S.css +1 -0
  38. package/dist/assets/index-BiHQqzdm.js +131 -0
  39. package/dist/assets/{index.es-B8wsP9dg.js → index.es-CijVATXs.js} +1 -1
  40. package/dist/assets/{pluginParsers-D5PQwW_l.js → pluginParsers-C9W6GBVx.js} +1 -1
  41. package/dist/assets/{student-workspace-C2tppsk6.js → student-workspace-CsbA8Cs4.js} +1 -1
  42. package/dist/assets/{vendor-icons-DGdjNlGa.js → vendor-icons-DaYNfRAc.js} +1 -1
  43. package/dist/assets/{vendor-pdf-DmaV9d9t.js → vendor-pdf-DVWGghIL.js} +3 -3
  44. package/dist/assets/{vendor-pptx-CpYklkz2.js → vendor-pptx-BqU7v5zS.js} +5 -5
  45. package/dist/index.html +4 -4
  46. package/dist/server.cjs +56 -13
  47. package/migrations/044_students_gender.sql +8 -0
  48. package/migrations/README.md +1 -0
  49. package/migrations/schema-golden.json +3 -2
  50. package/package.json +2 -2
  51. package/dist/assets/ClassroomCountdownWidget-DFGC4XNs.js +0 -2
  52. package/dist/assets/ComputerLabView-BnfpX3XL.js +0 -1
  53. package/dist/assets/ImportLessonsModal-CSlMGoet.js +0 -1
  54. package/dist/assets/InteractiveWhiteboard-v8FqsMj3.js +0 -65
  55. package/dist/assets/LiveClassroomView-CrLRO8vI.js +0 -61
  56. package/dist/assets/PluginView-CkICwR16.js +0 -77
  57. package/dist/assets/StudentLessonView-BgjjfEfD.js +0 -1
  58. package/dist/assets/index-BfKoCpo_.js +0 -131
  59. package/dist/assets/index-DIcDOunT.css +0 -1
package/dist/index.html CHANGED
@@ -1,5 +1,5 @@
1
1
  <!doctype html>
2
- <html lang="en">
2
+ <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -13,12 +13,12 @@
13
13
  property="og:description"
14
14
  content="An AI-powered Learning Management System featuring interactive classrooms, stage management, real-time student response systems, lesson editor, auto-grading, and an extensible third-party plugin ecosystem."
15
15
  />
16
- <script type="module" crossorigin src="/assets/index-BfKoCpo_.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-BiHQqzdm.js"></script>
17
17
  <link rel="modulepreload" crossorigin href="/assets/vendor-utils-BxOuk3id.js">
18
18
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DHoBZpn2.js">
19
- <link rel="modulepreload" crossorigin href="/assets/vendor-icons-DGdjNlGa.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/vendor-icons-DaYNfRAc.js">
20
20
  <link rel="modulepreload" crossorigin href="/assets/vendor-motion-BFAPOs-b.js">
21
- <link rel="stylesheet" crossorigin href="/assets/index-DIcDOunT.css">
21
+ <link rel="stylesheet" crossorigin href="/assets/index-BDFyco9S.css">
22
22
  </head>
23
23
  <body>
24
24
  <div id="root"></div>
package/dist/server.cjs CHANGED
@@ -916,7 +916,12 @@ var init_manifest_schema = __esm({
916
916
  teacherPanelSchema = import_zod.z.object({
917
917
  id: import_zod.z.string().min(1),
918
918
  title: import_zod.z.string().min(1),
919
- icon: import_zod.z.string().optional()
919
+ icon: import_zod.z.string().optional(),
920
+ // v0.7.6 双轨制 UI:第三方 ZIP 插件在 process 模式下无法传 React 组件,
921
+ // 只能声明可视沙箱 HTML 的相对路径。此处必须显式列出 —— zod 的 object
922
+ // 默认 strip 未声明键,漏声明会让 view 在 parse() 后被静默丢弃,
923
+ // 宿主便退回「占位空态」,插件作者看到的是空白面板且无任何报错。
924
+ view: import_zod.z.string().min(1).optional()
920
925
  });
921
926
  studentFullscreenSchema = import_zod.z.object({
922
927
  id: import_zod.z.string().min(1),
@@ -994,7 +999,11 @@ var init_manifest_schema = __esm({
994
999
  id: import_zod.z.string().min(1),
995
1000
  label: import_zod.z.string().min(1),
996
1001
  icon: import_zod.z.string().optional(),
997
- position: import_zod.z.number().int().optional()
1002
+ position: import_zod.z.number().int().optional(),
1003
+ // 可视沙箱视图(v0.7.6+)。同 teacherPanelSchema.view:漏声明 = 被 strip,
1004
+ // 第三方插件的教师页签会退化成空白占位。
1005
+ view: import_zod.z.string().min(1).optional(),
1006
+ route: import_zod.z.string().optional()
998
1007
  })
999
1008
  ).optional(),
1000
1009
  "teacher.dashboard.widget": import_zod.z.array(
@@ -1002,7 +1011,8 @@ var init_manifest_schema = __esm({
1002
1011
  id: import_zod.z.string().min(1),
1003
1012
  label: import_zod.z.string().min(1),
1004
1013
  icon: import_zod.z.string().optional(),
1005
- position: import_zod.z.number().int().optional()
1014
+ position: import_zod.z.number().int().optional(),
1015
+ view: import_zod.z.string().min(1).optional()
1006
1016
  })
1007
1017
  ).optional(),
1008
1018
  "student.view": import_zod.z.array(
@@ -1010,14 +1020,17 @@ var init_manifest_schema = __esm({
1010
1020
  id: import_zod.z.string().min(1),
1011
1021
  label: import_zod.z.string().min(1),
1012
1022
  icon: import_zod.z.string().optional(),
1013
- route: import_zod.z.string().optional()
1023
+ route: import_zod.z.string().optional(),
1024
+ // 可视沙箱视图(v0.7.6+)。学生端同理:process 模式只能走 HTML 页面。
1025
+ view: import_zod.z.string().min(1).optional()
1014
1026
  })
1015
1027
  ).optional(),
1016
1028
  "student.lesson.tool": import_zod.z.array(
1017
1029
  import_zod.z.object({
1018
1030
  id: import_zod.z.string().min(1),
1019
1031
  label: import_zod.z.string().min(1),
1020
- icon: import_zod.z.string().optional()
1032
+ icon: import_zod.z.string().optional(),
1033
+ view: import_zod.z.string().min(1).optional()
1021
1034
  })
1022
1035
  ).optional(),
1023
1036
  // E-1:补全 contribution-registry.ts 已定义的全部服务端 slot
@@ -21113,7 +21126,7 @@ var import_node_fs11 = __toESM(require("node:fs"), 1);
21113
21126
  var import_node_path13 = __toESM(require("node:path"), 1);
21114
21127
  var import_node_url5 = require("node:url");
21115
21128
  var import_meta7 = {};
21116
- var FALLBACK_VERSION = "0.7.7";
21129
+ var FALLBACK_VERSION = "0.7.8";
21117
21130
  function readPlatformVersion() {
21118
21131
  const bases = [];
21119
21132
  if (typeof __dirname !== "undefined") bases.push(__dirname);
@@ -34711,7 +34724,7 @@ function renderPrometheusMetrics(now = Date.now(), sample) {
34711
34724
  // server/middleware/request-observability.ts
34712
34725
  var REQUEST_ID_RE = /^[A-Za-z0-9._:-]{8,64}$/;
34713
34726
  var QUIET_PATHS = /* @__PURE__ */ new Set(["/health", "/health/ready", "/", "/index.html", "/favicon.ico", "/robots.txt"]);
34714
- var DEV_QUIET_PREFIXES = ["/@vite/", "/@fs/", "/src/", "/node_modules/", "/@id/"];
34727
+ var DEV_QUIET_PREFIXES = ["/@vite/", "/@fs/", "/src/", "/node_modules/", "/@id/", "/.vite-cache/"];
34715
34728
  var DEFAULT_HTTP_SLOW_MS = 1e3;
34716
34729
  function resolveHttpAccessLogMode(envAccessLog = process.env.HTTP_ACCESS_LOG, envLogLevel = process.env.LOG_LEVEL) {
34717
34730
  const normalized = envAccessLog?.trim().toLowerCase();
@@ -43585,6 +43598,14 @@ function sanitizePrivateNotes(raw) {
43585
43598
  }
43586
43599
 
43587
43600
  // server/services/roster-service.ts
43601
+ function normalizeStudentGender(value) {
43602
+ if (typeof value !== "string") return null;
43603
+ const v = value.trim().toLowerCase();
43604
+ if (!v) return null;
43605
+ if (["male", "m", "boy", "\u7537", "\u7537\u751F"].includes(v)) return "male";
43606
+ if (["female", "f", "girl", "\u5973", "\u5973\u751F"].includes(v)) return "female";
43607
+ return null;
43608
+ }
43588
43609
  var RosterService = class {
43589
43610
  constructor(db2 = kernelContainer.db, readDb, pluginHost) {
43590
43611
  this.db = db2;
@@ -43877,7 +43898,7 @@ var RosterService = class {
43877
43898
  const params = ownerScoped ? [scope.userId || "", scope.username || ""] : [];
43878
43899
  const total = this.reader.prepare(`SELECT COUNT(*) AS n FROM students s ${where}`).get(...params).n;
43879
43900
  const students = this.reader.prepare(
43880
- `SELECT s.id, s.student_number, s.name, s.email, s.avatar, s.locked_lesson_id, s.lock_mode, s.lock_target, s.private_notes, s.created_at
43901
+ `SELECT s.id, s.student_number, s.name, s.email, s.avatar, s.gender, s.locked_lesson_id, s.lock_mode, s.lock_target, s.private_notes, s.created_at
43881
43902
  FROM students s ${where}
43882
43903
  ORDER BY s.created_at DESC LIMIT ? OFFSET ?`
43883
43904
  ).all(
@@ -43913,6 +43934,7 @@ var RosterService = class {
43913
43934
  }
43914
43935
  createStudent(params) {
43915
43936
  const { name, email, student_number, password } = params;
43937
+ const gender = normalizeStudentGender(params.gender);
43916
43938
  if (!name?.trim()) throw new Error("Student name is required");
43917
43939
  const studentId = randomId();
43918
43940
  let finalNum = student_number && student_number.trim() !== "" ? student_number.trim() : "";
@@ -43929,7 +43951,9 @@ var RosterService = class {
43929
43951
  finalPlain = initialPassword;
43930
43952
  }
43931
43953
  const hashedPassword = hashPassword(finalPlain);
43932
- this.db.prepare("INSERT INTO students (id, student_number, name, email, password, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(studentId, finalNum, name.trim(), email || "", hashedPassword, Date.now());
43954
+ this.db.prepare(
43955
+ "INSERT INTO students (id, student_number, name, email, password, gender, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
43956
+ ).run(studentId, finalNum, name.trim(), email || "", hashedPassword, gender, Date.now());
43933
43957
  return {
43934
43958
  id: studentId,
43935
43959
  student_number: finalNum,
@@ -43938,6 +43962,9 @@ var RosterService = class {
43938
43962
  }
43939
43963
  updateStudent(id, params) {
43940
43964
  const { name, email, student_number, password, locked_lesson_id, lock_mode, lock_target, private_notes } = params;
43965
+ if (params.gender !== void 0) {
43966
+ this.db.prepare("UPDATE students SET gender = ? WHERE id = ?").run(normalizeStudentGender(params.gender), id);
43967
+ }
43941
43968
  if (name) this.db.prepare("UPDATE students SET name = ? WHERE id = ?").run(name.trim(), id);
43942
43969
  if (email !== void 0)
43943
43970
  this.db.prepare("UPDATE students SET email = ? WHERE id = ?").run(email?.trim() || null, id);
@@ -44055,7 +44082,7 @@ var RosterService = class {
44055
44082
  getClassStudents(classId) {
44056
44083
  return this.reader.prepare(
44057
44084
  `
44058
- SELECT s.id, s.student_number, s.name, s.email, s.avatar, s.locked_lesson_id, s.lock_mode, s.lock_target, s.private_notes, s.created_at, cs.joined_at
44085
+ SELECT s.id, s.student_number, s.name, s.email, s.avatar, s.gender, s.locked_lesson_id, s.lock_mode, s.lock_target, s.private_notes, s.created_at, cs.joined_at
44059
44086
  FROM students s
44060
44087
  INNER JOIN class_students cs ON s.id = cs.student_id
44061
44088
  WHERE cs.class_id = ?
@@ -44116,13 +44143,14 @@ var RosterService = class {
44116
44143
  email,
44117
44144
  studentNumber,
44118
44145
  notes,
44146
+ gender: normalizeStudentGender(st?.gender),
44119
44147
  plainPassword,
44120
44148
  autoPassword,
44121
44149
  hashedPassword: hashPassword(plainPassword)
44122
44150
  });
44123
44151
  }
44124
44152
  const insertStudent = this.db.prepare(
44125
- "INSERT INTO students (id, student_number, name, email, password, private_notes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
44153
+ "INSERT INTO students (id, student_number, name, email, password, private_notes, gender, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
44126
44154
  );
44127
44155
  const findByEmail = this.db.prepare("SELECT id, student_number, name, email FROM students WHERE email = ?");
44128
44156
  const findByNumber = this.db.prepare(
@@ -44149,7 +44177,16 @@ var RosterService = class {
44149
44177
  }
44150
44178
  const studentId = randomId("stu_");
44151
44179
  const finalNum = row.studentNumber || generateStudentNumber(this.db) || `ST_${studentId}`;
44152
- insertStudent.run(studentId, finalNum, row.name, row.email || null, row.hashedPassword, row.notes || null, now);
44180
+ insertStudent.run(
44181
+ studentId,
44182
+ finalNum,
44183
+ row.name,
44184
+ row.email || null,
44185
+ row.hashedPassword,
44186
+ row.notes || null,
44187
+ row.gender,
44188
+ now
44189
+ );
44153
44190
  results.push({
44154
44191
  id: studentId,
44155
44192
  name: row.name,
@@ -57945,10 +57982,16 @@ async function startServer() {
57945
57982
  })
57946
57983
  );
57947
57984
  const loginLimiter = createLoginLimiter(process.env.PLAYWRIGHT_TEST ? 1e3 : 5);
57948
- const writeLimiter = createWriteLimiter(process.env.PLAYWRIGHT_TEST ? 1e4 : 60);
57985
+ const isDev = process.env.NODE_ENV !== "production";
57986
+ const defaultWriteLimit = process.env.PLAYWRIGHT_TEST ? 1e4 : isDev ? 600 : 180;
57987
+ const writeLimiter = createWriteLimiter(defaultWriteLimit);
57988
+ const viewStateLimiter = createWriteLimiter(process.env.PLAYWRIGHT_TEST ? 1e4 : isDev ? 600 : 120);
57949
57989
  const aiLimiter = createAiLimiter(process.env.PLAYWRIGHT_TEST ? 1e3 : 10);
57950
57990
  app.use((req, res, next) => {
57951
57991
  if (req.method === "POST" || req.method === "PUT" || req.method === "DELETE") {
57992
+ if (/^\/api\/classroom\/sessions\/[^/]+\/view-state\/?$/.test(req.path)) {
57993
+ return viewStateLimiter(req, res, next);
57994
+ }
57952
57995
  return writeLimiter(req, res, next);
57953
57996
  }
57954
57997
  next();
@@ -0,0 +1,8 @@
1
+ -- UP
2
+ -- 学生性别:课堂「学生专注力监控」头像按性别渲染(男 / 女),未设置走中性人像。
3
+ -- 取值约定:'male' | 'female';NULL = 未设置 —— 历史数据全部为空,由教师新建/编辑时补录。
4
+ ALTER TABLE students ADD COLUMN gender TEXT;
5
+
6
+ -- DOWN
7
+ -- 回滚丢弃该列:性别只是展示用途,没有下游业务依赖它。
8
+ ALTER TABLE students DROP COLUMN gender;
@@ -59,3 +59,4 @@
59
59
  | 041 | 041_course_package_idempotency.sql | 课程包导入幂等(G-06):同一包重复导入不再产生重复课程 |
60
60
  | 042 | 042_assignment_grade_resubmit_audit.sql | 学生重交审计:`assignment_grade_history` 补 `old_content`,可追溯「这份分数是针对哪份内容给的」 |
61
61
  | 043 | 043_mark_sha256_passwords.sql | Issue #227:把仍是无盐 SHA-256 摘要的存量账号标记为 `must_change_password = 1`,强制走既有改密流程(SHA-256 不可逆,无法反推原口令) |
62
+ | 044 | 044_students_gender.sql | 学生性别 `students.gender`(male / female / NULL),供课堂专注力监控头像按性别渲染;历史数据为空,由教师新建时补录 |
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_comment": "数据库结构金样(自动生成,勿手改):空库跑完 migrations/ 全部迁移后的表→列快照。启动时 assertSchemaMatchesGolden 以子集校验(实际库缺金样结构即拒绝启动;多出的表/列不报错)。改迁移后请运行 pnpm db:gen-golden 再生成。",
3
- "migrationCount": 44,
4
- "generatedAt": "2026-10-25T04:30:00.287Z",
3
+ "migrationCount": 45,
4
+ "generatedAt": "2026-10-29T08:14:15.956Z",
5
5
  "tables": {
6
6
  "_academic_year_backfill": ["backfilled_at", "id"],
7
7
  "_class_passcode_encryption": ["id", "note"],
@@ -598,6 +598,7 @@
598
598
  "avatar",
599
599
  "created_at",
600
600
  "email",
601
+ "gender",
601
602
  "id",
602
603
  "lock_mode",
603
604
  "lock_target",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openlearn-next",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "private": false,
5
5
  "description": "A plugin-driven Educational OS / LMS platform with AI agent teaching assistant",
6
6
  "keywords": [
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "dependencies": {
87
87
  "@google/genai": "^2.4.0",
88
- "@openlearn/plugin-sdk": "3.7.4",
88
+ "@openlearn/plugin-sdk": "3.7.5",
89
89
  "bcryptjs": "^3.0.3",
90
90
  "better-sqlite3": "^12.11.1",
91
91
  "chokidar": "^5.0.0",
@@ -1,2 +0,0 @@
1
- import{r as l,j as e}from"./vendor-react-DHoBZpn2.js";import{l as fe,E as ie}from"./index-BfKoCpo_.js";import{O as ce,a2 as pe,aC as ge,R as we,D as je,h as ye,a3 as Z,Q as be,b0 as ve,U as Ne,b7 as ze,n as re,m as I,a9 as de,ax as ne,ay as oe,q as le,af as ae,ac as me,bR as Ce,M as ke}from"./vendor-icons-DGdjNlGa.js";import{a as Re,B as Se,C as Ae,X as $e,Y as Te,T as Ee,f as Pe,g as De,i as _e}from"./vendor-charts-skggaP9D.js";const ue=["#F59E0B","#6366F1","#10B981","#3B82F6","#0EA5E9"];function Fe({lang:n="zh",lessonId:o,classId:g,lessons:j=[],classes:C=[],schedules:v=[],students:y=[],addToast:V,className:r="",compact:B=!1,allowSimulation:N=!1}){const[p,q]=l.useState(o||""),[w,Y]=l.useState("score"),[ee,U]=l.useState(!1),[G,F]=l.useState(!1),[z,K]=l.useState([]),[L,M]=l.useState({totalParticipants:0,totalResponses:0,averageScore:0}),[te,P]=l.useState(null);l.useEffect(()=>{o&&o!==p&&q(o)},[o]),l.useEffect(()=>{if(!p&&j.length>0){const s=new Date().toISOString().split("T")[0],m=v.find(u=>u.scheduled_date===s);q(m?.lesson_id||j[0].id)}},[p,j,v]);const A=l.useCallback(async s=>{const m=s||p;if(m){U(!0);try{const u=g?`&classId=${encodeURIComponent(g)}`:"",x=await fetch(`/api/classroom/sessions/${encodeURIComponent(m)}/top-performers?limit=5${u}`);if(x.ok){const d=await x.json();d.success&&Array.isArray(d.topPerformers)&&d.topPerformers.length>0?(K(d.topPerformers),d.summary&&M(d.summary)):(K([]),M({totalParticipants:0,totalResponses:0,averageScore:0}))}}catch(u){console.warn("[TopPerformersWidget] fetch failed:",u)}finally{U(!1)}}},[p,g]);l.useEffect(()=>{p&&A(p)},[p,g,A]),l.useEffect(()=>{const s=x=>{if(P(Date.now()),!x||x.lessonId&&x.lessonId!==p)return;const d=x.studentId||x.id;Array.isArray(y)&&y.length>0&&!y.some(h=>h.id===d)||(K(h=>{const H=x.studentName||y.find(b=>b.id===d)?.name||`Student ${String(d).slice(-4)}`,T=Number(x.score)||0,_=x.isCorrect?1:0,X=h.findIndex(b=>b.studentId===d);let O;if(X>=0){const b=h[X],k=b.totalQuizzesAnswered+1,t=b.correctCount+_,i=b.cumulativeScore+T,a=Math.round(t/k*100),c={...b,cumulativeScore:i,totalQuizzesAnswered:k,correctCount:t,accuracy:a,lastSubmittedAt:Date.now()};O=[...h],O[X]=c}else{const b={rank:99,studentId:d,studentName:H,cumulativeScore:T,totalQuizzesAnswered:1,correctCount:_,accuracy:_?100:0,avgTimeSpentMs:x.time_spent_ms??null,lastSubmittedAt:Date.now()};O=[...h,b]}return O.sort((b,k)=>k.cumulativeScore-b.cumulativeScore||k.accuracy-b.accuracy||k.correctCount-b.correctCount),O.slice(0,5).map((b,k)=>({...b,rank:k+1}))}),M(h=>({...h,totalResponses:h.totalResponses+1})))};let m=null;const u=fe(x=>{m?.(),x.on("whiteboard-quiz-answered",s),m=()=>{typeof x.off=="function"&&x.off("whiteboard-quiz-answered",s)}});return()=>{u(),m?.()}},[p,y]);const W=async()=>{if(p){F(!0);try{(await fetch(`/api/classroom/sessions/${p}/simulate-quiz-responses`,{method:"POST",headers:{"Content-Type":"application/json"}})).ok&&(V?.(n==="zh"?"🎯 实时答题模拟成功":"🎯 Quiz Simulation Success",n==="zh"?"已为 8 位学生生成随堂测验提交,实时成绩榜单已即时重排!":"Generated simulated live quiz submissions. Top performers updated!","success"),await A(p))}catch(s){console.error("Failed to simulate quiz responses:",s)}finally{F(!1)}}},D=s=>{V?.(n==="zh"?"🏆 表扬已送达":"🏆 Praise Sent",n==="zh"?`已向榜首学生 [${s.studentName}] 发送课堂「随堂之星」荣誉勋章与鼓励!`:`Sent "Quiz Star" honor medal to top performer [${s.studentName}]!`,"success")},$=()=>{const s=["Rank","Student ID","Student Name","Cumulative Score","Answered","Correct","Accuracy %"],m=z.map(h=>[h.rank,h.studentId,h.studentName,h.cumulativeScore,h.totalQuizzesAnswered,h.correctCount,`${h.accuracy}%`]),u="data:text/csv;charset=utf-8,\uFEFF"+[s.join(","),...m.map(h=>h.join(","))].join(`
2
- `),x=encodeURI(u),d=document.createElement("a");d.setAttribute("href",x),d.setAttribute("download",`top_performers_quiz_${p||"class"}.csv`),document.body.appendChild(d),d.click(),document.body.removeChild(d),V?.(n==="zh"?"📥 导出成功":"📥 Exported",n==="zh"?"随堂测验 Top 5 榜单数据已导出为 CSV 文件。":"Top 5 performers exported to CSV.","info")},J=l.useMemo(()=>z.map(s=>{const m=s.studentName.length>8?`${s.studentName.slice(0,7)}…`:s.studentName;return{rank:s.rank,name:m,fullName:s.studentName,studentId:s.studentId,score:s.cumulativeScore,accuracy:s.accuracy,count:s.totalQuizzesAnswered,correctCount:s.correctCount,avgTimeSec:(s.avgTimeSpentMs/1e3).toFixed(1),chartValue:w==="score"?s.cumulativeScore:w==="accuracy"?s.accuracy:s.totalQuizzesAnswered}}),[z,w]),S=l.useMemo(()=>j.find(s=>s.id===p)?.title||p||"Classroom Session",[j,p]);return e.jsxs("div",{id:"top-performers-widget","data-testid":"top-performers-widget",className:`bg-surface border border-theme rounded-2xl p-5 shadow-sm transition-all relative flex flex-col gap-4 ${r}`,children:[e.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center justify-between gap-3 pb-3 border-b border-theme/60",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"w-10 h-10 rounded-xl bg-amber-500/10 text-amber-500 flex items-center justify-center font-bold shadow-xs",children:e.jsx(ce,{size:20,className:"text-amber-500 animate-bounce"})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h3",{className:"text-base font-bold text-main tracking-tight",children:n==="zh"?"随堂测验优秀榜 (Top 5 Performers)":"Live Quiz Top 5 Performers"}),te&&e.jsxs("span",{className:"flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-emerald-50 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800 animate-pulse",children:[e.jsx(pe,{size:10,className:"animate-spin text-emerald-500"}),e.jsx("span",{children:n==="zh"?"实时作答同步中":"Live Sync"})]})]}),e.jsx("p",{className:"text-xs text-muted mt-0.5",children:n==="zh"?"基于课堂互动白板随堂测试累计得分与答题正确率智能聚合":"Aggregated from cumulative scores and accuracy in live quiz responses"})]})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[j.length>0&&e.jsx("select",{value:p,onChange:s=>q(s.target.value),className:"bg-surface-secondary border border-theme text-xs rounded-lg px-2.5 py-1.5 text-main outline-none focus:ring-1 focus:ring-primary-theme cursor-pointer max-w-[160px] truncate",title:S,children:j.map(s=>e.jsx("option",{value:s.id,children:s.title},s.id))}),e.jsxs("div",{className:"bg-surface-secondary p-0.5 rounded-lg flex items-center border border-theme text-xs",children:[e.jsx("button",{onClick:()=>Y("score"),className:`px-2 py-1 rounded-md font-medium transition-all ${w==="score"?"bg-surface text-primary-theme shadow-xs font-bold":"text-muted hover:text-main"}`,title:n==="zh"?"按累计得分排序":"By Cumulative Score",children:n==="zh"?"累计得分":"Score"}),e.jsx("button",{onClick:()=>Y("accuracy"),className:`px-2 py-1 rounded-md font-medium transition-all ${w==="accuracy"?"bg-surface text-primary-theme shadow-xs font-bold":"text-muted hover:text-main"}`,title:n==="zh"?"按正确率排序":"By Accuracy",children:n==="zh"?"正确率":"Accuracy"}),e.jsx("button",{onClick:()=>Y("count"),className:`px-2 py-1 rounded-md font-medium transition-all ${w==="count"?"bg-surface text-primary-theme shadow-xs font-bold":"text-muted hover:text-main"}`,title:n==="zh"?"按答题题次排序":"By Quiz Count",children:n==="zh"?"题次":"Count"})]}),N&&e.jsxs("button",{onClick:W,disabled:G,className:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-bold bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-400 border border-amber-200 dark:border-amber-800 hover:bg-amber-100 transition-colors cursor-pointer shadow-xs disabled:opacity-50",title:n==="zh"?"模拟学生实时答题":"Simulate Quiz Responses",children:[e.jsx(ge,{size:12,className:G?"animate-spin text-amber-600":"text-amber-600"}),e.jsx("span",{className:"hidden sm:inline",children:n==="zh"?"模拟答题":"Simulate"})]}),e.jsx("button",{onClick:()=>A(p),disabled:ee,className:"p-1.5 rounded-lg border border-theme bg-surface hover:bg-surface-secondary text-muted hover:text-main transition-colors cursor-pointer shadow-xs disabled:opacity-50",title:n==="zh"?"刷新榜单数据":"Refresh rankings",children:e.jsx(we,{size:14,className:ee?"animate-spin text-primary-theme":""})}),e.jsx("button",{onClick:$,disabled:z.length===0,className:"p-1.5 rounded-lg border border-theme bg-surface hover:bg-surface-secondary text-muted hover:text-main transition-colors cursor-pointer shadow-xs disabled:opacity-40",title:n==="zh"?"导出 Top 5 榜单 CSV":"Export CSV",children:e.jsx(je,{size:14})})]})]}),z.length===0?e.jsxs("div",{className:"p-8 rounded-xl border border-dashed border-theme bg-surface-secondary/20 flex flex-col items-center justify-center text-center gap-2",children:[e.jsx(ce,{size:28,className:"text-muted/60"}),e.jsx("p",{className:"text-sm font-semibold text-main",children:n==="zh"?"暂无随堂作答数据":"No Quiz Responses Yet"}),e.jsx("p",{className:"text-xs text-muted max-w-sm",children:n==="zh"?"学生开始作答随堂测验或白板互动题后,实时榜单与答题数据将自动刷新呈现。":"Rankings and visual distribution will appear here once students submit answers."})]}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-3",children:z.slice(0,3).map(s=>{const m=s.rank===1,u=s.rank===2;s.rank;const x=m?"border-amber-400/50 bg-amber-500/5":u?"border-indigo-400/40 bg-indigo-500/5":"border-emerald-400/40 bg-emerald-500/5",d=m?"bg-amber-500 text-white":u?"bg-indigo-500 text-white":"bg-emerald-500 text-white",h=m?"🥇":u?"🥈":"🥉";return e.jsxs("div",{"data-testid":"top-performers-item",className:`p-3.5 rounded-xl border ${x} flex flex-col justify-between gap-3 relative transition-all hover:shadow-sm`,children:[e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2.5",children:[e.jsx("div",{className:`w-8 h-8 rounded-lg flex items-center justify-center font-extrabold text-sm shadow-xs ${d}`,children:h}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-bold text-main leading-tight truncate max-w-[130px]",children:s.studentName}),e.jsxs("span",{className:"text-xs text-muted flex items-center gap-1 mt-0.5",children:[e.jsx(ye,{size:11,className:"text-emerald-500"}),s.correctCount,"/",s.totalQuizzesAnswered," ",n==="zh"?"题全对":"correct"]})]})]}),e.jsxs("div",{className:"text-right",children:[e.jsxs("div",{className:"text-base font-extrabold text-main font-mono",children:[s.cumulativeScore," ",e.jsx("span",{className:"text-xs font-normal text-muted",children:n==="zh"?"分":"pts"})]}),e.jsxs("span",{className:"text-xs font-semibold px-1.5 py-0.2 rounded bg-surface border border-theme text-primary-theme",children:[s.accuracy,"% ",n==="zh"?"正确率":"acc"]})]})]}),e.jsxs("div",{className:"flex items-center justify-between text-xs text-muted pt-2 border-t border-theme/40",children:[e.jsxs("span",{className:"flex items-center gap-1 font-mono",children:[e.jsx(Z,{size:11})," ",s.avgTimeSpentMs?`${(s.avgTimeSpentMs/1e3).toFixed(1)}s`:"—"," ",n==="zh"?"均速":"avg"]}),m&&e.jsxs("button",{onClick:()=>D(s),className:"flex items-center gap-1 text-xs font-semibold text-amber-600 hover:text-amber-700 dark:text-amber-400 hover:underline cursor-pointer",title:n==="zh"?"一键表扬榜首":"Praise top performer",children:[e.jsx(be,{size:11})," ",n==="zh"?"一键表扬":"Praise"]})]})]},s.studentId)})}),e.jsxs("div",{className:"bg-surface-secondary/40 border border-theme rounded-xl p-4 flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-center justify-between text-xs text-muted px-1",children:[e.jsxs("span",{className:"font-semibold text-main flex items-center gap-1.5",children:[e.jsx(ve,{size:14,className:"text-primary-theme"}),n==="zh"?`Top 5 答题得分可视化分布 (${w==="score"?"累计得分":w==="accuracy"?"正确率%":"答题数"})`:`Top 5 Visual Distribution (${w.toUpperCase()})`]}),e.jsx("span",{className:"text-xs text-muted font-mono",children:n==="zh"?`全班平均得分: ${L.averageScore} 分 | 参与人次: ${L.totalResponses}`:`Class Avg: ${L.averageScore} pts | Responses: ${L.totalResponses}`})]}),e.jsx("div",{"data-testid":"top-performers-chart",className:"w-full h-56 pt-2",children:e.jsx(Re,{width:"100%",height:"100%",children:e.jsxs(Se,{data:J,layout:"vertical",margin:{top:8,right:36,left:10,bottom:8},children:[e.jsx(Ae,{strokeDasharray:"3 3",horizontal:!1,stroke:"currentColor",className:"text-theme/40"}),e.jsx($e,{type:"number",domain:[0,w==="accuracy"?100:"auto"],tick:{fontSize:11,fill:"currentColor"},className:"text-muted",unit:w==="accuracy"?"%":""}),e.jsx(Te,{type:"category",dataKey:"name",tick:{fontSize:12,fill:"currentColor",fontWeight:600},className:"text-main",width:85}),e.jsx(Ee,{content:({active:s,payload:m})=>{if(s&&m&&m.length){const u=m[0].payload;return e.jsxs("div",{className:"bg-surface border border-theme p-3 rounded-xl shadow-lg text-xs flex flex-col gap-1.5 min-w-[180px] z-50",children:[e.jsx("div",{className:"flex items-center justify-between font-bold text-main border-b border-theme/60 pb-1",children:e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsxs("span",{className:"w-5 h-5 rounded-full bg-primary-theme/10 text-primary-theme flex items-center justify-center font-mono",children:["#",u.rank]}),u.fullName]})}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-2 gap-y-1 text-muted pt-1",children:[e.jsx("span",{children:n==="zh"?"累计得分:":"Total Score:"}),e.jsxs("span",{className:"font-bold text-main font-mono text-right",children:[u.score," 分"]}),e.jsx("span",{children:n==="zh"?"正确率:":"Accuracy:"}),e.jsxs("span",{className:"font-bold text-emerald-600 dark:text-emerald-400 font-mono text-right",children:[u.accuracy,"%"]}),e.jsx("span",{children:n==="zh"?"答对题次:":"Correct Count:"}),e.jsxs("span",{className:"font-mono text-right",children:[u.correctCount," / ",u.count," 题"]}),e.jsx("span",{children:n==="zh"?"平均答题速度:":"Avg Speed:"}),e.jsxs("span",{className:"font-mono text-right",children:[u.avgTimeSec," 秒"]})]})]})}return null}}),e.jsxs(Pe,{dataKey:"chartValue",radius:[0,6,6,0],barSize:22,children:[J.map((s,m)=>e.jsx(De,{fill:ue[m%ue.length]},`cell-${s.studentId}-${m}`)),e.jsx(_e,{dataKey:"chartValue",position:"right",formatter:s=>w==="accuracy"?`${s}%`:w==="score"?`${s}分`:`${s}题`,className:"fill-main text-xs font-bold font-mono"})]})]})})})]})]}),e.jsxs("div",{className:"flex flex-col sm:flex-row items-center justify-between gap-2 text-xs text-muted pt-1",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(Ne,{size:12,className:"text-primary-theme"}),n==="zh"?"统计口径: 课中互动测验与答题器":"Scope: Live interactive quizzes & clickers"]}),e.jsx("span",{className:"hidden sm:inline text-theme",children:"•"}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(ze,{size:12,className:"text-amber-500"}),n==="zh"?"实时随堂竞赛积分制":"Gamified Live Quiz Points"]})]}),e.jsx("div",{className:"flex items-center gap-2",children:z.length>0&&e.jsx("span",{className:"text-xs text-muted font-medium",children:n==="zh"?`榜首: ${z[0].studentName} (${z[0].cumulativeScore}分)`:`Leader: ${z[0].studentName} (${z[0].cumulativeScore} pts)`})})]})]})}const xe=[{id:"1m",label:"1 分钟快速提问",duration:60,icon:"⚡"},{id:"3m",label:"3 分钟温故知新",duration:180,icon:"📖"},{id:"5m",label:"5 分钟随堂小测",duration:300,icon:"📝"},{id:"10m",label:"10 分钟小组研讨",duration:600,icon:"👥"},{id:"15m",label:"15 分钟探究实验",duration:900,icon:"🔬"},{id:"25m",label:"25 分钟番茄专注",duration:1500,icon:"🍅"},{id:"45m",label:"45 分钟完整自习",duration:2700,icon:"⏱️"}],he=["📝 随堂小测","👥 小组研讨","🎯 专注练习","⚡ 限时答题","📖 课前预热","💬 自由提问"];function Le(){try{const n=window.AudioContext||window.webkitAudioContext;if(!n)return;const o=new n,g=o.currentTime,j=o.createOscillator(),C=o.createGain();j.type="sine",j.frequency.setValueAtTime(523.25,g),j.frequency.exponentialRampToValueAtTime(783.99,g+.25),C.gain.setValueAtTime(.3,g),C.gain.exponentialRampToValueAtTime(.001,g+.8),j.connect(C),C.connect(o.destination),j.start(g),j.stop(g+.8);const v=o.createOscillator(),y=o.createGain();v.type="triangle",v.frequency.setValueAtTime(659.25,g+.15),v.frequency.exponentialRampToValueAtTime(1046.5,g+.4),y.gain.setValueAtTime(.2,g+.15),y.gain.exponentialRampToValueAtTime(.001,g+1.2),v.connect(y),y.connect(o.destination),v.start(g+.15),v.stop(g+1.2)}catch{}}function Ie({lessonId:n,lang:o="zh",compact:g=!1,variant:j="card",syncChannel:C,onlineStudentCount:v=0,onTimeRemainingChange:y,className:V=""}){const[r,B]=l.useState({lessonId:n,totalDuration:300,timeRemaining:300,isRunning:!1,isPaused:!1,label:o==="zh"?"📝 随堂小测":"📝 In-Class Quiz",endsAt:null,updatedAt:Date.now()}),[N,p]=l.useState(!0),[q,w]=l.useState(!1),[Y,ee]=l.useState("5"),[U,G]=l.useState(!1),[F,z]=l.useState(""),[K,L]=l.useState(!1),[M,te]=l.useState([]),[P,A]=l.useState(!1),W=l.useRef(null);l.useEffect(()=>{if(!P)return;const t=i=>{W.current&&!W.current.contains(i.target)&&A(!1)};return document.addEventListener("mousedown",t),()=>document.removeEventListener("mousedown",t)},[P]);const D=l.useRef(null),$=l.useRef(r);$.current=r;const J=l.useCallback(async()=>{if(!n)return;const t=`openlearn_classroom_countdown_${n}`;try{const i=localStorage.getItem(t);if(i){const a=JSON.parse(i);if(a&&typeof a.timeRemaining=="number"){if(a.isRunning&&a.endsAt){const c=Date.now(),f=Math.max(0,Math.round((a.endsAt-c)/1e3));a.timeRemaining=f,f===0&&(a.isRunning=!1,a.isPaused=!1,a.endsAt=null)}B(a)}}}catch{}try{const i=await fetch(`/api/classroom/sessions/${n}/countdown`);if(i.ok){const a=await i.json();a.success&&a.countdown&&B(c=>{const f=a.countdown;return f.totalDuration>0||f.isRunning?f:c})}}catch{}},[n]);l.useEffect(()=>{J()},[J]);const S=l.useCallback(t=>{if(B(t),n)try{localStorage.setItem(`openlearn_classroom_countdown_${n}`,JSON.stringify(t))}catch{}y?.(t.timeRemaining,t.isRunning),C&&C.broadcastCountdown(t),n&&fetch(`/api/classroom/sessions/${n}/countdown`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:t.isRunning?"start":t.isPaused?"pause":"set",duration:t.totalDuration,label:t.label})}).catch(()=>{});try{window.dispatchEvent(new CustomEvent("openlearn:countdown:updated",{detail:t}))}catch{}},[n,y,C]);l.useEffect(()=>(r.isRunning&&r.endsAt?D.current=setInterval(()=>{const t=$.current;if(!t.isRunning||!t.endsAt)return;const i=Date.now(),a=Math.max(0,Math.round((t.endsAt-i)/1e3));if(a!==t.timeRemaining){const c={...t,timeRemaining:a,updatedAt:i};if(a<=0){c.isRunning=!1,c.isPaused=!1,c.endsAt=null,N&&Le();try{window.dispatchEvent(new CustomEvent("openlearn:countdown:expired",{detail:{lessonId:n,label:t.label,totalDuration:t.totalDuration}}))}catch{}}B(c),y?.(c.timeRemaining,c.isRunning),C?.broadcastCountdown(c);try{window.dispatchEvent(new CustomEvent("openlearn:countdown:tick",{detail:{timeRemaining:c.timeRemaining,totalDuration:c.totalDuration}}))}catch{}}},500):D.current&&(clearInterval(D.current),D.current=null),()=>{D.current&&clearInterval(D.current)}),[r.isRunning,r.endsAt,N,n,y,C]);const s=(t,i)=>{const a=Date.now(),c=t>0?t:r.totalDuration||300,f=i||r.label,Q={lessonId:n,totalDuration:c,timeRemaining:c,isRunning:!0,isPaused:!1,label:f,endsAt:a+c*1e3,updatedAt:a};S(Q);try{window.dispatchEvent(new CustomEvent("openlearn:countdown:started",{detail:Q}))}catch{}},m=()=>{const t=Date.now(),i=$.current,a=i.endsAt?Math.max(0,Math.round((i.endsAt-t)/1e3)):i.timeRemaining,c={...i,timeRemaining:a,isRunning:!1,isPaused:!0,endsAt:null,updatedAt:t};S(c);try{window.dispatchEvent(new CustomEvent("openlearn:countdown:paused",{detail:c}))}catch{}},u=()=>{const t=Date.now(),i=$.current,a=i.timeRemaining>0?i.timeRemaining:i.totalDuration||300,c={...i,timeRemaining:a,isRunning:!0,isPaused:!1,endsAt:t+a*1e3,updatedAt:t};S(c);try{window.dispatchEvent(new CustomEvent("openlearn:countdown:resumed",{detail:c}))}catch{}},x=()=>{const t=Date.now(),i=$.current,a={...i,timeRemaining:i.totalDuration||300,isRunning:!1,isPaused:!1,endsAt:null,updatedAt:t};S(a);try{window.dispatchEvent(new CustomEvent("openlearn:countdown:reset",{detail:a}))}catch{}},d=t=>{const i=Date.now(),a=$.current,c=Math.max(0,a.timeRemaining+t),f=Math.max(a.totalDuration,c),Q={...a,totalDuration:f,timeRemaining:c,endsAt:a.isRunning?i+c*1e3:null,updatedAt:i};S(Q)};l.useEffect(()=>{const t={getState:()=>$.current,start:(R,E)=>s(R,E),pause:()=>m(),resume:()=>u(),reset:()=>x(),addTime:R=>d(R),registerPreset:R=>{te(E=>E.some(se=>se.id===R.id)?E:[...E,R])},getPresets:()=>[...xe,...M]};window.__openlearn_countdown=t;const i=R=>{const{duration:E,label:se}=R.detail||{};s(E||300,se)},a=()=>m(),c=()=>u(),f=()=>x(),Q=R=>{const{seconds:E}=R.detail||{};d(E||60)};return window.addEventListener("openlearn:countdown:trigger_start",i),window.addEventListener("openlearn:countdown:trigger_pause",a),window.addEventListener("openlearn:countdown:trigger_resume",c),window.addEventListener("openlearn:countdown:trigger_reset",f),window.addEventListener("openlearn:countdown:trigger_add_time",Q),()=>{window.removeEventListener("openlearn:countdown:trigger_start",i),window.removeEventListener("openlearn:countdown:trigger_pause",a),window.removeEventListener("openlearn:countdown:trigger_resume",c),window.removeEventListener("openlearn:countdown:trigger_reset",f),window.removeEventListener("openlearn:countdown:trigger_add_time",Q)}},[M]);const h=t=>{const i=Math.floor(t/60),a=t%60,c=f=>f.toString().padStart(2,"0");return`${c(i)}:${c(a)}`},H=r.totalDuration>0?Math.min(100,Math.max(0,r.timeRemaining/r.totalDuration*100)):0,T=r.timeRemaining>0&&r.timeRemaining<=60,_=r.timeRemaining>60&&r.timeRemaining<=180,X=T?"text-rose-500":_?"text-amber-500":"text-primary-theme",O=T?"bg-rose-500":_?"bg-amber-500":"bg-primary-theme",b=[...xe,...M],k=()=>q?e.jsxs("div",{id:"classroom-countdown-projector-modal",className:"fixed inset-0 z-50 bg-black/85 backdrop-blur-md flex flex-col items-center justify-center p-6 select-none animate-in fade-in duration-200",children:[e.jsxs("div",{className:"absolute top-6 right-6 flex items-center gap-3",children:[e.jsx("button",{type:"button",onClick:()=>p(!N),className:"p-2.5 rounded-xl bg-white/10 hover:bg-white/20 text-white transition-colors cursor-pointer",children:N?e.jsx(ne,{size:20}):e.jsx(oe,{size:20})}),e.jsx("button",{type:"button",onClick:()=>w(!1),className:"p-2.5 rounded-xl bg-white/10 hover:bg-white/20 text-white transition-colors cursor-pointer",children:e.jsx(ke,{size:20})})]}),e.jsxs("div",{className:"flex flex-col items-center gap-6 max-w-2xl w-full",children:[e.jsxs("div",{className:"flex items-center gap-2 px-4 py-1.5 rounded-full bg-white/10 border border-white/20 text-white text-sm font-bold",children:[e.jsx(be,{size:16,className:"text-amber-300"}),e.jsx("span",{children:r.label}),e.jsx("span",{className:"text-xs text-white/60 ml-2",children:o==="zh"?`全班 ${v} 位学生正在同步`:`${v} students connected`})]}),e.jsx("div",{className:`text-8xl md:text-9xl font-black font-mono tracking-widest drop-shadow-2xl transition-all ${T?"text-rose-500 animate-pulse":_?"text-amber-400":"text-emerald-400"}`,children:h(r.timeRemaining)}),e.jsx("div",{className:"w-full h-3 bg-white/10 rounded-full overflow-hidden border border-white/20 shadow-inner",children:e.jsx("div",{className:`h-full transition-all duration-500 ${T?"bg-rose-500":_?"bg-amber-400":"bg-emerald-400"}`,style:{width:`${H}%`}})}),e.jsxs("div",{className:"flex items-center gap-4 mt-2",children:[r.isRunning?e.jsxs("button",{type:"button",onClick:m,className:"px-8 py-3 rounded-2xl bg-amber-700 hover:bg-amber-800 text-white font-bold text-lg transition-transform active:scale-95 shadow-xl flex items-center gap-2 cursor-pointer",children:[e.jsx(re,{size:20}),e.jsx("span",{children:o==="zh"?"暂停":"Pause"})]}):r.isPaused?e.jsxs("button",{type:"button",onClick:u,className:"px-8 py-3 rounded-2xl bg-emerald-700 hover:bg-emerald-800 text-white font-bold text-lg transition-transform active:scale-95 shadow-xl flex items-center gap-2 cursor-pointer",children:[e.jsx(I,{size:20}),e.jsx("span",{children:o==="zh"?"继续":"Resume"})]}):e.jsxs("button",{type:"button",onClick:()=>s(r.totalDuration||300),className:"px-8 py-3 rounded-2xl bg-primary-theme hover:opacity-90 text-white font-bold text-lg transition-transform active:scale-95 shadow-xl flex items-center gap-2 cursor-pointer",children:[e.jsx(I,{size:20}),e.jsx("span",{children:o==="zh"?"开始":"Start"})]}),e.jsx("button",{type:"button",onClick:()=>d(60),className:"px-5 py-3 rounded-2xl bg-white/10 hover:bg-white/20 text-white font-bold text-base transition-colors cursor-pointer border border-white/20",children:"+1 分钟"}),e.jsx("button",{type:"button",onClick:()=>d(300),className:"px-5 py-3 rounded-2xl bg-white/10 hover:bg-white/20 text-white font-bold text-base transition-colors cursor-pointer border border-white/20",children:"+5 分钟"}),e.jsx("button",{type:"button",onClick:x,className:"p-3 rounded-2xl bg-white/10 hover:bg-white/20 text-white font-bold transition-colors cursor-pointer border border-white/20",title:"重置",children:e.jsx(ae,{size:20})})]})]})]}):null;return j==="header"?e.jsxs("div",{ref:W,id:"classroom-countdown-header-container",className:`relative flex items-center shrink-0 ${V}`,children:[e.jsxs("div",{id:"classroom-countdown-header-pill",className:`h-7 sm:h-8 px-2 py-0.5 rounded-lg border text-xs font-semibold flex items-center gap-1.5 shadow-2xs select-none transition-all ${r.isRunning?"bg-primary-theme/10 border-primary-theme/40 text-primary-theme ring-1 ring-primary-theme/20":"bg-surface border-theme text-main hover:bg-surface-secondary"}`,children:[e.jsx(Z,{size:13,className:`shrink-0 ${r.isRunning?"animate-spin text-primary-theme":"text-muted"}`,style:{animationDuration:"6s"}}),e.jsx("span",{className:"text-[11px] font-medium text-muted hidden xl:inline truncate max-w-[68px]",children:r.label}),e.jsx("span",{id:"classroom-countdown-digits",className:`font-mono font-black text-xs sm:text-sm tracking-wider ${T?"text-rose-500 animate-pulse":r.isRunning?"text-primary-theme font-extrabold":"text-main"}`,children:h(r.timeRemaining)}),e.jsxs("div",{className:"flex items-center gap-1 ml-0.5 border-l border-theme/60 pl-1.5",children:[r.isRunning?e.jsx("button",{type:"button",id:"classroom-countdown-pause-btn",onClick:m,className:"w-5 h-5 rounded-md bg-amber-700 text-white hover:bg-amber-800 flex items-center justify-center cursor-pointer transition-colors shadow-2xs",title:o==="zh"?"暂停倒计时":"Pause",children:e.jsx(re,{size:10})}):r.isPaused?e.jsx("button",{type:"button",id:"classroom-countdown-resume-btn",onClick:u,className:"w-5 h-5 rounded-md bg-emerald-600 text-white hover:bg-emerald-700 flex items-center justify-center cursor-pointer transition-colors shadow-2xs",title:o==="zh"?"继续倒计时":"Resume",children:e.jsx(I,{size:10})}):e.jsx("button",{type:"button",id:"classroom-countdown-start-btn",onClick:()=>s(r.totalDuration||300),className:"w-5 h-5 rounded-md bg-primary-theme text-white hover:opacity-90 flex items-center justify-center cursor-pointer transition-colors shadow-2xs",title:o==="zh"?"开始倒计时":"Start",children:e.jsx(I,{size:10})}),e.jsx("button",{type:"button",onClick:()=>d(60),className:"px-1.5 h-5 rounded bg-surface-secondary hover:bg-primary-theme/15 hover:text-primary-theme border border-theme/60 text-[10px] font-bold text-muted transition-colors cursor-pointer",title:o==="zh"?"增加1分钟":"+1m",children:"+1m"}),e.jsx("button",{type:"button",id:"classroom-countdown-settings-toggle",onClick:()=>A(!P),className:`w-5 h-5 rounded flex items-center justify-center text-muted hover:text-main hover:bg-surface-secondary transition-colors cursor-pointer ${P?"bg-surface-secondary text-main":""}`,title:o==="zh"?"倒计时设置与大屏投放":"Settings & Projector",children:e.jsx(de,{size:11,className:`transition-transform duration-200 ${P?"rotate-180":""}`})})]})]}),P&&e.jsxs("div",{id:"classroom-countdown-header-popover",className:"absolute right-0 top-full mt-1.5 w-72 bg-surface border border-theme rounded-2xl shadow-xl p-3 z-40 flex flex-col gap-2.5 animate-in fade-in slide-in-from-top-1 duration-150",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-theme/60 pb-2",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-xs font-bold text-main",children:[e.jsx(Z,{size:13,className:"text-primary-theme"}),e.jsx("span",{children:o==="zh"?"课堂倒计时设置":"Countdown Settings"})]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("button",{type:"button",onClick:()=>p(!N),className:`p-1 rounded-md text-xs transition-colors cursor-pointer border border-theme ${N?"text-primary-theme bg-surface":"text-muted bg-surface/50"}`,title:N?o==="zh"?"提示音已开启":"Sound On":o==="zh"?"提示音已静音":"Muted",children:N?e.jsx(ne,{size:12}):e.jsx(oe,{size:12})}),e.jsx("button",{type:"button",onClick:()=>{w(!0),A(!1)},className:"p-1 rounded-md text-xs text-muted hover:text-main bg-surface hover:bg-surface-secondary transition-colors cursor-pointer border border-theme",title:o==="zh"?"大屏全屏投放":"Projector Mode",children:e.jsx(le,{size:12})})]})]}),e.jsxs("div",{className:"flex items-center justify-between gap-1 text-[11px]",children:[e.jsx("span",{className:"text-muted font-medium",children:o==="zh"?"当前任务:":"Task:"}),e.jsx("div",{className:"flex items-center gap-1",children:e.jsx("select",{value:r.label,onChange:t=>S({...r,label:t.target.value}),className:"px-2 py-0.5 rounded-lg bg-surface border border-theme text-xs font-semibold text-main outline-none cursor-pointer",children:he.map(t=>e.jsx("option",{value:t,children:t},t))})})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("span",{className:"text-[10px] text-muted font-semibold uppercase tracking-wider",children:o==="zh"?"快捷预设":"Presets"}),e.jsx("div",{className:"grid grid-cols-3 gap-1",children:b.slice(0,6).map(t=>e.jsxs("button",{type:"button",onClick:()=>{s(t.duration,t.label),A(!1)},className:`px-1.5 py-1 rounded-lg text-[10px] font-bold transition-all cursor-pointer border flex items-center justify-center gap-1 ${r.totalDuration===t.duration&&r.isRunning?"bg-primary-theme text-white border-primary-theme shadow-2xs":"bg-surface hover:bg-surface-secondary text-main border-theme"}`,title:t.label,children:[e.jsx("span",{children:t.icon||"⏱️"}),e.jsxs("span",{children:[t.duration/60,"m"]})]},t.id))})]}),e.jsxs("div",{className:"flex items-center justify-between gap-1 pt-1.5 border-t border-theme/60",children:[e.jsxs("button",{type:"button",id:"classroom-countdown-header-reset-btn",onClick:x,className:"px-2 py-1 rounded-lg bg-surface hover:bg-surface-secondary border border-theme text-xs font-semibold text-muted hover:text-main transition-colors flex items-center gap-1 cursor-pointer",children:[e.jsx(ae,{size:11}),e.jsx("span",{children:o==="zh"?"重置":"Reset"})]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("button",{type:"button",onClick:()=>d(-60),disabled:r.timeRemaining<=60,className:"px-2 py-1 rounded-lg bg-surface hover:bg-surface-secondary border border-theme text-xs font-bold text-muted hover:text-main disabled:opacity-40 transition-colors cursor-pointer",children:"-1m"}),e.jsx("button",{type:"button",onClick:()=>d(60),className:"px-2 py-1 rounded-lg bg-surface hover:bg-surface-secondary border border-theme text-xs font-bold text-muted hover:text-main transition-colors cursor-pointer",children:"+1m"}),e.jsx("button",{type:"button",onClick:()=>d(300),className:"px-2 py-1 rounded-lg bg-surface hover:bg-surface-secondary border border-theme text-xs font-bold text-primary-theme hover:bg-primary-theme/10 transition-colors cursor-pointer",children:"+5m"})]})]})]}),k()]}):e.jsxs("div",{id:"classroom-countdown-widget",className:`bg-surface-secondary border border-theme rounded-2xl p-3.5 flex flex-col gap-2.5 shadow-sm transition-all ${V}`,children:[e.jsxs("div",{className:"flex items-center justify-between gap-1 select-none",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"p-1 rounded-lg bg-primary-theme/10 text-primary-theme shrink-0",children:e.jsx(Z,{size:13,className:r.isRunning?"animate-spin":"",style:{animationDuration:"6s"}})}),e.jsxs("div",{className:"flex flex-col min-w-0",children:[e.jsx("span",{className:"text-xs font-black uppercase tracking-wider text-main truncate flex items-center gap-1",children:o==="zh"?"课堂倒计时":"Classroom Countdown"}),e.jsxs("div",{className:"flex items-center gap-1 text-[10px] text-muted",children:[e.jsx(pe,{size:9,className:r.isRunning?"text-emerald-500 animate-pulse":"text-muted"}),e.jsx("span",{children:r.isRunning?o==="zh"?`实时广播中 (${v}人)`:`Broadcasting (${v})`:o==="zh"?"就绪":"Ready"})]})]})]}),e.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[e.jsx("button",{type:"button",onClick:()=>p(!N),className:`p-1 rounded-md text-xs transition-colors cursor-pointer border border-theme ${N?"text-primary-theme bg-surface":"text-muted bg-surface/50"}`,title:N?o==="zh"?"提示音已开启":"Sound On":o==="zh"?"提示音已静音":"Muted",children:N?e.jsx(ne,{size:12}):e.jsx(oe,{size:12})}),e.jsx("button",{type:"button",onClick:()=>w(!0),className:"p-1 rounded-md text-xs text-muted hover:text-main bg-surface hover:bg-surface-secondary transition-colors cursor-pointer border border-theme",title:o==="zh"?"展台大屏全屏投放":"Projector Mode",children:e.jsx(le,{size:12})})]})]}),e.jsxs("div",{className:"relative",children:[K?e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("input",{type:"text",value:F,onChange:t=>z(t.target.value),placeholder:"输入任务名称...",className:"flex-1 px-2 py-0.5 text-xs bg-surface border border-theme rounded-md text-main focus:outline-hidden focus:ring-1 focus:ring-primary-theme",autoFocus:!0}),e.jsx("button",{type:"button",onClick:()=>{F.trim()&&S({...r,label:F.trim()}),L(!1)},className:"p-1 rounded bg-primary-theme text-white text-xs cursor-pointer",children:e.jsx(me,{size:11})})]}):e.jsxs("div",{className:"flex items-center justify-between gap-1",children:[e.jsxs("button",{type:"button",onClick:()=>G(!U),className:"flex items-center gap-1 text-[11px] font-semibold text-muted hover:text-main px-1.5 py-0.5 rounded-md hover:bg-surface transition-colors cursor-pointer border border-transparent hover:border-theme truncate",title:"切换任务类型",children:[e.jsx(Ce,{size:10,className:"shrink-0 text-primary-theme"}),e.jsx("span",{className:"truncate",children:r.label}),e.jsx(de,{size:10,className:"shrink-0 opacity-60"})]}),e.jsx("button",{type:"button",onClick:()=>{z(r.label),L(!0)},className:"text-[10px] text-muted hover:text-primary-theme underline cursor-pointer shrink-0",children:o==="zh"?"自定义":"Custom"})]}),U&&e.jsx("div",{className:"absolute left-0 top-full mt-1 w-full bg-surface border border-theme rounded-xl shadow-lg p-1.5 z-30 flex flex-col gap-0.5",children:he.map(t=>e.jsxs("button",{type:"button",onClick:()=>{S({...r,label:t}),G(!1)},className:`text-left px-2 py-1 text-xs rounded-lg transition-colors cursor-pointer flex items-center justify-between ${r.label===t?"bg-primary-theme/10 text-primary-theme font-bold":"text-main hover:bg-surface-secondary"}`,children:[e.jsx("span",{children:t}),r.label===t&&e.jsx(me,{size:10})]},t))})]}),e.jsxs("div",{className:"flex flex-col items-center justify-center p-3 bg-surface border border-theme rounded-xl relative overflow-hidden",children:[e.jsx("div",{className:"absolute top-0 left-0 right-0 h-1 bg-surface-secondary overflow-hidden",children:e.jsx("div",{className:`h-full transition-all duration-500 ${O}`,style:{width:`${H}%`}})}),e.jsx("div",{id:"classroom-countdown-digits",className:`text-3xl font-black font-mono tracking-widest transition-colors ${T?"animate-pulse text-rose-500":X}`,children:h(r.timeRemaining)}),e.jsx("span",{className:"text-[10px] font-semibold text-muted mt-0.5",children:r.timeRemaining===0?o==="zh"?"⏰ 时间到!建议点评总结":"⏰ Time is up!":`${o==="zh"?"总设时长":"Total"}: ${h(r.totalDuration)}`})]}),e.jsxs("div",{className:"flex items-center gap-1.5 w-full",children:[r.isRunning?e.jsxs("button",{type:"button",id:"classroom-countdown-pause-btn",onClick:m,className:"flex-1 py-1.5 px-3 rounded-xl bg-amber-700 hover:bg-amber-800 text-white text-xs font-bold transition-all shadow-sm flex items-center justify-center gap-1.5 cursor-pointer",children:[e.jsx(re,{size:12}),e.jsx("span",{children:o==="zh"?"暂停计时":"Pause"})]}):r.isPaused?e.jsxs("button",{type:"button",id:"classroom-countdown-resume-btn",onClick:u,className:"flex-1 py-1.5 px-3 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white text-xs font-bold transition-all shadow-sm flex items-center justify-center gap-1.5 cursor-pointer",children:[e.jsx(I,{size:12}),e.jsx("span",{children:o==="zh"?"继续计时":"Resume"})]}):e.jsxs("button",{type:"button",id:"classroom-countdown-start-btn",onClick:()=>s(r.totalDuration||300),className:"flex-1 py-1.5 px-3 rounded-xl bg-primary-theme hover:opacity-90 text-white text-xs font-bold transition-all shadow-sm flex items-center justify-center gap-1.5 cursor-pointer",children:[e.jsx(I,{size:12}),e.jsx("span",{children:o==="zh"?"开始倒计时":"Start"})]}),e.jsxs("button",{type:"button",id:"classroom-countdown-reset-btn",onClick:x,className:"p-1.5 px-2.5 rounded-xl bg-surface hover:bg-surface-secondary border border-theme text-xs font-bold text-muted hover:text-main transition-colors flex items-center justify-center gap-1 cursor-pointer",title:o==="zh"?"重置计时":"Reset",children:[e.jsx(ae,{size:12}),e.jsx("span",{className:"sr-only",children:"重置"})]})]}),e.jsxs("div",{className:"flex items-center justify-between gap-1 pt-1 border-t border-theme/60",children:[e.jsx("span",{className:"text-[10px] text-muted font-medium",children:o==="zh"?"快速调整:":"Adjust:"}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("button",{type:"button",onClick:()=>d(-60),disabled:r.timeRemaining<=60,className:"px-1.5 py-0.5 rounded-md bg-surface hover:bg-surface-secondary border border-theme text-[11px] font-bold text-muted hover:text-main disabled:opacity-40 transition-colors cursor-pointer",title:"减去1分钟",children:"-1m"}),e.jsx("button",{type:"button",onClick:()=>d(60),className:"px-1.5 py-0.5 rounded-md bg-surface hover:bg-surface-secondary border border-theme text-[11px] font-bold text-muted hover:text-main transition-colors cursor-pointer",title:"增加1分钟",children:"+1m"}),e.jsx("button",{type:"button",onClick:()=>d(300),className:"px-1.5 py-0.5 rounded-md bg-surface hover:bg-surface-secondary border border-theme text-[11px] font-bold text-primary-theme hover:bg-primary-theme/10 transition-colors cursor-pointer",title:"增加5分钟",children:"+5m"})]})]}),e.jsxs("div",{className:"flex flex-col gap-1 pt-1",children:[e.jsx("span",{className:"text-[10px] text-muted font-semibold uppercase tracking-wider",children:o==="zh"?"预设时长":"Presets"}),e.jsx("div",{className:"flex flex-wrap gap-1",children:b.map(t=>e.jsxs("button",{type:"button",onClick:()=>s(t.duration,t.label),className:`px-1.5 py-0.5 rounded-lg text-[10px] font-bold transition-all cursor-pointer border ${r.totalDuration===t.duration&&r.isRunning?"bg-primary-theme text-white border-primary-theme":"bg-surface hover:bg-surface-secondary text-main border-theme"}`,title:t.label,children:[e.jsx("span",{children:t.icon||"⏱️"})," ",t.duration/60,"m"]},t.id))})]}),e.jsx(ie,{slot:"classroom.countdown.action",slotProps:{countdown:r,onStart:s,onPause:m,onReset:x,onAddTime:d,lang:o}}),e.jsx(ie,{slot:"classroom.countdown.widget",slotProps:{countdown:r,lang:o}}),k()]})}export{Ie as C,Fe as T};
@@ -1 +0,0 @@
1
- import{r as l,j as e}from"./vendor-react-DHoBZpn2.js";import{a0 as Ae,E as M,c as Te}from"./index-BfKoCpo_.js";import{ao as fe,am as Le,bu as J,bt as Pe,ae as Me,X as te,$ as Re,ac as be,U as je,Q as Oe,af as Fe,c as ye,ag as Ue,C as Be,a1 as Je}from"./vendor-icons-DGdjNlGa.js";import"./vendor-utils-BxOuk3id.js";import"./vendor-pdf-DmaV9d9t.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-skggaP9D.js";function qe({computerLabs:h,onRefresh:y,lang:n,classes:b}){const[q,w]=l.useState(!1),[R,G]=l.useState(null),[H,K]=l.useState(""),[N,V]=l.useState(5),[S,Y]=l.useState(6),[O,p]=l.useState(""),[se,ae]=l.useState(!1),ne="openlearn_computer_lab_selected",Q="openlearn_computer_lab_selected_class",[A,re]=l.useState(()=>{try{const t=localStorage.getItem(ne);if(t&&h.some(s=>s.id===t))return t}catch{}return h.length>0?h[0].id:null}),o=h.find(t=>t.id===(A||h[0]?.id)),[ve,ie]=l.useState([]),[d,F]=l.useState(()=>{const t=o??h[0];if(!t)return null;const s=b||[];try{const a=localStorage.getItem(Q),i=a?s.find(c=>c.id===a):void 0;if(i&&i.lab_id===t.id)return i.id}catch{}const r=s.find(a=>a.lab_id===t.id);return r?r.id:null}),[E,oe]=l.useState([]),[g,C]=l.useState([]),[le,de]=l.useState(!1),[X,ce]=l.useState(!1),[xe,W]=l.useState(""),[Z,k]=l.useState(!1),[f,v]=l.useState(null),[we,$]=l.useState(null),[x,T]=l.useState(null),[I,L]=l.useState(null);l.useEffect(()=>{b&&b.length>0?ie(b):fetch("/api/classes?pageSize=all").then(t=>t.ok?t.json():{data:[]}).then(t=>{const s=Ae(t);(s.length>0||Array.isArray(t?.data))&&ie(s)}).catch(t=>console.error("Failed to fetch classes in ComputerLabManager:",t))},[b]);const _=b&&b.length>0?b:ve,ue=l.useMemo(()=>o?_.filter(t=>t.lab_id===o.id):[],[_,o]);l.useEffect(()=>{if(!o)return;if(d){_.find(s=>s.id===d);return}try{const s=localStorage.getItem(Q),r=s?_.find(a=>a.id===s):void 0;if(r&&r.lab_id===o.id){F(r.id);return}}catch{}const t=_.find(s=>s.lab_id===o.id);t&&F(t.id)},[o?.id,_]),l.useEffect(()=>{try{A&&localStorage.setItem(ne,A)}catch{}},[A]),l.useEffect(()=>{try{d&&localStorage.setItem(Q,d)}catch{}},[d]);const me=l.useCallback(async(t,s)=>{de(!0),p(""),v(null),$(null);try{const[r,a]=await Promise.all([fetch(`/api/classes/${t}/seats`),fetch(`/api/classes/${t}/students`)]);let i=[];if(a.ok&&(i=await a.json(),oe(i)),r.ok){const u=(await r.json()).seats||[],D=new Map(i.map(j=>[j.id,j])),U=u.map(j=>{const B=D.get(j.student_id);return{...j,student_name:j.student_name||B?.name||"",student_number:j.student_number||B?.student_number||""}});C(U)}k(!1)}catch(r){console.error("Error loading seating:",r),p(r.message||"Failed to load class seating data")}finally{de(!1)}},[]);l.useEffect(()=>{d?me(d,o?.id):(oe([]),C([]),k(!1),v(null),$(null))},[d,me]);const ee=l.useMemo(()=>{const t=new Map;for(const s of g)t.set(`${s.row_idx}_${s.col_idx}`,s);return t},[g]),he=l.useMemo(()=>new Set(g.map(t=>t.student_id)),[g]),z=l.useMemo(()=>E.filter(t=>!he.has(t.id)).sort((t,s)=>String(t.student_number??t.name??"").localeCompare(String(s.student_number??s.name??""),"zh-Hans-CN",{numeric:!0})),[E,he]),pe=(t,s,r)=>{const a=g.find(u=>u.row_idx===s.row&&u.col_idx===s.col),i=!!r&&!!a&&a.student_id!==t.id;let c=g.filter(u=>!(u.row_idx===s.row&&u.col_idx===s.col)&&u.student_id!==t.id);i&&r&&(c=c.filter(u=>!(u.row_idx===r.row&&u.col_idx===r.col)),c=[...c,{...a,row_idx:r.row,col_idx:r.col}]),c=[...c,{student_id:t.id,row_idx:s.row,col_idx:s.col,student_name:t.name,student_number:t.student_number}],C(c),k(!0)},Ne=(t,s)=>{if(!d||!o)return;const r=ee.get(`${t}_${s}`);if(f){pe(f,{row:t,col:s},we),v(null),$(null);return}if(r){const a=E.find(i=>i.id===r.student_id)||{id:r.student_id,name:r.student_name||"Student",student_number:r.student_number||""};v(a),$({row:t,col:s})}},Se=(t,s,r)=>{t.stopPropagation(),C(a=>a.filter(i=>!(i.row_idx===s&&i.col_idx===r))),k(!0)},_e=()=>{if(!o||z.length===0)return;const t=[];for(let a=0;a<o.rows;a++)for(let i=0;i<o.cols;i++)ee.has(`${a}_${i}`)||t.push([a,i]);if(t.length===0)return;const r=z.slice(0,t.length).map((a,i)=>{const[c,u]=t[i];return{student_id:a.id,row_idx:c,col_idx:u,student_name:a.name,student_number:a.student_number}});C(a=>[...a,...r]),k(!0),v(null)},ze=()=>{g.length!==0&&confirm(n==="zh"?"确定要清空当前班级在该机房的所有机位排座吗?":"Are you sure you want to clear all seat assignments for this class in this lab?")&&(C([]),k(!0),v(null))},Ce=async()=>{if(!(!d||!o))try{ce(!0),p(""),W("");const t=await fetch(`/api/classes/${d}/seats`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({lab_id:o.id,seats:g.map(s=>({student_id:s.student_id,row_idx:s.row_idx,col_idx:s.col_idx}))})});if(t.ok)k(!1),W(n==="zh"?"统一机房排座已成功持久化至系统 student_seats 数据表!":"Seating assignments saved to system student_seats table successfully!"),y(),setTimeout(()=>W(""),4e3);else{const s=await t.json();p(s.error||"Failed to save seats")}}catch(t){p(t.message||"Network error saving seats")}finally{ce(!1)}},ke=()=>{K(""),V(5),Y(6),p(""),G(null),w(!0)},$e=t=>{K(t.room_number),V(t.rows),Y(t.cols),p(""),G(t.id),w(!0)},Ee=async t=>{if(t.preventDefault(),!H.trim()){p(n==="zh"?"请输入机房编号/名称":"Please enter room name or number");return}if(N<=0||N>15){p(n==="zh"?"行数应在 1 至 15 之间":"Rows should be between 1 and 15");return}if(S<=0||S>15){p(n==="zh"?"列数应在 1 至 15 之间":"Columns should be between 1 and 15");return}try{ae(!0),p("");const s=R?`/api/labs/${R}`:"/api/labs",a=await fetch(s,{method:R?"PUT":"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({room_number:H.trim(),rows:N,cols:S})});if(a.ok)y(),w(!1),G(null);else{const i=await a.json();p(i.error||"Server error saving lab")}}catch(s){p(s.message||"Network error")}finally{ae(!1)}},De=async t=>{if(confirm(n==="zh"?"确定要删除该机房吗?删除后对应的座位分配也会被重置。":"Are you sure you want to delete this lab? Seat mappings will be cleared."))try{(await fetch(`/api/labs/${t}`,{method:"DELETE"})).ok&&(y(),A===t&&re(null))}catch(s){console.error(s)}};return e.jsxs("div",{className:"flex-1 flex flex-col h-full overflow-hidden",id:"lab_manager_root",children:[e.jsxs("div",{className:"flex flex-col md:flex-row md:items-center justify-between pb-4 border-b border-gray-200 gap-4 shrink-0",children:[e.jsxs("div",{children:[e.jsxs("h2",{className:"text-xl font-bold text-gray-800 flex items-center gap-2",children:[e.jsx(fe,{className:"text-indigo-600",size:24}),n==="zh"?"统一机房座位管理":"Unified Computer Lab Seating"]}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:n==="zh"?"维护物理机房规格及其行列排列规则,并直接根据班级信息读取并编排系统中已存的统一机房座位数据(student_seats 表)。":"Maintain physical lab layouts and manage unified student seat allocations directly synced with existing system data tables."})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsxs("button",{onClick:ke,className:"bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-xl text-xs font-semibold flex items-center gap-1.5 shadow-sm transition-all focus:ring-2 focus:ring-indigo-500 cursor-pointer",children:[e.jsx(Le,{size:14}),n==="zh"?"新增机房规则":"New Lab Config"]}),e.jsx(M,{slot:"computer_lab.toolbar.action",slotProps:{activeLab:o,computerLabs:h,onRefresh:y,lang:n}})]})]}),e.jsxs("div",{className:"flex-1 min-h-0 grid grid-cols-1 lg:grid-cols-12 gap-6 pt-5 overflow-hidden",children:[e.jsxs("div",{className:"lg:col-span-4 flex flex-col min-h-0 bg-white border border-gray-200 rounded-2xl shadow-xs overflow-hidden",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 bg-gray-50 flex justify-between items-center shrink-0",children:[e.jsx("span",{className:"text-xs font-bold text-gray-700 uppercase tracking-wider block",children:n==="zh"?"机房列表":"Computer Labs"}),e.jsx("span",{className:"bg-gray-200 text-gray-700 font-bold px-2 py-0.5 rounded-full text-xs",children:h.length})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4 space-y-3",children:h.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center p-8 text-center text-gray-400 h-full",children:[e.jsx(fe,{size:40,className:"mb-2 opacity-30"}),e.jsx("p",{className:"text-xs font-medium",children:n==="zh"?"暂无机房编号规则":"No Computer Labs configured yet."}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:n==="zh"?"点击右上角按钮添加首个机房规格。":"Click the button above to add your first computer room."})]}):h.map(t=>{const s=o?.id===t.id,r=_.filter(a=>a.lab_id===t.id);return e.jsxs("div",{onClick:()=>{re(t.id),w(!1)},className:`p-3 rounded-xl border transition-all cursor-pointer flex flex-col gap-2 group ${s?"border-indigo-300 bg-indigo-50/60 shadow-xs ring-1 ring-indigo-200":"border-gray-150 hover:border-gray-300 hover:bg-gray-50"}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`p-2 rounded-lg ${s?"bg-indigo-600 text-white":"bg-gray-100 text-gray-500 group-hover:bg-gray-200"}`,children:e.jsx(J,{size:16})}),e.jsxs("div",{children:[e.jsx("div",{className:"text-sm font-bold text-gray-800 flex items-center gap-1.5",children:t.room_number}),e.jsxs("div",{className:"text-xs text-gray-400 font-mono mt-0.5",children:[t.rows," ",n==="zh"?"行":"Rows"," × ",t.cols," ",n==="zh"?"列":"Cols",e.jsx("span",{className:"mx-1.5",children:"•"}),t.rows*t.cols," ",n==="zh"?"个机位":"Seats"]})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity",children:[e.jsx("button",{onClick:a=>{a.stopPropagation(),$e(t)},className:"p-1 text-gray-500 hover:text-indigo-600 hover:bg-white rounded transition-colors",title:n==="zh"?"编辑规则":"Edit Rule",children:e.jsx(Pe,{size:13})}),e.jsx("button",{onClick:a=>{a.stopPropagation(),De(t.id)},className:"p-1 text-gray-500 hover:text-rose-600 hover:bg-white rounded transition-colors",title:n==="zh"?"删除机房间":"Delete Lab",children:e.jsx(Me,{size:14})})]})]}),r.length>0&&e.jsxs("div",{className:"flex items-center gap-1 flex-wrap pt-1 border-t border-gray-100/80",children:[e.jsx("span",{className:"text-[10px] text-gray-400 font-medium",children:n==="zh"?"绑定班级:":"Bound:"}),r.map(a=>e.jsx("span",{className:"text-[10px] font-semibold bg-indigo-100/70 text-indigo-700 px-1.5 py-0.5 rounded-md",children:a.name},a.id))]})]},t.id)})})]}),e.jsx("div",{className:"lg:col-span-8 flex flex-col min-h-0 bg-white border border-gray-200 rounded-2xl shadow-xs overflow-hidden",children:q?e.jsxs("form",{onSubmit:Ee,className:"flex-1 flex flex-col min-h-0",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 bg-gray-50 font-bold text-xs text-gray-700 uppercase tracking-wider shrink-0 flex items-center justify-between",children:[e.jsx("span",{children:R?n==="zh"?"编辑机房规则":"Edit Lab Seating":n==="zh"?"创建全新上机机房":"Create Computer Lab"}),e.jsx("button",{type:"button",onClick:()=>w(!1),className:"text-gray-400 hover:text-gray-600",children:e.jsx(te,{size:16})})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-4",children:[O&&e.jsxs("div",{className:"p-3 bg-red-50 border border-red-200 text-red-700 text-xs rounded-xl flex items-center gap-1.5",children:[e.jsx(Re,{size:14}),e.jsx("span",{children:O})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wide",children:n==="zh"?"机房编号 / 场所名称 *":"Lab Name / Number *"}),e.jsx("input",{type:"text",value:H,onChange:t=>K(t.target.value),placeholder:n==="zh"?"例如:305综合机房、软创中心、Lab A":"e.g. Lab 404, Software Sandbox, Suite C",className:"w-full text-xs p-2.5 border border-gray-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 rounded-xl mt-1.5",maxLength:32,required:!0})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wide",children:n==="zh"?"排列行数 (Rows) *":"Rows Direction *"}),e.jsx("input",{type:"number",value:N,onChange:t=>V(Math.max(1,parseInt(t.target.value)||1)),min:1,max:15,className:"w-full text-xs p-2.5 border border-gray-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 rounded-xl mt-1.5",required:!0}),e.jsx("span",{className:"text-xs text-gray-400 block mt-0.5",children:n==="zh"?"上下方向机位数 (最大15行)":"Horizontal sets count (Max 15)"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wide",children:n==="zh"?"排列列数 (Columns) *":"Columns Direction *"}),e.jsx("input",{type:"number",value:S,onChange:t=>Y(Math.max(1,parseInt(t.target.value)||1)),min:1,max:15,className:"w-full text-xs p-2.5 border border-gray-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 rounded-xl mt-1.5",required:!0}),e.jsx("span",{className:"text-xs text-gray-400 block mt-0.5",children:n==="zh"?"左右方向机位数 (最大15列)":"Vertical sets count (Max 15)"})]})]}),e.jsxs("div",{className:"border border-dashed border-gray-200 bg-gray-50/50 p-4 rounded-xl",children:[e.jsx("span",{className:"text-xs font-bold text-gray-400 uppercase tracking-wider block mb-2",children:n==="zh"?"布局网格缩略结构":"Layout Aspect Schema"}),e.jsxs("div",{className:"flex flex-col gap-1 items-center justify-center p-3 bg-white rounded-lg border border-gray-100 min-h-[120px]",children:[e.jsxs("div",{className:"flex flex-col gap-1",children:[Array.from({length:Math.min(N,6)}).map((t,s)=>e.jsxs("div",{className:"flex gap-1 justify-center",children:[Array.from({length:Math.min(S,8)}).map((r,a)=>e.jsx("div",{className:"w-4 h-4 rounded bg-indigo-100 border border-indigo-200 shrink-0"},a)),S>8&&e.jsx("div",{className:"w-4 text-xs text-gray-400 flex items-center justify-center",children:"..."})]},s)),N>6&&e.jsx("div",{className:"text-xs text-center text-gray-400 mt-1",children:"..."})]}),e.jsxs("span",{className:"text-xs text-gray-450 mt-3 font-medium",children:["配置容量:",N," × ",S," = ",N*S," 座位"]})]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-100 shrink-0 flex items-center justify-end gap-2 bg-gray-50",children:[e.jsx("button",{type:"button",onClick:()=>w(!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:n==="zh"?"取消":"Cancel"}),e.jsxs("button",{type:"submit",disabled:se,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:[e.jsx(be,{size:14}),e.jsx("span",{children:se?n==="zh"?"提交中...":"Saving...":n==="zh"?"提交保存":"Save Config"})]})]})]}):e.jsxs("div",{className:"flex-1 flex flex-col min-h-0",children:[e.jsxs("div",{className:"p-3 border-b border-gray-100 bg-gray-50 flex flex-wrap items-center justify-between gap-3 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs font-bold text-gray-700 uppercase tracking-wider flex items-center gap-1.5",children:[e.jsx(je,{size:14,className:"text-indigo-600"}),n==="zh"?"班级机位联动:":"Class Seating Link:"]}),e.jsxs("select",{value:d||"",onChange:t=>F(t.target.value?t.target.value:null),className:"text-xs font-semibold bg-white border border-gray-300 rounded-lg px-2.5 py-1.5 text-gray-700 focus:outline-none focus:ring-1 focus:ring-indigo-500 cursor-pointer shadow-2xs",children:[e.jsx("option",{value:"",children:n==="zh"?"📐 物理规格总览 (无班级)":"📐 Physical Blueprint (No Class)"}),_.map(t=>{const s=t.lab_id===o?.id;return e.jsxs("option",{value:t.id,children:[t.name," ",s?n==="zh"?"★ 已绑定此机房":"★ Bound":""]},t.id)})]})]}),d&&o&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("button",{type:"button",onClick:_e,disabled:z.length===0,className:"px-2.5 py-1.5 text-xs font-semibold bg-indigo-50 hover:bg-indigo-100 text-indigo-700 border border-indigo-200 rounded-lg flex items-center gap-1 transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed",title:n==="zh"?"将未排座学生按学号依次填入空闲机位":"Auto assign unplaced students",children:[e.jsx(Oe,{size:13}),e.jsx("span",{children:n==="zh"?"一键排座":"Auto Fill"})]}),e.jsxs("button",{type:"button",onClick:ze,disabled:g.length===0,className:"px-2.5 py-1.5 text-xs font-semibold bg-gray-100 hover:bg-rose-50 text-gray-600 hover:text-rose-600 border border-gray-200 rounded-lg flex items-center gap-1 transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed",title:n==="zh"?"清空当前班级排座":"Clear Seating",children:[e.jsx(Fe,{size:13}),e.jsx("span",{children:n==="zh"?"清空":"Clear"})]}),e.jsxs("button",{type:"button",onClick:Ce,disabled:X||!Z&&!o,className:`px-3 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 shadow-xs transition-all cursor-pointer ${Z?"bg-emerald-600 hover:bg-emerald-700 text-white animate-pulse":"bg-indigo-600 hover:bg-indigo-700 text-white disabled:opacity-60 disabled:cursor-not-allowed"}`,children:[X?e.jsx(ye,{size:13,className:"animate-spin"}):e.jsx(Ue,{size:13}),e.jsx("span",{children:X?n==="zh"?"保存中...":"Saving...":Z?n==="zh"?"保存排座 (已修改)":"Save (Modified)":n==="zh"?"已保存":"Saved"})]}),e.jsx(M,{slot:"computer_lab.seating.toolbar",slotProps:{labId:o.id,classId:d,assignedSeats:g,setAssignedSeats:C,lang:n}})]})]}),xe&&e.jsxs("div",{className:"bg-emerald-50 border-b border-emerald-200 text-emerald-800 text-xs px-4 py-2 flex items-center gap-1.5 animate-fadeIn",children:[e.jsx(be,{size:14,className:"text-emerald-600 shrink-0"}),e.jsx("span",{className:"font-medium",children:xe})]}),O&&e.jsxs("div",{className:"bg-red-50 border-b border-red-200 text-red-700 text-xs px-4 py-2 flex items-center gap-1.5",children:[e.jsx(Be,{size:14,className:"text-red-500 shrink-0"}),e.jsx("span",{children:O})]}),o?e.jsxs("div",{className:"flex-1 flex flex-col min-h-0 bg-slate-950 text-white overflow-hidden relative",children:[e.jsx("div",{className:"py-2.5 flex justify-center shrink-0 border-b border-slate-900 bg-slate-950/80 backdrop-blur-xs z-10",children:e.jsx("div",{className:"w-56 bg-slate-900 border border-slate-700 py-1 rounded text-center text-xs font-bold text-slate-300 uppercase tracking-widest select-none shadow-sm flex items-center justify-center gap-1.5",children:e.jsxs("span",{children:["📽️ ",n==="zh"?"讲台 / 教师主荧幕":"Classroom Stage / Screen"]})})}),e.jsx("div",{className:"flex-1 overflow-auto p-4 flex flex-col items-center",children:le?e.jsxs("div",{className:"flex flex-col items-center justify-center h-48 gap-2 text-slate-400",children:[e.jsx(ye,{size:24,className:"animate-spin text-indigo-400"}),e.jsx("span",{className:"text-xs",children:n==="zh"?"正在读取统一机房座位数据...":"Loading lab seating data..."})]}):e.jsx("div",{className:"flex flex-col gap-2.5 py-2 max-w-full",children:Array.from({length:o.rows}).map((t,s)=>e.jsxs("div",{className:"flex gap-2.5 justify-center items-center",children:[e.jsxs("span",{className:"text-xs font-bold font-mono text-slate-500 w-6 text-right select-none pr-0.5",children:["R",s+1]}),Array.from({length:o.cols}).map((r,a)=>{const i=ee.get(`${s}_${a}`),c=!!i,u=f&&(!c||i.student_id!==f.id),D=!!x?.from&&x.from.row===s&&x.from.col===a,U=!!x&&!!I&&I.row===s&&I.col===a&&!D,j=U&&c&&x?.type==="seat"&&i.student_id!==x.studentId,B=U&&(!c||x?.type==="pool");return e.jsx("div",{"data-seat-id":`${s}_${a}`,onClick:()=>Ne(s,a),draggable:!!d&&c,onDragStart:m=>{!d||!i||(T({type:"seat",studentId:i.student_id,from:{row:s,col:a}}),m.dataTransfer.setData("text/plain",JSON.stringify({type:"seat",studentId:i.student_id,from:{row:s,col:a}})),m.dataTransfer.effectAllowed="move")},onDragEnd:()=>{T(null),L(null)},onDragOver:m=>{!x||D||(m.preventDefault(),m.dataTransfer.dropEffect="move",L({row:s,col:a}))},onDragLeave:()=>{L(m=>m&&m.row===s&&m.col===a?null:m)},onDrop:m=>{if(m.preventDefault(),x&&!D){const ge=x.type==="pool"?z.find(P=>P.id===x.studentId)||E.find(P=>P.id===x.studentId):E.find(P=>P.id===x.studentId);ge&&(pe(ge,{row:s,col:a},x.from),v(null),$(null))}T(null),L(null)},className:`group relative rounded-xl flex flex-col items-center justify-center transition-all select-none shadow-sm ${d&&c?"cursor-grab":"cursor-pointer"} ${d?"w-20 h-14":"w-11 h-11"} ${c?"bg-slate-850 border border-indigo-500/70 hover:border-indigo-400 shadow-indigo-950/50":u?"bg-indigo-950/40 border border-dashed border-indigo-400 hover:bg-indigo-900/50 ring-1 ring-indigo-400/50 animate-pulse":"bg-slate-900 border border-slate-800 hover:border-slate-700 hover:bg-slate-850"}${j?" ring-2 ring-amber-400 border-amber-400 bg-amber-950/40":""}${B&&!j?" ring-2 ring-emerald-400 border-emerald-400 bg-emerald-950/40":""}${D?" opacity-40":""}`,children:c?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"w-full h-full p-1.5 flex flex-col justify-between items-center text-center",children:[e.jsxs("div",{className:"flex items-center justify-between w-full px-0.5",children:[e.jsxs("span",{className:"text-[10px] font-mono text-indigo-300/80",children:[s+1,"-",a+1]}),d&&e.jsx("button",{type:"button",onClick:m=>Se(m,s,a),className:"opacity-0 group-hover:opacity-100 text-slate-400 hover:text-rose-400 p-0.5 rounded transition-opacity",title:n==="zh"?"移出此座位":"Unseat student",children:e.jsx(te,{size:11})})]}),e.jsx("div",{className:"font-bold text-xs text-white truncate max-w-[70px] leading-tight",children:i.student_name||"Student"}),e.jsx("div",{className:"text-[9px] font-mono text-slate-400 truncate max-w-[70px]",children:i.student_number||`ID:${i.student_id.slice(-4)}`}),e.jsx("div",{className:"w-full min-w-0",onClick:m=>m.stopPropagation(),children:e.jsx(M,{slot:"computer_lab.seat.overlay",slotProps:{labId:o.id,row:s,col:a,seat:i,isOccupied:!0}})})]}),e.jsxs("div",{className:"absolute bottom-16 bg-slate-900 text-white text-xs rounded-lg px-2.5 py-1.5 hidden group-hover:block whitespace-nowrap z-30 font-sans border border-indigo-700 shadow-2xl pointer-events-none",children:[e.jsx("div",{className:"font-bold text-indigo-300",children:i.student_name}),e.jsxs("div",{className:"text-[10px] text-slate-400 font-mono",children:[n==="zh"?"学号:":"No: ",i.student_number||i.student_id]}),e.jsx("div",{className:"text-[10px] text-slate-300 mt-0.5",children:n==="zh"?`第 ${s+1} 排,第 ${a+1} 列`:`Row ${s+1}, Col ${a+1}`})]})]}):e.jsxs(e.Fragment,{children:[e.jsx(J,{size:d?14:15,className:u?"text-indigo-400 animate-bounce":"text-slate-600 opacity-60"}),e.jsxs("span",{className:"text-[10px] font-mono text-slate-500 mt-0.5",children:[s+1,"-",a+1]}),e.jsx("div",{className:"absolute bottom-12 bg-slate-900 text-white text-xs rounded px-2 py-1 hidden group-hover:block whitespace-nowrap z-30 font-sans border border-slate-700 shadow-xl pointer-events-none",children:f?n==="zh"?`点击安排 [${f.name}] 到第 ${s+1} 排第 ${a+1} 列`:`Place [${f.name}] here`:n==="zh"?`第 ${s+1} 排第 ${a+1} 列 (空闲)`:`Row ${s+1} / Column ${a+1} (Empty)`})]})},a)})]},s))})}),d&&o&&!le&&e.jsx("div",{className:"shrink-0 px-4 pb-2",children:e.jsx(M,{slot:"computer_lab.seating.sidebar",slotProps:{labId:o.id,classId:d,assignedSeats:g,unassignedStudents:z}})}),d?e.jsxs("div",{className:"border-t border-slate-900 bg-slate-900/90 p-3 shrink-0 flex flex-col gap-2",children:[e.jsxs("div",{className:"flex items-center justify-between text-xs",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"font-bold text-slate-300 flex items-center gap-1.5",children:[e.jsx(je,{size:13,className:"text-indigo-400"}),n==="zh"?"待排座学生池:":"Unassigned Students Pool:"]}),e.jsxs("span",{className:"text-[11px] font-mono text-slate-400",children:[z.length," ",n==="zh"?"人未排":"unplaced"," / ",E.length," ",n==="zh"?"人总计":"total"]}),f&&e.jsxs("span",{className:"bg-indigo-900/80 text-indigo-300 border border-indigo-700 text-[11px] px-2 py-0.5 rounded-full flex items-center gap-1 animate-pulse",children:[e.jsx("span",{children:n==="zh"?`当前选择: ${f.name} (点击上方空位落座)`:`Selected: ${f.name} (click empty seat)`}),e.jsx("button",{type:"button",onClick:()=>{v(null),$(null)},className:"hover:text-white",children:e.jsx(te,{size:10})})]})]}),e.jsxs("div",{className:"text-[11px] font-mono text-slate-400",children:[n==="zh"?"机房已占:":"Occupied: ",e.jsx("span",{className:"text-indigo-400 font-bold",children:g.length})," /"," ",o.rows*o.cols]})]}),z.length===0?e.jsxs("div",{className:"text-xs text-slate-500 py-1 italic flex items-center gap-1",children:[e.jsx(Je,{size:13,className:"text-emerald-400"}),e.jsx("span",{children:n==="zh"?"当前班级全部学生均已成功分配机位!":"All students in this class have been assigned seats!"})]}):e.jsx("div",{className:"flex items-center gap-1.5 overflow-x-auto py-1 max-h-16 pr-2",children:z.map(t=>{const s=f?.id===t.id,r=x?.type==="pool"&&x.studentId===t.id;return e.jsxs("div",{className:"shrink-0 flex items-center gap-1","data-unassigned-id":t.id,children:[e.jsxs("button",{type:"button",draggable:!0,onDragStart:a=>{T({type:"pool",studentId:t.id}),a.dataTransfer.setData("text/plain",JSON.stringify({type:"pool",studentId:t.id})),a.dataTransfer.effectAllowed="move"},onDragEnd:()=>{T(null),L(null)},onClick:()=>{v(s?null:t),$(null)},className:`px-2.5 py-1 rounded-lg text-xs font-medium border transition-all cursor-grab active:cursor-grabbing flex items-center gap-1.5 ${s?"bg-indigo-600 text-white border-indigo-400 ring-2 ring-indigo-400/50 shadow-md":"bg-slate-800 text-slate-300 border-slate-700 hover:border-indigo-500 hover:bg-slate-750"}${r?" opacity-40":""}`,children:[e.jsx("span",{children:t.name}),t.student_number&&e.jsx("span",{className:"text-[10px] font-mono text-slate-400",children:t.student_number})]}),e.jsx(M,{slot:"computer_lab.unassigned.item",slotProps:{student:t,classId:d,labId:o?.id}})]},t.id)})})]}):e.jsxs("div",{className:"border-t border-slate-900 bg-slate-900/60 p-3 shrink-0 flex items-center justify-between text-xs text-slate-400 select-none",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("div",{className:"w-3.5 h-3.5 rounded bg-slate-800 border border-slate-700 flex items-center justify-center p-0.5",children:e.jsx(J,{size:10,className:"text-indigo-400 opacity-60"})}),e.jsx("span",{children:n==="zh"?"标准上机终端":"Console Terminals"})]}),ue.length>0&&e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"text-slate-500",children:n==="zh"?"已绑定班级:":"Bound Classes:"}),ue.map(t=>e.jsx("button",{type:"button",onClick:()=>F(t.id),className:"text-indigo-400 hover:text-indigo-300 underline font-semibold text-xs cursor-pointer ml-1",children:t.name},t.id))]})]}),e.jsxs("div",{className:"font-mono text-slate-500",children:[n==="zh"?"总行数:":"Rows: ",e.jsx("span",{className:"text-indigo-400 font-bold",children:o.rows})," |",n==="zh"?" 总列数:":" Columns: ",e.jsx("span",{className:"text-indigo-400 font-bold",children:o.cols})," |",n==="zh"?" 配置席位:":" Total: ",e.jsx("span",{className:"text-white font-bold",children:o.rows*o.cols})]})]})]}):e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center text-center p-8 text-gray-400 h-full",children:[e.jsx(J,{size:48,className:"opacity-20 mb-3"}),e.jsx("p",{className:"text-xs font-medium",children:n==="zh"?"请选择一个机房以预览其座位图":"Please select a lab config to preview details."})]})]})})]})]})}function We(h={}){const y=Te(),n=h.computerLabs??y?.computerLabs??[],b=h.onRefresh??y?.fetchLabs??(async()=>{}),q=h.lang??y?.lang??"zh",w=h.classes??y?.classes??[];return e.jsx(qe,{computerLabs:n,onRefresh:b,lang:q,classes:w})}export{We as ComputerLabView};
@@ -1 +0,0 @@
1
- import{j as e}from"./vendor-react-DHoBZpn2.js";import{m as $,C as P}from"./index-BfKoCpo_.js";import{ab as b,D,aW as T,aX as O,c as u,h as V,ac as f}from"./vendor-icons-DGdjNlGa.js";import"./vendor-utils-BxOuk3id.js";import"./vendor-pdf-DmaV9d9t.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-skggaP9D.js";const j=Math.round(P/(1024*1024));function _(y){const{isImportLessonsOpen:N,setIsImportLessonsOpen:o,lang:s,importStatus:a,setIsDraggingImport:m,handleCSVFileChange:F,handleImportFileChange:h,downloadCsvTemplate:v,downloadCourseTemplate:w,packagePreview:t,handlePackageImportSubmit:p,packageDuplicate:n=null,handlePackageDuplicateDismiss:k=()=>{},isDraggingImport:C,previewImportData:l,setPreviewImportData:g,setImportStatus:d,importProgress:i,importProgressTotal:c,importErrorMsg:z,setImportErrorMsg:S,handleCSVImportSubmit:I}=y;return e.jsx(e.Fragment,{children:N&&e.jsx("div",{className:"fixed inset-0 bg-gray-900/50 backdrop-blur-sm flex items-center justify-center p-4 md:p-6 z-50 overflow-y-auto text-gray-850",children:e.jsxs($.div,{initial:{opacity:0,y:15,scale:.98},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:10,scale:.98},className:"bg-white border text-gray-900 border-gray-250 rounded-2xl shadow-2xl w-full max-w-2xl flex flex-col overflow-hidden max-h-[90vh] font-sans text-left",children:[e.jsxs("div",{className:"p-4 md:p-5 border-b border-gray-100 flex items-center justify-between bg-slate-50 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 bg-indigo-50 border border-indigo-100 rounded-lg text-indigo-650",children:e.jsx(b,{size:20,className:"animate-pulse"})}),e.jsxs("div",{children:[e.jsx("h2",{className:"font-bold text-gray-850 text-base md:text-lg",children:s==="zh"?"批量导入课程":"Bulk-Import Courses"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:s==="zh"?"CSV 为「简易导入」(仅标题与正文);需要教学环节 + 白板组件 + 课件附件的完整课程请用课程包 JSON。":'CSV is a "quick import" (title and body only). For a complete lesson — segments, whiteboard components and assets — use a course package JSON.'})]})]}),e.jsx("button",{onClick:()=>{a!=="importing"&&o(!1)},disabled:a==="importing",className:"text-gray-400 hover:text-gray-650 font-bold p-1 rounded-lg hover:bg-gray-150 transition-all text-xl leading-none disabled:opacity-40",children:"×"})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-5",children:[a==="idle"&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"p-4 bg-indigo-50/50 border border-indigo-100 rounded-xl",children:[e.jsxs("h4",{className:"text-xs font-bold text-indigo-850 uppercase tracking-wide flex items-center gap-2",children:[e.jsx("span",{children:"📌"}),s==="zh"?"简易导入(CSV)":"Quick import (CSV)",e.jsx("span",{className:"text-[10px] font-mono font-normal normal-case tracking-normal text-indigo-700 bg-indigo-100 border border-indigo-200 rounded px-1.5 py-0.5","data-testid":"csv-simple-import-badge",children:s==="zh"?"仅标题 + 正文":"title + body only"})]}),e.jsx("p",{className:"text-xs text-indigo-900 mt-1 leading-relaxed",children:s==="zh"?"CSV 文件的首行必须定义列标题(不区分大小写、无多余空格),包含以下两项必需内容:":"Your CSV file must include exactly these header columns on the first row (case-insensitive):"}),e.jsxs("ul",{className:"list-disc pl-5 mt-2 text-xs text-indigo-950 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"title"}),":"," ",s==="zh"?'课程名 (非空,例如 "代数几何")':'Course title (Required, e.g. "Linear Algebra")']}),e.jsxs("li",{children:[e.jsx("strong",{children:"content"}),":"," ",s==="zh"?"教学大纲 / Markdown 格式的课堂细目":"Syllabus content supporting rich markdown."]})]}),e.jsxs("div",{className:"mt-3.5 flex flex-wrap gap-2 justify-start",children:[e.jsxs("button",{onClick:v,className:"flex items-center gap-1 p-2 text-xs font-bold text-indigo-700 bg-white border border-indigo-200 rounded-lg shadow-3xs hover:bg-indigo-50 hover:-translate-y-0.5 hover:shadow-xs transition-all cursor-pointer",children:[e.jsx(D,{size:12}),s==="zh"?"获取标准 CSV 模板":"Download Template CSV"]}),e.jsxs("button",{onClick:w,"data-testid":"download-course-package-template-btn",className:"flex items-center gap-1 p-2 text-xs font-bold text-emerald-700 bg-white border border-emerald-200 rounded-lg shadow-3xs hover:bg-emerald-50 hover:-translate-y-0.5 hover:shadow-xs transition-all cursor-pointer",children:[e.jsx(T,{size:12}),s==="zh"?"下载课程模板(JSON)":"Download Course Template (JSON)"]})]}),e.jsx("p",{className:"text-[11px] text-indigo-800/80 mt-2.5 leading-relaxed",children:s==="zh"?"课程模板是一个自包含的 JSON 文件:包含教学环节、全部预设组件的白板元素,以及内嵌的 PDF / PPTX 课件附件 —— 拖回本对话框即可导入成一节完整的课。完整课程统一走这条通道,CSV 只做批量建空壳。":"The course template is a self-contained JSON file: teaching segments, whiteboard elements for every preset component, and embedded PDF/PPTX assets — drop it back here to import a complete lesson. Complete lessons go through this path; CSV only creates shells in bulk."})]}),e.jsxs("div",{onDragOver:r=>{r.preventDefault(),m(!0)},onDragLeave:()=>m(!1),onDrop:r=>{r.preventDefault(),m(!1),r.dataTransfer.files&&r.dataTransfer.files[0]&&h(r.dataTransfer.files[0])},onClick:()=>{document.getElementById("import-csv-file-picker")?.click()},className:`border-2 border-dashed rounded-2xl p-8 text-center cursor-pointer transition-all flex flex-col items-center justify-center min-h-[180px] ${C?"border-indigo-500 bg-indigo-50/70 scale-[1.01]":"border-gray-250 bg-gray-50/50 hover:bg-gray-50 hover:border-indigo-400"}`,children:[e.jsx("input",{id:"import-csv-file-picker",type:"file",accept:".csv,.json",className:"hidden",onChange:r=>{r.target.files&&r.target.files[0]&&h(r.target.files[0])}}),e.jsx("div",{className:"p-3 bg-gray-100 border border-gray-200 rounded-full text-indigo-600 mb-3 group-hover:scale-105 transition-all",children:e.jsx(b,{size:24})}),e.jsx("span",{className:"text-sm font-bold text-gray-800",children:s==="zh"?"选择 CSV / 课程包 JSON 文件,或拖放至此处":"Click to select or drag and drop a CSV / course-package JSON file here"}),e.jsx("span",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?`CSV(简易导入)或课程包 JSON(完整课程),单文件最大 ${j}MB`:`CSV (quick import) or course-package JSON (full lesson), up to ${j}MB`})]})]}),a==="parsing"&&t&&e.jsxs("div",{className:"space-y-4","data-testid":"course-package-preview",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-xs font-bold text-emerald-600 bg-emerald-50 border border-emerald-200 px-2.5 py-1 rounded-full",children:["✓"," ",s==="zh"?`课程包解析成功:${t.segments} 个教学环节 · ${t.elements} 个白板元素`:`Course package parsed: ${t.segments} segments · ${t.elements} elements`]}),e.jsx("button",{onClick:()=>{g([]),d("idle")},className:"text-xs text-indigo-600 hover:text-indigo-800 font-semibold",children:s==="zh"?"重新上传":"Upload Different File"})]}),e.jsxs("div",{className:"p-4 bg-white border border-gray-200 rounded-xl shadow-3xs",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"p-2 bg-emerald-50 border border-emerald-100 rounded-lg text-emerald-600",children:e.jsx(O,{size:18})}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h4",{className:"text-sm font-bold text-gray-800 truncate",title:t.title,children:t.title}),t.metaName&&e.jsx("p",{className:"text-[11px] text-gray-500 mt-0.5 truncate",children:t.metaName}),e.jsx("p",{className:"text-[11px] text-gray-400 mt-0.5 font-mono truncate",children:t.fileName})]})]}),e.jsxs("dl",{className:"grid grid-cols-3 gap-2 mt-3",children:[e.jsxs("div",{className:"p-2 bg-gray-50 border border-gray-150 rounded-lg",children:[e.jsx("dt",{className:"text-[10px] text-gray-500 font-semibold",children:s==="zh"?"教学环节":"Segments"}),e.jsx("dd",{className:"text-base font-bold text-gray-800",children:t.segments})]}),e.jsxs("div",{className:"p-2 bg-gray-50 border border-gray-150 rounded-lg",children:[e.jsx("dt",{className:"text-[10px] text-gray-500 font-semibold",children:s==="zh"?"白板元素":"Elements"}),e.jsx("dd",{className:"text-base font-bold text-gray-800",children:t.elements})]}),e.jsxs("div",{className:"p-2 bg-gray-50 border border-gray-150 rounded-lg",children:[e.jsx("dt",{className:"text-[10px] text-gray-500 font-semibold",children:s==="zh"?"课件附件":"Assets"}),e.jsx("dd",{className:"text-base font-bold text-gray-800",children:t.assets})]})]}),t.componentTypes.length>0&&e.jsxs("div",{className:"mt-3",children:[e.jsx("p",{className:"text-[10px] text-gray-500 font-semibold mb-1.5",children:s==="zh"?"覆盖的预设组件":"Covered components"}),e.jsx("div",{className:"flex flex-wrap gap-1",children:t.componentTypes.map(r=>e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 border border-indigo-100 text-[10px] font-mono text-indigo-700",children:r},r))})]})]}),e.jsxs("div",{className:"bg-amber-50 border border-amber-100 text-amber-900 text-xs rounded-xl p-3 flex gap-2.5 items-start",children:[e.jsx("span",{className:"text-base leading-none",children:"⚠️"}),e.jsx("p",{className:"leading-relaxed",children:s==="zh"?"导入将以单个事务写入:教学环节、白板元素与课件附件要么全部成功,要么全部回滚,不会留下半截课程。":"The import runs as a single transaction: segments, whiteboard elements and assets are either all written or all rolled back."})]})]}),a==="parsing"&&!t&&l.length>0&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-xs font-bold text-emerald-600 bg-emerald-50 border border-emerald-200 px-2.5 py-1 rounded-full",children:["✓"," ",s==="zh"?`解析成功:查找到 ${l.length} 门课程`:`Parsed Successfully: Found ${l.length} records`]}),e.jsx("button",{onClick:()=>{g([]),d("idle")},className:"text-xs text-indigo-600 hover:text-indigo-800 font-semibold",children:s==="zh"?"重新上传":"Upload Different File"})]}),e.jsx("div",{className:"border border-gray-200 rounded-xl overflow-hidden shadow-3xs max-h-[300px] overflow-y-auto",children:e.jsxs("table",{className:"w-full text-left text-xs border-collapse",children:[e.jsx("thead",{className:"bg-gray-50 text-gray-600 border-b border-gray-200 font-bold",children:e.jsxs("tr",{children:[e.jsx("th",{className:"p-3 w-1/4",children:s==="zh"?"课程名称":"Course Title"}),e.jsx("th",{className:"p-3 w-3/4",children:s==="zh"?"大纲简介片段":"Syllabus Preview"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100 bg-white",children:l.map((r,x)=>e.jsxs("tr",{className:"hover:bg-slate-50/50",children:[e.jsx("td",{className:"p-3 font-semibold text-gray-800 align-top truncate max-w-[150px]",title:r.title,children:r.title}),e.jsx("td",{className:"p-3 text-gray-500 font-mono text-xs leading-relaxed break-words col-span-2",children:r.content.length>150?r.content.substring(0,150)+"...":r.content||e.jsx("em",{className:"text-gray-300 italic",children:"None"})})]},x))})]})}),e.jsxs("div",{className:"bg-amber-50 border border-amber-100 text-amber-900 text-xs rounded-xl p-3 flex gap-2.5 items-start",children:[e.jsx("span",{className:"text-base leading-none",children:"⚠️"}),e.jsx("p",{className:"leading-relaxed",children:s==="zh"?'请确认课程名称没有与系统已有的课程同名。确认无误后点击下方"开始导入"写入 SQLite。':"Please ensure column details are accurate. Clicking Import will instantly commit all parsed courses into the server SQLite backend."})]})]}),a==="importing"&&t&&e.jsxs("div",{className:"py-8 flex flex-col items-center justify-center space-y-4",children:[e.jsx("div",{className:"relative w-20 h-20 flex items-center justify-center",children:e.jsx(u,{size:36,className:"text-indigo-600 animate-spin"})}),e.jsxs("div",{className:"text-center",children:[e.jsx("h3",{className:"font-bold text-gray-800 text-sm",children:s==="zh"?"正在导入课程包":"Importing course package"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:s==="zh"?`正在写入 ${t.segments} 个教学环节、${t.elements} 个白板元素与 ${t.assets} 份课件附件...`:`Writing ${t.segments} segments, ${t.elements} elements and ${t.assets} assets...`})]}),e.jsx("div",{className:"w-full max-w-md bg-gray-50 rounded-xl p-3 border border-gray-150 font-mono text-xs text-gray-400",children:e.jsx("div",{children:"[API] POST /api/course-packages/import -> single transaction..."})})]}),a==="importing"&&!t&&e.jsxs("div",{className:"py-8 flex flex-col items-center justify-center space-y-4",children:[e.jsxs("div",{className:"relative w-20 h-20 flex items-center justify-center",children:[e.jsx(u,{size:36,className:"text-indigo-600 animate-spin"}),e.jsxs("span",{className:"absolute text-xs font-extrabold text-indigo-700",children:[Math.round(i/c*100),"%"]})]}),e.jsxs("div",{className:"text-center",children:[e.jsx("h3",{className:"font-bold text-gray-800 text-sm",children:s==="zh"?"正在写入数据库":"Populating Database Records"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:s==="zh"?`正在导入第 ${i} / ${c} 项...`:`Importing item ${i} of ${c}...`})]}),e.jsx("div",{className:"w-full max-w-sm bg-gray-100 h-2 rounded-full overflow-hidden border border-gray-200",children:e.jsx("div",{className:"bg-indigo-600 h-full transition-all duration-300 rounded-full",style:{width:`${i/c*100}%`}})}),e.jsxs("div",{className:"w-full max-w-md bg-gray-50 rounded-xl p-3 border border-gray-150 font-mono text-xs text-gray-400 max-h-[140px] overflow-y-auto",children:[e.jsx("div",{children:"[API] POST /api/lessons -> one request per course (no cross-course transaction)"}),l.slice(0,i).map((r,x)=>e.jsx("div",{className:"text-indigo-600 font-bold mt-1",children:`✓ [${x+1}] "${r.title}" -> status 200 (Success)`},x))]})]}),a==="success"&&e.jsxs("div",{className:"py-8 text-center flex flex-col items-center justify-center space-y-4",children:[e.jsx("div",{className:"w-16 h-16 bg-emerald-50 rounded-full flex items-center justify-center border border-emerald-200 text-emerald-600 animate-bounce",children:e.jsx(V,{size:32})}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-bold text-gray-800 text-base",children:s==="zh"?"🎉 批量导入大功告成":"🎉 Bulk-Import Complete"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1 max-w-md mx-auto",children:t?s==="zh"?`课程包已导入:${t.segments} 个教学环节、${t.elements} 个白板元素、${t.assets} 份课件附件均已写入。`:`Course package imported: ${t.segments} segments, ${t.elements} elements and ${t.assets} assets written.`:s==="zh"?`所有 ${l.length} 门学科教案数据已顺畅写入系统底层 SQLite 数据仓库,现在已可以用于备课。`:`All ${l.length} curriculum lessons records have been successfully saved into security logs and SQLite storage.`})]})]}),a==="error"&&e.jsxs("div",{className:"space-y-4",children:[n?e.jsxs("div",{className:"p-4 bg-amber-50 border border-amber-100 rounded-xl flex gap-3 items-start","data-testid":"course-package-duplicate-notice",children:[e.jsx("span",{className:"text-amber-600 font-bold text-lg leading-none",children:"⧉"}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-amber-850",children:s==="zh"?"该课程包已经导入过了":"This course package was already imported"}),e.jsx("p",{className:"text-xs text-amber-900 mt-1 leading-relaxed",children:s==="zh"?e.jsxs(e.Fragment,{children:["已存在课程「",n.lessonTitle,"」",n.hasDeclaredId?"(同一个课程包 id)":"(内容完全相同)",", 再次导入会得到两节内容相同的课程。"]}):e.jsxs(e.Fragment,{children:['A lesson named "',n.lessonTitle,'" already exists',n.hasDeclaredId?" (same course-package id)":" (identical content)",". Importing again would create two lessons with the same content."]})})]})]}):e.jsxs("div",{className:"p-4 bg-rose-50 border border-rose-100 rounded-xl flex gap-3 items-start",children:[e.jsx("span",{className:"text-rose-600 font-bold text-lg leading-none",children:"⚠️"}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-rose-850",children:s==="zh"?"数据导入或解析中断":"Import or Parsing Error"}),e.jsx("p",{className:"text-xs text-rose-900 mt-1 leading-relaxed",children:z||(s==="zh"?"未知异常或文件破损。":"An unknown exception or corrupted CSV formatting occurred.")})]})]}),e.jsx("div",{className:"flex gap-3 justify-end",children:n?e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:k,"data-testid":"course-package-duplicate-cancel",className:"px-4 py-2 text-xs font-bold text-gray-700 bg-white border border-gray-250 cursor-pointer hover:bg-gray-50 rounded-lg transition-all",children:s==="zh"?"取消":"Cancel"}),e.jsx("button",{onClick:()=>p(!0),"data-testid":"course-package-duplicate-force",className:"px-4 py-2 text-xs font-bold text-white bg-amber-600 cursor-pointer hover:bg-amber-700 rounded-lg transition-all",children:s==="zh"?"仍然导入副本":"Import as a Copy Anyway"})]}):e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>{d("idle"),S("")},className:"px-4 py-2 text-xs font-bold text-gray-700 bg-white border border-gray-250 cursor-pointer hover:bg-gray-50 rounded-lg transition-all",children:s==="zh"?"返回重试":"Go Back & Retry"}),e.jsx("button",{onClick:()=>o(!1),className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 cursor-pointer hover:bg-indigo-700 rounded-lg transition-all",children:s==="zh"?"关闭窗口":"Close"})]})})]})]}),a!=="idle"&&a!=="error"&&e.jsxs("div",{className:"p-4 md:p-5 border-t border-gray-100 flex items-center justify-between bg-slate-50 shrink-0",children:[e.jsx("button",{onClick:()=>{g([]),d("idle")},disabled:a==="importing",className:"px-4 py-2 text-xs font-bold text-gray-650 bg-white border border-gray-200 hover:bg-gray-100 rounded-lg transition-all disabled:opacity-40 cursor-pointer",children:s==="zh"?"重置重选":"Reset & Clear"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:()=>o(!1),disabled:a==="importing",className:"px-4 py-2 text-xs font-bold text-gray-600 hover:text-gray-800 transition-all disabled:opacity-40 cursor-pointer",children:s==="zh"?"取消":"Cancel"}),a==="parsing"&&t&&e.jsxs("button",{onClick:()=>p(!1),"data-testid":"import-course-package-btn",className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-all flex items-center gap-1 cursor-pointer",children:[e.jsx(f,{size:12}),s==="zh"?"导入课程包":"Import Course Package"]}),a==="parsing"&&!t&&e.jsxs("button",{onClick:I,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-all flex items-center gap-1 cursor-pointer",children:[e.jsx(f,{size:12}),s==="zh"?`开始导入 (${l.length} 类)`:`Proceed and Import (${l.length})`]}),a==="success"&&e.jsx("button",{onClick:()=>o(!1),className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-all cursor-pointer",children:s==="zh"?"完成":"Done"})]})]})]})})})}export{_ as ImportLessonsModal};