diff --git a/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx b/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx
index 934e7b074..da1bc8dea 100644
--- a/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx
+++ b/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx
@@ -162,8 +162,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
width: '100%',
}}
className="ConfigMonacoEditor"
- >
-
+ />
);
}
});
diff --git a/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx b/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx
index f83547190..eac61ff56 100644
--- a/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx
+++ b/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx
@@ -7,6 +7,7 @@ import React from 'react';
import { DndProvider, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { useDispatch, useSelector, useStore } from 'react-redux';
+import { i18n } from 'vj/utils';
import { RootState } from './reducer';
import { AddTestcase } from './tree/AddTestcase';
import { SelectionManager } from './tree/SelectionManager';
@@ -63,13 +64,13 @@ export function SubtaskNode(props: { subtaskId: number }) {
{subtaskId !== -1 && setExpand((e) => !e)}>
-
Subtask {subtaskId}
+
{i18n('Subtask {0}', subtaskId)}
ev.stopPropagation()}>
- Are you sure you want to delete this subtask?
+ {i18n('Are you sure you want to delete this subtask?')}
- Delete
+ {i18n('Delete')}
}
>
@@ -94,8 +95,8 @@ export function SubtaskNode(props: { subtaskId: number }) {
{subtaskId === -1
- ? 'No testcase here'
- : 'Drag and drop testcases here:'}
+ ? i18n('No testcase here')
+ : i18n('Drag and drop testcases here:')}
)}
@@ -119,13 +120,10 @@ export function SubtaskConfigTree() {
return (
-
+
- Auto Configure
+ {i18n('Auto configure')}
@@ -136,7 +134,7 @@ export function SubtaskConfigTree() {
>
- Add New Subtask
+ {i18n('Add new subtask')}
diff --git a/packages/ui-default/components/problemconfig/index.tsx b/packages/ui-default/components/problemconfig/index.tsx
index 1bff20346..d3124f222 100644
--- a/packages/ui-default/components/problemconfig/index.tsx
+++ b/packages/ui-default/components/problemconfig/index.tsx
@@ -1,4 +1,6 @@
-import { Tab, Tabs } from '@blueprintjs/core';
+import {
+ Button, Dialog, DialogBody, DialogFooter, Tab, Tabs,
+} from '@blueprintjs/core';
import React from 'react';
import { useSelector } from 'react-redux';
import { i18n } from 'vj/utils';
@@ -13,14 +15,12 @@ interface Props {
export default function ProblemConfig(props: Props) {
const [selected, setSelected] = React.useState('basic');
+ const [open, setOpen] = React.useState(false);
const valid = useSelector((state: RootState) => state.config.__valid);
const errors = useSelector((state: RootState) => state.config.__errors, (a, b) => JSON.stringify(a) === JSON.stringify(b));
-
- function save() {
- // eslint-disable-next-line
- if (!valid && !confirm('Errors detected in the config. Still save?')) return;
- props.onSave();
- }
+ React.useEffect(() => {
+ if (valid) setOpen(false);
+ }, [valid]);
return (<>
@@ -29,13 +29,22 @@ export default function ProblemConfig(props: Props) {
(t !== 'errors' && setSelected(t.toString()))} selectedTabId={valid ? selected : 'errors'}>
- } />
- } />
+ } />
+ } />
{errors.map((i) => ({i} ))} } />
- {i18n('Submit')}
+ setOpen(false)}>
+
+ {i18n('Errors detected in the config. Confirm save?')}
+
+
+ setOpen(false)}>{i18n('Cancel')}
+ {i18n('Save')}
+ >} />
+
+ setOpen(true)}>{i18n('Save')}
>);
}
diff --git a/packages/ui-default/components/problemconfig/reducer/config.ts b/packages/ui-default/components/problemconfig/reducer/config.ts
index bff419ca7..65addaf51 100644
--- a/packages/ui-default/components/problemconfig/reducer/config.ts
+++ b/packages/ui-default/components/problemconfig/reducer/config.ts
@@ -72,8 +72,6 @@ export default function reducer(state = {
}
} else if (action.key === 'add') {
subtasks.push({
- time: state.time || '1s',
- memory: state.memory || '256m',
cases: [],
score: 0,
id: Object.keys(subtasks).map((i) => subtasks[i].id).reduce((a, b) => Math.max(+a, +b), 0) + 1,
diff --git a/packages/ui-default/components/problemconfig/tree/AddTestcase.tsx b/packages/ui-default/components/problemconfig/tree/AddTestcase.tsx
index 6b9bb3dec..abfbea4b9 100644
--- a/packages/ui-default/components/problemconfig/tree/AddTestcase.tsx
+++ b/packages/ui-default/components/problemconfig/tree/AddTestcase.tsx
@@ -4,6 +4,7 @@ import {
import { readSubtasksFromFiles } from '@hydrooj/utils/lib/common';
import React, { useEffect, useRef } from 'react';
import { useSelector, useStore } from 'react-redux';
+import { i18n } from 'vj/utils';
import FileSelectAutoComplete from '../../autocomplete/components/FileSelectAutoComplete';
import { RootState } from '../reducer';
@@ -72,7 +73,7 @@ export function AddTestcase() {
>
- Auto detect
+ {i18n('Auto detect')}
- Add testcase
+ {i18n('Add testcase')}
setOpen(false)}>
@@ -98,7 +99,7 @@ export function AddTestcase() {
label="Input"
width="100%"
onChange={(e) => setInput(e)}
- placeholder="Input"
+ placeholder={i18n('Input')}
value={input || ''}
/>
@@ -109,20 +110,21 @@ export function AddTestcase() {
label="Output"
width="100%"
onChange={(e) => setOutput(e)}
- placeholder="Output"
+ placeholder={i18n('Output')}
value={input || ''}
/>
- } />
+ />}
+ />
>);
}
diff --git a/packages/ui-default/components/problemconfig/tree/SubtaskSettings.tsx b/packages/ui-default/components/problemconfig/tree/SubtaskSettings.tsx
index c178ded2e..8177d2df8 100644
--- a/packages/ui-default/components/problemconfig/tree/SubtaskSettings.tsx
+++ b/packages/ui-default/components/problemconfig/tree/SubtaskSettings.tsx
@@ -5,6 +5,7 @@ import {
} from '@blueprintjs/core';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import { i18n } from 'vj/utils';
import { RootState } from '../reducer';
interface SubtaskSettingsProps {
@@ -41,7 +42,7 @@ export function SubtaskSettings(props: SubtaskSettingsProps) {
}
return (<>
- setOpen(false)}>
+ setOpen(false)}>
- {time || props.time || '1s'}
+ {time || props.time || '1s'}
- {memory || props.memory || '256m'}
+ {memory || props.memory || '256m'}
{' '}
{score || 0}
@@ -106,7 +107,7 @@ export function GlobalSettings() {
setOpen(false);
}
return (<>
- setOpen(false)}>
+ setOpen(false)}>
- {time || '1s'}
+ {time || '1s'}
{' '}
- {memory || '256m'}
+ {memory || '256m'}
>);
diff --git a/packages/ui-default/components/problemconfig/tree/Testcase.tsx b/packages/ui-default/components/problemconfig/tree/Testcase.tsx
index f80c9eff3..26e193264 100644
--- a/packages/ui-default/components/problemconfig/tree/Testcase.tsx
+++ b/packages/ui-default/components/problemconfig/tree/Testcase.tsx
@@ -4,6 +4,7 @@ import { TestCaseConfig } from 'hydrooj';
import { omit } from 'lodash';
import React from 'react';
import { useDrag } from 'react-dnd';
+import { i18n } from 'vj/utils';
interface TestcaseNodeProps {
c: TestCaseConfig;
@@ -28,12 +29,12 @@ export function TestcaseNode(props: TestcaseNodeProps) {
data-selected={selected}
content={
-
+
{subtaskIds.filter((i) => i !== subtaskId).map((i) => (
-
+
))}
{subtaskIds.length <= 1 && (
-
+
)}
diff --git a/packages/ui-default/locales/zh.yaml b/packages/ui-default/locales/zh.yaml
index 081a67e72..829b1ca22 100644
--- a/packages/ui-default/locales/zh.yaml
+++ b/packages/ui-default/locales/zh.yaml
@@ -26,6 +26,7 @@ __langname: 简体中文
'Copy failed :(': '复制失败 :('
'Current dataset: {0}': '当前测试数据: {0}'
'Discussion: Terms Of Service': 讨论区服务条款
+'Drag and drop testcases here:': '拖拽测试点到此处:'
'Failed to get credential: {0}': '获取认证数据失败: {0}'
'File upload failed: {0}': 文件上传失败:{0}
'Format: category 1, category 2, ..., category n': 格式:分类1, 分类2, ..., 分类n
@@ -69,6 +70,8 @@ Add Authenticator: 添加认证器
Add File: 添加文件
Add module: 添加模块
Add new data: 添加新数据
+Add new subtask: 添加新子任务
+Add testcase: 添加测试点
Add User: 添加用户
Add: 添加
additional_file: 附加文件
@@ -88,6 +91,7 @@ An example of dataset: 测试数据集的一个例子
Any user is allowed to join this domain with an invitation code: 任意用户都可以通过邀请码加入此域
Any user is allowed to join this domain: 任意用户都可以加入此域
API: API
+Are you sure you want to delete this subtask?: 确认删除此子任务?
Argument {0} is invalid.: 非法的参数 {0} 。
Arguments: 参数
Assign User or Group: 分配给用户或小组
@@ -101,9 +105,12 @@ Attendee Manage: 参赛者管理
Authenticator: 认证器
Authenticators: 认证器
author: 作者
+Auto configure: 自动配置
+Auto detect: 自动检测
Auto hide problems during the contest: 在比赛过程中自动隐藏题目
Auto Read Tasks: 自动识别任务
Basic Info: 基础信息
+Basic: 基础
Be Copied: 被复制
Begin at: 开始于
Begin Date: 开始日期
@@ -340,6 +347,7 @@ Enroll Training: 参加训练
Enrolled: 已参加
Enrollees: 参加人数
error: 错误
+Errors detected in the config. Confirm save?: 配置中存在错误。确认保存?
Evaluated difficulty: 估计的难度
Exit: 退出
expand: 展开
@@ -519,6 +527,7 @@ Month: 月
Monthly Popular: 月度最受欢迎
More: 更多
Most Upvoted Solutions: 最被赞同的题解
+Move to subtask: 移动到子任务
Multi Platform Authenticator: 跨平台认证器
My Domains: 我的域
My Files: 我的文件
@@ -549,6 +558,8 @@ No Permission to Submit: 您没有权限递交
No problem.: 没有题目。
No solutions so far...: 目前还没有题解...
No Submissions: 没有递交
+No target available: 无可用目标
+No testcase here: 此处没有测试点
No user is allowed to join this domain: 没有用户可以自助加入此域
no_translation_warn: ''
None: 无
@@ -764,6 +775,7 @@ Set Privilege: 设置权限
Set Role: 设置角色
Set Roles for Selected User: 设置所选用户的角色
Set Roles for selected users: 为所选用户设置角色
+Set time and memory limits: 设置时空限制
setting_customize: 个性化
setting_display: 显示偏好
setting_domain: 域设置
@@ -813,6 +825,8 @@ Submit to Judge: 递交以评测
Submit Your Code: 递交代码进行正式评测
Submit: 递交
Submitted: 已递交
+Subtask {0}: 子任务 {0}
+Subtasks: 子任务
SuperUser: 超级管理员
Support: 支持
Switch account: 切换账户