ui: problemconfig: support new CompilableSource

This commit is contained in:
undefined committed 2025-02-09 15:17:10 +00:00
1 parent 4a33816a37
commit 5c9cecc197
17 files changed
+252 -228

No files matched your search

@@ -1,6 +1,7 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { i18n } from 'vj/utils';
import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete';
import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete';
import type { RootState } from './reducer/index';
@@ -21,6 +22,7 @@ export function FormItem({
type KeyType<K, T = string | number> = {
[Q in keyof K]: K[Q] extends T ? Q : never;
}[keyof K];
type FileSelectKey = 'checker' | 'interactor' | 'manager';
export function ManagedInput({ placeholder, formKey }: { placeholder?: string, formKey: KeyType<RootState['config']> }) {
const value = useSelector((state: RootState) => state.config[formKey]);
@@ -54,16 +56,42 @@ export function ManagedSelect({ options, formKey }: { options: string[], formKey
);
}
export function SingleFileSelect({ formKey }: { formKey: KeyType<RootState['config']> }) {
const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0]));
const data = Object.keys(window.LANGS).filter((i) => !prefixes.has(i))
.map((i) => ({ name: `${i.includes('.') ? `${window.LANGS[i.split('.')[0]].display || ''}/` : ''}${window.LANGS[i].display}`, _id: i }));
data.unshift({ name: 'Auto', _id: 'auto' });
export function SingleFileSelect({ formKey, withLang = false, label = 'Checker' }: { formKey: FileSelectKey, withLang?: boolean, label?: string }) {
const value = useSelector((state: RootState) => state.config[formKey]);
const Files = useSelector((state: RootState) => state.testdata);
const dispatch = useDispatch();
return (
<FileSelectAutoComplete
width="100%"
data={Files}
selectedKeys={[value]}
onChange={(val) => dispatch({ type: 'CONFIG_FORM_UPDATE', key: formKey, value: val })}
/>
);
const selectedFile = typeof value === 'string' ? value : value?.file || '';
const selectedLang = typeof value === 'string' ? 'auto' : value?.lang || 'auto';
const update = (file: string, lang: string) => {
if (file === selectedFile && lang === selectedLang) return;
if (withLang) dispatch({ type: 'CONFIG_FORM_UPDATE', key: formKey, value: file ? { file, lang } : null });
else dispatch({ type: 'CONFIG_FORM_UPDATE', key: formKey, value: file });
};
return withLang ? (<>
<FormItem columns={5} label={label}>
<FileSelectAutoComplete
width="100%"
data={Files}
selectedKeys={[selectedFile]}
onChange={(val) => update(val, selectedLang)}
/>
</FormItem>
<FormItem columns={3} label="Language">
<CustomSelectAutoComplete
data={data}
selectedKeys={[selectedLang]}
onChange={(val) => update(selectedFile, val)}
/>
</FormItem>
</>) : (<FileSelectAutoComplete
width="100%"
data={Files}
selectedKeys={[selectedFile]}
onChange={(val) => update(val, selectedLang)}
/>);
}
@@ -20,7 +20,8 @@ export default function ProblemType() {
const dispatcher = (base) => (value) => dispatch({ ...base, value });
useEffect(() => {
if (category || !checker) return;
if (checker.includes('.')) setCategory('custom');
const name = typeof checker === 'string' ? checker : checker.file;
if (name.includes('.')) setCategory('custom');
else setCategory('preset');
}, [checker]);
return (
@@ -75,7 +76,7 @@ export default function ProblemType() {
title="testlib"
panel={(
<div className="row">
<FormItem columns={6} label="Type">
<FormItem columns={4} label="Type">
<select
value={category}
onChange={(ev) => {
@@ -89,9 +90,9 @@ export default function ProblemType() {
</select>
</FormItem>
{category === 'preset'
? <FormItem columns={6} label="Checker">
? <FormItem columns={8} label="Checker">
<select
value={checker}
value={typeof checker === 'string' ? checker : checker.file}
onChange={(ev) => dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'checker', value: ev.currentTarget.value })}
className="select"
>
@@ -100,9 +101,7 @@ export default function ProblemType() {
))}
</select>
</FormItem>
: <FormItem columns={6} label="Checker">
<SingleFileSelect formKey="checker" />
</FormItem>}
: <SingleFileSelect formKey="checker" label="Checker" withLang />}
</div>
)}
/>
@@ -111,12 +110,10 @@ export default function ProblemType() {
title="other"
panel={(
<div className="row">
<FormItem columns={6} label="Interface">
<FormItem columns={4} label="Interface">
<ManagedSelect options={['syzoj', 'hustoj', 'qduoj', 'lemon']} formKey="checker_type" />
</FormItem>
<FormItem columns={6} label="Checker">
<SingleFileSelect formKey="checker" />
</FormItem>
<SingleFileSelect formKey="checker" label="Checker" withLang />
</div>
)}
/>
@@ -128,9 +125,7 @@ export default function ProblemType() {
title={i18n('problem_type.interactive')}
panel={(
<div className="row">
<FormItem columns={6} label="Interactor">
<SingleFileSelect formKey="interactor" />
</FormItem>
<SingleFileSelect formKey="interactor" label="Interactor" withLang />
</div>
)}
/>
@@ -139,10 +134,8 @@ export default function ProblemType() {
title={i18n('problem_type.communication')}
panel={(
<div className="row">
<FormItem columns={6} label="Manager">
<SingleFileSelect formKey="manager" />
</FormItem>
<FormItem columns={6} label="Number of Processes">
<SingleFileSelect formKey="manager" label="Manager" withLang />
<FormItem columns={4} label="Number of Processes">
<input
defaultValue={numProcesses || 2}
placeholder="2"
@@ -51,7 +51,7 @@ export default function reducer(state = {
const next = { ...state, [action.key]: action.value };
if (action.key === 'score' && action.value) next.score = +next.score;
if (action.key === 'checker_type' && action.value === 'other') next.checker_type = 'syzoj';
if (!action.value || (typeof action.value === 'object' && !action.value.join(''))) delete next[action.key];
if (!action.value || (action.value instanceof Array && !action.value.join(''))) delete next[action.key];
return next;
}
case 'CONFIG_CODE_UPDATE': {