Basic & advanced configuration forms

main
Boris Sekachev 5 years ago
parent 4a0cf6b0f8
commit 87f360aa13

@ -2,16 +2,17 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React, { RefObject } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import { PercentageOutlined } from '@ant-design/icons'; import { PercentageOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import Checkbox from 'antd/lib/checkbox'; import Checkbox from 'antd/lib/checkbox';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form, { FormInstance, RuleObject, RuleRender } from 'antd/lib/form';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import patterns from 'utils/validation-patterns'; import patterns from 'utils/validation-patterns';
import { Store } from 'antd/lib/form/interface';
export interface AdvancedConfiguration { export interface AdvancedConfiguration {
bugTracker?: string; bugTracker?: string;
@ -29,273 +30,266 @@ export interface AdvancedConfiguration {
copyData?: boolean; copyData?: boolean;
} }
type Props = FormComponentProps & { const initialValues: AdvancedConfiguration = {
imageQuality: 70,
lfs: false,
useZipChunks: true,
useCache: true,
copyData: false,
};
interface Props {
onSubmit(values: AdvancedConfiguration): void; onSubmit(values: AdvancedConfiguration): void;
installedGit: boolean; installedGit: boolean;
activeFileManagerTab: string; activeFileManagerTab: string;
}; }
function isPositiveInteger(_: any, value: any, callback: any): void { function validateURL(_: RuleObject, value: string): Promise<void> {
if (!value) { if (value && !patterns.validateURL.pattern.test(value)) {
callback(); return Promise.reject(new Error('URL is not a valid URL'));
return;
} }
const intValue = +value; return Promise.resolve();
if (Number.isNaN(intValue) || !Number.isInteger(intValue) || intValue < 1) {
callback('Value must be a positive integer');
}
callback();
} }
function isNonNegativeInteger(_: any, value: any, callback: any): void { function validateRepositoryPath(_: RuleObject, value: string): Promise<void> {
if (!value) { if (value && !patterns.validatePath.pattern.test(value)) {
callback(); return Promise.reject(new Error('Repository path is not a valid path'));
return;
} }
const intValue = +value; return Promise.resolve();
if (Number.isNaN(intValue) || intValue < 0) { }
callback('Value must be a non negative integer');
function validateRepository(_: RuleObject, value: string): Promise<[void, void]> | Promise<void> {
if (value) {
const [url, path] = value.split(/\s+/);
return Promise.all([validateURL(_, url), validateRepositoryPath(_, path)]);
} }
callback(); return Promise.resolve();
} }
function isIntegerRange(min: number, max: number, _: any, value: any, callback: any): void { const isInteger = ({ min, max }: { min?: number, max?: number }) => (
if (!value) { _: RuleObject, value?: number | string,
callback(); ): Promise<void> => {
return; if (typeof value === 'undefined' || value === '') {
return Promise.resolve();
} }
const intValue = +value; const intValue = +value;
if (Number.isNaN(intValue) || !Number.isInteger(intValue) || intValue < min || intValue > max) { if (Number.isNaN(intValue) || !Number.isInteger(intValue)) {
callback(`Value must be an integer [${min}, ${max}]`); return Promise.reject(new Error('Value must be a positive integer'));
} }
callback(); if (typeof min !== 'undefined' && intValue < min) {
} return Promise.reject(new Error(`Value must be more than ${min}`));
}
if (typeof max !== 'undefined' && intValue > max) {
return Promise.reject(new Error(`Value must be less than ${max}`));
}
return Promise.resolve();
};
const validateOverlapSize: RuleRender = ({ getFieldValue }): RuleObject => ({
validator(_: RuleObject, value?: string | number): Promise<void> {
if (typeof value !== 'undefined' && value !== '') {
const segmentSize = getFieldValue('segmentSize');
if (typeof segmentSize !== 'undefined' && segmentSize !== '') {
if (+segmentSize <= +value) {
return Promise.reject(new Error('Segment size must be more than overlap size'));
}
}
}
return Promise.resolve();
},
});
const validateStopFrame: RuleRender = ({ getFieldValue }): RuleObject => ({
validator(_: RuleObject, value?: string | number): Promise<void> {
if (typeof value !== 'undefined' && value !== '') {
const startFrame = getFieldValue('startFrame');
if (typeof startFrame !== 'undefined' && startFrame !== '') {
if (+startFrame > +value) {
return Promise.reject(new Error('Start frame must not be more than stop frame'));
}
}
}
return Promise.resolve();
},
});
class AdvancedConfigurationForm extends React.PureComponent<Props> { class AdvancedConfigurationForm extends React.PureComponent<Props> {
private formRef: RefObject<FormInstance>;
public constructor(props: Props) {
super(props);
this.formRef = React.createRef<FormInstance>();
}
public submit(): Promise<void> { public submit(): Promise<void> {
return new Promise((resolve, reject) => { const { onSubmit } = this.props;
const { form, onSubmit } = this.props; if (this.formRef.current) {
this.formRef.current.resetFields();
form.validateFields((error, values): void => { return this.formRef.current.validateFields().then((values: Store): Promise<void> => {
if (!error) { const frameFilter = values.frameStep ? `step=${values.frameStep}` : undefined;
const filteredValues = { ...values }; const entries = Object.entries(values)
delete filteredValues.frameStep; .filter((entry: [string, unknown]): boolean => entry[0] !== frameFilter);
if (values.overlapSize && +values.segmentSize <= +values.overlapSize) { onSubmit({
reject(new Error('Segment size must be more than overlap size')); ...Object.fromEntries(entries) as any as AdvancedConfiguration,
} frameFilter,
});
if ( return Promise.resolve();
typeof values.startFrame !== 'undefined' &&
typeof values.stopFrame !== 'undefined' &&
+values.stopFrame < +values.startFrame
) {
reject(new Error('Stop frame must be more or equal start frame'));
}
onSubmit({
...values,
frameFilter: values.frameStep ? `step=${values.frameStep}` : undefined,
});
resolve();
} else {
reject();
}
}); });
}); }
return Promise.reject(new Error('Form ref is empty'));
} }
public resetFields(): void { public resetFields(): void {
const { form } = this.props; if (this.formRef.current) {
form.resetFields(); this.formRef.current.resetFields();
}
} }
renderCopyDataChechbox(): JSX.Element { /* eslint-disable class-methods-use-this */
const { form } = this.props; private renderCopyDataChechbox(): JSX.Element {
return ( return (
<Row> <Form.Item
<Col> help='If you have a low data transfer rate over the network you can copy data into CVAT to speed up work'
<Form.Item help='If you have a low data transfer rate over the network you can copy data into CVAT to speed up work'> name='copyData'
{form.getFieldDecorator('copyData', { valuePropName='checked'
initialValue: false, >
valuePropName: 'checked', <Checkbox>
})( <Text className='cvat-text-color'>Copy data into CVAT</Text>
<Checkbox> </Checkbox>
<Text className='cvat-text-color'>Copy data into CVAT</Text> </Form.Item>
</Checkbox>,
)}
</Form.Item>
</Col>
</Row>
); );
} }
private renderImageQuality(): JSX.Element { private renderImageQuality(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Image quality</span>}> <Tooltip title='Defines images compression level' mouseLeaveDelay={0}>
<Tooltip title='Defines image quality level' mouseLeaveDelay={0}> <Form.Item
{form.getFieldDecorator('imageQuality', { label='Image quality'
initialValue: 70, name='imageQuality'
rules: [ rules={[
{ {
required: true, required: true,
message: 'The field is required.', message: 'The field is required.',
}, },
{ { validator: isInteger({ min: 5, max: 100 }) },
validator: isIntegerRange.bind(null, 5, 100), ]}
}, >
], <Input
})(<Input size='large' type='number' suffix={<PercentageOutlined />} />)} size='large'
</Tooltip> type='number'
</Form.Item> min={5}
max={100}
suffix={<PercentageOutlined />}
/>
</Form.Item>
</Tooltip>
); );
} }
private renderOverlap(): JSX.Element { private renderOverlap(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Overlap size</span>}> <Tooltip title='Defines a number of intersected frames between different segments' mouseLeaveDelay={0}>
<Tooltip title='Defines a number of intersected frames between different segments' mouseLeaveDelay={0}> <Form.Item
{form.getFieldDecorator('overlapSize', { label='Overlap size'
rules: [ name='overlapSize'
{ dependencies={['segmentSize']}
validator: isNonNegativeInteger, rules={[{ validator: isInteger({ min: 0 }) }, validateOverlapSize]}
}, >
], <Input size='large' type='number' min={0} />
})(<Input size='large' type='number' />)} </Form.Item>
</Tooltip> </Tooltip>
</Form.Item>
); );
} }
private renderSegmentSize(): JSX.Element { private renderSegmentSize(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Segment size</span>}> <Tooltip title='Defines a number of frames in a segment' mouseLeaveDelay={0}>
<Tooltip title='Defines a number of frames in a segment' mouseLeaveDelay={0}> <Form.Item
{form.getFieldDecorator('segmentSize', { label='Segment size'
rules: [ name='segmentSize'
{ rules={[{ validator: isInteger({ min: 1 }) }]}
validator: isPositiveInteger, >
}, <Input size='large' type='number' min={1} />
], </Form.Item>
})(<Input size='large' type='number' />)} </Tooltip>
</Tooltip>
</Form.Item>
); );
} }
private renderStartFrame(): JSX.Element { private renderStartFrame(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Start frame</span>}> <Form.Item
{form.getFieldDecorator('startFrame', { label='Start frame'
rules: [ name='startFrame'
{ rules={[{ validator: isInteger({ min: 0 }) }]}
validator: isNonNegativeInteger, >
}, <Input size='large' type='number' min={0} step={1} />
],
})(<Input size='large' type='number' min={0} step={1} />)}
</Form.Item> </Form.Item>
); );
} }
private renderStopFrame(): JSX.Element { private renderStopFrame(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Stop frame</span>}> <Form.Item
{form.getFieldDecorator('stopFrame', { label='Stop frame'
rules: [ name='stopFrame'
{ dependencies={['startFrame']}
validator: isNonNegativeInteger, rules={[{ validator: isInteger({ min: 0 }) }, validateStopFrame]}
}, >
], <Input size='large' type='number' min={0} step={1} />
})(<Input size='large' type='number' min={0} step={1} />)}
</Form.Item> </Form.Item>
); );
} }
private renderFrameStep(): JSX.Element { private renderFrameStep(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Frame step</span>}> <Form.Item
{form.getFieldDecorator('frameStep', { label='Frame step'
rules: [ name='frameStep'
{ rules={[{ validator: isInteger({ min: 1 }) }]}
validator: isPositiveInteger, >
}, <Input size='large' type='number' min={1} step={1} />
],
})(<Input size='large' type='number' min={1} step={1} />)}
</Form.Item> </Form.Item>
); );
} }
private renderGitLFSBox(): JSX.Element { private renderGitLFSBox(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item help='If annotation files are large, you can use git LFS feature'> <Form.Item
{form.getFieldDecorator('lfs', { help='If annotation files are large, you can use git LFS feature'
valuePropName: 'checked', name='lfs'
initialValue: false, valuePropName='checked'
})( >
<Checkbox> <Checkbox>
<Text className='cvat-text-color'>Use LFS (Large File Support):</Text> <Text className='cvat-text-color'>Use LFS (Large File Support):</Text>
</Checkbox>, </Checkbox>
)}
</Form.Item> </Form.Item>
); );
} }
private renderGitRepositoryURL(): JSX.Element { private renderGitRepositoryURL(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item <Form.Item
hasFeedback hasFeedback
label={<span>Dataset repository URL</span>} name='repository'
label='Dataset repository URL'
extra='Attach a repository to store annotations there' extra='Attach a repository to store annotations there'
rules={[{ validator: validateRepository }]}
> >
{form.getFieldDecorator('repository', { <Input
rules: [ size='large'
{ placeholder='e.g. https//github.com/user/repos [annotation/<anno_file_name>.zip]'
validator: (_, value, callback): void => { />
if (!value) {
callback();
} else {
const [url, path] = value.split(/\s+/);
if (!patterns.validateURL.pattern.test(url)) {
callback('Git URL is not a valid');
}
if (path && !patterns.validatePath.pattern.test(path)) {
callback('Git path is not a valid');
}
callback();
}
},
},
],
})(
<Input
size='large'
placeholder='e.g. https//github.com/user/repos [annotation/<anno_file_name>.zip]'
/>,
)}
</Form.Item> </Form.Item>
); );
} }
@ -304,124 +298,106 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
return ( return (
<> <>
<Row> <Row>
<Col>{this.renderGitRepositoryURL()}</Col> <Col span={24}>{this.renderGitRepositoryURL()}</Col>
</Row> </Row>
<Row> <Row>
<Col>{this.renderGitLFSBox()}</Col> <Col span={24}>{this.renderGitLFSBox()}</Col>
</Row> </Row>
</> </>
); );
} }
private renderBugTracker(): JSX.Element { private renderBugTracker(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item <Form.Item
hasFeedback hasFeedback
label={<span>Issue tracker</span>} name='bugTracker'
label='Issue tracker'
extra='Attach issue tracker where the task is described' extra='Attach issue tracker where the task is described'
rules={[{ validator: validateURL }]}
> >
{form.getFieldDecorator('bugTracker', { <Input size='large' />
rules: [
{
validator: (_, value, callback): void => {
if (value && !patterns.validateURL.pattern.test(value)) {
callback('Issue tracker must be URL');
} else {
callback();
}
},
},
],
})(<Input size='large' />)}
</Form.Item> </Form.Item>
); );
} }
private renderUzeZipChunks(): JSX.Element { private renderUzeZipChunks(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item help='Force to use zip chunks as compressed data. Actual for videos only.'> <Form.Item
{form.getFieldDecorator('useZipChunks', { help='Force to use zip chunks as compressed data. Actual for videos only.'
initialValue: true, name='useZipChunks'
valuePropName: 'checked', valuePropName='checked'
})( >
<Checkbox> <Checkbox>
<Text className='cvat-text-color'>Use zip chunks</Text> <Text className='cvat-text-color'>Use zip chunks</Text>
</Checkbox>, </Checkbox>
)}
</Form.Item> </Form.Item>
); );
} }
private renderCreateTaskMethod(): JSX.Element { private renderCreateTaskMethod(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item help='Using cache to store data.'> <Form.Item
{form.getFieldDecorator('useCache', { help='Using cache to store data.'
initialValue: true, name='useCache'
valuePropName: 'checked', valuePropName='checked'
})( >
<Checkbox> <Checkbox>
<Text className='cvat-text-color'>Use cache</Text> <Text className='cvat-text-color'>Use cache</Text>
</Checkbox>, </Checkbox>
)}
</Form.Item> </Form.Item>
); );
} }
private renderChunkSize(): JSX.Element { private renderChunkSize(): JSX.Element {
const { form } = this.props;
return ( return (
<Form.Item label={<span>Chunk size</span>}> <Tooltip
<Tooltip title={(
title={( <>
<> Defines a number of frames to be packed in a chunk when send from client to server. Server
Defines a number of frames to be packed in a chunk when send from client to server. Server defines automatically if empty.
defines automatically if empty. <br />
<br /> Recommended values:
Recommended values: <br />
<br /> 1080p or less: 36
1080p or less: 36 <br />
<br /> 2k or less: 8 - 16
2k or less: 8 - 16 <br />
<br /> 4k or less: 4 - 8
4k or less: 4 - 8 <br />
<br /> More: 1 - 4
More: 1 - 4 </>
</> )}
)} mouseLeaveDelay={0}
mouseLeaveDelay={0} >
<Form.Item
label='Chunk size'
name='dataChunkSize'
rules={[{ validator: isInteger({ min: 1 }) }]}
> >
{form.getFieldDecorator('dataChunkSize', { <Input size='large' type='number' />
rules: [ </Form.Item>
{ </Tooltip>
validator: isPositiveInteger,
},
],
})(<Input size='large' type='number' />)}
</Tooltip>
</Form.Item>
); );
} }
public render(): JSX.Element { public render(): JSX.Element {
const { installedGit, activeFileManagerTab } = this.props; const { installedGit, activeFileManagerTab } = this.props;
return ( return (
<Form> <Form initialValues={initialValues} ref={this.formRef} layout='vertical'>
{activeFileManagerTab === 'share' ? (
{activeFileManagerTab === 'share' ? this.renderCopyDataChechbox() : null} <Row>
<Col>
{ this.renderCopyDataChechbox() }
</Col>
</Row>
) : null}
<Row> <Row>
<Col>{this.renderUzeZipChunks()}</Col> <Col>{this.renderUzeZipChunks()}</Col>
</Row> </Row>
<Row> <Row>
<Col>{this.renderCreateTaskMethod()}</Col> <Col>{this.renderCreateTaskMethod()}</Col>
</Row> </Row>
<Row type='flex' justify='start'> <Row type='flex' justify='start'>
<Col span={7}>{this.renderImageQuality()}</Col> <Col span={7}>{this.renderImageQuality()}</Col>
<Col span={7} offset={1}> <Col span={7} offset={1}>
@ -449,11 +425,11 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
{installedGit ? this.renderGit() : null} {installedGit ? this.renderGit() : null}
<Row> <Row>
<Col>{this.renderBugTracker()}</Col> <Col span={24}>{this.renderBugTracker()}</Col>
</Row> </Row>
</Form> </Form>
); );
} }
} }
export default Form.create<Props>()(AdvancedConfigurationForm); export default AdvancedConfigurationForm;

@ -2,60 +2,62 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React, { RefObject } from 'react';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form, { FormInstance } from 'antd/lib/form';
import { Store } from 'antd/lib/form/interface';
export interface BaseConfiguration { export interface BaseConfiguration {
name: string; name: string;
} }
type Props = FormComponentProps & { interface Props {
onSubmit(values: BaseConfiguration): void; onSubmit(values: BaseConfiguration): void;
}; }
export default class BasicConfigurationForm extends React.PureComponent<Props> {
private formRef: RefObject<FormInstance>;
public constructor(props: Props) {
super(props);
this.formRef = React.createRef<FormInstance>();
}
class BasicConfigurationForm extends React.PureComponent<Props> {
public submit(): Promise<void> { public submit(): Promise<void> {
return new Promise((resolve, reject) => { const { onSubmit } = this.props;
const { form, onSubmit } = this.props; if (this.formRef.current) {
return this.formRef.current.validateFields().then((values: Store): Promise<void> => {
form.validateFields((error, values): void => { onSubmit({ name: values.name });
if (!error) { return Promise.resolve();
onSubmit({
name: values.name,
});
resolve();
} else {
reject();
}
}); });
}); }
return Promise.reject(new Error('Form ref is empty'));
} }
public resetFields(): void { public resetFields(): void {
const { form } = this.props; if (this.formRef.current) {
form.resetFields(); this.formRef.current.resetFields();
}
} }
public render(): JSX.Element { public render(): JSX.Element {
const { form } = this.props;
const { getFieldDecorator } = form;
return ( return (
<Form onSubmit={(e: React.FormEvent): void => e.preventDefault()}> <Form ref={this.formRef} layout='vertical'>
<Form.Item hasFeedback label={<span>Name</span>}> <Form.Item
{getFieldDecorator('name', { hasFeedback
rules: [ name='name'
{ label={<span>Name</span>}
required: true, rules={[
message: 'Please, specify a name', {
}, required: true,
], message: 'Task name cannot be empty',
})(<Input />)} },
]}
>
<Input />
</Form.Item> </Form.Item>
</Form> </Form>
); );
} }
} }
export default Form.create<Props>()(BasicConfigurationForm);

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React, { RefObject } from 'react';
import { RouteComponentProps } from 'react-router'; import { RouteComponentProps } from 'react-router';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
@ -58,15 +58,15 @@ const defaultState = {
}; };
class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps, State> { class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps, State> {
private basicConfigurationComponent: any; private basicConfigurationComponent: RefObject<BasicConfigurationForm>;
private advancedConfigurationComponent: RefObject<AdvancedConfigurationForm>;
private advancedConfigurationComponent: any;
private fileManagerContainer: any; private fileManagerContainer: any;
public constructor(props: Props & RouteComponentProps) { public constructor(props: Props & RouteComponentProps) {
super(props); super(props);
this.state = { ...defaultState }; this.state = { ...defaultState };
this.basicConfigurationComponent = React.createRef<BasicConfigurationForm>();
this.advancedConfigurationComponent = React.createRef<AdvancedConfigurationForm>();
} }
public componentDidMount(): void { public componentDidMount(): void {
@ -88,9 +88,11 @@ class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps,
btn, btn,
}); });
this.basicConfigurationComponent.resetFields(); if (this.basicConfigurationComponent.current) {
if (this.advancedConfigurationComponent) { this.basicConfigurationComponent.current.resetFields();
this.advancedConfigurationComponent.resetFields(); }
if (this.advancedConfigurationComponent.current) {
this.advancedConfigurationComponent.current.resetFields();
} }
this.fileManagerContainer.reset(); this.fileManagerContainer.reset();
@ -138,7 +140,7 @@ class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps,
const values = this.state; const values = this.state;
this.setState({ this.setState({
...values, ...values,
activeFileManagerTab: key activeFileManagerTab: key,
}); });
}; };
@ -159,36 +161,34 @@ class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps,
return; return;
} }
this.basicConfigurationComponent if (this.basicConfigurationComponent.current) {
.submit() this.basicConfigurationComponent.current.submit()
.then(() => { .then(() => {
if (this.advancedConfigurationComponent) { if (this.advancedConfigurationComponent.current) {
return this.advancedConfigurationComponent.submit(); return this.advancedConfigurationComponent.current.submit();
} }
return new Promise((resolve): void => { return new Promise((resolve): void => {
resolve(); resolve();
});
}).then((): void => {
const { onCreate } = this.props;
onCreate(this.state);
})
.catch((error: Error): void => {
notification.error({
message: 'Could not create a task',
description: error.toString(),
});
}); });
}) }
.then((): void => {
const { onCreate } = this.props;
onCreate(this.state);
})
.catch((error: Error): void => {
notification.error({
message: 'Could not create a task',
description: error.toString(),
});
});
}; };
private renderBasicBlock(): JSX.Element { private renderBasicBlock(): JSX.Element {
return ( return (
<Col span={24}> <Col span={24}>
<BasicConfigurationForm <BasicConfigurationForm
wrappedComponentRef={(component: any): void => { ref={this.basicConfigurationComponent}
this.basicConfigurationComponent = component;
}}
onSubmit={this.handleSubmitBasicConfiguration} onSubmit={this.handleSubmitBasicConfiguration}
/> />
</Col> </Col>
@ -260,16 +260,15 @@ class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps,
private renderAdvancedBlock(): JSX.Element { private renderAdvancedBlock(): JSX.Element {
const { installedGit } = this.props; const { installedGit } = this.props;
const { activeFileManagerTab } = this.state;
return ( return (
<Col span={24}> <Col span={24}>
<Collapse> <Collapse>
<Collapse.Panel key='1' header={<Text className='cvat-title'>Advanced configuration</Text>}> <Collapse.Panel key='1' header={<Text className='cvat-title'>Advanced configuration</Text>}>
<AdvancedConfigurationForm <AdvancedConfigurationForm
installedGit={installedGit} installedGit={installedGit}
activeFileManagerTab={this.state.activeFileManagerTab} activeFileManagerTab={activeFileManagerTab}
wrappedComponentRef={(component: any): void => { ref={this.advancedConfigurationComponent}
this.advancedConfigurationComponent = component;
}}
onSubmit={this.handleSubmitAdvancedConfiguration} onSubmit={this.handleSubmitAdvancedConfiguration}
/> />
</Collapse.Panel> </Collapse.Panel>
@ -283,7 +282,7 @@ class CreateTaskContent extends React.PureComponent<Props & RouteComponentProps,
const loading = !!status && status !== 'CREATED' && status !== 'FAILED'; const loading = !!status && status !== 'CREATED' && status !== 'FAILED';
return ( return (
<Row type='flex' justify='start' align='middle' className='cvat-create-task-content'> <Row justify='start' align='middle' className='cvat-create-task-content'>
<Col span={24}> <Col span={24}>
<Text className='cvat-title'>Basic configuration</Text> <Text className='cvat-title'>Basic configuration</Text>
</Col> </Col>

Loading…
Cancel
Save