import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import i18n from 'vj/utils/i18n'; import { isEqual } from 'lodash'; import { Switch, Tab, Tabs } from '@blueprintjs/core'; import type { SubtaskConfig, TestCaseConfig } from 'hydrooj/src/interface'; import type { RootState } from './reducer/index'; import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete'; const SelectValue = { type: ['default', 'interactive', 'submit_answer', 'objective'], checker_type: ['default', 'lemon', 'syzoj', 'hustoj', 'testlib', 'qduoj'], task_type: ['min', 'max', 'sum'], }; const eq = (a: TestCaseConfig, b: TestCaseConfig) => isEqual(a, b); const eqId = (a: SubtaskConfig[], b: SubtaskConfig[]) => { if (a.length !== b.length) return true; for (let i = 0; i < a.length; i++) { if (a[i].id !== b[i].id) return true; } return false; }; function FormItem({ columns, label, children, helpText = '', disableLabel = false, ...props }) { return (
); } type KeyType = { [Q in keyof K]: K[Q] extends T ? Q : never; }[keyof K]; function ManagedInput({ placeholder, formKey }: { placeholder: string, formKey: KeyType }) { const value = useSelector((state: RootState) => state.config[formKey]); const dispatch = useDispatch(); return ( { dispatch({ type: 'CONFIG_FORM_UPDATE', key: formKey, value: ev.currentTarget.value }); }} className="textbox" /> ); } function ManagedSelect({ placeholder, formKey }: { placeholder: string, formKey: KeyType }) { const value = useSelector((state: RootState) => state.config[formKey]); const dispatch = useDispatch(); return ( ); } function SingleFileSelect({ formKey }: { formKey: KeyType }) { const value = useSelector((state: RootState) => state.config[formKey]); const Files = useSelector((state: RootState) => state.testdata); const dispatch = useDispatch(); return ( dispatch({ type: 'CONFIG_FORM_UPDATE', key: formKey, value: val })} /> ); } function BasicInfo() { const Type = useSelector((state: RootState) => state.config.type); const checkerType = useSelector((state: RootState) => state.config.checker_type); const dispatch = useDispatch(); return ( <> { dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'checker_type', value }); }} renderActiveTabPanelOnly > {i18n('CheckerType')} { SelectValue.checker_type.map((i) => ( {['default', 'strict'].includes(i) ? ( { dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'checker_type', value: checkerType === 'strict' ? 'default' : 'strict' }); } } /> ) : ( )} )} /> )) } ); } function ExtraFilesConfig() { const [showTab, setshowTab] = useState(false); const Files = useSelector((state: RootState) => state.testdata); const userExtraFiles = useSelector((state: RootState) => state.config.user_extra_files); const judgeExtraFiles = useSelector((state: RootState) => state.config.judge_extra_files); const dispatch = useDispatch(); return ( setshowTab(!showTab)} />
dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'user_extra_files', value: val.split(',') })} multi /> )} /> dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'judge_extra_files', value: val.split(',') })} multi /> )} />
); } function CasesSubCasesTable({ index, subindex }) { const subcases = useSelector((state: RootState) => (index === -1 ? state.config.cases[subindex] : state.config.subtasks[index].cases[subindex]), eq); const Files = useSelector((state: RootState) => state.testdata); const dispatch = useDispatch(); const dispatcher = (casesKey: string): React.ChangeEventHandler => (ev) => { dispatch({ type: index === -1 ? 'CONFIG_CASES_UPDATE' : 'CONFIG_SUBTASK_UPDATE', id: index, key: 'cases-edit', casesId: subindex, casesKey, value: ev.currentTarget.value, }); }; const dispatcher1 = (casesKey: string): React.ChangeEventHandler => (val) => { dispatch({ type: index === -1 ? 'CONFIG_CASES_UPDATE' : 'CONFIG_SUBTASK_UPDATE', id: index, key: 'cases-edit', casesId: subindex, casesKey, value: val, }); }; return ( { index === -1 && ( <> ) } dispatch({ type: index === -1 ? 'CONFIG_CASES_UPDATE' : 'CONFIG_SUBTASK_UPDATE', id: index, key: 'cases-delete', value: subindex, })} > ); } function CasesTable({ index }) { const casesLength = useSelector((state: RootState) => (index === -1 ? state.config.cases?.length : state.config.subtasks[index].cases?.length)); const dispatch = useDispatch(); return ( { index === -1 && ( <> ) } {casesLength && [...Array(casesLength).keys()].map((i) => )}
{i18n('Time')} {i18n('Memory')} {i18n('Input')} {i18n('Output')} dispatch({ type: index === -1 ? 'CONFIG_CASES_UPDATE' : 'CONFIG_SUBTASK_UPDATE', id: index, key: 'cases-add', value: index === -1 ? { time: 1000, memory: 256, input: '', output: '', } : { input: '', output: '' }, })} >
); } function SubtasksIds({ index }) { const subtasks = useSelector((state: RootState) => state.config.subtasks, eqId); const subtaskIf = useSelector((state: RootState) => state.config.subtasks[index].if); const dispatch = useDispatch(); const subtasksIds = subtasks.map((i) => i.id).filter((i) => i !== undefined); return ( if `${item}`} onChange={(val) => dispatch({ type: 'CONFIG_SUBTASK_UPDATE', id: index, key: 'if', value: val.split(','), })} multi /> ); } function SubtasksTable({ index }) { const subtask = (key: string) => useSelector((state: RootState) => state.config.subtasks[index][key]); const dispatch = useDispatch(); const dispatcher = (key: string): React.ChangeEventHandler => (ev) => { dispatch({ type: 'CONFIG_SUBTASK_UPDATE', id: index, key, value: ev.currentTarget.value, }); }; return ( <> Subtasks #{index + 1} { dispatch({ type: 'CONFIG_SUBTASK_UPDATE', id: index, key: 'add' }); }}> { dispatch({ type: 'CONFIG_SUBTASK_UPDATE', id: index, key: 'delete' }); }} >
# Score Type Time Memory
); } function TaskConfig({ onAutoLoad }) { const subtasksLength = useSelector((state: RootState) => state.config.subtasks?.length); const casesLength = useSelector((state: RootState) => state.config.cases?.length); const dispatch = useDispatch(); return (
{casesLength && dispatch({ type: 'CONFIG_SUBTASKS_SWITCH', value: true })} />} { subtasksLength || casesLength ? subtasksLength && [...Array(subtasksLength).keys()].map((i) => ) || casesLength && () : ( onAutoLoad()}> {i18n('Auto Read Tasks')} ) }
); } function LangConfig() { const [showTab, setshowTab] = useState(false); const langs = useSelector((state: RootState) => state.config.langs) || []; const prefixes = new Set(Object.keys(LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0])); const data = Object.keys(LANGS).filter((i) => !prefixes.has(i)) .map((i) => ({ name: LANGS[i].display, _id: i })); const dispatch = useDispatch(); const ref = React.useRef(); const selectedKeys = langs.filter((i) => !prefixes.has(i)); React.useEffect(() => { ref.current.setSelectedKeys(selectedKeys); }, [JSON.stringify(selectedKeys)]); return ( setshowTab(!showTab)} /> { const value = val.split(','); value.push(...Array.from(new Set(value.filter((i) => i.includes('.')).map((i) => i.split('.')[0])))); dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'langs', value }); }} multi /> ); } export default function ProblemConfigForm({ onAutoLoad }) { return (
); }