Part of forms was updated

main
Boris Sekachev 5 years ago
parent 55f4d95e67
commit 4a0cf6b0f8

@ -4,8 +4,10 @@
import './styles.scss'; import './styles.scss';
import React from 'react'; import React from 'react';
import Menu, { ClickParam } from 'antd/lib/menu'; import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal'; import Modal from 'antd/lib/modal';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import DumpSubmenu from './dump-submenu'; import DumpSubmenu from './dump-submenu';
import LoadSubmenu from './load-submenu'; import LoadSubmenu from './load-submenu';
@ -22,7 +24,7 @@ interface Props {
exportActivities: string[] | null; exportActivities: string[] | null;
inferenceIsActive: boolean; inferenceIsActive: boolean;
onClickMenu: (params: ClickParam, file?: File) => void; onClickMenu: (params: MenuInfo, file?: File) => void;
} }
export enum Actions { export enum Actions {
@ -48,8 +50,8 @@ export default function ActionsMenuComponent(props: Props): JSX.Element {
loadActivity, loadActivity,
} = props; } = props;
let latestParams: ClickParam | null = null; let latestParams: MenuInfo | null = null;
function onClickMenuWrapper(params: ClickParam | null, file?: File): void { function onClickMenuWrapper(params: MenuInfo | null, file?: File): void {
const copyParams = params || latestParams; const copyParams = params || latestParams;
if (!copyParams) { if (!copyParams) {
return; return;
@ -67,7 +69,7 @@ export default function ActionsMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams, file); onClickMenu(copyParams, file);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Update', okText: 'Update',
}); });
@ -83,7 +85,7 @@ export default function ActionsMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams); onClickMenu(copyParams);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Delete', okText: 'Delete',
}); });

@ -7,7 +7,7 @@ import { AnyAction } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Slider, { SliderValue } from 'antd/lib/slider'; import Slider from 'antd/lib/slider';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Collapse from 'antd/lib/collapse'; import Collapse from 'antd/lib/collapse';
@ -42,8 +42,8 @@ interface StateToProps {
interface DispatchToProps { interface DispatchToProps {
collapseAppearance(): void; collapseAppearance(): void;
changeShapesColorBy(event: RadioChangeEvent): void; changeShapesColorBy(event: RadioChangeEvent): void;
changeShapesOpacity(event: SliderValue): void; changeShapesOpacity(value: number): void;
changeSelectedShapesOpacity(event: SliderValue): void; changeSelectedShapesOpacity(value: number): void;
changeShapesOutlinedBorders(outlined: boolean, color: string): void; changeShapesOutlinedBorders(outlined: boolean, color: string): void;
changeShowBitmap(event: CheckboxChangeEvent): void; changeShowBitmap(event: CheckboxChangeEvent): void;
changeShowProjections(event: CheckboxChangeEvent): void; changeShowProjections(event: CheckboxChangeEvent): void;
@ -107,11 +107,11 @@ function mapDispatchToProps(dispatch: Dispatch<AnyAction>): DispatchToProps {
changeShapesColorBy(event: RadioChangeEvent): void { changeShapesColorBy(event: RadioChangeEvent): void {
dispatch(changeShapesColorByAction(event.target.value)); dispatch(changeShapesColorByAction(event.target.value));
}, },
changeShapesOpacity(value: SliderValue): void { changeShapesOpacity(value: number): void {
dispatch(changeShapesOpacityAction(value as number)); dispatch(changeShapesOpacityAction(value));
}, },
changeSelectedShapesOpacity(value: SliderValue): void { changeSelectedShapesOpacity(value: number): void {
dispatch(changeSelectedShapesOpacityAction(value as number)); dispatch(changeSelectedShapesOpacityAction(value));
}, },
changeShapesOutlinedBorders(outlined: boolean, color: string): void { changeShapesOutlinedBorders(outlined: boolean, color: string): void {
dispatch(changeShapesOutlinedBordersAction(outlined, color)); dispatch(changeShapesOutlinedBordersAction(outlined, color));

@ -4,7 +4,9 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Menu, { ClickParam } from 'antd/lib/menu'; import Menu from 'antd/lib/menu';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item'; import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
@ -27,7 +29,7 @@ interface ReviewContextMenuProps {
top: number; top: number;
left: number; left: number;
latestComments: string[]; latestComments: string[];
onClick: (param: ClickParam) => void; onClick: (param: MenuInfo) => void;
} }
enum ReviewContextMenuKeys { enum ReviewContextMenuKeys {
@ -95,7 +97,7 @@ export default function CanvasContextMenu(props: Props): JSX.Element | null {
top={top} top={top}
left={left} left={left}
latestComments={latestComments} latestComments={latestComments}
onClick={(param: ClickParam) => { onClick={(param: MenuInfo) => {
const [state] = objectStates.filter( const [state] = objectStates.filter(
(_state: any): boolean => _state.clientID === contextMenuClientID, (_state: any): boolean => _state.clientID === contextMenuClientID,
); );

@ -129,9 +129,9 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
</Col> </Col>
<Col span={10}> <Col span={10}>
<InputNumber <InputNumber
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangePoints(Math.floor(clamp(value, minimumPoints, Number.MAX_SAFE_INTEGER))); onChangePoints(Math.floor(clamp(+value, minimumPoints, Number.MAX_SAFE_INTEGER)));
} else if (!value) { } else if (!value) {
onChangePoints(undefined); onChangePoints(undefined);
} }

@ -531,10 +531,10 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
min={1} min={1}
precision={0} precision={0}
max={jobInstance.stopFrame - frame} max={jobInstance.stopFrame - frame}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value !== 'undefined') { if (typeof value !== 'undefined') {
this.setState({ this.setState({
trackingFrames: value, trackingFrames: +value,
}); });
} }
}} }}

@ -134,9 +134,9 @@ function ItemAttributeComponent(props: Props): JSX.Element {
<InputNumber <InputNumber
disabled={readonly} disabled={readonly}
size='small' size='small'
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
changeAttribute(attrID, `${clamp(value, min, max)}`); changeAttribute(attrID, `${clamp(+value, min, max)}`);
} }
}} }}
value={+attrValue} value={+attrValue}

@ -52,10 +52,10 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element {
size='small' size='small'
min={minPropagateFrames} min={minPropagateFrames}
value={propagateFrames} value={propagateFrames}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
changePropagateFrames( changePropagateFrames(
Math.floor(clamp(value, minPropagateFrames, Number.MAX_SAFE_INTEGER)), Math.floor(clamp(+value, minPropagateFrames, Number.MAX_SAFE_INTEGER)),
); );
} }
}} }}
@ -67,9 +67,9 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element {
value={propagateUpToFrame} value={propagateUpToFrame}
min={frameNumber + 1} min={frameNumber + 1}
max={stopFrame} max={stopFrame}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
changeUpToFrame(Math.floor(clamp(value, frameNumber + 1, stopFrame))); changeUpToFrame(Math.floor(clamp(+value, frameNumber + 1, stopFrame)));
} }
}} }}
/> />

@ -3,8 +3,10 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Menu, { ClickParam } from 'antd/lib/menu'; import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal'; import Modal from 'antd/lib/modal';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import DumpSubmenu from 'components/actions-menu/dump-submenu'; import DumpSubmenu from 'components/actions-menu/dump-submenu';
import LoadSubmenu from 'components/actions-menu/load-submenu'; import LoadSubmenu from 'components/actions-menu/load-submenu';
@ -19,7 +21,7 @@ interface Props {
exportActivities: string[] | null; exportActivities: string[] | null;
isReviewer: boolean; isReviewer: boolean;
jobInstance: any; jobInstance: any;
onClickMenu(params: ClickParam, file?: File): void; onClickMenu(params: MenuInfo, file?: File): void;
setForceExitAnnotationFlag(forceExit: boolean): void; setForceExitAnnotationFlag(forceExit: boolean): void;
saveAnnotations(jobInstance: any, afterSave?: () => void): void; saveAnnotations(jobInstance: any, afterSave?: () => void): void;
} }
@ -54,15 +56,15 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
const jobStatus = jobInstance.status; const jobStatus = jobInstance.status;
const taskID = jobInstance.task.id; const taskID = jobInstance.task.id;
let latestParams: ClickParam | null = null; let latestParams: MenuInfo | null = null;
function onClickMenuWrapper(params: ClickParam | null, file?: File): void { function onClickMenuWrapper(params: MenuInfo | null, file?: File): void {
const copyParams = params || latestParams; const copyParams = params || latestParams;
if (!copyParams) { if (!copyParams) {
return; return;
} }
latestParams = params; latestParams = params;
function checkUnsavedChanges(_copyParams: ClickParam): void { function checkUnsavedChanges(_copyParams: MenuInfo): void {
if (jobInstance.annotations.hasUnsavedChanges()) { if (jobInstance.annotations.hasUnsavedChanges()) {
Modal.confirm({ Modal.confirm({
title: 'The job has unsaved annotations', title: 'The job has unsaved annotations',
@ -100,7 +102,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams, file); onClickMenu(copyParams, file);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Update', okText: 'Update',
}); });
@ -118,7 +120,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams); onClickMenu(copyParams);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Delete', okText: 'Delete',
}); });

@ -6,9 +6,10 @@ import React, { useState, useEffect } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import { LinkOutlined } from '@ant-design/icons'; import { LinkOutlined } from '@ant-design/icons';
import Slider, { SliderValue } from 'antd/lib/slider'; import Slider from 'antd/lib/slider';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import InputNumber from 'antd/lib/input-number'; import InputNumber from 'antd/lib/input-number';
import Input from 'antd/lib/input';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { clamp } from 'utils/math'; import { clamp } from 'utils/math';
@ -19,8 +20,8 @@ interface Props {
frameNumber: number; frameNumber: number;
frameFilename: string; frameFilename: string;
focusFrameInputShortcut: string; focusFrameInputShortcut: string;
inputFrameRef: React.RefObject<InputNumber>; inputFrameRef: React.RefObject<Input>;
onSliderChange(value: SliderValue): void; onSliderChange(value: number): void;
onInputChange(value: number): void; onInputChange(value: number): void;
onURLIconClick(): void; onURLIconClick(): void;
} }
@ -76,12 +77,13 @@ function PlayerNavigation(props: Props): JSX.Element {
<Col> <Col>
<Tooltip title={`Press ${focusFrameInputShortcut} to focus here`} mouseLeaveDelay={0}> <Tooltip title={`Press ${focusFrameInputShortcut} to focus here`} mouseLeaveDelay={0}>
<InputNumber <InputNumber
ref={inputFrameRef}
className='cvat-player-frame-selector' className='cvat-player-frame-selector'
type='number' type='number'
value={frameInputValue} value={frameInputValue}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof (value) !== 'undefined') {
setFrameInputValue(Math.floor(clamp(value, startFrame, stopFrame))); setFrameInputValue(Math.floor(clamp(+value, startFrame, stopFrame)));
} }
}} }}
onBlur={() => { onBlur={() => {
@ -90,7 +92,6 @@ function PlayerNavigation(props: Props): JSX.Element {
onPressEnter={() => { onPressEnter={() => {
onInputChange(frameInputValue); onInputChange(frameInputValue);
}} }}
ref={inputFrameRef}
/> />
</Tooltip> </Tooltip>
</Col> </Col>

@ -5,8 +5,7 @@
import React from 'react'; import React from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import InputNumber from 'antd/lib/input-number'; import Input from 'antd/lib/input';
import { SliderValue } from 'antd/lib/slider';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
import LeftGroup from './left-group'; import LeftGroup from './left-group';
@ -20,7 +19,7 @@ interface Props {
savingStatuses: string[]; savingStatuses: string[];
frameNumber: number; frameNumber: number;
frameFilename: string; frameFilename: string;
inputFrameRef: React.RefObject<InputNumber>; inputFrameRef: React.RefObject<Input>;
startFrame: number; startFrame: number;
stopFrame: number; stopFrame: number;
undoAction?: string; undoAction?: string;
@ -49,7 +48,7 @@ interface Props {
onLastFrame(): void; onLastFrame(): void;
setPrevButtonType(type: 'regular' | 'filtered' | 'empty'): void; setPrevButtonType(type: 'regular' | 'filtered' | 'empty'): void;
setNextButtonType(type: 'regular' | 'filtered' | 'empty'): void; setNextButtonType(type: 'regular' | 'filtered' | 'empty'): void;
onSliderChange(value: SliderValue): void; onSliderChange(value: number): void;
onInputChange(value: number): void; onInputChange(value: number): void;
onURLIconClick(): void; onURLIconClick(): void;
onUndoClick(): void; onUndoClick(): void;

@ -3,12 +3,12 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import { LockOutlined } from '@ant-design/icons'; import { LockOutlined } from '@ant-design/icons';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import patterns from 'utils/validation-patterns'; import { validateConfirmation, validatePassword } from 'components/register-page/register-form';
export interface ChangePasswordData { export interface ChangePasswordData {
oldPassword: string; oldPassword: string;
@ -16,159 +16,79 @@ export interface ChangePasswordData {
newPassword2: string; newPassword2: string;
} }
type ChangePasswordFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(loginData: ChangePasswordData): void; onSubmit(loginData: ChangePasswordData): void;
} & FormComponentProps; }
class ChangePasswordFormComponent extends React.PureComponent<ChangePasswordFormProps> {
private validateConfirmation = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (value && value !== form.getFieldValue('newPassword1')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
};
private validatePassword = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (!patterns.validatePasswordLength.pattern.test(value)) {
callback(patterns.validatePasswordLength.message);
}
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
callback(patterns.passwordContainsNumericCharacters.message);
}
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsUpperCaseCharacter.message);
}
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsLowerCaseCharacter.message);
}
if (value) {
form.validateFields(['newPassword2'], { force: true });
}
callback();
};
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit } = this.props;
form.validateFields((error, values): void => {
if (!error) {
const validatedFields = {
...values,
confirmations: [],
};
onSubmit(validatedFields);
}
});
};
private renderOldPasswordField(): JSX.Element {
const { form } = this.props;
return ( function ChangePasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element {
<Form.Item hasFeedback> return (
{form.getFieldDecorator('oldPassword', { <Form onFinish={onSubmit} className='change-password-form'>
rules: [ <Form.Item
{ hasFeedback
required: true, name='oldPassword'
message: 'Please input your current password!', rules={[
}, {
], required: true,
})( message: 'Please input your current password!',
<Input.Password },
autoComplete='new-password' ]}
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} >
placeholder='Current password' <Input.Password
/>, autoComplete='current-password'
)} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Current password'
/>
</Form.Item> </Form.Item>
);
}
private renderNewPasswordField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('newPassword1', { name='newPassword1'
rules: [ rules={[
{ {
required: true, required: true,
message: 'Please input new password!', message: 'Please input new password!',
}, }, validatePassword,
{ ]}
validator: this.validatePassword, >
}, <Input.Password
], autoComplete='new-password'
})( prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
<Input.Password placeholder='New password'
autoComplete='new-password' />
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='New password'
/>,
)}
</Form.Item> </Form.Item>
);
}
private renderNewPasswordConfirmationField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
name='newPassword2'
return ( dependencies={['newPassword1']}
<Form.Item hasFeedback> rules={[
{form.getFieldDecorator('newPassword2', { {
rules: [ required: true,
{ message: 'Please confirm your new password!',
required: true, }, validateConfirmation('newPassword1'),
message: 'Please confirm your new password!', ]}
}, >
{ <Input.Password
validator: this.validateConfirmation, autoComplete='new-password'
}, prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
], placeholder='Confirm new password'
})( />
<Input.Password
autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Confirm new password'
/>,
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return ( <Form.Item>
<Form onSubmit={this.handleSubmit} className='change-password-form'> <Button
{this.renderOldPasswordField()} type='primary'
{this.renderNewPasswordField()} htmlType='submit'
{this.renderNewPasswordConfirmationField()} className='change-password-form-button'
loading={fetching}
<Form.Item> disabled={fetching}
<Button >
type='primary' Submit
htmlType='submit' </Button>
className='change-password-form-button' </Form.Item>
loading={fetching} </Form>
disabled={fetching} );
>
Submit
</Button>
</Form.Item>
</Form>
);
}
} }
export default Form.create<ChangePasswordFormProps>()(ChangePasswordFormComponent); export default React.memo(ChangePasswordFormComponent);

@ -149,6 +149,7 @@ export default class FileManager extends React.PureComponent<Props, State> {
className='cvat-share-tree' className='cvat-share-tree'
checkable checkable
showLine showLine
height={256}
checkStrictly={false} checkStrictly={false}
expandedKeys={expandedKeys} expandedKeys={expandedKeys}
checkedKeys={files.share} checkedKeys={files.share}

@ -5,10 +5,7 @@
@import '../../base.scss'; @import '../../base.scss';
.cvat-share-tree { .cvat-share-tree {
height: fit-content; height: 32 * $grid-unit-size;
min-height: 10em;
max-height: 20em;
overflow: auto;
} }
.cvat-empty-share-tree { .cvat-empty-share-tree {

@ -89,9 +89,9 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
min={minFrameStep} min={minFrameStep}
max={maxFrameStep} max={maxFrameStep}
value={frameStep} value={frameStep}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeFrameStep(Math.floor(clamp(value, minFrameStep, maxFrameStep))); onChangeFrameStep(Math.floor(clamp(+value, minFrameStep, maxFrameStep)));
} }
}} }}
/> />
@ -174,9 +174,9 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
max={maxGridSize} max={maxGridSize}
value={gridSize} value={gridSize}
disabled={!grid} disabled={!grid}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeGridSize(Math.floor(clamp(value, minGridSize, maxGridSize))); onChangeGridSize(Math.floor(clamp(+value, minGridSize, maxGridSize)));
} }
}} }}
/> />

@ -70,10 +70,10 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
max={maxAutoSaveInterval} max={maxAutoSaveInterval}
step={1} step={1}
value={Math.round(autoSaveInterval / (60 * 1000))} value={Math.round(autoSaveInterval / (60 * 1000))}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeAutoSaveInterval( onChangeAutoSaveInterval(
Math.floor(clamp(value, minAutoSaveInterval, maxAutoSaveInterval)) * 60 * 1000, Math.floor(clamp(+value, minAutoSaveInterval, maxAutoSaveInterval)) * 60 * 1000,
); );
} }
}} }}
@ -112,7 +112,8 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Col> <Col>
<Text type='secondary'> <Text type='secondary'>
{' '} {' '}
Show text for an object on the canvas not only when the object is activated{' '} Show text for an object on the canvas not only when the object is activated
{' '}
</Text> </Text>
</Col> </Col>
</Row> </Row>
@ -131,7 +132,8 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Col> <Col>
<Text type='secondary'> <Text type='secondary'>
{' '} {' '}
Enable automatic bordering for polygons and polylines during drawing/editing{' '} Enable automatic bordering for polygons and polylines during drawing/editing
{' '}
</Text> </Text>
</Col> </Col>
</Row> </Row>
@ -142,9 +144,9 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
min={minAAMMargin} min={minAAMMargin}
max={maxAAMMargin} max={maxAAMMargin}
value={aamZoomMargin} value={aamZoomMargin}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeAAMZoomMargin(Math.floor(clamp(value, minAAMMargin, maxAAMMargin))); onChangeAAMZoomMargin(Math.floor(clamp(+value, minAAMMargin, maxAAMMargin)));
} }
}} }}
/> />

@ -3,9 +3,11 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Select, { OptionProps, SelectProps } from 'antd/lib/select'; import Select, { SelectProps } from 'antd/lib/select';
// eslint-disable-next-line import/no-extraneous-dependencies
import { OptionData, OptionGroupData } from 'rc-select/lib/interface';
interface Props extends SelectProps { interface Props extends SelectProps<string> {
labels: any[]; labels: any[];
value: any | number | null; value: any | number | null;
onChange: (label: any) => void; onChange: (label: any) => void;
@ -26,10 +28,12 @@ export default function LabelSelector(props: Props): JSX.Element {
{...rest} {...rest}
{...dinamicProps} {...dinamicProps}
showSearch showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => { filterOption={(input: string, option?: OptionData | OptionGroupData) => {
const { children } = option.props; if (option) {
if (typeof children === 'string') { const { children } = option.props;
return children.toLowerCase().includes(input.toLowerCase()); if (typeof children === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
} }
return false; return false;

@ -467,8 +467,8 @@ class LabelForm extends React.PureComponent<Props, {}> {
<Col offset={1}> <Col offset={1}>
<Tooltip title='Do not save the label and return' mouseLeaveDelay={0}> <Tooltip title='Do not save the label and return' mouseLeaveDelay={0}>
<Button <Button
danger
style={{ width: '150px' }} style={{ width: '150px' }}
type='danger'
onClick={(): void => { onClick={(): void => {
onSubmit(null); onSubmit(null);
}} }}

@ -101,8 +101,8 @@ class RawViewer extends React.PureComponent<Props> {
<Col offset={1}> <Col offset={1}>
<Tooltip title='Do not save the label and return' mouseLeaveDelay={0}> <Tooltip title='Do not save the label and return' mouseLeaveDelay={0}>
<Button <Button
danger
style={{ width: '150px' }} style={{ width: '150px' }}
type='danger'
onClick={(): void => { onClick={(): void => {
form.resetFields(); form.resetFields();
}} }}

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import { UserOutlined, LockOutlined } from '@ant-design/icons'; import { UserOutlined, LockOutlined } from '@ant-design/icons';
@ -13,93 +13,63 @@ export interface LoginData {
password: string; password: string;
} }
type LoginFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(loginData: LoginData): void; onSubmit(loginData: LoginData): void;
} & FormComponentProps; }
class LoginFormComponent extends React.PureComponent<LoginFormProps> {
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit } = this.props;
form.validateFields((error, values): void => {
if (!error) {
onSubmit(values);
}
});
};
private renderUsernameField(): JSX.Element {
const { form } = this.props;
const { getFieldDecorator } = form;
return ( function LoginFormComponent(props: Props): JSX.Element {
<Form.Item hasFeedback> const { fetching, onSubmit } = props;
{getFieldDecorator('username', { return (
rules: [ <Form onFinish={onSubmit} className='login-form'>
{ <Form.Item
required: true, hasFeedback
message: 'Please specify a username', name='username'
}, rules={[
], {
})( required: true,
<Input message: 'Please specify a username',
autoComplete='username' },
prefix={<UserOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} ]}
placeholder='Username' >
/>, <Input
)} autoComplete='username'
prefix={<UserOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Username'
/>
</Form.Item> </Form.Item>
);
}
private renderPasswordField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
const { getFieldDecorator } = form; name='password'
rules={[
return ( {
<Form.Item hasFeedback> required: true,
{getFieldDecorator('password', { message: 'Please specify a password',
rules: [ },
{ ]}
required: true, >
message: 'Please specify a password', <Input
}, autoComplete='current-password'
], prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
})( placeholder='Password'
<Input type='password'
autoComplete='current-password' />
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Password'
type='password'
/>,
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='login-form'>
{this.renderUsernameField()}
{this.renderPasswordField()}
<Form.Item> <Form.Item>
<Button <Button
type='primary' type='primary'
loading={fetching} loading={fetching}
disabled={fetching} disabled={fetching}
htmlType='submit' htmlType='submit'
className='login-form-button' className='login-form-button'
> >
Sign in Sign in
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default Form.create<LoginFormProps>()(LoginFormComponent); export default React.memo(LoginFormComponent);

@ -6,7 +6,7 @@ import './styles.scss';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import { CloseCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { CloseCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import Select, { OptionProps } from 'antd/lib/select'; import Select from 'antd/lib/select';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import Tag from 'antd/lib/tag'; import Tag from 'antd/lib/tag';
@ -14,9 +14,12 @@ import Text from 'antd/lib/typography/Text';
import InputNumber from 'antd/lib/input-number'; import InputNumber from 'antd/lib/input-number';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import notification from 'antd/lib/notification'; import notification from 'antd/lib/notification';
// eslint-disable-next-line import/no-extraneous-dependencies
import { OptionData, OptionGroupData } from 'rc-select/lib/interface';
import { Model, StringObject } from 'reducers/interfaces'; import { Model, StringObject } from 'reducers/interfaces';
import { clamp } from 'utils/math';
import consts from 'consts'; import consts from 'consts';
interface Props { interface Props {
@ -95,10 +98,12 @@ function DetectorRunner(props: Props): JSX.Element {
onChange={onChange} onChange={onChange}
style={{ width: '100%' }} style={{ width: '100%' }}
showSearch showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => { filterOption={(input: string, option?: OptionData | OptionGroupData) => {
const { children } = option.props; if (option) {
if (typeof children === 'string') { const { children } = option.props;
return children.toLowerCase().includes(input.toLowerCase()); if (typeof children === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
} }
return false; return false;
@ -106,7 +111,7 @@ function DetectorRunner(props: Props): JSX.Element {
> >
{labels.map( {labels.map(
(label: string): JSX.Element => ( (label: string): JSX.Element => (
<Select.Option key={label}>{label}</Select.Option> <Select.Option value={label} key={label}>{label}</Select.Option>
), ),
)} )}
</Select> </Select>
@ -138,7 +143,7 @@ function DetectorRunner(props: Props): JSX.Element {
> >
{models.map( {models.map(
(_model: Model): JSX.Element => ( (_model: Model): JSX.Element => (
<Select.Option key={_model.id}>{_model.name}</Select.Option> <Select.Option value={_model.id} key={_model.id}>{_model.name}</Select.Option>
), ),
)} )}
</Select> </Select>
@ -176,13 +181,19 @@ function DetectorRunner(props: Props): JSX.Element {
<> <>
<Row type='flex' justify='start' align='middle'> <Row type='flex' justify='start' align='middle'>
<Col span={10}> <Col span={10}>
{renderSelector(match.model || '', 'Model labels', modelLabels, (modelLabel: string) => {renderSelector(
updateMatch(modelLabel, null), match.model || '',
'Model labels',
modelLabels,
(modelLabel: string) => updateMatch(modelLabel, null),
)} )}
</Col> </Col>
<Col span={10} offset={1}> <Col span={10} offset={1}>
{renderSelector(match.task || '', 'Task labels', taskLabels, (taskLabel: string) => {renderSelector(
updateMatch(null, taskLabel), match.task || '',
'Task labels',
taskLabels,
(taskLabel: string) => updateMatch(null, taskLabel),
)} )}
</Col> </Col>
<Col span={1} offset={1}> <Col span={1} offset={1}>
@ -219,9 +230,9 @@ function DetectorRunner(props: Props): JSX.Element {
step={0.01} step={0.01}
max={1} max={1}
value={threshold} value={threshold}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
setThreshold(value); setThreshold(clamp(+value, 0.01, 1));
} }
}} }}
/> />
@ -238,9 +249,9 @@ function DetectorRunner(props: Props): JSX.Element {
placeholder='Threshold' placeholder='Threshold'
min={1} min={1}
value={distance} value={distance}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
setDistance(value); setDistance(+value);
} }
}} }}
/> />
@ -258,12 +269,10 @@ function DetectorRunner(props: Props): JSX.Element {
runInference( runInference(
task, task,
model, model,
model.type === 'detector' model.type === 'detector' ? { mapping, cleanup } : {
? { mapping, cleanup } threshold,
: { max_distance: distance,
threshold, },
max_distance: distance,
},
); );
}} }}
> >

@ -26,7 +26,7 @@ export default function ProjectActionsMenuComponent(props: Props): JSX.Element {
dispatch(deleteProjectAsync(projectInstance)); dispatch(deleteProjectAsync(projectInstance));
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Delete', okText: 'Delete',
}); });

@ -3,8 +3,8 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form';
import { UserAddOutlined, MailOutlined, LockOutlined } from '@ant-design/icons'; import { UserAddOutlined, MailOutlined, LockOutlined } from '@ant-design/icons';
import Form, { RuleRender, RuleObject } from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import Checkbox from 'antd/lib/checkbox'; import Checkbox from 'antd/lib/checkbox';
@ -29,312 +29,240 @@ export interface RegisterData {
confirmations: UserConfirmation[]; confirmations: UserConfirmation[];
} }
type RegisterFormProps = { interface Props {
fetching: boolean; fetching: boolean;
userAgreements: UserAgreement[]; userAgreements: UserAgreement[];
onSubmit(registerData: RegisterData): void; onSubmit(registerData: RegisterData): void;
} & FormComponentProps; }
class RegisterFormComponent extends React.PureComponent<RegisterFormProps> { function validateUsername(_: RuleObject, value: string): Promise<void> {
private validateConfirmation = (rule: any, value: any, callback: any): void => { if (!patterns.validateUsernameLength.pattern.test(value)) {
const { form } = this.props; return Promise.reject(new Error(patterns.validateUsernameLength.message));
if (value && value !== form.getFieldValue('password1')) { }
callback('Two passwords that you enter is inconsistent!');
} else { if (!patterns.validateUsernameCharacters.pattern.test(value)) {
callback(); return Promise.reject(new Error(patterns.validateUsernameCharacters.message));
} }
};
return Promise.resolve();
}
private validatePassword = (_: any, value: any, callback: any): void => { export const validatePassword: RuleRender = (): RuleObject => ({
const { form } = this.props; validator(_: RuleObject, value: string): Promise<void> {
if (!patterns.validatePasswordLength.pattern.test(value)) { if (!patterns.validatePasswordLength.pattern.test(value)) {
callback(patterns.validatePasswordLength.message); return Promise.reject(new Error(patterns.validatePasswordLength.message));
} }
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) { if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
callback(patterns.passwordContainsNumericCharacters.message); return Promise.reject(new Error(patterns.passwordContainsNumericCharacters.message));
} }
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) { if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsUpperCaseCharacter.message); return Promise.reject(new Error(patterns.passwordContainsUpperCaseCharacter.message));
} }
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) { if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsLowerCaseCharacter.message); return Promise.reject(new Error(patterns.passwordContainsLowerCaseCharacter.message));
} }
if (value) { return Promise.resolve();
form.validateFields(['password2'], { force: true }); },
} });
callback();
};
private validateUsername = (_: any, value: any, callback: any): void => {
if (!patterns.validateUsernameLength.pattern.test(value)) {
callback(patterns.validateUsernameLength.message);
}
if (!patterns.validateUsernameCharacters.pattern.test(value)) { export const validateConfirmation: ((firstFieldName: string) => RuleRender) = (
callback(patterns.validateUsernameCharacters.message); firstFieldName: string,
): RuleRender => ({ getFieldValue }): RuleObject => ({
validator(_: RuleObject, value: string): Promise<void> {
if (value && value !== getFieldValue(firstFieldName)) {
return Promise.reject(new Error('Two passwords that you enter is inconsistent!'));
} }
callback(); return Promise.resolve();
}; },
});
private validateAgrement = (agreement: any, value: any, callback: any): void => {
const { userAgreements } = this.props; const validateAgreement: ((userAgreements: UserAgreement[]) => RuleRender) = (
let isValid = true; userAgreements: UserAgreement[],
for (const userAgreement of userAgreements) { ): RuleRender => () => ({
if (agreement.field === userAgreement.name && userAgreement.required && !value) { validator(rule: any, value: boolean): Promise<void> {
isValid = false; const [, name] = rule.field.split(':');
callback(`You must accept the ${userAgreement.displayText} to continue!`); const [agreement] = userAgreements
break; .filter((userAgreement: UserAgreement): boolean => userAgreement.name === name);
} if (agreement.required && !value) {
return Promise.reject(new Error(`You must accept ${agreement.displayText} to continue!`));
} }
if (isValid) {
callback();
}
};
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit, userAgreements } = this.props;
form.validateFields((error, values): void => {
if (!error) {
const validatedFields = {
...values,
confirmations: [],
};
for (const userAgreement of userAgreements) { return Promise.resolve();
validatedFields.confirmations.push({ },
name: userAgreement.name, });
value: validatedFields[userAgreement.name],
}); function RegisterFormComponent(props: Props): JSX.Element {
delete validatedFields[userAgreement.name]; const { fetching, userAgreements, onSubmit } = props;
} return (
<Form
onSubmit(validatedFields); onFinish={(values: Record<string, string | boolean>) => {
} const agreements = Object.keys(values)
}); .filter((key: string):boolean => key.startsWith('agreement:'));
}; const confirmations = agreements
.map((key: string): UserConfirmation => ({ name: key.split(':')[1], value: (values[key] as boolean) }));
private renderFirstNameField(): JSX.Element { const rest = Object.entries(values)
const { form } = this.props; .filter((entry: (string | boolean)[]) => !agreements.includes(entry[0] as string));
return ( onSubmit({
<Form.Item hasFeedback> ...(Object.fromEntries(rest) as any as RegisterData),
{form.getFieldDecorator('firstName', { confirmations,
rules: [ });
{ }}
required: true, className='register-form'
message: 'Please specify a first name', >
pattern: patterns.validateName.pattern, <Row gutter={8}>
}, <Col span={12}>
], <Form.Item
})( hasFeedback
<Input name='firstName'
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} rules={[
placeholder='First name' {
/>, required: true,
)} message: 'Please specify a first name',
</Form.Item> pattern: patterns.validateName.pattern,
); },
} ]}
>
private renderLastNameField(): JSX.Element { <Input
const { form } = this.props; prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='First name'
return ( />
<Form.Item hasFeedback> </Form.Item>
{form.getFieldDecorator('lastName', { </Col>
rules: [ <Col span={12}>
{ <Form.Item
required: true, hasFeedback
message: 'Please specify a last name', name='lastName'
pattern: patterns.validateName.pattern, rules={[
}, {
], required: true,
})( message: 'Please specify a last name',
<Input pattern: patterns.validateName.pattern,
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} },
placeholder='Last name' ]}
/>, >
)} <Input
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Last name'
/>
</Form.Item>
</Col>
</Row>
<Form.Item
hasFeedback
name='username'
rules={[
{
required: true,
message: 'Please specify a username',
},
{
validator: validateUsername,
},
]}
>
<Input
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Username'
/>
</Form.Item> </Form.Item>
);
}
private renderUsernameField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
name='email'
return ( rules={[
<Form.Item hasFeedback> {
{form.getFieldDecorator('username', { type: 'email',
rules: [ message: 'The input is not valid E-mail!',
{ },
required: true, {
message: 'Please specify a username', required: true,
}, message: 'Please specify an email address',
{ },
validator: this.validateUsername, ]}
}, >
], <Input
})( autoComplete='email'
<Input prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} placeholder='Email address'
placeholder='Username' />
/>,
)}
</Form.Item> </Form.Item>
);
}
private renderEmailField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('email', { name='password1'
rules: [ rules={[
{ {
type: 'email', required: true,
message: 'The input is not valid E-mail!', message: 'Please input your password!',
}, }, validatePassword,
{ ]}
required: true, >
message: 'Please specify an email address', <Input.Password
}, autoComplete='new-password'
], prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
})( placeholder='Password'
<Input />
autoComplete='email'
prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Email address'
/>,
)}
</Form.Item> </Form.Item>
);
}
private renderPasswordField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('password1', { name='password2'
rules: [ dependencies={['password1']}
{ rules={[
required: true, {
message: 'Please input your password!', required: true,
}, message: 'Please confirm your password!',
{ }, validateConfirmation('password1'),
validator: this.validatePassword, ]}
}, >
], <Input.Password
})( autoComplete='new-password'
<Input.Password prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
autoComplete='new-password' placeholder='Confirm password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} />
placeholder='Password'
/>,
)}
</Form.Item> </Form.Item>
);
}
private renderPasswordConfirmationField(): JSX.Element {
const { form } = this.props;
return ( {userAgreements.map((userAgreement: UserAgreement): JSX.Element => (
<Form.Item hasFeedback> <Form.Item
{form.getFieldDecorator('password2', { name={`agreement:${userAgreement.name}`}
rules: [ key={userAgreement.name}
initialValue={false}
valuePropName='checked'
rules={[
{ {
required: true, required: true,
message: 'Please confirm your password!', message: 'You must accept to continue!',
}, }, validateAgreement(userAgreements),
{ ]}
validator: this.validateConfirmation, >
}, <Checkbox>
], I read and accept the
})( <a rel='noopener noreferrer' target='_blank' href={userAgreement.url}>
<Input.Password {` ${userAgreement.displayText}`}
autoComplete='new-password' </a>
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} </Checkbox>
placeholder='Confirm password'
/>,
)}
</Form.Item>
);
}
private renderUserAgreements(): JSX.Element[] {
const { form, userAgreements } = this.props;
const getUserAgreementsElements = (): JSX.Element[] => {
const agreementsList: JSX.Element[] = [];
for (const userAgreement of userAgreements) {
agreementsList.push(
<Form.Item key={userAgreement.name}>
{form.getFieldDecorator(userAgreement.name, {
initialValue: false,
valuePropName: 'checked',
rules: [
{
required: true,
message: 'You must accept to continue!',
},
{
validator: this.validateAgrement,
},
],
})(
<Checkbox>
I read and accept the
<a rel='noopener noreferrer' target='_blank' href={userAgreement.url}>
{` ${userAgreement.displayText}`}
</a>
</Checkbox>,
)}
</Form.Item>,
);
}
return agreementsList;
};
return getUserAgreementsElements();
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='login-form'>
<Row gutter={8}>
<Col span={12}>{this.renderFirstNameField()}</Col>
<Col span={12}>{this.renderLastNameField()}</Col>
</Row>
{this.renderUsernameField()}
{this.renderEmailField()}
{this.renderPasswordField()}
{this.renderPasswordConfirmationField()}
{this.renderUserAgreements()}
<Form.Item>
<Button
type='primary'
htmlType='submit'
className='register-form-button'
loading={fetching}
disabled={fetching}
>
Submit
</Button>
</Form.Item> </Form.Item>
</Form> ))}
);
} <Form.Item>
<Button
type='primary'
htmlType='submit'
className='register-form-button'
loading={fetching}
disabled={fetching}
>
Submit
</Button>
</Form.Item>
</Form>
);
} }
export default Form.create<RegisterFormProps>()(RegisterFormComponent); export default React.memo(RegisterFormComponent);

@ -3,14 +3,13 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import { LockOutlined } from '@ant-design/icons'; import { LockOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import patterns from 'utils/validation-patterns'; import { validateConfirmation, validatePassword } from 'components/register-page/register-form';
export interface ResetPasswordConfirmData { export interface ResetPasswordConfirmData {
newPassword1: string; newPassword1: string;
@ -19,140 +18,73 @@ export interface ResetPasswordConfirmData {
token: string; token: string;
} }
type ResetPasswordConfirmFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(resetPasswordConfirmData: ResetPasswordConfirmData): void; onSubmit(resetPasswordConfirmData: ResetPasswordConfirmData): void;
} & FormComponentProps & RouteComponentProps; }
class ResetPasswordConfirmFormComponent extends React.PureComponent<ResetPasswordConfirmFormProps> {
private validateConfirmation = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (value && value !== form.getFieldValue('newPassword1')) {
callback('Passwords do not match!');
} else {
callback();
}
};
private validatePassword = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (!patterns.validatePasswordLength.pattern.test(value)) {
callback(patterns.validatePasswordLength.message);
}
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
callback(patterns.passwordContainsNumericCharacters.message);
}
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsUpperCaseCharacter.message);
}
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsLowerCaseCharacter.message);
}
if (value) {
form.validateFields(['newPassword2'], { force: true });
}
callback();
};
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit, location } = this.props;
const params = new URLSearchParams(location.search);
const uid = params.get('uid');
const token = params.get('token');
form.validateFields((error, values): void => {
if (!error) {
const validatedFields = {
...values,
uid,
token,
};
onSubmit(validatedFields);
}
});
};
private renderNewPasswordField(): JSX.Element {
const { form } = this.props;
return ( function ResetPasswordConfirmFormComponent({ fetching, onSubmit }: Props): JSX.Element {
<Form.Item hasFeedback> const location = useLocation();
{form.getFieldDecorator('newPassword1', { return (
rules: [ <Form
{ onFinish={(values: Record<string, string>): void => {
required: true, const params = new URLSearchParams(location.search);
message: 'Please input new password!', const uid = params.get('uid');
}, const token = params.get('token');
{ if (uid && token) {
validator: this.validatePassword, onSubmit(({ ...values, uid, token } as ResetPasswordConfirmData));
}, }
], }}
})( className='cvat-reset-password-confirm-form'
<Input.Password >
autoComplete='new-password' <Form.Item
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} hasFeedback
placeholder='New password' name='newPassword1'
/>, rules={[
)} {
required: true,
message: 'Please input new password!',
}, validatePassword,
]}
>
<Input.Password
autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='New password'
/>
</Form.Item> </Form.Item>
);
}
private renderNewPasswordConfirmationField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
name='newPassword2'
return ( dependencies={['newPassword1']}
<Form.Item hasFeedback> rules={[
{form.getFieldDecorator('newPassword2', { {
rules: [ required: true,
{ message: 'Please confirm your new password!',
required: true, }, validateConfirmation('newPassword1'),
message: 'Please confirm your new password!', ]}
}, >
{ <Input.Password
validator: this.validateConfirmation, autoComplete='new-password'
}, prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
], placeholder='Confirm new password'
})( />
<Input.Password
autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Confirm new password'
/>,
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return ( <Form.Item>
<Form onSubmit={this.handleSubmit} className='cvat-reset-password-confirm-form'> <Button
{this.renderNewPasswordField()} type='primary'
{this.renderNewPasswordConfirmationField()} htmlType='submit'
className='cvat-reset-password-confirm-form-button'
<Form.Item> loading={fetching}
<Button disabled={fetching}
type='primary' >
htmlType='submit' Change password
className='cvat-reset-password-confirm-form-button' </Button>
loading={fetching} </Form.Item>
disabled={fetching} </Form>
> );
Change password
</Button>
</Form.Item>
</Form>
);
}
} }
export default withRouter(Form.create<ResetPasswordConfirmFormProps>()(ResetPasswordConfirmFormComponent)); export default React.memo(ResetPasswordConfirmFormComponent);

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import { MailOutlined } from '@ant-design/icons'; import { MailOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
@ -12,70 +12,48 @@ export interface ResetPasswordData {
email: string; email: string;
} }
type ResetPasswordFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(resetPasswordData: ResetPasswordData): void; onSubmit(resetPasswordData: ResetPasswordData): void;
} & FormComponentProps; }
class ResetPasswordFormComponent extends React.PureComponent<ResetPasswordFormProps> {
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit } = this.props;
form.validateFields((error, values): void => {
if (!error) {
onSubmit(values);
}
});
};
private renderEmailField(): JSX.Element {
const { form } = this.props;
return ( function ResetPasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element {
<Form.Item hasFeedback> return (
{form.getFieldDecorator('email', { <Form onFinish={onSubmit} className='cvat-reset-password-form'>
rules: [ <Form.Item
{ hasFeedback
type: 'email', name='email'
message: 'The input is not valid E-mail!', rules={[
}, {
{ type: 'email',
required: true, message: 'The input is not valid E-mail!',
message: 'Please specify an email address', },
}, {
], required: true,
})( message: 'Please specify an email address',
<Input },
autoComplete='email' ]}
prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} >
placeholder='Email address' <Input
/>, autoComplete='email'
)} prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Email address'
/>
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='cvat-reset-password-form'>
{this.renderEmailField()}
<Form.Item> <Form.Item>
<Button <Button
type='primary' type='primary'
loading={fetching} loading={fetching}
disabled={fetching} disabled={fetching}
htmlType='submit' htmlType='submit'
className='cvat-reset-password-form-button' className='cvat-reset-password-form-button'
> >
Reset password Reset password
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default Form.create<ResetPasswordFormProps>()(ResetPasswordFormComponent); export default React.memo(ResetPasswordFormComponent);

@ -47,7 +47,9 @@ export default function AutomaticAnnotationProgress(props: Props): JSX.Element |
Modal.confirm({ Modal.confirm({
title: 'You are going to cancel automatic annotation?', title: 'You are going to cancel automatic annotation?',
content: 'Reached progress will be lost. Continue?', content: 'Reached progress will be lost. Continue?',
okType: 'danger', okButtonProps: {
danger: true,
},
onOk() { onOk() {
cancelAutoAnnotation(); cancelAutoAnnotation();
}, },

@ -10,7 +10,8 @@ import { CombinedState } from 'reducers/interfaces';
import { modelsActions } from 'actions/models-actions'; import { modelsActions } from 'actions/models-actions';
import { dumpAnnotationsAsync, loadAnnotationsAsync, exportDatasetAsync, deleteTaskAsync } from 'actions/tasks-actions'; import { dumpAnnotationsAsync, loadAnnotationsAsync, exportDatasetAsync, deleteTaskAsync } from 'actions/tasks-actions';
import { ClickParam } from 'antd/lib/menu'; // eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
interface OwnProps { interface OwnProps {
taskInstance: any; taskInstance: any;
@ -89,7 +90,7 @@ function ActionsMenuContainer(props: OwnProps & StateToProps & DispatchToProps):
openRunModelWindow, openRunModelWindow,
} = props; } = props;
function onClickMenu(params: ClickParam, file?: File): void { function onClickMenu(params: MenuInfo, file?: File): void {
if (params.keyPath.length > 1) { if (params.keyPath.length > 1) {
const [additionalKey, action] = params.keyPath; const [additionalKey, action] = params.keyPath;
if (action === Actions.DUMP_TASK_ANNO) { if (action === Actions.DUMP_TASK_ANNO) {

@ -5,7 +5,8 @@
import React from 'react'; import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router'; import { withRouter, RouteComponentProps } from 'react-router';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ClickParam } from 'antd/lib/menu/index'; // eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import { CombinedState, TaskStatus } from 'reducers/interfaces'; import { CombinedState, TaskStatus } from 'reducers/interfaces';
import AnnotationMenuComponent, { Actions } from 'components/annotation-page/top-bar/annotation-menu'; import AnnotationMenuComponent, { Actions } from 'components/annotation-page/top-bar/annotation-menu';
@ -120,7 +121,7 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
updateJob, updateJob,
} = props; } = props;
const onClickMenu = (params: ClickParam, file?: File): void => { const onClickMenu = (params: MenuInfo, file?: File): void => {
if (params.keyPath.length > 1) { if (params.keyPath.length > 1) {
const [additionalKey, action] = params.keyPath; const [additionalKey, action] = params.keyPath;
if (action === Actions.DUMP_TASK_ANNO) { if (action === Actions.DUMP_TASK_ANNO) {

@ -8,8 +8,7 @@ import { connect } from 'react-redux';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
import { GlobalHotKeys, ExtendedKeyMapOptions } from 'react-hotkeys'; import { GlobalHotKeys, ExtendedKeyMapOptions } from 'react-hotkeys';
import InputNumber from 'antd/lib/input-number'; import Input from 'antd/lib/input';
import { SliderValue } from 'antd/lib/slider';
import { import {
changeFrameAsync, changeFrameAsync,
@ -154,15 +153,13 @@ interface State {
type Props = StateToProps & DispatchToProps & RouteComponentProps; type Props = StateToProps & DispatchToProps & RouteComponentProps;
class AnnotationTopBarContainer extends React.PureComponent<Props, State> { class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
private inputFrameRef: React.RefObject<InputNumber>; private inputFrameRef: React.RefObject<Input>;
private autoSaveInterval: number | undefined; private autoSaveInterval: number | undefined;
private unblock: any; private unblock: any;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.inputFrameRef = React.createRef<InputNumber>(); this.inputFrameRef = React.createRef<Input>();
this.state = { this.state = {
prevButtonType: 'regular', prevButtonType: 'regular',
nextButtonType: 'regular', nextButtonType: 'regular',
@ -408,7 +405,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
onSaveAnnotation(jobInstance); onSaveAnnotation(jobInstance);
}; };
private onChangePlayerSliderValue = (value: SliderValue): void => { private onChangePlayerSliderValue = (value: number): void => {
const { playing, onSwitchPlay } = this.props; const { playing, onSwitchPlay } = this.props;
if (playing) { if (playing) {
onSwitchPlay(false); onSwitchPlay(false);

Loading…
Cancel
Save