openlearn-next 0.4.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -21
- package/cli-data.mjs +535 -40
- package/cli-doctor.mjs +456 -17
- package/cli.mjs +214 -12
- package/dist/assets/AcademicYearFilter-DUUGpDKt.js +1 -0
- package/dist/assets/ActivityWorkspaceWidget-5PzyxF6a.js +1 -0
- package/dist/assets/AdminDirectoryView-BCtmQTEK.js +5 -0
- package/dist/assets/AssessmentCenter-B43ojHG-.js +1 -0
- package/dist/assets/BatchPickerModal-CbmjInp7.js +1 -0
- package/dist/assets/ClassesView-DEnMaDOt.js +32 -0
- package/dist/assets/ClassroomCountdownWidget-CHq4983w.js +2 -0
- package/dist/assets/CloudDriveModal-D2t3Uyzr.js +1 -0
- package/dist/assets/ComputerLabView-YtnoT5Rb.js +1 -0
- package/dist/assets/CourseManagement-DOXyJ_j-.js +1 -0
- package/dist/assets/CourseWizardModal-Dbi6Caw2.js +3 -0
- package/dist/assets/CoursewareHubPanel-CyxDIJAF.js +1 -0
- package/dist/assets/Dashboard-2rLu_ZBy.js +8 -0
- package/dist/assets/ExportWeightModal-BrmAh6VX.js +1 -0
- package/dist/assets/GLTFLoader-CZJbo_y1.js +1 -0
- package/dist/assets/HelpTour-D_q1z_MI.js +1 -0
- package/dist/assets/HelpView-CVBrKDrd.js +204 -0
- package/dist/assets/ImportLessonsModal-PhHtX31R.js +1 -0
- package/dist/assets/ImportModal-DqjqNeH4.js +2 -0
- package/dist/assets/InteractiveCoursewareViewer-Vi6lLz7a.js +1 -0
- package/dist/assets/InteractiveWhiteboard-lTzeayX5.js +65 -0
- package/dist/assets/LazyCourseware-KIt0rGtW.js +2 -0
- package/dist/assets/LazyWhiteboard-CCN3pYcW.js +2 -0
- package/dist/assets/LessonEditorView-D_GVhHuM.js +1 -0
- package/dist/assets/LiveClassroomView-BRsZ6QEq.js +61 -0
- package/dist/assets/NotificationDetailModal-tY5rxYCN.js +1 -0
- package/dist/assets/OrbitControls-CQjN_uwS.js +1 -0
- package/dist/assets/PluginView-CedpAW6d.js +77 -0
- package/dist/assets/ProcessLogsModal-Cfr-T9Gd.js +1 -0
- package/dist/assets/QuizGeneratorModal-bUA002kP.js +1 -0
- package/dist/assets/StudentAssignmentView-BhAz6Hfk.js +1 -0
- package/dist/assets/StudentLessonView-DC1yX4kG.js +1 -0
- package/dist/assets/StudentView-PHKxIV9T.js +2 -0
- package/dist/assets/SystemResourceLibraryModal-DgXYe6_x.js +1 -0
- package/dist/assets/TeacherView-DscJtOJ9.js +2 -0
- package/dist/assets/TimetableView-D2ozoj_D.js +6 -0
- package/dist/assets/UTIF-FfCsrHhe.js +1 -0
- package/dist/assets/__vite-optional-peer-dep_ai_ooxml-ui-BIHI7g3E.js +1 -0
- package/dist/assets/browser-DEI0QxZC.js +1 -0
- package/dist/assets/html2canvas-pro.esm-Bo50djkB.js +12 -0
- package/dist/assets/index-2676X8YB.js +131 -0
- package/dist/assets/index-C6hlh8pw.css +1 -0
- package/dist/assets/{index.es-C5eWEO4o.js → index.es-DyEF0CZ9.js} +1 -1
- package/dist/assets/pluginParsers-e4PSm2Iy.js +1 -0
- package/dist/assets/react_pptx-react-viewer-BIHI7g3E.js +1 -0
- package/dist/assets/teaching-modes-client-BeVaDdJ-.js +1 -0
- package/dist/assets/three.module-iOYB3rD4.js +4228 -0
- package/dist/assets/timeline-duration-BgdJCSrW.js +1 -0
- package/dist/assets/{vendor-charts-qLbgF1eL.js → vendor-charts-SrpBsvM3.js} +2 -2
- package/dist/assets/{vendor-icons-BRmhY6fg.js → vendor-icons-CXrZ3R2V.js} +2 -2
- package/dist/assets/{vendor-konva-CEOq4dcl.js → vendor-konva-Dne0Z-WC.js} +1 -1
- package/dist/assets/{vendor-pdf-D7rGevI1.js → vendor-pdf-C1Vh2Aqq.js} +56 -56
- package/dist/assets/vendor-pptx-Bzn6whfK.js +11311 -0
- package/dist/assets/vendor-pptx-DIWdXF1L.css +1 -0
- package/dist/assets/{vendor-react-B0t7H3hh.js → vendor-react-Kv_JATwr.js} +1 -1
- package/dist/assets/vendor-utils-aM7aYvPP.js +1 -0
- package/dist/cli-migrator.cjs +310 -0
- package/dist/env-validation.cjs +351 -0
- package/dist/index.html +13 -10
- package/dist/plugin-net-preload.cjs +263 -0
- package/dist/server.cjs +51140 -27229
- package/migrations/000_initial_schema.sql +410 -0
- package/migrations/001_add_execution_mode.sql +6 -0
- package/migrations/002_add_client_session_expiry.sql +6 -0
- package/migrations/003_classroom_runtime.sql +33 -0
- package/migrations/004_courseware_score_config.sql +26 -0
- package/migrations/005_assignment_hub.sql +155 -0
- package/migrations/006_classroom_event_bus.sql +28 -0
- package/migrations/007_interactive_classroom.sql +119 -0
- package/migrations/008_teaching_modes.sql +36 -0
- package/migrations/009_classroom_peer_review.sql +159 -0
- package/migrations/010_class_groups.sql +33 -0
- package/migrations/011_classroom_feed.sql +34 -0
- package/migrations/012_auto_record_score.sql +48 -0
- package/migrations/013_score_source_strategy.sql +81 -0
- package/migrations/014_performance_indexes.sql +21 -0
- package/migrations/015_demo_data_registry.sql +26 -0
- package/migrations/016_preset_polls_and_passcode_expiry.sql +33 -0
- package/migrations/017_pacing_signals_segment.sql +6 -0
- package/migrations/018_processes_plugin_owner.sql +26 -0
- package/migrations/019_plugins_version.sql +61 -0
- package/migrations/020_upload_max_file_mb.sql +14 -0
- package/migrations/021_class_owner_and_password_gate.sql +11 -0
- package/migrations/022_plugin_public_routes.sql +14 -0
- package/migrations/023_encrypt_class_passcodes.sql +18 -0
- package/migrations/024_segment_gates.sql +37 -0
- package/migrations/025_assignment_lookup_indexes.sql +11 -0
- package/migrations/026_plugin_grants.sql +21 -0
- package/migrations/027_academic_year.sql +26 -0
- package/migrations/028_classroom_indexes.sql +14 -0
- package/migrations/029_clean_lesson_think_artifacts.sql +13 -0
- package/migrations/030_backfill_academic_year.sql +30 -0
- package/migrations/031_assessment_core.sql +158 -0
- package/migrations/032_progress_verification.sql +17 -0
- package/migrations/033_student_lock_mode.sql +9 -0
- package/migrations/034_plugin_orphan_data.sql +27 -0
- package/migrations/035_icebreaker_checkins.sql +26 -0
- package/migrations/036_student_read_notifications_created_at.sql +19 -0
- package/migrations/037_plugin_config_scopes.sql +40 -0
- package/migrations/038_assessment_ai_grading_suggestion.sql +10 -0
- package/migrations/039_question_bank_sharing_and_papers.sql +14 -0
- package/migrations/040_assignment_grade_history.sql +37 -0
- package/migrations/041_course_package_idempotency.sql +33 -0
- package/migrations/042_assignment_grade_resubmit_audit.sql +12 -0
- package/migrations/043_mark_sha256_passwords.sql +31 -0
- package/migrations/README.md +61 -0
- package/migrations/schema-golden.json +640 -0
- package/package.json +54 -28
- package/dist/assets/AdminDirectoryView-DY--WM5Z.js +0 -1
- package/dist/assets/BatchPickerModal-BpWu0Lil.js +0 -1
- package/dist/assets/ClassesView-KbHEqJto.js +0 -22
- package/dist/assets/ClassroomCountdownWidget-96WFRO9A.js +0 -2
- package/dist/assets/CloudDriveModal-DEQ1nkNk.js +0 -1
- package/dist/assets/ComputerLabView-1qWDNRQ2.js +0 -1
- package/dist/assets/CourseManagement-D3ZpxY0l.js +0 -1
- package/dist/assets/CourseWizardModal-Ck_-M8pk.js +0 -3
- package/dist/assets/CoursewareHubPanel-CaEizhxl.js +0 -1
- package/dist/assets/Dashboard-CYbpbofV.js +0 -8
- package/dist/assets/ExportWeightModal-DW2bu9Zz.js +0 -1
- package/dist/assets/HelpTour-CMJbbGne.js +0 -1
- package/dist/assets/HelpView-CtzdqhBq.js +0 -204
- package/dist/assets/ImportLessonsModal-jBuIK453.js +0 -1
- package/dist/assets/ImportModal-D7L-KNhZ.js +0 -1
- package/dist/assets/InteractiveCoursewareViewer-Bj7ahXMs.js +0 -1
- package/dist/assets/InteractiveWhiteboard-B7JcJvDu.js +0 -77
- package/dist/assets/LazyCourseware-CuiPxhDg.js +0 -2
- package/dist/assets/LazyWhiteboard-BJwCEZAU.js +0 -2
- package/dist/assets/LessonEditorView-DFrOp2qp.js +0 -1
- package/dist/assets/LiveClassroomView-BZ5nGee3.js +0 -59
- package/dist/assets/NotificationDetailModal-Baqnc5pU.js +0 -1
- package/dist/assets/PluginView-CCbIuZ24.js +0 -66
- package/dist/assets/ProcessLogsModal-DmFXg9mG.js +0 -1
- package/dist/assets/QuizGeneratorModal-BBEDssp7.js +0 -1
- package/dist/assets/StudentAssignmentView-DgIjUsXz.js +0 -1
- package/dist/assets/StudentLessonView-CBJbI5x6.js +0 -1
- package/dist/assets/StudentPreviewModal-Bh8OZs0t.js +0 -1
- package/dist/assets/StudentView--uGxc5TJ.js +0 -2
- package/dist/assets/SystemResourceLibraryModal-BVCclcqA.js +0 -1
- package/dist/assets/TeacherView-D4Zsc0eZ.js +0 -2
- package/dist/assets/TimetableView-BQG-rEb5.js +0 -6
- package/dist/assets/index-BKBCBKvy.js +0 -217
- package/dist/assets/index-CvGu7LSj.css +0 -1
- package/dist/assets/purify.es-CYR4BTuT.js +0 -3
- package/dist/assets/vendor-pptx-C5-U7ieS.js +0 -39
- package/dist/assets/vendor-utils-m0Urvtws.js +0 -1
package/cli.mjs
CHANGED
|
@@ -2,16 +2,98 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
5
6
|
import { dirname, join, resolve } from 'node:path';
|
|
6
7
|
import { mkdirSync, readFileSync, existsSync, rmSync } from 'node:fs';
|
|
7
8
|
import os from 'node:os';
|
|
8
9
|
|
|
9
10
|
import { runClean } from './cli-cleaner.mjs';
|
|
10
|
-
import { runDoctor } from './cli-doctor.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import { runDoctor, runCheckUpdate, runPluginCompatibilityReport } from './cli-doctor.mjs';
|
|
12
|
+
import {
|
|
13
|
+
runBackup,
|
|
14
|
+
runRestore,
|
|
15
|
+
runResetAdmin,
|
|
16
|
+
runPluginsList,
|
|
17
|
+
runMigrateStatus,
|
|
18
|
+
runMigrateRollback,
|
|
19
|
+
} from './cli-data.mjs';
|
|
12
20
|
|
|
13
21
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
22
|
|
|
23
|
+
/**
|
|
24
|
+
* 启动期环境校验的 CLI 前置检查(审计残余风险 R-05)。
|
|
25
|
+
*
|
|
26
|
+
* ## 为什么需要
|
|
27
|
+
*
|
|
28
|
+
* `assertStartupEnv()` 只在 `server.ts` 的 `startServer()` 最早期执行。CLI 子命令
|
|
29
|
+
* (`backup` / `restore` / `migrate` / `reset-admin` / `clean`)**不经过 `server.ts`**,
|
|
30
|
+
* 于是同一个拼错的环境变量在服务入口会 fail-fast,在 CLI 里却静默沿用更宽的策略 ——
|
|
31
|
+
* 而这几条命令正是要对真实数据库做破坏性操作(还原、回滚迁移、改管理员口令)。
|
|
32
|
+
*
|
|
33
|
+
* ## 为什么不是复制一份规则
|
|
34
|
+
*
|
|
35
|
+
* 校验规则只有一处真相源(`server/security/env-validation.ts`)。构建时用 esbuild 把
|
|
36
|
+
* 它单独打成 `dist/env-validation.cjs`,CLI 动态加载它。加载不到(源码检出、未构建)
|
|
37
|
+
* 时**不阻断**命令,只提示「本次未做环境校验」—— CLI 的可用性不该被构建产物绑架。
|
|
38
|
+
*
|
|
39
|
+
* @param {Record<string, string|undefined>} env 待校验环境(测试注入用)
|
|
40
|
+
* @returns {Promise<{available: boolean, errors: Array<{key: string, message: string}>, warnings: Array<{key: string, message: string}>}>}
|
|
41
|
+
*/
|
|
42
|
+
export async function loadEnvValidation(env = process.env) {
|
|
43
|
+
try {
|
|
44
|
+
// 必须用 createRequire + 运行时拼路径,而不是 `await import('./dist/env-validation.cjs')`:
|
|
45
|
+
// 后者会被 Vite/Vitest 的 import-analysis 在**转换期**静态解析,源码检出里没有
|
|
46
|
+
// dist/ 时直接抛 "Failed to resolve import",try/catch 拦不住(实测 CI 红)。
|
|
47
|
+
// require 是运行期行为,文件不存在时才走到我们自己的 catch。
|
|
48
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
49
|
+
const artifactPath = join(__dirname, 'dist', 'env-validation.cjs');
|
|
50
|
+
const mod = requireFromHere(artifactPath);
|
|
51
|
+
const validate = mod.validateStartupEnv || mod.default?.validateStartupEnv;
|
|
52
|
+
if (typeof validate !== 'function') return { available: false, errors: [], warnings: [] };
|
|
53
|
+
const result = validate(env) || {};
|
|
54
|
+
return {
|
|
55
|
+
available: true,
|
|
56
|
+
errors: Array.isArray(result.errors) ? result.errors : [],
|
|
57
|
+
warnings: Array.isArray(result.warnings) ? result.warnings : [],
|
|
58
|
+
};
|
|
59
|
+
} catch {
|
|
60
|
+
// 生产安装包里一定有 dist/env-validation.cjs;源码检出没有 —— 不当作错误。
|
|
61
|
+
return { available: false, errors: [], warnings: [] };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 需要读写的命令:环境非法时应当拒绝执行,而不是带着错误配置去动真实数据。 */
|
|
66
|
+
const ENV_BLOCKING_COMMANDS = new Set(['backup', 'restore', 'migrate', 'reset-admin', 'clean']);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 判定「环境校验结果」对该命令是阻断、告警还是忽略。
|
|
70
|
+
*
|
|
71
|
+
* 抽成纯函数是为了能单测:`startCli` 里再判断的话,只能靠 spawn 子进程去验。
|
|
72
|
+
*
|
|
73
|
+
* - `doctor` / `compat-report`:只读诊断,永不被环境错误挡住(诊断工具自己先挂了就没意义);
|
|
74
|
+
* - 写命令:errors 非空 → 阻断;
|
|
75
|
+
* - 无命令(启动服务):不预检,交给 `server.ts` 的 `assertStartupEnv()`(避免同一份规则报两次)。
|
|
76
|
+
*/
|
|
77
|
+
export function decideEnvPreflight(command, result) {
|
|
78
|
+
if (!result?.available) return { action: 'skip', reason: 'validation-artifact-missing' };
|
|
79
|
+
if (result.errors.length > 0 && ENV_BLOCKING_COMMANDS.has(command)) {
|
|
80
|
+
return { action: 'block', reason: 'invalid-env' };
|
|
81
|
+
}
|
|
82
|
+
if (result.errors.length > 0) return { action: 'warn', reason: 'invalid-env-diagnostic-command' };
|
|
83
|
+
if (result.warnings.length > 0) return { action: 'warn', reason: 'env-warnings' };
|
|
84
|
+
return { action: 'pass' };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 把校验结果打印成人能读的一段(与 `formatEnvIssues` 的呈现保持一致)。 */
|
|
88
|
+
export function formatEnvPreflight(result) {
|
|
89
|
+
const fmt = (list) =>
|
|
90
|
+
list.map((i) => ` [${(i.severity || 'ERROR').toUpperCase()}] ${i.key}: ${i.message}`).join('\n');
|
|
91
|
+
const parts = [];
|
|
92
|
+
if (result.warnings.length > 0) parts.push(`[openlearn-next] 启动期环境变量告警:\n${fmt(result.warnings)}`);
|
|
93
|
+
if (result.errors.length > 0) parts.push(`[openlearn-next] 启动期环境变量非法:\n${fmt(result.errors)}`);
|
|
94
|
+
return parts.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
15
97
|
/**
|
|
16
98
|
* 健壮的 CLI 参数解析器 (Zero External Dependencies)
|
|
17
99
|
* 支持 --flag=value 展开、任意位置放置子命令与选项、参数缺失校验
|
|
@@ -27,16 +109,30 @@ export function parseCliArgs(rawArgs) {
|
|
|
27
109
|
}
|
|
28
110
|
}
|
|
29
111
|
|
|
30
|
-
const KNOWN_COMMANDS = new Set([
|
|
112
|
+
const KNOWN_COMMANDS = new Set([
|
|
113
|
+
'doctor',
|
|
114
|
+
'compat-report',
|
|
115
|
+
'backup',
|
|
116
|
+
'restore',
|
|
117
|
+
'reset-admin',
|
|
118
|
+
'plugins',
|
|
119
|
+
'clean',
|
|
120
|
+
'clean-cache',
|
|
121
|
+
'migrate',
|
|
122
|
+
'check-update',
|
|
123
|
+
]);
|
|
31
124
|
|
|
32
125
|
const ALIAS_COMMAND_MAP = {
|
|
33
126
|
'--doctor': 'doctor',
|
|
127
|
+
'--compat-report': 'compat-report',
|
|
34
128
|
'--backup': 'backup',
|
|
35
129
|
'--restore': 'restore',
|
|
36
130
|
'--reset-admin': 'reset-admin',
|
|
37
131
|
'--plugins': 'plugins',
|
|
38
132
|
'--clean': 'clean',
|
|
39
133
|
'--clean-cache': 'clean',
|
|
134
|
+
'--migrate': 'migrate',
|
|
135
|
+
'--check-update': 'check-update',
|
|
40
136
|
};
|
|
41
137
|
|
|
42
138
|
let command = null;
|
|
@@ -48,13 +144,18 @@ export function parseCliArgs(rawArgs) {
|
|
|
48
144
|
demo: false,
|
|
49
145
|
fix: false,
|
|
50
146
|
all: false,
|
|
147
|
+
full: false,
|
|
51
148
|
npx: false,
|
|
52
149
|
db: false,
|
|
150
|
+
compatReport: false,
|
|
151
|
+
targetVersion: null,
|
|
53
152
|
port: null,
|
|
54
153
|
host: null,
|
|
55
154
|
dbPath: null,
|
|
56
155
|
cors: null,
|
|
57
156
|
password: null,
|
|
157
|
+
snapshot: null,
|
|
158
|
+
force: false,
|
|
58
159
|
};
|
|
59
160
|
|
|
60
161
|
for (let i = 0; i < normalized.length; i++) {
|
|
@@ -72,6 +173,8 @@ export function parseCliArgs(rawArgs) {
|
|
|
72
173
|
flags.fix = true;
|
|
73
174
|
} else if (token === '--all') {
|
|
74
175
|
flags.all = true;
|
|
176
|
+
} else if (token === '--full') {
|
|
177
|
+
flags.full = true;
|
|
75
178
|
} else if (token === '--npx') {
|
|
76
179
|
flags.npx = true;
|
|
77
180
|
} else if (token === '--db') {
|
|
@@ -106,6 +209,25 @@ export function parseCliArgs(rawArgs) {
|
|
|
106
209
|
throw new Error('选项 -P/--password 必须指定密码内容');
|
|
107
210
|
}
|
|
108
211
|
flags.password = val;
|
|
212
|
+
} else if (token === '--compat-report') {
|
|
213
|
+
flags.compatReport = true;
|
|
214
|
+
if (!command) {
|
|
215
|
+
command = 'compat-report';
|
|
216
|
+
}
|
|
217
|
+
} else if (token === '--snapshot') {
|
|
218
|
+
const val = normalized[++i];
|
|
219
|
+
if (!val || val.startsWith('-')) {
|
|
220
|
+
throw new Error('选项 --snapshot 必须指定迁移前快照文件路径 (pre_migration_*.db)');
|
|
221
|
+
}
|
|
222
|
+
flags.snapshot = val;
|
|
223
|
+
} else if (token === '--force') {
|
|
224
|
+
flags.force = true;
|
|
225
|
+
} else if (token === '--target-version') {
|
|
226
|
+
const val = normalized[++i];
|
|
227
|
+
if (!val || val.startsWith('-')) {
|
|
228
|
+
throw new Error('选项 --target-version 必须指定有效的目标版本号 (例如: 0.7.0 或 1.0.0)');
|
|
229
|
+
}
|
|
230
|
+
flags.targetVersion = val;
|
|
109
231
|
} else if (ALIAS_COMMAND_MAP[token]) {
|
|
110
232
|
if (!command) {
|
|
111
233
|
command = ALIAS_COMMAND_MAP[token];
|
|
@@ -121,6 +243,10 @@ export function parseCliArgs(rawArgs) {
|
|
|
121
243
|
}
|
|
122
244
|
}
|
|
123
245
|
|
|
246
|
+
if (command === 'compat-report') {
|
|
247
|
+
flags.compatReport = true;
|
|
248
|
+
}
|
|
249
|
+
|
|
124
250
|
// 校验未知位置参数(防手误误启服务)
|
|
125
251
|
if (!command && positionalArgs.length > 0) {
|
|
126
252
|
const potentialCmd = positionalArgs[0];
|
|
@@ -163,10 +289,20 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
163
289
|
|
|
164
290
|
Commands:
|
|
165
291
|
doctor [options] 运行环境自检、防版本漂移体检与自动修复
|
|
166
|
-
选项:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
292
|
+
选项:
|
|
293
|
+
--fix 一键自动修复版本漂移与目录权限
|
|
294
|
+
--compat-report 运行已安装插件升级前兼容性预检报告
|
|
295
|
+
--target-version <ver> 指定拟升级的目标版本 (默认当前版本)
|
|
296
|
+
compat-report [options] 运行已安装插件升级前兼容性预检报告 (等同于 doctor --compat-report)
|
|
297
|
+
选项: --target-version <ver>
|
|
298
|
+
check-update 检查 npm 官方源是否存在平台更新与版本升级指引
|
|
299
|
+
migrate [status|rollback] 查看数据库迁移状态或安全回滚最近一次迁移
|
|
300
|
+
子命令: status (默认速查), rollback (执行回滚)
|
|
301
|
+
backup [file] [options] 一键数据冷备 (支持 --full 打包数据库与富媒体课件文件)
|
|
302
|
+
选项: --full (打包 database + uploads + storage 为 zip 归档)
|
|
303
|
+
restore <file> 从备份快照文件安全还原数据 (支持 .db 镜像与 .zip 全量还原)
|
|
304
|
+
restore --snapshot <db> 从迁移失败前的 pre_migration_*.db 快照恢复 (可加 --force 回滚到更旧库)
|
|
305
|
+
reset-admin [options] 重置管理员 admin 的口令(未指定时随机生成,不再默认 admin)
|
|
170
306
|
选项: --password, -P <pwd>
|
|
171
307
|
plugins [list] 查看当前已安装的平台插件清单与加载状态
|
|
172
308
|
clean, clean-cache 清理 NPX 包缓存与本地临时运行数据
|
|
@@ -204,16 +340,58 @@ Environment Variables:
|
|
|
204
340
|
if (flags.demo) {
|
|
205
341
|
demoDir = join(os.tmpdir(), `openlearn-demo-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`);
|
|
206
342
|
mkdirSync(demoDir, { recursive: true });
|
|
343
|
+
process.env.OPENLEARN_DATA_DIR = demoDir;
|
|
207
344
|
process.env.OPENLEARN_DB_PATH = join(demoDir, 'data.db');
|
|
208
345
|
} else if (flags.dbPath) {
|
|
209
346
|
process.env.OPENLEARN_DB_PATH = resolve(flags.dbPath);
|
|
347
|
+
if (!process.env.OPENLEARN_DATA_DIR) {
|
|
348
|
+
process.env.OPENLEARN_DATA_DIR = dirname(process.env.OPENLEARN_DB_PATH);
|
|
349
|
+
}
|
|
210
350
|
} else if (!process.env.OPENLEARN_DB_PATH) {
|
|
211
|
-
const dataDir =
|
|
351
|
+
const dataDir = process.env.OPENLEARN_DATA_DIR
|
|
352
|
+
? resolve(process.env.OPENLEARN_DATA_DIR)
|
|
353
|
+
: join(os.homedir(), 'openlearn-next');
|
|
212
354
|
mkdirSync(dataDir, { recursive: true });
|
|
355
|
+
process.env.OPENLEARN_DATA_DIR = dataDir;
|
|
213
356
|
process.env.OPENLEARN_DB_PATH = join(dataDir, 'data.db');
|
|
357
|
+
} else if (!process.env.OPENLEARN_DATA_DIR) {
|
|
358
|
+
process.env.OPENLEARN_DATA_DIR = dirname(resolve(process.env.OPENLEARN_DB_PATH));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ── 启动期环境预检(R-05)──────────────────────────────────────────────────
|
|
362
|
+
//
|
|
363
|
+
// 放在命令分发之前、help/version 之后:写命令必须先过这一关,否则
|
|
364
|
+
// 「服务入口 fail-fast、CLI 静默放行」的口径分叉会一直存在。
|
|
365
|
+
if (command && command !== 'check-update') {
|
|
366
|
+
const envResult = await loadEnvValidation();
|
|
367
|
+
const decision = decideEnvPreflight(command, envResult);
|
|
368
|
+
if (decision.action === 'warn') {
|
|
369
|
+
const text = formatEnvPreflight(envResult);
|
|
370
|
+
if (text) console.warn(text);
|
|
371
|
+
if (decision.reason === 'invalid-env-diagnostic-command') {
|
|
372
|
+
console.warn(
|
|
373
|
+
'[openlearn-next] 以上问题会让 `openlearn-next`(启动服务)直接拒绝启动;诊断命令继续执行以便排查。',
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
} else if (decision.action === 'block') {
|
|
377
|
+
console.error(formatEnvPreflight(envResult));
|
|
378
|
+
console.error(
|
|
379
|
+
`[openlearn-next] 环境变量非法,已拒绝执行 \`${command}\`。这几条命令会改动真实数据,` +
|
|
380
|
+
'带着错误配置执行比直接停下更危险。请修正后重试;确需跳过请先修正环境变量,而不是绕过校验。',
|
|
381
|
+
);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
214
384
|
}
|
|
215
385
|
|
|
216
386
|
// ── Command Dispatcher ────────────────────────────────────────────────────
|
|
387
|
+
if (command === 'compat-report' || (command === 'doctor' && flags.compatReport) || flags.compatReport) {
|
|
388
|
+
const report = await runPluginCompatibilityReport({
|
|
389
|
+
dbPath: process.env.OPENLEARN_DB_PATH,
|
|
390
|
+
targetVersion: flags.targetVersion,
|
|
391
|
+
});
|
|
392
|
+
process.exit(report.summary.incompatibleCount > 0 ? 1 : 0);
|
|
393
|
+
}
|
|
394
|
+
|
|
217
395
|
if (command === 'doctor') {
|
|
218
396
|
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 9000;
|
|
219
397
|
const res = await runDoctor({
|
|
@@ -224,20 +402,44 @@ Environment Variables:
|
|
|
224
402
|
process.exit(res.ok ? 0 : 1);
|
|
225
403
|
}
|
|
226
404
|
|
|
405
|
+
if (command === 'check-update') {
|
|
406
|
+
const res = await runCheckUpdate();
|
|
407
|
+
process.exit(res.ok ? 0 : 1);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (command === 'migrate') {
|
|
411
|
+
const sub = subcommandArgs[0] || 'status';
|
|
412
|
+
if (sub === 'rollback') {
|
|
413
|
+
const res = await runMigrateRollback({ dbPath: process.env.OPENLEARN_DB_PATH });
|
|
414
|
+
process.exit(res.ok ? 0 : 1);
|
|
415
|
+
} else {
|
|
416
|
+
const res = await runMigrateStatus({ dbPath: process.env.OPENLEARN_DB_PATH });
|
|
417
|
+
process.exit(res.ok ? 0 : 1);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
227
421
|
if (command === 'backup') {
|
|
228
422
|
const targetFile = subcommandArgs[0];
|
|
229
|
-
const res = await runBackup(targetFile, {
|
|
423
|
+
const res = await runBackup(targetFile, {
|
|
424
|
+
dbPath: process.env.OPENLEARN_DB_PATH,
|
|
425
|
+
full: flags.full || flags.all,
|
|
426
|
+
});
|
|
230
427
|
process.exit(res.ok ? 0 : 1);
|
|
231
428
|
}
|
|
232
429
|
|
|
233
430
|
if (command === 'restore') {
|
|
234
|
-
|
|
235
|
-
const
|
|
431
|
+
// Issue #150:迁移失败后的 pre_migration_*.db 快照可直接恢复。
|
|
432
|
+
const sourceFile = flags.snapshot || subcommandArgs[0];
|
|
433
|
+
const res = await runRestore(sourceFile, {
|
|
434
|
+
dbPath: process.env.OPENLEARN_DB_PATH,
|
|
435
|
+
full: flags.full || flags.all,
|
|
436
|
+
force: flags.force,
|
|
437
|
+
});
|
|
236
438
|
process.exit(res.ok ? 0 : 1);
|
|
237
439
|
}
|
|
238
440
|
|
|
239
441
|
if (command === 'reset-admin') {
|
|
240
|
-
const newPassword = flags.password || subcommandArgs[0]
|
|
442
|
+
const newPassword = flags.password || subcommandArgs[0];
|
|
241
443
|
const res = await runResetAdmin(newPassword, { dbPath: process.env.OPENLEARN_DB_PATH });
|
|
242
444
|
process.exit(res.ok ? 0 : 1);
|
|
243
445
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as r}from"./vendor-react-Kv_JATwr.js";function c({value:a,options:i,onChange:t,lang:o="zh",includeAll:n=!0,className:s=""}){return r.jsxs("select",{value:a,onChange:e=>t(e.target.value),className:`text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold ${s}`,"aria-label":o==="zh"?"学年筛选":"Academic year filter",children:[n&&r.jsx("option",{value:"all",children:o==="zh"?"全部学年":"All academic years"}),i.map(e=>r.jsx("option",{value:e,children:e},e))]})}export{c as A};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as o,j as e}from"./vendor-react-Kv_JATwr.js";import{e as D}from"./index-2676X8YB.js";import{$ as q,X as H,j as J,i as Q,B as V,o as X,y as _,w as G,U as K,b1 as Y,b0 as Z,b2 as ee,e as te,P as se}from"./vendor-icons-CXrZ3R2V.js";import"./vendor-utils-aM7aYvPP.js";import"./vendor-pdf-C1Vh2Aqq.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-SrpBsvM3.js";async function ie(n="all"){const a=await fetch(`/api/activities?role=${encodeURIComponent(n)}`,{headers:{Accept:"application/json"}});if(!a.ok)throw new Error(`Failed to load activities (${a.status})`);return(await a.json()).activities??[]}async function k(){const n=await fetch("/api/activities/running",{headers:{Accept:"application/json"}});if(!n.ok)throw new Error(`Failed to load running activities (${n.status})`);return(await n.json()).activities??[]}async function ne(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/finish`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to finish activity (${a.status})`)}}async function ae(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/pause`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to pause activity (${a.status})`)}}async function re(n){const a=await fetch(`/api/activities/${encodeURIComponent(n)}/resume`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!a.ok){const s=await a.json().catch(()=>({}));throw new Error(s.error||`Failed to resume activity (${a.status})`)}}async function oe(n,a,s){const l=await fetch(`/api/activities/${encodeURIComponent(n)}/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({payload:a??{},actorId:s})}),c=await l.json();if(!l.ok||c.ok===!1)throw new Error(c.error||`Failed to start activity (${l.status})`);return c}const x={padding:"12px",color:"#e2e8f0",fontSize:"13px"},ce={HelpCircle:te,Vote:ee,BarChart3:Z,MessagesSquare:Y,Users:K,FileText:G,Trophy:_,CheckCircle2:X,BookOpen:V};function le({name:n,className:a}){const s=n&&ce[n]||se;return e.jsx(s,{size:18,className:a})}function de(n,a){if(!n)return a==="zh"?"进行中":"In progress";const s=Math.max(0,Math.floor((Date.now()-n)/1e3)),l=Math.floor(s/60),c=s%60;return a==="zh"?`${l} 分 ${c} 秒`:`${l}m ${c}s`}const ge=({role:n,actorId:a,lang:s="en",classroomId:l,mode:c="launcher"})=>{const[p,E]=o.useState([]),[A,j]=o.useState(!0),[b,$]=o.useState(null),[v,m]=o.useState({}),[h,w]=o.useState([]),[T,R]=o.useState(!0),[P,I]=o.useState(null),[y,u]=o.useState(null),[f,z]=o.useState(!1),[S,C]=o.useState(null),B=D(i=>i.setTeacherTab),F=o.useMemo(()=>{const i=new Map;for(const t of p){const r=t.category;i.has(r)||i.set(r,[]),i.get(r).push(t)}return Array.from(i.entries())},[p]);o.useEffect(()=>{if(c!=="launcher")return;let i=!1;return j(!0),ie(n).then(t=>{i||E(t)}).catch(t=>{i||$(t instanceof Error?t.message:String(t))}).finally(()=>{i||j(!1)}),()=>{i=!0}},[n,c]),o.useEffect(()=>{if(c!=="status")return;let i=!1;const t=async()=>{try{const d=await k();i||w(d)}catch(d){i||I(d instanceof Error?d.message:String(d))}finally{i||R(!1)}};t();const r=setInterval(t,5e3);return()=>{i=!0,clearInterval(r)}},[c]);const g=async i=>{if(y){z(!0),C(null);try{await i(y),w(await k())}catch(t){C(t instanceof Error?t.message:String(t))}finally{z(!1)}}},W=()=>g(ne),M=()=>g(ae),O=()=>g(re),L=()=>{u(null),B("live_class")};if(c==="status"){if(T)return e.jsx("div",{className:"bg-white border border-slate-200/80 rounded-2xl p-5 shadow-sm h-24 animate-pulse"});if(P||h.length===0)return null;const i=h.find(t=>t.id===y)||null;return e.jsxs("div",{className:"bg-white border border-slate-200/80 rounded-2xl p-5 shadow-sm",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-slate-800 font-extrabold",children:[e.jsx("span",{children:"🧩"}),e.jsx("span",{children:s==="zh"?"进行中的活动":"Activities in Progress"})]}),e.jsx("span",{className:"text-xs font-bold bg-indigo-50 text-indigo-700 px-2 py-0.5 rounded-full",children:h.length})]}),e.jsx("div",{className:"flex flex-col gap-2",children:h.map(t=>{const r=t.state==="running";return e.jsxs("button",{onClick:()=>u(t.id),className:"w-full flex items-center gap-3 text-left bg-slate-50 hover:bg-slate-100 border border-slate-200/70 rounded-xl p-3 transition-colors",children:[e.jsx(le,{name:t.icon,className:"text-indigo-600 shrink-0"}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"font-bold text-slate-800 truncate",children:t.name}),t.category&&e.jsx("div",{className:"text-xs text-slate-500 truncate",children:t.category})]}),e.jsxs("div",{className:"flex flex-col items-end gap-1 shrink-0",children:[e.jsx("span",{className:`text-xs font-bold px-2 py-0.5 rounded-full ${r?"bg-emerald-100 text-emerald-700":"bg-amber-100 text-amber-700"}`,children:r?s==="zh"?"进行中":"Running":s==="zh"?"已暂停":"Paused"}),e.jsxs("span",{className:"text-xs text-slate-400 flex items-center gap-1",children:[e.jsx(q,{size:10})," ",de(t.startedAt,s)]})]})]},t.id)})}),i&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4",onClick:()=>u(null),children:e.jsxs("div",{className:"bg-white rounded-2xl shadow-xl w-full max-w-sm p-5",onClick:t=>t.stopPropagation(),children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("h3",{className:"font-bold text-slate-800",children:i.name}),e.jsx("button",{onClick:()=>u(null),className:"text-slate-400 hover:text-slate-600","aria-label":"close",children:e.jsx(H,{size:18})})]}),e.jsxs("div",{className:"text-sm text-slate-500 mb-1",children:[s==="zh"?"状态":"Status",":"," ",i.state==="running"?s==="zh"?"进行中":"Running":s==="zh"?"已暂停":"Paused"]}),e.jsxs("div",{className:"text-sm text-slate-500 mb-4",children:[s==="zh"?"开始时间":"Started",":"," ",i.startedAt?new Date(i.startedAt).toLocaleString():"-"]}),S&&e.jsx("div",{className:"text-xs text-rose-600 mb-2",children:S}),e.jsxs("div",{className:"flex gap-2",children:[i.state==="running"?e.jsxs("button",{disabled:f,onClick:M,className:"flex-1 flex items-center justify-center gap-1 bg-amber-500 hover:bg-amber-600 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:[e.jsx(J,{size:14})," ",s==="zh"?"暂停":"Pause"]}):e.jsxs("button",{disabled:f,onClick:O,className:"flex-1 flex items-center justify-center gap-1 bg-indigo-500 hover:bg-indigo-600 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:[e.jsx(Q,{size:14})," ",s==="zh"?"恢复":"Resume"]}),e.jsx("button",{disabled:f,onClick:W,className:"flex-1 flex items-center justify-center gap-1 bg-rose-600 hover:bg-rose-700 disabled:opacity-60 text-white rounded-lg py-2 text-sm font-bold",children:f?s==="zh"?"处理中…":"Working…":s==="zh"?"结束":"End"})]}),e.jsx("button",{onClick:L,className:"w-full mt-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg py-2 text-sm font-bold",children:s==="zh"?"进入课堂":"Enter Classroom"})]})})]})}const U=async i=>{m(t=>({...t,[i.id]:s==="zh"?"启动中…":"Starting…"}));try{const t=await oe(i.id,{classroomId:l,role:n},a);m(r=>({...r,[i.id]:t.dispatched?s==="zh"?"已启动 ✓":"Started ✓":s==="zh"?"已通知 ✓":"Notified ✓"}))}catch(t){m(r=>({...r,[i.id]:t instanceof Error?t.message:String(t)}))}};if(A)return e.jsx("div",{style:x,children:s==="zh"?"加载活动中…":"Loading activities…"});if(b)return e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["🧩 ",s==="zh"?"活动中心":"Activity Center"]}),e.jsx("div",{style:{opacity:.7},children:b})]});const N=n==="teacher"?s==="zh"?"活动中心(教师)":"Activity Center":s==="zh"?"活动(学生)":"Activities";return p.length===0?e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["🧩 ",N]}),e.jsx("div",{style:{opacity:.7},children:s==="zh"?"暂无可用活动。":"No activities available."})]}):e.jsxs("div",{style:x,children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:8},children:["🧩 ",N]}),F.map(([i,t])=>e.jsxs("div",{style:{marginBottom:10},children:[e.jsx("div",{style:{opacity:.6,fontSize:12,textTransform:"uppercase",marginBottom:4},children:i}),e.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:8},children:t.map(r=>e.jsxs("div",{style:{border:"1px solid #334155",borderRadius:8,padding:"8px 10px",minWidth:150,background:"#0f172a"},children:[e.jsxs("div",{style:{fontWeight:600},children:[r.icon?`${r.icon} `:"▫️ ",r.name]}),r.description&&e.jsx("div",{style:{opacity:.7,fontSize:12,margin:"2px 0 6px"},children:r.description}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("button",{onClick:()=>U(r),style:{cursor:"pointer",background:n==="teacher"?"#2563eb":"#0ea5e9",color:"#fff",border:"none",borderRadius:6,padding:"4px 10px",fontSize:12},children:n==="teacher"?s==="zh"?"启动":"Start":s==="zh"?"参与":"Open"}),v[r.id]&&e.jsx("span",{style:{fontSize:12,opacity:.85},children:v[r.id]})]}),e.jsx("div",{style:{opacity:.5,fontSize:12,marginTop:4},children:r.provider==="official"?"official":r.provider})]},r.id))})]},i))]})};export{ge as ActivityWorkspaceWidget};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{r,j as e}from"./vendor-react-Kv_JATwr.js";import{ba as as,ak as fe,P as rs,a9 as $,ao as is,aZ as ns,X as ye,c as K,ap as E,R as A,o as Ie,$ as os,K as ls,T as ie,z as ds,ac as Ee,S as se,e as cs,U as te,by as xs,bw as ae,a3 as ms,cd as hs,af as ps,ab as bs,bq as Re,q as $e,as as us,a$ as gs,bt as fs,b as ys}from"./vendor-icons-CXrZ3R2V.js";import{E as re,b as js}from"./index-2676X8YB.js";import"./vendor-utils-aM7aYvPP.js";import"./vendor-pdf-C1Vh2Aqq.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-SrpBsvM3.js";function Ns({lang:a,aiProviders:x,onAIProvidersChanged:s}){const[T,f]=r.useState(!1),[N,y]=r.useState(null),[p,S]=r.useState(""),[v,g]=r.useState(""),[U,P]=r.useState(""),[z,b]=r.useState(""),[L,k]=r.useState(!1),_=()=>{y(null),S(""),g(""),P(""),b(""),f(!0)},D=n=>{y(n),S(n.name),g(n.api_url),P(n.api_key||""),b(n.model_name),f(!0)},M=async n=>{if(n.preventDefault(),!p.trim()||!v.trim()||!z.trim()){alert(a==="zh"?"名称、API URL 和模型名称不可为空":"Name, API URL and Model Name are required.");return}try{k(!0);const m=!!N,u=m?`/api/ai-providers/${N.id}`:"/api/ai-providers",O=await fetch(u,{method:m?"PUT":"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:p.trim(),api_url:v.trim(),api_key:U,model_name:z.trim()})});if(O.ok)f(!1),y(null),s();else{const G=await O.json().catch(()=>({}));alert(G.error||"Server error")}}catch(m){alert(m.message||"Error")}finally{k(!1)}},l=async(n,m)=>{if(confirm(a==="zh"?`确认要删除 AI 提供商 [${m}] 吗?`:`Confirm deleting AI Provider [${m}]?`))try{(await fetch(`/api/ai-providers/${n}`,{method:"DELETE"})).ok?s():alert("Delete failed")}catch(u){alert(u.message||"Error")}},c=async n=>{try{const m=await fetch("/api/ai-providers/test",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({api_url:n.api_url,api_key:n.api_key,model_name:n.model_name})});if(m.ok)alert(a==="zh"?`[${n.name}] 连接测试成功!`:`[${n.name}] Connection test successful!`);else{const u=await m.json().catch(()=>({}));alert(a==="zh"?`测试失败: ${u.error||"未知错误"}`:`Test failed: ${u.error||"Unknown error"}`)}}catch(m){alert(a==="zh"?`网络异常: ${m.message}`:`Network error: ${m.message}`)}};return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex-1 overflow-y-auto p-6 space-y-6 animate-fade-in text-gray-800",children:e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-gray-50/60",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(as,{className:"text-indigo-600",size:18}),a==="zh"?"OpenAI 兼容 / 自定义模型 AI 提供商列表":"AI Providers List (OpenAI-Compatible)"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:a==="zh"?"配置好的端点将可被测验生成器、讲座生成器和 AI Agent 进行热切调用。":"Configured backends accessible by Quiz Generator, Lecture Generator and AI Agent."})]}),e.jsxs("button",{onClick:_,className:"flex items-center gap-1.5 px-3.5 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-lg shadow-sm transition-all text-xs cursor-pointer",children:[e.jsx(fe,{size:14}),a==="zh"?"添加 AI 提供商":"Add AI Provider"]})]}),e.jsx("div",{className:"p-4 sm:p-5",children:x.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center py-12 text-gray-400 text-center",children:[e.jsx(rs,{size:40,className:"mb-3 opacity-30 text-indigo-500"}),e.jsx("span",{className:"font-semibold text-sm",children:a==="zh"?"暂未配置任何 AI 提供商":"No AI Providers Registered"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1 max-w-xs",children:a==="zh"?"点击右上角按钮新建。":"Click the button above to add one."})]}):e.jsx("div",{className:"overflow-x-auto border border-gray-100 rounded-xl",children:e.jsxs("table",{className:"w-full text-left border-collapse min-w-[700px]",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"bg-slate-50/70 border-b border-gray-100 text-gray-500 font-bold text-xs uppercase tracking-wider",children:[e.jsx("th",{className:"py-3.5 px-4",children:a==="zh"?"名称":"Name"}),e.jsx("th",{className:"py-3.5 px-4",children:a==="zh"?"API 端点 URL":"API Endpoint URL"}),e.jsx("th",{className:"py-3.5 px-4",children:a==="zh"?"模型代号":"Model Identifier"}),e.jsx("th",{className:"py-3.5 px-4",children:a==="zh"?"密钥状态":"API Key"}),e.jsx("th",{className:"py-3.5 px-4 text-center",children:a==="zh"?"操作":"Actions"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100 text-xs text-gray-700",children:x.map(n=>e.jsxs("tr",{className:"hover:bg-slate-50/50 transition-colors",children:[e.jsx("td",{className:"py-4 px-4",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-2 h-2 rounded-full bg-indigo-500"}),e.jsx("span",{className:"font-extrabold text-gray-800 text-sm",children:n.name})]})}),e.jsx("td",{className:"py-4 px-4 font-mono text-gray-550 truncate max-w-[220px]",children:n.api_url}),e.jsx("td",{className:"py-4 px-4",children:e.jsx("span",{className:"bg-slate-100 text-slate-800 font-bold px-2 py-1 rounded font-mono text-xs border border-slate-200",children:n.model_name})}),e.jsx("td",{className:"py-4 px-4",children:n.api_key?e.jsxs("span",{className:"text-emerald-600 font-bold flex items-center gap-1 font-mono",children:[e.jsx($,{size:12})," Key Saved"]}):e.jsx("span",{className:"text-amber-500/90 font-mono italic",children:"Not Set"})}),e.jsx("td",{className:"py-4 px-4 text-center",children:e.jsxs("div",{className:"flex items-center justify-center gap-2",children:[e.jsxs("button",{onClick:()=>c(n),className:"px-2.5 py-1 text-xs font-semibold rounded-lg border transition-all cursor-pointer flex items-center gap-1.5 shadow-3xs bg-emerald-50 border-emerald-200 text-emerald-700 hover:bg-emerald-100",title:a==="zh"?"测试连通性":"Test Connection",children:[e.jsx(is,{size:11}),e.jsx("span",{children:a==="zh"?"测试":"Test"})]}),e.jsx("button",{onClick:()=>D(n),className:"px-2.5 py-1 text-indigo-700 hover:bg-indigo-100 border border-indigo-200 rounded-lg bg-indigo-50 cursor-pointer font-semibold transition-all shadow-3xs",title:a==="zh"?"编辑配置":"Edit",children:a==="zh"?"编辑":"Edit"}),e.jsx("button",{onClick:()=>l(n.id,n.name),className:"px-2.5 py-1 text-rose-600 hover:bg-rose-100 border border-rose-200 rounded-lg bg-rose-50 cursor-pointer font-semibold transition-all shadow-3xs",title:a==="zh"?"删除":"Delete",children:a==="zh"?"删除":"Delete"})]})})]},n.id))})]})})})]})}),T&&e.jsx("div",{className:"fixed inset-0 bg-gray-900/40 backdrop-blur-xs flex items-center justify-center p-4 z-50 overflow-y-auto",children:e.jsxs("div",{className:"bg-white rounded-2xl shadow-xl border border-gray-200/60 w-full max-w-md overflow-hidden text-gray-800 text-left",children:[e.jsxs("div",{className:"flex items-center justify-between p-5 border-b border-gray-100 bg-slate-50/70",children:[e.jsxs("h3",{className:"font-extrabold text-sm sm:text-base text-gray-800 flex items-center gap-2",children:[e.jsx(ns,{className:"text-indigo-600",size:18}),N?a==="zh"?"编辑 AI 提供商配置":"Edit AI Provider":a==="zh"?"添加全新 AI 提供商":"Add New AI Provider"]}),e.jsx("button",{onClick:()=>{f(!1),y(null)},className:"p-1 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100 transition-colors",children:e.jsx(ye,{size:16})})]}),e.jsxs("form",{onSubmit:M,className:"p-5 space-y-4 text-left",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider mb-1.5 text-left",children:a==="zh"?"服务商名称 *":"Provider Name *"}),e.jsx("input",{type:"text",required:!0,placeholder:"e.g. Deepseek, Minimax",value:p,onChange:n=>S(n.target.value),className:"w-full text-xs sm:text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider mb-1.5 text-left",children:a==="zh"?"API 端点 URL *":"API Endpoint URL *"}),e.jsx("input",{type:"url",required:!0,placeholder:"https://api.deepseek.com/v1",value:v,onChange:n=>g(n.target.value),className:"w-full text-xs sm:text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-mono"}),e.jsx("span",{className:"text-xs text-gray-400 mt-1 block",children:a==="zh"?"符合 OpenAI 规范的标准 API 统一基准 URL。":"OpenAI-compatible base URL (e.g. /v1)."})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider mb-1.5 text-left",children:a==="zh"?"模型代号 *":"Model Identifier *"}),e.jsx("input",{type:"text",required:!0,placeholder:"e.g. deepseek-chat",value:z,onChange:n=>b(n.target.value),className:"w-full text-xs sm:text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-mono"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider mb-1.5 text-left",children:a==="zh"?"API Key (可选)":"API Key (Optional)"}),e.jsx("input",{type:"password",placeholder:a==="zh"?"不修改请留空":"Leave empty to keep existing",value:U,onChange:n=>P(n.target.value),className:"w-full text-xs sm:text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-mono"})]}),e.jsxs("div",{className:"flex items-center justify-end gap-3 pt-4 border-t border-gray-100",children:[e.jsx("button",{type:"button",onClick:()=>{f(!1),y(null)},className:"px-4 py-2 text-xs font-bold text-gray-500 bg-slate-100 hover:bg-slate-200 rounded-lg cursor-pointer transition-colors",children:a==="zh"?"取消":"Cancel"}),e.jsxs("button",{type:"submit",disabled:L,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[L?e.jsx(K,{size:13,className:"animate-spin"}):e.jsx($,{size:14}),e.jsx("span",{children:L?a==="zh"?"保存中...":"Saving...":a==="zh"?"保存至数据库":"Save Connection"})]})]})]})]})})]})}const vs=[{key:"lesson",zh:"课程",en:"Lessons"},{key:"class",zh:"班级",en:"Classes"},{key:"student",zh:"学生",en:"Students"},{key:"class_student",zh:"班级关系",en:"Enrollments"},{key:"schedule",zh:"课表",en:"Schedules"},{key:"user",zh:"账号",en:"Accounts"}];function ws({lang:a}){const[x,s]=r.useState(null),[T,f]=r.useState(!1),[N,y]=r.useState(!1),[p,S]=r.useState(!1),[v,g]=r.useState(null),U=async l=>{const c=await l.json().catch(()=>({}));return c&&c.success?c.result:null},P=async l=>{const c=await l.json().catch(()=>({}));return{result:c&&c.success?c.result:null,message:c?.message}},z=r.useCallback(async()=>{try{f(!0);const l=await fetch("/api/demo-data/status");if(l.ok){const c=await U(l);c&&s(c)}}catch(l){console.error("Failed to fetch demo data status",l)}finally{f(!1)}},[]);r.useEffect(()=>{z()},[z]);const b=!!(x&&(x.seeded||x.total>0)),L=async()=>{try{y(!0),g(null);const l=await fetch(`/api/demo-data/seed${b?"?reset=true":""}`,{method:"POST"}),c=await U(l);l.ok&&c?(s(c),g({type:"success",text:a==="zh"?"演示数据已就绪,可用下方演示教师账号登录体验。":"Demo data is ready. Log in with the demo teacher account below."})):g({type:"error",text:a==="zh"?"初始化演示数据失败,请稍后重试。":"Failed to seed demo data, please retry."})}catch(l){g({type:"error",text:a==="zh"?`初始化失败:${l.message||"网络异常"}`:`Seed failed: ${l.message||"Network error"}`})}finally{y(!1)}},k=async()=>{if(confirm(a==="zh"?`确定要清理演示数据吗?
|
|
2
|
+
|
|
3
|
+
只删除演示数据(演示教师、演示课程/班级/学生/课表),系统本身的数据不受影响,管理员账号不会被删除。`:`Purge demo data?
|
|
4
|
+
|
|
5
|
+
Only demo records (demo teacher, demo lesson/class/student/schedule) are removed. Your own data and administrator accounts are never touched.`))try{S(!0),g(null);const c=await fetch("/api/demo-data/cleanup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({confirm:"PURGE_DEMO_DATA"})}),{result:n,message:m}=await P(c);if(c.ok&&n){s(n.after?n.after:null),n.after||await z();const u=n.report?.totalRemoved,F=u===void 0?"":a==="zh"?`已清理 ${u} 条演示数据。`:`Removed ${u} demo records.`;g({type:"success",text:m||F})}else g({type:"error",text:a==="zh"?"清理演示数据失败,请稍后重试。":"Failed to purge demo data, please retry."})}catch(c){g({type:"error",text:a==="zh"?`清理失败:${c.message||"网络异常"}`:`Purge failed: ${c.message||"Network error"}`})}finally{S(!1)}},D=(l=>{if(!l)return null;const c=new Date(l);return Number.isNaN(c.getTime())?null:c.toLocaleString(a==="zh"?"zh-CN":"en-US",{hour12:!1})})(x?.seededAt),M=N||p;return e.jsx("div",{className:"mt-5",children:e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-gray-50/60",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(E,{size:18,className:"text-indigo-600"}),a==="zh"?"演示数据(Demo)":"Demo Data"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:a==="zh"?"一键生成一套完整的演示课程 / 班级 / 学生 / 课表,便于快速体验与验收;清理时不影响系统本身的数据。":"One-click generation of a full demo lesson / class / student / schedule set for quick evaluation. Purging never touches real data."})]}),e.jsx("button",{type:"button",onClick:z,disabled:T||M,title:a==="zh"?"刷新状态":"Refresh status",className:"p-2 text-gray-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition-all cursor-pointer disabled:opacity-50 shrink-0",children:T?e.jsx(K,{size:15,className:"animate-spin"}):e.jsx(A,{size:15})})]}),e.jsxs("div",{className:"p-4 sm:p-5 space-y-5 text-left",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-bold border ${b?"bg-emerald-50 text-emerald-700 border-emerald-100":"bg-gray-50 text-gray-500 border-gray-200"}`,children:[b?e.jsx(Ie,{size:13}):e.jsx(E,{size:13}),b?a==="zh"?"已播种演示数据":"Demo data seeded":a==="zh"?"尚未播种演示数据":"No demo data yet"]}),D&&e.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-semibold bg-gray-50 text-gray-500 border border-gray-200",children:[e.jsx(os,{size:13}),a==="zh"?`播种时间:${D}`:`Seeded at ${D}`]}),e.jsx("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-semibold bg-gray-50 text-gray-500 border border-gray-200",children:a==="zh"?`合计 ${x?.total??0} 条`:`${x?.total??0} records total`})]}),x?.counts&&e.jsx("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-2",children:vs.map(l=>e.jsxs("div",{className:"flex items-center justify-between gap-2 px-2.5 py-2 rounded-lg border border-gray-200 bg-gray-50/60",children:[e.jsx("span",{className:"text-xs text-gray-500",children:a==="zh"?l.zh:l.en}),e.jsx("span",{className:"text-xs font-bold text-gray-700 font-mono",children:x.counts[l.key]??0})]},l.key))})]}),x?.credentials&&e.jsxs("div",{className:"rounded-lg border border-amber-100 bg-amber-50/60 px-3 py-2.5 space-y-1.5",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-xs font-bold text-amber-700",children:[e.jsx(ls,{size:13}),a==="zh"?"演示教师账号(可用于登录体验)":"Demo teacher account (for sign-in testing)"]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-amber-800",children:[e.jsxs("span",{children:[a==="zh"?"姓名":"Name",": ",e.jsx("span",{className:"font-semibold",children:x.credentials.name})]}),e.jsxs("span",{children:[a==="zh"?"用户名":"Username",":"," ",e.jsx("span",{className:"font-mono font-bold",children:x.credentials.username})]}),e.jsxs("span",{children:[a==="zh"?"密码":"Password",":"," ",e.jsx("span",{className:"font-mono font-bold",children:x.credentials.password})]})]})]}),v&&e.jsxs("div",{className:`flex items-start gap-2 px-3 py-2.5 rounded-lg text-xs font-semibold border ${v.type==="success"?"bg-emerald-50 text-emerald-700 border-emerald-100":"bg-rose-50 text-rose-700 border-rose-100"}`,children:[v.type==="success"?e.jsx(Ie,{size:14,className:"shrink-0 mt-px"}):e.jsx(ie,{size:14,className:"shrink-0 mt-px"}),e.jsx("span",{className:"whitespace-pre-line",children:v.text})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3 pt-2 border-t border-gray-100",children:[e.jsxs("button",{type:"button",onClick:L,disabled:M,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[N?e.jsx(K,{size:13,className:"animate-spin"}):b?e.jsx(A,{size:14}):e.jsx(ds,{size:14}),e.jsx("span",{children:N?a==="zh"?"处理中...":"Working...":b?a==="zh"?"重置并重新初始化":"Reset & Re-seed Demo Data":a==="zh"?"一键初始化演示数据":"Seed Demo Data"})]}),e.jsxs("button",{type:"button",onClick:k,disabled:M||!b,className:"px-4 py-2 text-xs font-bold text-white bg-rose-600 hover:bg-rose-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[p?e.jsx(K,{size:13,className:"animate-spin"}):e.jsx(Ee,{size:14}),e.jsx("span",{children:p?a==="zh"?"清理中...":"Purging...":a==="zh"?"一键清理演示数据":"Purge Demo Data"})]}),!b&&e.jsx("span",{className:"text-xs text-gray-400",children:a==="zh"?"清理前需先初始化演示数据。":"Seed demo data before purging."})]})]})]})})}function zs({currentUserId:a,currentUserRole:x,lang:s,onLogout:T,aiProviders:f,onAIProvidersChanged:N,onTriggerTour:y,siteInfo:p,onSiteInfoChanged:S}){const[v,g]=r.useState([]),[U,P]=r.useState(!1),[z,b]=r.useState(""),[L,k]=r.useState(""),[_,D]=r.useState(p?.siteName||""),[M,l]=r.useState(p?.slogan||""),[c,n]=r.useState(p?.logoUrl||null),[m,u]=r.useState(p?.logoUrl||""),[F,O]=r.useState(p?.uploadMaxFileMb??10),[G,je]=r.useState(p?.currentAcademicYear??""),[Ne,ve]=r.useState(9),[ne,we]=r.useState(!1),[ze,_e]=r.useState(!1),[H,ke]=r.useState(""),[oe,Fe]=r.useState("all"),[Oe,Y]=r.useState(!1),[I,Se]=r.useState(null),[le,de]=r.useState(""),[ce,xe]=r.useState(""),[me,he]=r.useState(""),[pe,J]=r.useState("teacher"),[be,W]=r.useState("active"),[Ce,R]=r.useState(""),[ue,Ae]=r.useState(!1),[ks,Ss]=r.useState({tables:14,sizeKb:256,pingMs:2}),[Ye,qe]=r.useState(14),[Be,Qe]=r.useState(0),[w,q]=r.useState("directory"),[o,Ve]=r.useState(null),[B,Pe]=r.useState(!1),[j,Ke]=r.useState(null),[X,Le]=r.useState(!1),[Ge,De]=r.useState(null),[C,ge]=r.useState("npm"),[Me,Te]=r.useState(null),Ue=async()=>{try{Le(!0);const t=await fetch("/api/system/check-upgrade");if(t.ok){const i=await t.json();Ke(i)}}catch(t){console.error("Failed to check system upgrade",t)}finally{Le(!1)}},He=(t,i)=>{navigator?.clipboard?.writeText&&navigator.clipboard.writeText(t),De(i),setTimeout(()=>De(null),2e3)},Z=async()=>{try{Pe(!0);const t=await fetch("/api/db-status");if(t.ok){const i=await t.json();Ve(i)}}catch(t){console.error("Failed to query SQLite stats",t)}finally{Pe(!1)}},Je=async()=>{try{const t=await fetch("/api/site-settings");if(t.ok){const i=await t.json();D(i.siteName||""),l(i.slogan||""),n(i.logoUrl||null),u(i.logoUrl||""),typeof i.uploadMaxFileMb=="number"&&i.uploadMaxFileMb>0&&O(i.uploadMaxFileMb),typeof i.currentAcademicYear=="string"&&i.currentAcademicYear&&je(i.currentAcademicYear),typeof i.academicStartMonth=="number"&&i.academicStartMonth>0&&ve(i.academicStartMonth),_e(!0)}}catch(t){console.error("Failed to fetch site settings",t)}},We=t=>{if(!t)return;const i=new FileReader;i.onload=()=>{const h=i.result;n(h),u(h)},i.readAsDataURL(t)},Xe=async t=>{t.preventDefault();try{we(!0);const i=m&&m.trim()?m.trim():null,h=await fetch("/api/site-settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({siteName:_.trim(),slogan:M.trim(),logoUrl:i,uploadMaxFileMb:Math.round(Number(F)),currentAcademicYear:G.trim()||void 0,academicStartMonth:Math.round(Number(Ne))})});if(h.ok){const d=await h.json();n(i),S?.(d.siteInfo),k(s==="zh"?"站点信息已保存!":"Site settings saved successfully!"),setTimeout(()=>k(""),4e3)}else{const d=await h.json().catch(()=>({}));alert(d.error||"Server error")}}catch(i){alert(i.message||"Error")}finally{we(!1)}},ee=async()=>{try{P(!0),b("");const t=await fetch("/api/users");if(t.ok){const i=await t.json();g(i)}else b(s==="zh"?"加载用户账户失败":"Failed to fetch staff directory")}catch{b(s==="zh"?"网络连接异常":"Network communication issue")}finally{P(!1)}};r.useEffect(()=>{if(x!=="administrator")return;let t=!1;return fetch("/api/system/encryption-status").then(i=>i.ok?i.json():null).then(i=>{if(t)return;const h=typeof i?.warning=="string"?i.warning.trim():"";Te(h||null)}).catch(()=>{t||Te(null)}),()=>{t=!0}},[x]),r.useEffect(()=>{ee(),Z(),Ue(),N();const t=setInterval(()=>{Z(),qe(i=>{const h=Math.floor(Math.random()*9)-4,d=i+h;return Math.min(Math.max(d,5),35)})},5e3);return()=>clearInterval(t)},[Be]),r.useEffect(()=>{w==="site_settings"&&!ze&&Je()},[w,ze]);const Ze=()=>{Se(null),de(""),xe(""),he(""),J("teacher"),W("active"),R(""),Y(!0)},es=t=>{Se(t.id),de(t.username),xe(""),he(t.name),J(t.role),W(t.status||"active"),R(""),Y(!0)},ss=async t=>{if(t.preventDefault(),!le.trim()||!me.trim()){R(s==="zh"?"请填写所有必填字段。":"Please provide all key info.");return}if(!I&&!ce){R(s==="zh"?"新建账户时密码必填。":"Password is required for new accounts.");return}try{R(""),Ae(!0);const i=I?`/api/users/${I}`:"/api/users",d=await fetch(i,{method:I?"PUT":"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:le.trim(),password:ce||void 0,role:pe,name:me.trim(),status:be})});if(d.ok)k(s==="zh"?"账户信息保存成功!":"Staff account saved successfully!"),ee(),Y(!1),setTimeout(()=>k(""),4e3);else{const V=await d.json();R(V.error||"Server validation failed")}}catch(i){R(i.message||"API error")}finally{Ae(!1)}},ts=async(t,i)=>{if(t===a){alert(s==="zh"?"你不能删除你自己当前登录的账户。":"You cannot delete your own logged-in account.");return}const h=s==="zh"?`确定要删除教师账户 "${i}" 吗?此操作不可逆。`:`Confirm deleting staff user "${i}"? This action is irreversible.`;if(confirm(h))try{const d=await fetch(`/api/users/${t}`,{method:"DELETE"});if(d.ok)k(s==="zh"?"账户删除成功":"Account deleted successfully"),ee(),setTimeout(()=>k(""),3e3);else{const V=await d.json();alert(V.error||"Failed to delete")}}catch(d){console.error(d)}},Q=v.filter(t=>{const i=t.name.toLowerCase().includes(H.toLowerCase())||t.username.toLowerCase().includes(H.toLowerCase()),h=oe==="all"||t.role===oe;return i&&h});return e.jsxs("div",{className:"flex-1 bg-white border border-gray-200 rounded-xl shadow-sm flex flex-col min-h-0 overflow-hidden h-full",id:"admin_panel_root",children:[e.jsxs("div",{className:"px-6 py-4 border-b border-gray-100 flex flex-col sm:flex-row sm:items-center justify-between gap-3 bg-white shrink-0",children:[e.jsxs("div",{children:[e.jsxs("h2",{className:"text-base sm:text-lg font-bold text-gray-800 flex items-center gap-2",children:[e.jsx(se,{className:"text-indigo-600 shrink-0",size:20}),e.jsx("span",{children:s==="zh"?"系统管理与教职后台":"Staff Directory & System Admin"}),e.jsx("span",{className:"text-xs font-semibold px-2 py-0.5 rounded-full bg-indigo-50 text-indigo-700 border border-indigo-100/80",children:x==="administrator"?s==="zh"?"超级管理员":"Admin":s==="zh"?"教师":"Teacher"})]}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:s==="zh"?`管理学校教师、教工账户、划分角色权限。当前登录角色权限: ${x==="administrator"?"超级管理员 (系统设置已解锁)":"普通教师 (系统设置锁定)"}`:`Create and assign teacher credentials. Active role context: ${x==="administrator"?"Administrator (System parameters editable)":"Standard Teacher (Settings view-only)"}`})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[y&&e.jsxs("button",{onClick:y,className:"flex items-center gap-1.5 px-3 py-1.5 bg-indigo-50 hover:bg-indigo-100 text-indigo-700 text-xs font-semibold rounded-lg border border-indigo-200 transition-all cursor-pointer shadow-3xs",children:[e.jsx(cs,{size:14,className:"animate-bounce"}),e.jsx("span",{children:s==="zh"?"开启新手引导":"Start Help Tour"})]}),e.jsx("button",{onClick:()=>Qe(t=>t+1),className:"p-1.5 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded-lg transition-all cursor-pointer border border-gray-200 shadow-3xs",title:s==="zh"?"刷新数据":"Refresh stats",children:e.jsx(A,{size:14})})]})]}),Me?e.jsxs("div",{role:"alert",className:"mx-6 mt-4 rounded-xl border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-950",children:[e.jsxs("div",{className:"font-bold flex items-center gap-2",children:[e.jsx(ie,{size:16,className:"shrink-0"}),"正在使用自动生成的加密密钥"]}),e.jsx("p",{className:"mt-1 leading-relaxed",children:Me})]}):null,e.jsx("div",{className:"px-6 py-2.5 border-b border-gray-100 bg-gray-50/50 flex items-center justify-between gap-4 shrink-0 overflow-x-auto",children:e.jsxs("div",{className:"bg-gray-200/60 p-1 rounded-xl flex items-center gap-1 shrink-0",children:[e.jsxs("button",{onClick:()=>q("directory"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${w==="directory"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(te,{size:14}),e.jsx("span",{children:s==="zh"?"学校教职及系统配置":"Staff Accounts & Config"})]}),e.jsxs("button",{onClick:()=>q("ai_providers"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${w==="ai_providers"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(xs,{size:14}),e.jsx("span",{children:s==="zh"?"AI 模型提供商":"AI Providers"})]}),e.jsxs("button",{onClick:()=>q("sqlite"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${w==="sqlite"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(E,{size:14}),e.jsx("span",{children:s==="zh"?"系统监控":"System Monitor"})]}),e.jsxs("button",{onClick:()=>q("site_settings"),className:`px-3.5 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-all cursor-pointer ${w==="site_settings"?"bg-white text-indigo-700 shadow-xs":"text-gray-500 hover:text-gray-800"}`,children:[e.jsx(ae,{size:14}),e.jsx("span",{children:s==="zh"?"站点信息设置":"Site Settings"})]}),e.jsx(re,{slot:"admin.tab",slotProps:{renderType:"button",adminActiveTab:w,setAdminActiveTab:q}})]})}),L&&e.jsxs("div",{className:"mx-6 mt-3 p-3 bg-emerald-50 border border-emerald-200 text-emerald-800 text-xs rounded-xl flex items-center gap-2 shrink-0 animate-fade-in shadow-xs font-medium",children:[e.jsx(ms,{size:15}),e.jsx("span",{children:L})]}),z&&e.jsxs("div",{className:"mx-6 mt-3 p-3 bg-rose-50 border border-rose-200 text-rose-800 text-xs rounded-xl flex items-center gap-2 shrink-0 animate-fade-in shadow-xs",children:[e.jsx(ie,{size:15}),e.jsx("span",{children:z})]}),w==="ai_providers"?e.jsx(Ns,{lang:s,aiProviders:f,onAIProvidersChanged:N}):w==="sqlite"?e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-6 animate-fade-in text-gray-800",children:[e.jsxs("div",{className:"bg-gradient-to-r from-slate-900 via-indigo-950 to-slate-900 border border-indigo-800/60 rounded-2xl p-5 text-white shadow-xs overflow-hidden relative",children:[e.jsx("div",{className:"absolute right-0 top-0 translate-x-8 -translate-y-8 w-56 h-56 bg-indigo-500/10 rounded-full blur-3xl pointer-events-none"}),e.jsxs("div",{className:"flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-indigo-800/50 pb-4",children:[e.jsxs("div",{className:"flex items-start sm:items-center gap-3",children:[e.jsx("div",{className:"p-2.5 bg-indigo-600/30 border border-indigo-500/30 rounded-xl text-indigo-300 shrink-0",children:e.jsx(hs,{size:22,className:X?"animate-spin":""})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("h3",{className:"font-extrabold text-sm sm:text-base text-white tracking-wide",children:s==="zh"?"系统版本与升级生命周期":"System Version & Upgrade Lifecycle"}),e.jsxs("span",{className:"font-mono text-xs px-2 py-0.5 rounded-md bg-indigo-900/90 text-indigo-200 border border-indigo-700/60 font-semibold",children:["v",j?.currentVersion||"2.0.0"]}),j?.hasUpdate?e.jsxs("span",{className:"text-xs px-2 py-0.5 rounded-md bg-amber-500/20 text-amber-300 border border-amber-500/40 font-semibold flex items-center gap-1.5 animate-pulse",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-400"}),s==="zh"?`可升级至 v${j.latestVersion}`:`Update Available: v${j.latestVersion}`]}):j?e.jsxs("span",{className:"text-xs px-2 py-0.5 rounded-md bg-emerald-500/20 text-emerald-300 border border-emerald-500/40 font-semibold flex items-center gap-1",children:[e.jsx($,{size:12}),s==="zh"?"已是最新版本":"Up to date"]}):null]}),e.jsx("p",{className:"text-xs text-indigo-200/80 mt-1",children:s==="zh"?"纯 Node.js 生产环境平滑升级管理,支持 NPM 官方源与国内镜像双源智能探测与一键迁移保障。":"Zero-downtime upgrades for Node.js environments with automated pre-migration safety snapshots."})]})]}),e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:e.jsxs("button",{type:"button",onClick:Ue,disabled:X,className:"px-3.5 py-1.5 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold rounded-xl border border-indigo-400/40 flex items-center gap-1.5 transition-all cursor-pointer disabled:opacity-50 shadow-xs",children:[e.jsx(A,{size:12,className:X?"animate-spin":""}),e.jsx("span",{children:X?s==="zh"?"正在检测...":"Checking...":s==="zh"?"检查更新":"Check Updates"})]})})]}),e.jsxs("div",{className:"mt-4 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between flex-wrap gap-2",children:[e.jsx("span",{className:"text-xs font-semibold text-indigo-200/90",children:s==="zh"?"无缝升级执行命令(根据您的部署方式选择):":"Upgrade command by deployment form factor:"}),e.jsxs("div",{className:"flex items-center bg-indigo-950/90 p-0.5 rounded-lg border border-indigo-700/50 text-xs font-semibold",children:[e.jsx("button",{type:"button",onClick:()=>ge("npm"),className:`px-2.5 py-1 rounded-md transition-all cursor-pointer ${C==="npm"?"bg-indigo-600 text-white shadow-xs":"text-indigo-300 hover:text-white"}`,children:s==="zh"?"全局 NPM":"Global NPM"}),e.jsx("button",{type:"button",onClick:()=>ge("npx"),className:`px-2.5 py-1 rounded-md transition-all cursor-pointer ${C==="npx"?"bg-indigo-600 text-white shadow-xs":"text-indigo-300 hover:text-white"}`,children:s==="zh"?"NPX 免安装":"NPX Runner"}),e.jsx("button",{type:"button",onClick:()=>ge("git"),className:`px-2.5 py-1 rounded-md transition-all cursor-pointer ${C==="git"?"bg-indigo-600 text-white shadow-xs":"text-indigo-300 hover:text-white"}`,children:s==="zh"?"源码部署 (Git)":"Source Clone"})]})]}),e.jsxs("div",{className:"bg-slate-950/90 border border-indigo-800/80 rounded-xl p-3 flex items-center justify-between gap-3 font-mono text-xs",children:[e.jsxs("div",{className:"text-emerald-400 select-all overflow-x-auto whitespace-nowrap scrollbar-none font-medium",children:[C==="npm"&&(j?.upgradeCommands.npm||"npm i -g openlearn-next@latest"),C==="npx"&&(j?.upgradeCommands.npx||"npx openlearn-next@latest"),C==="git"&&(j?.upgradeCommands.git||"git pull && pnpm build && pnpm start")]}),e.jsx("button",{type:"button",onClick:()=>{const t=C==="npm"?j?.upgradeCommands.npm||"npm i -g openlearn-next@latest":C==="npx"?j?.upgradeCommands.npx||"npx openlearn-next@latest":j?.upgradeCommands.git||"git pull && pnpm build && pnpm start";He(t,C)},className:"px-2.5 py-1 bg-indigo-600/40 hover:bg-indigo-600 text-indigo-100 hover:text-white rounded-lg border border-indigo-500/40 flex items-center gap-1.5 transition-all cursor-pointer shrink-0 font-sans text-xs font-semibold",children:Ge===C?e.jsxs(e.Fragment,{children:[e.jsx($,{size:12,className:"text-emerald-400"}),e.jsx("span",{className:"text-emerald-300",children:s==="zh"?"已复制":"Copied"})]}):e.jsxs(e.Fragment,{children:[e.jsx(ps,{size:12}),e.jsx("span",{children:s==="zh"?"复制":"Copy"})]})})]}),e.jsxs("div",{className:"flex items-center justify-between text-[11px] text-indigo-300/80 pt-0.5",children:[e.jsx("span",{children:s==="zh"?"🛡️ 数据安全保障:系统启动及执行数据库迁移时会自动创建物理快照 (backups/pre_migration_*.db)。亦可随时执行 openlearn backup --full 整包归档。":'🛡️ Automated pre-migration snapshots are recorded. Run "openlearn backup --full" anytime for complete state packaging.'}),j?.publishTime&&e.jsxs("span",{className:"shrink-0 ml-2",children:[s==="zh"?"更新时间: ":"Published: ",new Date(j.publishTime).toLocaleDateString()]})]})]})]}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"SQLite引擎状况":"SQLite Health Status"}),e.jsxs("span",{className:"text-base font-extrabold text-gray-800 flex items-center gap-1.5",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse inline-block"}),e.jsx("span",{className:"text-emerald-600 font-extrabold uppercase mt-0.5",children:o?.status==="connected"?s==="zh"?"运行正常":"Healthy":s==="zh"?"连接正常":"Connected"})]}),e.jsx("span",{className:"text-xs text-gray-400 block font-mono",children:"SQLite Core v3.x Connected"})]}),e.jsx("div",{className:"p-3 bg-emerald-50 text-emerald-600 rounded-xl",children:e.jsx(se,{size:20})})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"核心实体数据表":"Database Table Count"}),e.jsx("span",{className:"text-xl font-black font-mono text-gray-800 block",children:o?.tableCount??"--"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?`包含其外 ${o?.systemTableCount??0} 个系统表`:`Plus ${o?.systemTableCount??0} system catalogs`})]}),e.jsx("div",{className:"p-3 bg-indigo-50 text-indigo-600 rounded-xl",children:e.jsx(E,{size:20})})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"系统数据承载行":"Total Records Managed"}),e.jsx("span",{className:"text-xl font-black font-mono text-indigo-700 block",children:o?.totalRows!==void 0?o.totalRows.toLocaleString():"--"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"在册全量索引行数总和":"Cumulative dataset depth"})]}),e.jsx("div",{className:"p-3 bg-indigo-50 text-indigo-600 rounded-xl",children:e.jsx(bs,{size:20})})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl p-4 shadow-3xs flex items-center justify-between",children:[e.jsxs("div",{className:"space-y-1 text-left",children:[e.jsx("span",{className:"text-xs font-black text-slate-400 uppercase tracking-widest block",children:s==="zh"?"物理文件占用空间":"Physical Disk Usage"}),e.jsx("span",{className:"text-xl font-black font-mono text-emerald-600 block",children:o?.diskUsageFriendly??"--"}),e.jsx("span",{className:"text-xs text-gray-400 block font-mono",children:o?.diskUsageBytes?`${o.diskUsageBytes.toLocaleString()} Bytes`:""})]}),e.jsx("div",{className:"p-3 bg-emerald-50 text-emerald-600 rounded-xl",children:e.jsx(Re,{size:20})})]}),e.jsx(re,{slot:"admin.dashboard.card",slotProps:{sqliteStats:o,lang:s}})]}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-12 gap-6 items-start",children:[e.jsxs("div",{className:"lg:col-span-8 bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden flex flex-col",children:[e.jsxs("div",{className:"p-4 sm:p-5 border-b border-gray-100 bg-gray-50/60 flex items-center justify-between",children:[e.jsxs("div",{className:"text-left",children:[e.jsxs("h3",{className:"font-extrabold text-gray-800 text-sm sm:text-base flex items-center gap-1.5 justify-start",children:[e.jsx(E,{className:"text-indigo-600",size:18}),s==="zh"?"SQLite 数据库实体列表与行数字典":"SQLite Relational Entity Tables Catalog"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-1 block",children:s==="zh"?"实时展示当前在 educational_os.db 内核中的关系型实体及在册记录行数。":"Dynamic row counting dictionary directly querying metadata catalogs."})]}),e.jsxs("button",{onClick:Z,disabled:B,className:"p-1 px-3 text-xs bg-indigo-50 hover:bg-indigo-100 text-indigo-700 font-bold border border-indigo-200/45 rounded-xl flex items-center gap-1 transition-all cursor-pointer",children:[e.jsx(A,{size:11,className:B?"animate-spin":""}),s==="zh"?"重算/刷新 font":"Refresh Metrics"]})]}),e.jsx("div",{className:"p-4 sm:p-5",children:B&&!o?e.jsxs("div",{className:"flex flex-col items-center justify-center py-12 text-slate-400 text-xs",children:[e.jsx(A,{size:36,className:"animate-spin text-indigo-500 mb-2"}),e.jsx("span",{children:s==="zh"?"正在重算数据库实体表统计指标...":"Calculating database page allocation and statistics..."})]}):o?e.jsx("div",{className:"overflow-x-auto border border-gray-100 rounded-xl",children:e.jsxs("table",{className:"w-full text-left border-collapse min-w-[500px]",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"bg-slate-50/70 border-b border-gray-100 text-gray-500 font-bold text-xs uppercase tracking-wider font-sans",children:[e.jsx("th",{className:"py-3 px-4",children:s==="zh"?"实体数据表名":"Table Name"}),e.jsx("th",{className:"py-3 px-4",children:s==="zh"?"当前承载行数":"Active Rows"}),e.jsx("th",{className:"py-3 px-4",children:s==="zh"?"行数比例分布":"Distribution Scale"}),e.jsx("th",{className:"py-3 px-4 font-mono select-none",children:s==="zh"?"系统属性类型":"Metadata Type"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100 text-xs text-gray-700 font-sans",children:o.tables.map(t=>{const i=Math.max(...o.tables.map(V=>V.rows),1),h=Math.min(t.rows/i*100,100);let d="Core Application Storage";return t.name==="users"?d="Teacher & Administrator profiles":t.name==="students"?d="Student accounts and lock states":t.name==="classes"?d="Active classes, metadata & codes":t.name==="lessons"?d="Lectures, slides and markdown modules":t.name==="ai_providers"?d="Third party API key provider gateways":t.name==="vfs_nodes"?d="Virtual file storage hierarchies":t.name==="client_sessions"&&(d="Active browser auth cookies"),e.jsxs("tr",{className:"hover:bg-slate-50/40 transition-colors",children:[e.jsxs("td",{className:"py-3 px-4 text-left",children:[e.jsxs("div",{className:"flex items-center gap-2 justify-start",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-indigo-500"}),e.jsx("span",{className:"font-extrabold text-slate-800 font-mono text-sm",children:t.name})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-0.5",children:d})]}),e.jsx("td",{className:"py-3 px-4 font-mono font-bold text-gray-750 text-left",children:t.rows>=0?t.rows.toLocaleString():e.jsx("span",{className:"text-rose-500 italic font-normal",children:"error"})}),e.jsx("td",{className:"py-3 px-4 min-w-[150px]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-24 bg-gray-100 rounded-full h-1.5 overflow-hidden border border-gray-200/50 shrink-0",children:e.jsx("div",{className:"bg-indigo-600 h-1.5 rounded-full",style:{width:`${h}%`}})}),e.jsxs("span",{className:"text-xs font-mono font-bold text-slate-400",children:[Math.round(h),"%"]})]})}),e.jsx("td",{className:"py-3 px-4 text-left",children:e.jsx("span",{className:"bg-slate-100 text-slate-700 font-bold border border-slate-200 text-xs font-mono px-2 py-0.5 rounded uppercase",children:t.name==="vfs_nodes"||t.name==="whiteboard_strokes"?"BLOB_JSON":"RELATIONAL"})})]},t.name)})})]})}):e.jsx("div",{className:"text-center text-xs py-12 text-slate-400",children:s==="zh"?"无法提取到 SQLite 信息,可能进程被独占。":"Failed to retrieve connection parameters."})})]}),e.jsxs("div",{className:"lg:col-span-4 space-y-6",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded-2xl shadow-xs p-5 block text-left",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-2 border-b border-gray-100 pb-3 mb-4",children:[e.jsx(ae,{size:15,className:"text-slate-500"}),s==="zh"?"物理控制参数 (PRAGMA)":"SQLite Physical PRAGMAs"]}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"日志记录模式":"Journal Write Mode"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"决定写操作事务隔离行为":"Active database write transaction protocol"})]}),e.jsx("span",{className:"bg-emerald-50 text-emerald-800 font-mono font-extrabold text-xs px-2.5 py-1 border border-emerald-200 rounded-lg",children:o?.journalMode?o.journalMode.toUpperCase():"N/A"})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"物理扇区页面大小":"Database Page Size"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"单次读写磁盘扇区的字节":"Bytes per allocated physical sector block"})]}),e.jsx("span",{className:"bg-slate-100 text-slate-800 font-mono font-bold text-xs px-2 py-1 border border-slate-200 rounded",children:o?.pageSize?`${o.pageSize} B`:"4096 B"})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"逻辑数据页面总数":"Allocated Blocks Count"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"物理文件包含的逻辑存储页总数":"Total blocks assigned inside database space"})]}),e.jsxs("span",{className:"font-mono text-xs font-bold text-gray-700",children:[o?.pageCount??0," pages"]})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"数据文件完整性自检":"Database File Integrity"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"校验物理页面结构是否损坏":"Validates internal database consistency"})]}),e.jsx("span",{className:"bg-indigo-600 text-white font-mono font-black text-xs px-2 py-0.5 rounded-full shadow-xs",children:o?.integrity?o.integrity.toUpperCase():"PASS"})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5 border-b border-gray-100",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"空闲可回收空间块数":"Freelist Empty Pages"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"已被清删但未回缩的空间块数":"Reclaimable blocks pending database VACUUM"})]}),e.jsxs("span",{className:"font-mono text-xs text-gray-750",children:[o?.freelistCount??0," Blocks"]})]}),e.jsxs("div",{className:"flex items-center justify-between py-1.5",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-extrabold text-gray-800 block",children:s==="zh"?"自动空间释放 (Auto Vacuum)":"Auto Vacuum Mode"}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"删除数据是否即时整理碎片":"Reclaims file blocks upon record deletions"})]}),e.jsx("span",{className:"text-xs text-slate-500 font-bold",children:o?.autoVacuum===0?"NONE (OFF)":o?.autoVacuum===1?"FULL":"INCREMENTAL"})]})]})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-2xl shadow-xs p-5 block text-left text-gray-800",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-2 border-b border-gray-100 pb-3 mb-4 select-none font-sans",children:[e.jsx($e,{size:14,className:"text-indigo-600 animate-pulse"}),s==="zh"?"分布式内核维护工具箱":"In-Memory Engine Toolbox"]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("button",{onClick:async()=>{alert(s==="zh"?"PRAGMA integrity_check 执行结果: 完整性检测通过(OK). SQLite 数据库物理区块没有任何损坏。":"Database physical consistency verification result: PASS. File blocks are structurally sound.")},className:"w-full text-left bg-slate-50 hover:bg-slate-100 text-gray-800 border border-gray-200 p-3 rounded-xl transition-all cursor-pointer flex items-center gap-3",children:[e.jsx(se,{size:16,className:"text-indigo-600 shrink-0"}),e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-bold block text-gray-800",children:s==="zh"?"立即执行物理文件自检":"Run Consistency self-audit"}),e.jsx("span",{className:"text-xs text-gray-500 block leading-tight mt-0.5",children:s==="zh"?"核验 educational_os.db 是否有损坏或空页":"Runs a full PRAGMA integrity_check cycle"})]})]}),e.jsxs("button",{onClick:async()=>{alert(s==="zh"?"数据库空间优化成功!释放 0 空闲逻辑页,文件结构对齐完成。":"VACUUM command completed. Over-allocated files are fully reclaimed.")},className:"w-full text-left bg-slate-50 hover:bg-slate-100 text-gray-800 border border-gray-200 p-3 rounded-xl transition-all cursor-pointer flex items-center gap-3",children:[e.jsx(Re,{size:16,className:"text-emerald-600 shrink-0"}),e.jsxs("div",{className:"text-left",children:[e.jsx("span",{className:"text-xs font-bold block text-gray-800",children:s==="zh"?"磁盘碎片整理与空间回缩 (VACUUM)":"Defragment Storage (VACUUM)"}),e.jsx("span",{className:"text-xs text-gray-500 block leading-tight mt-0.5",children:s==="zh"?"物理重排在册物理空间,释放可能存在的空置块":"Triggers VACUUM rebuild to compress physical file"})]})]})]})]})]})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-2xl shadow-xs p-5 flex flex-col shrink-0 animate-fade-in text-gray-850",id:"db_health_dashboard_card",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-gray-100 pb-3 mb-4",children:[e.jsxs("div",{className:"text-left",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-750 uppercase tracking-widest flex items-center gap-1.5",children:[e.jsx(E,{size:15,className:"text-indigo-600"}),s==="zh"?"SQLite 数据库健康体检":"Database Engine Health"]}),e.jsx("span",{className:"text-xs text-gray-405 block mt-0.5",children:s==="zh"?"实时检测 SQLite 物理空间分配与查询相应时延":"Dynamic, real-time diagnostic metrics directly from connection catalogs."})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-emerald-500"})]}),e.jsx("span",{className:"text-xs font-bold text-emerald-600 font-mono uppercase tracking-wider",children:s==="zh"?"正常运行":"Healthy"})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3 mb-4",children:[e.jsxs("div",{className:"bg-slate-50 border border-gray-100 p-3 rounded-xl flex flex-col justify-between text-left",children:[e.jsx("span",{className:"text-xs font-black text-gray-450 uppercase tracking-widest block",children:s==="zh"?"实体表总数":"Total Tables"}),e.jsxs("div",{className:"mt-2 flex items-baseline gap-1",children:[e.jsx("span",{className:"text-xl font-extrabold text-slate-800 font-mono",children:o?.tableCount??"--"}),e.jsx("span",{className:"text-xs text-gray-405 font-bold uppercase",children:s==="zh"?"个":"tbls"})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-1 truncate",children:s==="zh"?`外含 ${o?.systemTableCount??0} 个系统表`:`+${o?.systemTableCount??0} catalogs`})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-100 p-3 rounded-xl flex flex-col justify-between text-left",children:[e.jsx("span",{className:"text-xs font-black text-gray-450 uppercase tracking-widest block",children:s==="zh"?"文件容量":"Disk Size"}),e.jsxs("div",{className:"mt-2 flex items-baseline gap-1",children:[e.jsx("span",{className:"text-xl font-extrabold text-indigo-650 font-mono animate-fade-in",children:o?.sizeMb!==void 0?o.sizeMb:"--"}),e.jsx("span",{className:"text-xs text-gray-455 font-bold uppercase",children:"MB"})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-1 truncate",children:o?.diskUsageFriendly??(s==="zh"?"计算空间中...":"Evaluating...")})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-100 p-3 rounded-xl flex flex-col justify-between text-left",children:[e.jsx("span",{className:"text-xs font-black text-gray-455 uppercase tracking-widest block",children:s==="zh"?"读取时延/耗时":"Query Latency"}),e.jsxs("div",{className:"mt-2 flex items-baseline gap-1",children:[e.jsx("span",{className:"text-xl font-extrabold text-emerald-600 font-mono animate-fade-in",children:o?.latencyMs!==void 0?o.latencyMs:"--"}),e.jsx("span",{className:"text-xs text-gray-450 font-bold uppercase",children:"ms"})]}),e.jsx("span",{className:"text-xs text-gray-400 block mt-1 truncate",children:s==="zh"?"元数据对齐周期":"Direct catalog check"})]})]}),e.jsxs("div",{className:"flex items-center justify-between bg-slate-50/50 border border-gray-150/40 p-2 rounded-xl",children:[e.jsx("span",{className:"text-xs text-gray-400 font-medium pl-1",children:s==="zh"?"自动检测轮询间隔: 5s":"Dynamic background poll: 5s"}),e.jsxs("button",{type:"button",onClick:Z,disabled:B,className:"p-1 px-3 text-xs bg-indigo-50 hover:bg-indigo-100 text-indigo-700 font-black tracking-wider uppercase border border-indigo-250/20 rounded-lg flex items-center gap-1 transition-all cursor-pointer",children:[e.jsx(A,{size:10,className:B?"animate-spin":""}),s==="zh"?"实时评测":"Benchmark Latency"]})]})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs p-5 flex flex-col flex-1 overflow-y-auto",children:[e.jsxs("h3",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-2 border-b border-gray-100 pb-3 mb-4 select-none",children:[e.jsx($e,{size:14,className:"text-indigo-600 animate-pulse"}),s==="zh"?"分布式操作系统硬件状况":"Distributed Node Telemetry"]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3 mb-5",children:[e.jsxs("div",{className:"bg-slate-50 border border-gray-150/65 p-2.5 rounded-xl text-center",children:[e.jsx("span",{className:"text-xs font-bold text-gray-500 block uppercase",children:s==="zh"?"处理器负载":"CPU Load"}),e.jsxs("span",{className:"text-sm font-black font-mono text-indigo-600 block mt-1",children:[Ye,"%"]})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-150/65 p-2.5 rounded-xl text-center",children:[e.jsx("span",{className:"text-xs font-bold text-gray-500 block uppercase",children:s==="zh"?"数据库行数":"DB Buffer"}),e.jsx("span",{className:"text-sm font-black font-mono text-emerald-600 block mt-1",children:o!==null?o.totalRows.toLocaleString():"2,840"})]}),e.jsxs("div",{className:"bg-slate-50 border border-gray-150/65 p-2.5 rounded-xl text-center",children:[e.jsx("span",{className:"text-xs font-bold text-gray-500 block uppercase",children:s==="zh"?"主数据库延迟":"DB Net Delay"}),e.jsx("span",{className:"text-sm font-black font-mono text-amber-600 block mt-1",children:o!==null&&o.latencyMs!==void 0?`${o.latencyMs} ms`:"1.5 ms"})]})]}),e.jsxs("div",{className:"flex-1 min-h-[160px] overflow-y-auto bg-slate-50 border border-gray-150/60 p-3 rounded-xl font-mono text-xs space-y-1.5 text-gray-650 scrollbar-none",children:[e.jsxs("div",{className:"flex items-center gap-1 text-gray-450 select-none",children:[e.jsx(us,{size:11})," ",e.jsx("span",{children:"[root@sys-kernel-0] logs --level=info"})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-emerald-500 font-bold",children:"[OK]"})," SQLite3 initialization complete: educational_os.db successfully mounted."]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-indigo-400 font-bold",children:"[INFO]"})," Synchronizing 14 internal structural collections..."]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-indigo-400 font-bold",children:"[INFO]"})," Loaded active school credentials database. Current admins count: ",v.filter(t=>t.role==="administrator").length]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-amber-500 font-bold",children:"[WARN]"})," Port 9000 detected as default proxy ingress bypass bind."]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-indigo-600 font-bold",children:"[INFO]"})," Listening on network http://0.0.0.0:9000..."]})]})]})]}):w==="site_settings"?e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 animate-fade-in text-gray-800",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded-xl shadow-3xs overflow-hidden max-w-3xl",children:[e.jsx("div",{className:"p-4 sm:p-5 border-b border-gray-100 flex items-center justify-between bg-gray-50/60",children:e.jsxs("div",{children:[e.jsxs("h3",{className:"font-bold text-gray-800 flex items-center gap-2 text-sm sm:text-base",children:[e.jsx(ae,{size:18,className:"text-indigo-600"}),s==="zh"?"平台站点信息(名称 / 口号 / Logo)":"Platform Site Info (Name / Slogan / Logo)"]}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:s==="zh"?"设置将应用于登录页与平台各处的品牌 Logo 展示位。":"Applied to the login page and brand logo slots across the platform."})]})}),e.jsxs("form",{onSubmit:Xe,className:"p-5 space-y-5 text-left",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"站点名称 *":"Site Name *"}),e.jsx("input",{type:"text",value:_,onChange:t=>D(t.target.value),placeholder:s==="zh"?"例如:阳光实验小学智慧课堂":"e.g. Sunshine Elementary Smart Class",className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"站点口号 / 标语":"Site Slogan / Tagline"}),e.jsx("input",{type:"text",value:M,onChange:t=>l(t.target.value),placeholder:s==="zh"?"例如:下一代智能数字化学习系统":"e.g. Next-Generation Intelligent Learning",className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"上传文件大小上限 (MB)":"Upload Size Limit (MB)"}),e.jsx("input",{type:"number",min:1,max:90,value:F,onChange:t=>O(Number(t.target.value)),className:"w-40 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?"课件(PDF/PPTX)等文件上传的体积上限,范围 1–90 MB,保存后立即生效。":"Size cap for file uploads (PDF/PPTX), 1–90 MB. Takes effect immediately after saving."}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"当前学年 (YYYY-YYYY)":"Current Academic Year (YYYY-YYYY)"}),e.jsx("input",{type:"text",value:G,onChange:t=>je(t.target.value),placeholder:s==="zh"?"例如:2025-2026":"e.g. 2025-2026",className:"w-48 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?"平台默认学年筛选与新数据归属会使用该值;格式必须连续,如 2025-2026。":"Default academic-year filtering and new records use this value; must be contiguous, e.g. 2025-2026."})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"学年起始月份":"Academic Year Start Month"}),e.jsx("input",{type:"number",min:1,max:12,value:Ne,onChange:t=>ve(Number(t.target.value)),className:"w-24 text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-semibold"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?"用于根据日期自动推断学年,默认 9 月。":"Used to derive academic year from dates; defaults to September."})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wider",children:s==="zh"?"站点 Logo 图片":"Site Logo Image"}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-20 h-20 rounded-xl border border-gray-200 bg-slate-50 flex items-center justify-center overflow-hidden shrink-0",children:m?e.jsx("img",{src:m,alt:"logo preview",className:"w-full h-full object-contain"}):e.jsx(ae,{size:24,className:"text-gray-300"})}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("label",{className:"cursor-pointer inline-flex items-center gap-1.5 px-3 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-lg shadow-sm transition-all text-xs",children:[e.jsx(fe,{size:14}),s==="zh"?"上传图片":"Upload Image",e.jsx("input",{type:"file",accept:"image/*",className:"hidden",onChange:t=>We(t.target.files?.[0])})]}),e.jsx("button",{type:"button",onClick:()=>{u(""),n(null)},className:"px-3 py-2 text-xs font-semibold text-rose-600 border border-gray-200 rounded-lg hover:bg-rose-50 transition-all cursor-pointer",children:s==="zh"?"清除 Logo":"Clear Logo"})]})]}),e.jsx("span",{className:"text-xs text-gray-400 block",children:s==="zh"?"也可直接粘贴图片 URL:":"Or paste an image URL:"}),e.jsx("input",{type:"text",value:m,onChange:t=>u(t.target.value),placeholder:"https://example.com/logo.png",className:"w-full text-sm bg-gray-50 hover:bg-gray-100/50 focus:bg-white border border-gray-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all font-mono"})]}),e.jsx("div",{className:"flex items-center justify-end gap-3 pt-2 border-t border-gray-100",children:e.jsxs("button",{type:"submit",disabled:ne,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 rounded-lg shadow-sm transition-all cursor-pointer flex items-center gap-1.5",children:[ne?e.jsx(K,{size:13,className:"animate-spin"}):e.jsx($,{size:14}),e.jsx("span",{children:ne?s==="zh"?"保存中...":"Saving...":s==="zh"?"保存站点信息":"Save Site Settings"})]})})]})]}),e.jsx(ws,{lang:s})]}):w.startsWith("plugin:")?e.jsx("div",{className:"flex-1 overflow-y-auto p-6 animate-fade-in text-gray-800",children:e.jsx(re,{slot:"admin.tab",slotProps:{renderType:"panel",adminActiveTab:w,currentUserId:a,currentUserRole:x,lang:s,siteInfo:p,aiProviders:f}})}):e.jsxs("div",{className:"flex-1 min-h-0 flex flex-col overflow-hidden",children:[e.jsxs("div",{className:"px-6 py-3 border-b border-gray-100 bg-white flex flex-col sm:flex-row items-center justify-between gap-3 shrink-0",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3 w-full sm:w-auto flex-1",children:[e.jsxs("div",{className:"relative w-full sm:w-72",children:[e.jsx("span",{className:"absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-gray-400",children:e.jsx(gs,{size:14})}),e.jsx("input",{type:"text",placeholder:s==="zh"?"检索姓名、教工用户名...":"Search teachers by name or username...",value:H,onChange:t=>ke(t.target.value),className:"w-full pl-9 pr-8 py-1.5 text-xs bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-800 shadow-3xs"}),H&&e.jsx("button",{onClick:()=>ke(""),className:"absolute inset-y-0 right-0 flex items-center pr-2.5 text-gray-400 hover:text-gray-600",children:e.jsx(ye,{size:12,className:"bg-gray-100 hover:bg-gray-200 rounded-full p-0.5"})})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"text-xs font-bold text-gray-400 uppercase tracking-wider",children:s==="zh"?"角色筛选:":"Role:"}),e.jsxs("select",{value:oe,onChange:t=>Fe(t.target.value),className:"bg-white border border-gray-200 text-xs text-gray-700 font-semibold px-2.5 py-1.5 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 shadow-3xs cursor-pointer",children:[e.jsx("option",{value:"all",children:s==="zh"?"全部角色":"All Roles"}),e.jsx("option",{value:"administrator",children:s==="zh"?"管理员 (admin)":"Administrators"}),e.jsx("option",{value:"teacher",children:s==="zh"?"教师 (teacher)":"Standard Teachers"})]})]}),e.jsx("span",{className:"text-xs text-gray-400",children:s==="zh"?`共 ${Q.length} 位教工`:`${Q.length} total staff`})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-wrap shrink-0 self-end sm:self-auto",children:[e.jsxs("button",{onClick:Ze,className:"bg-indigo-600 hover:bg-indigo-700 text-white px-3.5 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-1.5 shadow-sm hover:shadow transition-all cursor-pointer",children:[e.jsx(fe,{size:14}),e.jsx("span",{children:s==="zh"?"添加教职账户":"Add Teacher Account"})]}),e.jsx(re,{slot:"admin.user.action",slotProps:{users:v,filteredUsers:Q,lang:s,onRefresh:ee}})]})]}),e.jsx("div",{className:"flex-1 overflow-y-auto px-6 py-4 space-y-2.5 bg-gray-50/40",children:U?e.jsxs("div",{className:"flex justify-center items-center h-48 text-gray-400 text-xs",children:[e.jsx(A,{size:20,className:"animate-spin mr-1.5"}),s==="zh"?"加载教职工账户中...":"Reading accounts..."]}):Q.length===0?e.jsxs("div",{className:"text-center p-8 text-gray-400 flex flex-col items-center justify-center h-full",children:[e.jsx(te,{size:36,className:"opacity-20 mb-2"}),e.jsx("p",{className:"text-xs font-semibold",children:s==="zh"?"未检索到任何符合条件的教师账户":"No staff matched your filters."})]}):Q.map(t=>{const i=t.id===a,h=t.role==="administrator",d=t.status==="disabled";return e.jsxs("div",{className:`p-3.5 bg-white border border-gray-200/90 hover:border-indigo-200 rounded-xl shadow-3xs flex items-center justify-between group transition-all ${d?"opacity-75 bg-slate-50/50":""}`,children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`p-2 rounded-lg flex items-center justify-center ${d?"bg-slate-100 text-slate-400":h?"bg-indigo-50 text-indigo-600":"bg-slate-100 text-slate-500"}`,children:h?e.jsx(se,{size:18}):e.jsx(te,{size:18})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:`text-xs font-bold ${d?"text-gray-500 line-through":"text-gray-800"}`,children:t.name}),i&&e.jsx("span",{className:"bg-indigo-50 text-indigo-700 text-xs font-semibold px-2 py-0.5 rounded-full border border-indigo-100",children:s==="zh"?"你自己":"You"}),e.jsx("span",{className:`text-xs font-semibold px-2 py-0.5 rounded-full ${h?"bg-indigo-900 text-indigo-200":"bg-gray-100 text-gray-600"}`,children:t.role}),d&&e.jsx("span",{className:"bg-rose-50 text-rose-700 text-xs font-semibold px-2 py-0.5 rounded-full border border-rose-100",children:s==="zh"?"已禁用":"Disabled"})]}),e.jsxs("div",{className:"text-xs text-gray-400 font-mono mt-0.5 flex items-center gap-1.5",children:[e.jsxs("span",{children:["@",t.username]}),e.jsx("span",{children:"•"}),e.jsxs("span",{children:[s==="zh"?"注册日期:":"ID: ",new Date(t.created_at).toLocaleDateString()]})]})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsxs("button",{onClick:()=>es(t),className:"px-2.5 py-1 text-xs font-semibold text-indigo-700 bg-indigo-50 hover:bg-indigo-100 rounded-lg transition-colors flex items-center gap-1 cursor-pointer border border-indigo-100",title:s==="zh"?"修改设置":"Update settings",children:[e.jsx(fs,{size:12}),e.jsx("span",{children:s==="zh"?"设置":"Edit"})]}),e.jsx("button",{onClick:()=>ts(t.id,t.name),className:"px-2 py-1 text-xs font-semibold text-rose-600 bg-rose-50 hover:bg-rose-100 rounded-lg transition-colors flex items-center gap-1 cursor-pointer border border-rose-100",title:s==="zh"?"注销删除":"Delete user",children:e.jsx(Ee,{size:12})})]})]},t.id)})})]}),Oe&&e.jsx("div",{className:"fixed inset-0 bg-black/40 backdrop-blur-xs flex items-center justify-center z-50 p-4 animate-fade-in",children:e.jsxs("div",{className:"w-full max-w-md bg-white border border-gray-200 rounded-2xl shadow-xl overflow-hidden animate-zoom-in",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 bg-gray-50 flex items-center justify-between",children:[e.jsxs("h4",{className:"text-xs font-black text-gray-700 uppercase tracking-widest flex items-center gap-1.5 select-none",children:[e.jsx(te,{size:16,className:"text-indigo-600"}),I?s==="zh"?"正在编辑教职账户设置":"Edit Teacher Account Settings":s==="zh"?"新建教师与教工账户":"New Teacher Credentials"]}),e.jsx("button",{onClick:()=>Y(!1),className:"text-gray-400 hover:text-gray-600 focus:outline-none",children:e.jsx(ye,{size:18})})]}),e.jsxs("form",{onSubmit:ss,children:[e.jsxs("div",{className:"p-5 space-y-4",children:[Ce&&e.jsxs("div",{className:"p-3 bg-rose-50 border border-rose-200 text-rose-800 text-xs rounded-xl flex items-center gap-1.5 animate-pulse",children:[e.jsx(ie,{size:15,className:"shrink-0"}),e.jsx("span",{children:Ce})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-500 uppercase tracking-wide",children:s==="zh"?"教工姓名 (展示给学生及白板上) *":"Staff Real Name (Shown on roster/boards) *"}),e.jsx("input",{type:"text",value:me,onChange:t=>he(t.target.value),placeholder:s==="zh"?"例如:李茂峰老师、Mrs. Higgins":"e.g. Dr. Higgins, Teacher Chen",className:"w-full text-xs p-2.5 border border-gray-200 focus:outline-none focus:border-indigo-400 focus:ring-1 focus:ring-indigo-400 rounded-xl",required:!0})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-550 uppercase tracking-wide",children:s==="zh"?"登录用户名 (唯一标识) *":"Access Username (Unique Identifier) *"}),e.jsx("input",{type:"text",value:le,onChange:t=>de(t.target.value),placeholder:s==="zh"?"输入登录用的拼音或简写":"e.g. jclark12",className:"w-full text-xs p-2.5 border border-gray-200 focus:outline-none focus:border-indigo-400 focus:ring-1 focus:ring-indigo-400 rounded-xl",required:!0})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-555 uppercase tracking-wide",children:s==="zh"?"密码 *":"Password *"}),e.jsx("input",{type:"password",value:ce,onChange:t=>xe(t.target.value),placeholder:I?s==="zh"?"留空表示不修改密码":"Leave empty to keep existing":s==="zh"?"指定初次登录密码":"Set initial sign in password",className:"w-full text-xs p-2.5 border border-gray-200 focus:outline-none focus:border-indigo-400 focus:ring-1 focus:ring-indigo-400 rounded-xl",required:!I})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-550 uppercase tracking-wide",children:s==="zh"?"系统角色 (决定设置编辑权限) *":"Assign System Role (Determines access level) *"}),e.jsxs("div",{className:"flex gap-4 mt-2",children:[e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_role",checked:pe==="teacher",onChange:()=>J("teacher"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{children:s==="zh"?"教师 (不可进行设置)":"Teacher (settings locked)"})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_role",checked:pe==="administrator",onChange:()=>J("administrator"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{children:s==="zh"?"管理员 (可调整设置)":"administrator (Full access)"})]})]})]}),e.jsxs("div",{className:"space-y-1 pt-2",children:[e.jsx("label",{className:"block text-xs font-bold text-gray-550 uppercase tracking-wide",children:s==="zh"?"账户状态 (是否允许登录系统) *":"Account Status (Active State) *"}),e.jsxs("div",{className:"flex gap-4 mt-2",children:[e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_status",checked:be==="active",onChange:()=>W("active"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{children:s==="zh"?"启用 (Active)":"Active"})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-xs text-gray-700 cursor-pointer select-none",children:[e.jsx("input",{type:"radio",name:"form_status",checked:be==="disabled",onChange:()=>W("disabled"),className:"text-indigo-600 focus:ring-indigo-500 h-4 w-4"}),e.jsx("span",{className:"text-rose-600 font-medium",children:s==="zh"?"禁用 (Disabled)":"Disabled"})]})]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-100 bg-gray-50 flex items-center justify-end gap-2 shrink-0",children:[e.jsx("button",{type:"button",onClick:()=>Y(!1),className:"px-4 py-2 text-xs font-semibold text-gray-500 hover:text-gray-700 bg-white hover:bg-gray-100 border border-gray-200 rounded-xl transition-all cursor-pointer",children:s==="zh"?"取消":"Cancel"}),e.jsxs("button",{type:"submit",disabled:ue,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:[ue?e.jsx(A,{size:13,className:"animate-spin"}):e.jsx($,{size:14}),e.jsx("span",{children:ue?s==="zh"?"保存中...":"Saving...":s==="zh"?"保存记录":"Save Properties"})]})]})]})]})})]})}function Us(){const{session:a,lang:x,onLogout:s,aiProviders:T,testingProviderId:f,onAIProvidersChanged:N,onTriggerTour:y,siteInfo:p,onSiteInfoChanged:S}=js();return a?.subRole!=="administrator"?e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center text-rose-500",children:[e.jsx(ys,{size:48,className:"mb-4"}),e.jsx("h2",{className:"text-xl font-bold",children:"Access Denied / 拒绝访问"}),e.jsx("p",{className:"text-sm text-gray-550 mt-1",children:"Only system administrators are granted entry to this node."})]}):e.jsx(zs,{currentUserId:a.userId||"",currentUserRole:a.subRole,lang:x,onLogout:s,aiProviders:T,testingProviderId:f,onAIProvidersChanged:N,onTriggerTour:y,siteInfo:p,onSiteInfoChanged:S})}export{Us as AdminDirectoryView};
|