diff --git a/.gitignore b/.gitignore index 6d02767..af57abe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,26 @@ -``` # Dependencies node_modules/ -target/ -dist -# IDE -.vscode/ -.idea/ +# Build artifacts +dist/ +build/ +target/ # Logs *.log -# Environment +# Environment variables .env .env.local *.env.* +# Editor/IDE +.vscode/ +.idea/ +*.swp +*.swo +*.tmp + # Python __pycache__/ *.pyc @@ -34,16 +39,520 @@ pip-delete-this-directory.txt nosetests.xml coverage.xml *.cover +*.db *.log -.git/modules/ -*.sublime-workspace +*.pot +*.pyc +*.pyo +*.pyd +.MF +.TemporaryItems +.deps +.nfs* .pytest_cache/ -.hypothesis/ -.DS_Store -Thumbs.db +.mypy_cache/ -# Tauri local/generated artifacts -src-tauri/data.sql -src-tauri/gen/ -AGENTS.md -``` +# Node.js +npm-debug.log* +yarn-debug.log* +yarn-error.log* +node_modules/ +jspm_packages/ +*.min.js +*.min.css + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +desktop.ini + +# Compiled and binary files +*.exe +*.dll +*.so +*.a +*.o +*.out +*.class +*.jar +*.war +*.ear +*.zip +*.tar +*.gz +*.tgz +*.bz2 +*.xz +*.7z +*.rar +*.zst +*.lz4 +*.lzh +*.cab +*.arj +*.rpm +*.deb +*.Z +*.lz +*.lzo +*.tar.gz +*.tar.bz2 +*.tar.xz +*.tar.zst + +# Go +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.prof +coverage.out +vendor/ + +# Java +*.class +*.jar +*.war +*.ear +*.zip +hs_err_pid* +replay_pid* + +# C/C++ +*.o +*.a +*.so +*.obj +*.exe +*.dll +*.lib +*.so* +*.dylib* +*.plist +*.swp +*~ +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +desktop.ini + +# Ruby +*.gem +*.rbc +.bundle/ +.config/byebug_history +.yardoc/ +_yardoc/ +doc/ +.rdoc +.coveralls.yml +.travis.yml +.circleci/ +.semaphore/ +appveyor.yml +.codeclimate.yml +.codacy-coverage.yml +.codacy.yml +.lgtm.yml +.reviewdog.yml +.sider.yml +.styleci.yml +.php_cs +.php_cs.dist +.php-cs-fixer.dist.php +.php-cs-fixer.php +.editorconfig +.eslintrc +.jshintrc +.jscsrc +.babelrc +.postcssrc +.prettierrc +.stylelint +.stylelintrc +.hintrc +.remarkrc +.gitattributes +.gitignore +.pre-commit-config.yaml +.mdlrc +.rubocop.yml +.slim-lint.yml +.rubocop_todo.yml +.slim-lint-report.json +.scss-lint.yml +.scss-lint-report.json +.jshintignore +.eslintignore +.babelignore +.prettierignore +.stylelintignore +.stylelintrcignore +.hintignore +.remarkignore +.gitmodules +.gitreview +.gitlab-ci.yml +.drone.yml +.direnv +.envrc +.secrets.baseline +.checkov.yaml +.tflint.hcl +.kitchen.local.yml +.kitchen.yml +.kitchen/ +.vagrant/ +.terraform/ +.terragrunt-cache/ +.chef/knife.rb +.chef/config.rb +.chef/environment/ +.chef/role/ +.chef/data_bags/ +.chef/cookbooks/ +.chef/site-cookbooks/ +.chef/environments/ +.chef/roles/ +.chef/data_bags/ +.chef/encrypted_data_bag_secret +.chef/trusted_certs/ +.chef/clients/ +.chef/nodes/ +.chef/users/ +.chef/validation.pem +.chef/client.pem +.chef/server.pem +.chef/webui_priv.pem +.chef/webui_pub.pem +.chef/private-chef-secrets.json +.chef/local-mode-cache/ +chefignore +Berksfile.lock +Berksfile +Policyfile.rb +Policyfile.lock.json +Guardfile +Capfile +Rakefile +Vagrantfile +Gemfile +Gemfile.lock +Puppetfile +Puppetfile.lock +.gitlab/issue_templates/ +.gitlab/mr_templates/ +.gitlab-ci/ +.gitlab/ci/ +.gitlab/merge_request_templates/ +.gitlab/issue_templates/ +.gitlab-feature-flag/ +.gitlab-feature-flag.yml +.gitlab-feature-flag.yaml +.gitlab-feature-flag.json +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go +.gitlab-feature-flag.java +.gitlab-feature-flag.cpp +.gitlab-feature-flag.c +.gitlab-feature-flag.h +.gitlab-feature-flag.hpp +.gitlab-feature-flag.cc +.gitlab-feature-flag.hxx +.gitlab-feature-flag.cs +.gitlab-feature-flag.fs +.gitlab-feature-flag.fsi +.gitlab-feature-flag.fsx +.gitlab-feature-flag.fsscript +.gitlab-feature-flag.vb +.gitlab-feature-flag.vbs +.gitlab-feature-flag.asp +.gitlab-feature-flag.aspx +.gitlab-feature-flag.html +.gitlab-feature-flag.htm +.gitlab-feature-flag.xhtml +.gitlab-feature-flag.css +.gitlab-feature-flag.scss +.gitlab-feature-flag.sass +.gitlab-feature-flag.less +.gitlab-feature-flag.styl +.gitlab-feature-flag.json +.gitlab-feature-flag.yaml +.gitlab-feature-flag.yml +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go +.gitlab-feature-flag.java +.gitlab-feature-flag.cpp +.gitlab-feature-flag.c +.gitlab-feature-flag.h +.gitlab-feature-flag.hpp +.gitlab-feature-flag.cc +.gitlab-feature-flag.hxx +.gitlab-feature-flag.cs +.gitlab-feature-flag.fs +.gitlab-feature-flag.fsi +.gitlab-feature-flag.fsx +.gitlab-feature-flag.fsscript +.gitlab-feature-flag.vb +.gitlab-feature-flag.vbs +.gitlab-feature-flag.asp +.gitlab-feature-flag.aspx +.gitlab-feature-flag.html +.gitlab-feature-flag.htm +.gitlab-feature-flag.xhtml +.gitlab-feature-flag.css +.gitlab-feature-flag.scss +.gitlab-feature-flag.sass +.gitlab-feature-flag.less +.gitlab-feature-flag.styl +.gitlab-feature-flag.json +.gitlab-feature-flag.yaml +.gitlab-feature-flag.yml +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go +.gitlab-feature-flag.java +.gitlab-feature-flag.cpp +.gitlab-feature-flag.c +.gitlab-feature-flag.h +.gitlab-feature-flag.hpp +.gitlab-feature-flag.cc +.gitlab-feature-flag.hxx +.gitlab-feature-flag.cs +.gitlab-feature-flag.fs +.gitlab-feature-flag.fsi +.gitlab-feature-flag.fsx +.gitlab-feature-flag.fsscript +.gitlab-feature-flag.vb +.gitlab-feature-flag.vbs +.gitlab-feature-flag.asp +.gitlab-feature-flag.aspx +.gitlab-feature-flag.html +.gitlab-feature-flag.htm +.gitlab-feature-flag.xhtml +.gitlab-feature-flag.css +.gitlab-feature-flag.scss +.gitlab-feature-flag.sass +.gitlab-feature-flag.less +.gitlab-feature-flag.styl +.gitlab-feature-flag.json +.gitlab-feature-flag.yaml +.gitlab-feature-flag.yml +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go +.gitlab-feature-flag.java +.gitlab-feature-flag.cpp +.gitlab-feature-flag.c +.gitlab-feature-flag.h +.gitlab-feature-flag.hpp +.gitlab-feature-flag.cc +.gitlab-feature-flag.hxx +.gitlab-feature-flag.cs +.gitlab-feature-flag.fs +.gitlab-feature-flag.fsi +.gitlab-feature-flag.fsx +.gitlab-feature-flag.fsscript +.gitlab-feature-flag.vb +.gitlab-feature-flag.vbs +.gitlab-feature-flag.asp +.gitlab-feature-flag.aspx +.gitlab-feature-flag.html +.gitlab-feature-flag.htm +.gitlab-feature-flag.xhtml +.gitlab-feature-flag.css +.gitlab-feature-flag.scss +.gitlab-feature-flag.sass +.gitlab-feature-flag.less +.gitlab-feature-flag.styl +.gitlab-feature-flag.json +.gitlab-feature-flag.yaml +.gitlab-feature-flag.yml +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go +.gitlab-feature-flag.java +.gitlab-feature-flag.cpp +.gitlab-feature-flag.c +.gitlab-feature-flag.h +.gitlab-feature-flag.hpp +.gitlab-feature-flag.cc +.gitlab-feature-flag.hxx +.gitlab-feature-flag.cs +.gitlab-feature-flag.fs +.gitlab-feature-flag.fsi +.gitlab-feature-flag.fsx +.gitlab-feature-flag.fsscript +.gitlab-feature-flag.vb +.gitlab-feature-flag.vbs +.gitlab-feature-flag.asp +.gitlab-feature-flag.aspx +.gitlab-feature-flag.html +.gitlab-feature-flag.htm +.gitlab-feature-flag.xhtml +.gitlab-feature-flag.css +.gitlab-feature-flag.scss +.gitlab-feature-flag.sass +.gitlab-feature-flag.less +.gitlab-feature-flag.styl +.gitlab-feature-flag.json +.gitlab-feature-flag.yaml +.gitlab-feature-flag.yml +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go +.gitlab-feature-flag.java +.gitlab-feature-flag.cpp +.gitlab-feature-flag.c +.gitlab-feature-flag.h +.gitlab-feature-flag.hpp +.gitlab-feature-flag.cc +.gitlab-feature-flag.hxx +.gitlab-feature-flag.cs +.gitlab-feature-flag.fs +.gitlab-feature-flag.fsi +.gitlab-feature-flag.fsx +.gitlab-feature-flag.fsscript +.gitlab-feature-flag.vb +.gitlab-feature-flag.vbs +.gitlab-feature-flag.asp +.gitlab-feature-flag.aspx +.gitlab-feature-flag.html +.gitlab-feature-flag.htm +.gitlab-feature-flag.xhtml +.gitlab-feature-flag.css +.gitlab-feature-flag.scss +.gitlab-feature-flag.sass +.gitlab-feature-flag.less +.gitlab-feature-flag.styl +.gitlab-feature-flag.json +.gitlab-feature-flag.yaml +.gitlab-feature-flag.yml +.gitlab-feature-flag.toml +.gitlab-feature-flag.ini +.gitlab-feature-flag.cfg +.gitlab-feature-flag.conf +.gitlab-feature-flag.properties +.gitlab-feature-flag.xml +.gitlab-feature-flag.csv +.gitlab-feature-flag.tsv +.gitlab-feature-flag.sql +.gitlab-feature-flag.sh +.gitlab-feature-flag.bat +.gitlab-feature-flag.cmd +.gitlab-feature-flag.ps1 +.gitlab-feature-flag.pl +.gitlab-feature-flag.rb +.gitlab-feature-flag.py +.gitlab-feature-flag.js +.gitlab-feature-flag.ts +.gitlab-feature-flag.go...... \ No newline at end of file diff --git a/src/components/AutoScoreManager.tsx b/src/components/AutoScoreManager.tsx deleted file mode 100644 index 20c3e8f..0000000 --- a/src/components/AutoScoreManager.tsx +++ /dev/null @@ -1,465 +0,0 @@ -import { useState, useEffect } from "react" -import { HolderOutlined } from "@ant-design/icons" -import { useTranslation } from "react-i18next" -import Rule from "./autoScore/Rule" -import { - Card, - Form, - Input, - Button, - message, - Table, - Space, - Switch, - Popconfirm, - Select, - Tooltip, - Pagination, -} from "antd" -import type { ColumnsType } from "antd/es/table" - -interface AutoScoreRule { - id: number - enabled: boolean - name: string - studentNames: string[] - lastExecuted?: string - triggers?: TriggerItem[] - actions?: ActionItem[] -} - -interface TriggerItem { - id: number - eventName: string - value: string - relation: "AND" | "OR" -} - -interface ActionItem { - id: number - eventName: string - value: string - reason: string -} - -interface AutoScoreRuleFormValues { - name: string - studentNames: string[] -} - -const TRIGGER_DEFINITIONS = [ - { eventName: "interval_time_passed", labelKey: "autoScore.triggerIntervalTime" }, - { eventName: "student_has_tag", labelKey: "autoScore.triggerStudentTag" }, -] - -const ACTION_DEFINITIONS = [ - { eventName: "add_score", labelKey: "autoScore.actionAddScore" }, - { eventName: "add_tag", labelKey: "autoScore.actionAddTag" }, -] - -export const AutoScoreManager: React.FC = () => { - const { t } = useTranslation() - const [rules, setRules] = useState([]) - const [students, setStudents] = useState<{ id: number; name: string }[]>([]) - const [loading, setLoading] = useState(false) - const [currentPage, setCurrentPage] = useState(1) - const [pageSize, setPageSize] = useState(50) - const [form] = Form.useForm() - const [editingRuleId, setEditingRuleId] = useState(null) - const [triggerList, setTriggerList] = useState([]) - const [actionList, setActionList] = useState([]) - const [messageApi, contextHolder] = message.useMessage() - const getTriggerLabel = (eventName: string): string => { - const def = TRIGGER_DEFINITIONS.find((d) => d.eventName === eventName) - return def ? t(def.labelKey) : eventName - } - - const getActionLabel = (eventName: string): string => { - const def = ACTION_DEFINITIONS.find((d) => d.eventName === eventName) - return def ? t(def.labelKey) : eventName - } - - const fetchRules = async () => { - if (!(window as any).api) return - - setLoading(true) - try { - try { - const authRes = await (window as any).api.authGetStatus() - if (!authRes || !authRes.success || authRes.data?.permission !== "admin") { - messageApi.error(t("autoScore.adminRequired")) - setLoading(false) - return - } - } catch (e) { - console.warn("Auth check failed", e) - } - - const [rulesRes, studentsRes] = await Promise.all([ - (window as any).api.invoke("auto-score:getRules", {}), - (window as any).api.queryStudents({}), - ]) - if (rulesRes.success) { - setRules(rulesRes.data) - } else { - messageApi.error(rulesRes.message || t("autoScore.fetchFailed")) - } - if (studentsRes.success) { - setStudents(studentsRes.data) - } - } catch (error) { - console.error("Failed to fetch auto score rules:", error) - messageApi.error(t("autoScore.fetchFailed")) - } finally { - setLoading(false) - } - } - - useEffect(() => { - fetchRules() - }, []) - - const handleSubmit = async () => { - if (!(window as any).api) return - - const values = form.getFieldsValue(true) as unknown as AutoScoreRuleFormValues - - if (!values.name) { - messageApi.warning(t("autoScore.nameRequired")) - return - } - - if (triggerList.length === 0) { - messageApi.warning(t("autoScore.triggerRequired")) - return - } - - if (actionList.length === 0) { - messageApi.warning(t("autoScore.actionRequired")) - return - } - - const studentNames = Array.isArray(values.studentNames) ? values.studentNames : [] - - const triggers = triggerList.map((t) => ({ - event: t.eventName, - value: t.value, - relation: t.relation, - })) - - const actions = actionList.map((a) => ({ - event: a.eventName, - value: a.value, - reason: a.reason, - })) - - const ruleDataToSubmit = { - enabled: true, - name: values.name, - studentNames, - triggers, - actions, - } - - try { - const authRes = await (window as any).api.authGetStatus() - if (!authRes || !authRes.success || authRes.data?.permission !== "admin") { - messageApi.error(t("autoScore.adminCreateRequired")) - return - } - } catch (e) { - console.warn("Auth check failed", e) - } - - try { - let res: { success: boolean; message?: string; data?: any } - if (editingRuleId !== null) { - res = await (window as any).api.invoke("auto-score:updateRule", { - id: editingRuleId, - ...ruleDataToSubmit, - }) - } else { - res = await (window as any).api.invoke("auto-score:addRule", ruleDataToSubmit) - } - - if (res.success) { - messageApi.success( - editingRuleId !== null ? t("autoScore.updateSuccess") : t("autoScore.createSuccess") - ) - form.setFieldsValue({ - name: "", - studentNames: [], - }) - setEditingRuleId(null) - setTriggerList([]) - setActionList([]) - fetchRules() - } else { - messageApi.error( - res.message || - (editingRuleId !== null ? t("autoScore.updateFailed") : t("autoScore.createFailed")) - ) - } - } catch (error) { - console.error("Failed to submit auto score rule:", error) - messageApi.error( - editingRuleId !== null ? t("autoScore.updateFailed") : t("autoScore.createFailed") - ) - } - } - - const handleEdit = (rule: AutoScoreRule) => { - setEditingRuleId(rule.id) - form.setFieldsValue({ - name: rule.name, - studentNames: rule.studentNames, - }) - setTriggerList( - (rule.triggers || []).map((t, index) => ({ - id: index + 1, - eventName: t.eventName, - value: t.value || "", - relation: t.relation || "AND", - })) - ) - setActionList( - (rule.actions || []).map((a, index) => ({ - id: index + 1, - eventName: a.eventName, - value: a.value || "", - reason: a.reason || "", - })) - ) - } - - const handleDelete = async (ruleId: number) => { - if (!(window as any).api) return - try { - const authRes = await (window as any).api.authGetStatus() - if (!authRes || !authRes.success || authRes.data?.permission !== "admin") { - messageApi.error(t("autoScore.adminDeleteRequired")) - return - } - } catch (e) { - console.warn("Auth check failed", e) - } - - try { - const res = await (window as any).api.invoke("auto-score:deleteRule", ruleId) - if (res.success) { - messageApi.success(t("autoScore.deleteSuccess")) - fetchRules() - } else { - messageApi.error(res.message || t("autoScore.deleteFailed")) - } - } catch (error) { - console.error("Failed to delete auto score rule:", error) - messageApi.error(t("autoScore.deleteFailed")) - } - } - - const handleToggle = async (ruleId: number, enabled: boolean) => { - if (!(window as any).api) return - try { - const authRes = await (window as any).api.authGetStatus() - if (!authRes || !authRes.success || authRes.data?.permission !== "admin") { - messageApi.error(t("autoScore.adminToggleRequired")) - return - } - } catch (e) { - console.warn("Auth check failed", e) - } - - try { - const res = await (window as any).api.invoke("auto-score:toggleRule", { ruleId, enabled }) - if (res.success) { - messageApi.success(enabled ? t("autoScore.enabled") : t("autoScore.disabled")) - fetchRules() - } else { - messageApi.error( - res.message || (enabled ? t("autoScore.enableFailed") : t("autoScore.disableFailed")) - ) - } - } catch (error) { - console.error("Failed to toggle auto score rule:", error) - messageApi.error(enabled ? t("autoScore.enableFailed") : t("autoScore.disableFailed")) - } - } - - const handleResetForm = () => { - form.setFieldsValue({ - name: "", - studentNames: [], - }) - setEditingRuleId(null) - setTriggerList([]) - setActionList([]) - } - - const columns: ColumnsType = [ - { - key: "drag", - title: t("autoScore.sort"), - width: 60, - render: () => , - }, - { - title: t("autoScore.status"), - dataIndex: "enabled", - key: "enabled", - width: 80, - render: (enabled: boolean, row) => ( - handleToggle(row.id, value)} size="small" /> - ), - }, - { title: t("autoScore.name"), dataIndex: "name", key: "name", width: 150 }, - { - title: t("autoScore.triggers"), - dataIndex: "triggers", - key: "triggers", - width: 150, - render: (triggers: AutoScoreRule["triggers"]) => { - if (!triggers || triggers.length === 0) { - return {t("common.none")} - } - const triggerLabels = triggers.map((t) => getTriggerLabel(t.eventName)) - return ( - - {t("autoScore.triggerCount", { count: triggers.length })} - - ) - }, - }, - { - title: t("autoScore.actions"), - dataIndex: "actions", - key: "actions", - width: 150, - render: (actions: AutoScoreRule["actions"]) => { - if (!actions || actions.length === 0) { - return {t("common.none")} - } - const actionLabels = actions.map((a) => getActionLabel(a.eventName)) - return ( - - {t("autoScore.actionCount", { count: actions.length })} - - ) - }, - }, - { - title: t("autoScore.applicableStudents"), - dataIndex: "studentNames", - key: "studentNames", - width: 130, - render: (studentNames: string[]) => { - if (!studentNames || studentNames.length === 0) { - return {t("autoScore.allStudents")} - } - const studentList = studentNames.join(",\n") - return ( - - {t("autoScore.studentCount", { count: studentNames.length })} - - ) - }, - }, - { - title: t("autoScore.lastExecuted"), - dataIndex: "lastExecuted", - key: "lastExecuted", - width: 180, - render: (lastExecuted: string) => { - if (!lastExecuted) return {t("autoScore.notExecuted")} - try { - const date = new Date(lastExecuted) - return date.toLocaleString() - } catch { - return {t("autoScore.invalidTime")} - } - }, - }, - { - title: t("common.operation"), - key: "operation", - width: 150, - render: (_, row) => ( - - - handleDelete(row.id)}> - - - - ), - }, - ] - - return ( -
- {contextHolder} -

{t("autoScore.title")}

- - -
-
- - - - - - - - - -