import { normalizeSubtasks, readSubtasksFromFiles } from '@hydrooj/common'; import { TestCaseConfig } from 'hydrooj'; import { Button, Text, Tree } from '@mantine/core'; import { isEqual } from 'lodash'; 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 { confirm } from 'vj/components/dialog/index'; import { i18n } from 'vj/utils'; import { RootState } from './reducer'; import { AddTestcase } from './tree/AddTestcase'; import { SelectionManager } from './tree/SelectionManager'; import { GlobalSettings, SubtaskSettings } from './tree/SubtaskSettings'; interface TestcasesDndItem { cases: TestCaseConfig[]; subtaskId: number; } export function SubtaskNode(props: { subtaskId: number }) { const { subtaskId } = props; const subtaskIds = useSelector((s: RootState) => Object.values(s.config?.subtasks || []).map((i) => i.id), isEqual); const cases = useSelector((state: RootState) => (subtaskId === -1 ? state.config.__cases : state.config.subtasks.find((i) => i.id === subtaskId).cases || [])); const time = useSelector((s: RootState) => s.config?.time); const memory = useSelector((s: RootState) => s.config?.memory); const dispatch = useDispatch(); const [expand, setExpand] = React.useState(true); const [, drop] = useDrop(() => ({ accept: 'cases', canDrop(item) { return item.subtaskId !== subtaskId; }, drop(item) { dispatch({ type: 'problemconfig/moveTestcases', payload: { target: subtaskId, source: item.subtaskId, cases: item.cases, }, }); }, })); function deleteSubtask() { dispatch({ type: 'problemconfig/deleteSubtask', id: subtaskId, }); } return (
{subtaskId !== -1 && (
setExpand((e) => !e)}> {expand ? : } {i18n('Subtask {0}', subtaskId)}
)}
{subtaskId !== -1 && expand && ( )} {expand ? :
{cases.length} testcases.
} {!cases.length && (
{subtaskId === -1 ? i18n('No testcase here') : i18n('Drag and drop testcases here:')}
)}
); } export function SubtaskConfigTree() { const ids = useSelector((s: RootState) => Object.values(s.config?.subtasks || []).map((i) => i.id), isEqual); const dispatch = useDispatch(); const store = useStore(); const autoConfigure = React.useCallback(() => { const state = store.getState(); const subtasks = readSubtasksFromFiles(state.testdata.map((t) => t.name), state.config); const cases = subtasks.reduce((a, b) => a.concat(b.cases), []); dispatch({ type: 'CONFIG_AUTOCASES_UPDATE', subtasks: normalizeSubtasks(subtasks, (i) => i, state.config.time, state.config.memory, true), }); dispatch({ type: 'problemconfig/delTestcases', cases, }); }, [dispatch, store]); const rootNodes = React.useMemo(() => [ { id: 'auto', label: i18n('Auto configure'), type: 'action' as const }, { id: 'global', label: i18n('Global settings'), type: 'global' as const }, ...ids.map((id) => ({ id: `sub-${id}`, label: i18n('Subtask {0}', id), type: 'subtask' as const, subtaskId: id })), { id: 'add', label: i18n('Add new subtask'), type: 'add' as const }, ], [ids]); return ( { const n: any = node; if (n.type === 'action') { return
{i18n('Auto configure')}
; } if (n.type === 'global') return ; if (n.type === 'add') { return
dispatch({ type: 'problemconfig/addSubtask' })}> {i18n('Add new subtask')}
; } return ; }} /> ); } export function ProblemConfigTree() { return (
); }