diff --git a/.vscode/settings.json b/.vscode/settings.json index 971c0a063eec8093c58e37dac20ad17942eb3665..f83561156008c49618027509b56b368711e85824 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,8 @@ "eslint.enable": true, "eslint.validate": [ "javascript", - "typescript" + "typescript", + "typescriptreact", ], "eslint.workingDirectories": [ { diff --git a/cvat-ui/.eslintrc.js b/cvat-ui/.eslintrc.js index 48445ed0d2979769dc0a602979a5870ec38ee59d..f40cac1879fee7ca32a3e7aa61f80bb096d8776f 100644 --- a/cvat-ui/.eslintrc.js +++ b/cvat-ui/.eslintrc.js @@ -19,16 +19,23 @@ module.exports = { ], 'extends': [ 'plugin:@typescript-eslint/recommended', - 'airbnb-typescript/base', + 'airbnb-typescript', 'plugin:import/errors', 'plugin:import/warnings', 'plugin:import/typescript', ], 'rules': { '@typescript-eslint/indent': ['warn', 4], + 'react/jsx-indent': ['warn', 4], + 'react/jsx-indent-props': ['warn', 4], + 'react/jsx-props-no-spreading': 0, + 'jsx-quotes': ['error', 'prefer-single'], + 'arrow-parens': ['error', 'always'], '@typescript-eslint/no-explicit-any': [0], 'no-restricted-syntax': [0, {'selector': 'ForOfStatement'}], 'no-plusplus': [0], + 'lines-between-class-members': 0, + 'react/no-did-update-set-state': 0, // https://github.com/airbnb/javascript/issues/1875 }, 'settings': { 'import/resolver': { diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 9422a2bc8456950a20c2303cfffcb222efec02e4..a10d1935690b33aaf1f51f031eb1b357c2159aeb 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -899,6 +899,24 @@ } } }, + "@babel/runtime-corejs3": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.7.4.tgz", + "integrity": "sha512-BBIEhzk8McXDcB3IbOi8zQPzzINUp4zcLesVlBSOcyGhzPUU8Xezk5GAG7Sy5GVhGmAO0zGd2qRSeY2g4Obqxw==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, "@babel/template": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", @@ -1059,27 +1077,39 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz", - "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz", + "integrity": "sha512-rT51fNLW0u3fnDGnAHVC5nu+Das+y2CpW10yqvf6/j5xbuUV3FxA3mBaIbM24CXODXjbgUznNb4Kg9XZOUxKAw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "1.13.0", - "eslint-utils": "^1.3.1", + "@typescript-eslint/experimental-utils": "2.10.0", + "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", - "regexpp": "^2.0.1", - "tsutils": "^3.7.0" + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" } }, "@typescript-eslint/experimental-utils": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", - "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz", + "integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-scope": "^4.0.0" + "@typescript-eslint/typescript-estree": "2.10.0", + "eslint-scope": "^5.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } } }, "@typescript-eslint/parser": { @@ -1092,22 +1122,70 @@ "@typescript-eslint/experimental-utils": "1.13.0", "@typescript-eslint/typescript-estree": "1.13.0", "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", + "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-scope": "^4.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", + "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } } }, "@typescript-eslint/typescript-estree": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", - "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.10.0.tgz", + "integrity": "sha512-oOYnplddQNm/LGVkqbkAwx4TIBuuZ36cAQq9v3nFIU9FmhemHuVzAesMSXNQDdAzCa5bFgCrfD3JWhYVKlRN2g==", "dev": true, "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", "lodash.unescape": "4.0.1", - "semver": "5.5.0" + "semver": "^6.3.0", + "tsutils": "^3.17.1" }, "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -1634,12 +1712,30 @@ "dev": true }, "axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.1.tgz", + "integrity": "sha512-lF98xa/yvy6j3fBHAgQXIYl+J4eZadOSqsPojemUqClzNbBV38wWGpUbQbVEyf4eUF5yF7eHmGgGA2JiHyjeqw==", "dev": true, "requires": { - "ast-types-flow": "0.0.7" + "@babel/runtime": "^7.7.4", + "@babel/runtime-corejs3": "^7.7.4" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", + "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } } }, "babel": { @@ -2580,6 +2676,12 @@ } } }, + "core-js-pure": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.4.7.tgz", + "integrity": "sha512-Am3uRS8WCdTFA3lP7LtKR0PxgqYzjAMGKXaZKSNSC/8sqU0Wfq8R/YzoRs2rqtOVEunfgH+0q3O0BKOg0AvjPw==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3484,6 +3586,12 @@ } } }, + "eslint-plugin-eslint-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-2.1.0.tgz", + "integrity": "sha512-kT3A/ZJftt28gbl/Cv04qezb/NQ1dwYIbi8lyf806XMxkus7DvOVCLIfTXMrorp322Pnoez7+zabXH29tADIDg==", + "dev": true + }, "eslint-plugin-import": { "version": "2.18.2", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", @@ -3538,20 +3646,21 @@ } }, "eslint-plugin-react": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.16.0.tgz", - "integrity": "sha512-GacBAATewhhptbK3/vTP09CbFrgUJmBSaaRcWdbQLFvUZy9yVcQxigBNHGPU/KE2AyHpzj3AWXpxoMTsIDiHug==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.17.0.tgz", + "integrity": "sha512-ODB7yg6lxhBVMeiH1c7E95FLD4E/TwmFjltiU+ethv7KPdCwgiFuOZg9zNRHyufStTDLl/dEFqI2Q1VPmCd78A==", "dev": true, "requires": { "array-includes": "^3.0.3", "doctrine": "^2.1.0", + "eslint-plugin-eslint-plugin": "^2.1.0", "has": "^1.0.3", - "jsx-ast-utils": "^2.2.1", + "jsx-ast-utils": "^2.2.3", "object.entries": "^1.1.0", - "object.fromentries": "^2.0.0", + "object.fromentries": "^2.0.1", "object.values": "^1.1.0", "prop-types": "^15.7.2", - "resolve": "^1.12.0" + "resolve": "^1.13.1" }, "dependencies": { "doctrine": { @@ -3562,9 +3671,24 @@ "requires": { "esutils": "^2.0.2" } + }, + "resolve": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", + "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } } } }, + "eslint-plugin-react-hooks": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz", + "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==", + "dev": true + }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -8141,9 +8265,9 @@ } }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", "dev": true }, "regexpu-core": { @@ -9444,9 +9568,9 @@ "dev": true }, "typescript": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", - "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", "dev": true }, "ua-parser-js": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 50d78f82c35bae348767df44a21b3e46f0187c1b..59b3196f25292472ea51deffd4b0f0e228950e6a 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -14,7 +14,7 @@ "@babel/preset-env": "^7.6.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.6.0", - "@typescript-eslint/eslint-plugin": "^1.13.0", + "@typescript-eslint/eslint-plugin": "^2.10.0", "babel": "^6.23.0", "babel-loader": "^8.0.6", "babel-plugin-import": "^1.12.2", @@ -22,11 +22,12 @@ "eslint-config-airbnb-typescript": "^4.0.1", "eslint-plugin-import": "^2.18.2", "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-react": "^7.14.3", + "eslint-plugin-react": "^7.17.0", + "eslint-plugin-react-hooks": "^1.7.0", "html-webpack-plugin": "^3.2.0", "nodemon": "^1.19.2", "style-loader": "^1.0.0", - "typescript": "^3.6.3", + "typescript": "^3.7.3", "webpack": "^4.41.2", "webpack-cli": "^3.3.8", "webpack-dev-server": "^3.8.0" diff --git a/cvat-ui/src/actions/models-actions.ts b/cvat-ui/src/actions/models-actions.ts index 50fc54a9ad8c07142772d3cec4691ba2e97b9360..e8b60da56096ee35f6d06e081116cbea0107a1f9 100644 --- a/cvat-ui/src/actions/models-actions.ts +++ b/cvat-ui/src/actions/models-actions.ts @@ -431,7 +431,7 @@ async function timeoutCallback( dispatch(getInferenceStatusSuccess(taskID, activeInference)); } catch (error) { dispatch(getInferenceStatusFailed(taskID, new Error( - `Server request for the task ${taskID} was failed` + `Server request for the task ${taskID} was failed`, ))); } } diff --git a/cvat-ui/src/components/actions-menu/actions-menu.tsx b/cvat-ui/src/components/actions-menu/actions-menu.tsx index f6057cb8574b6fd75cea60f96fd1a84b346af583..a9be862bba44be41bb920ecd40abb91d3bf18a51 100644 --- a/cvat-ui/src/components/actions-menu/actions-menu.tsx +++ b/cvat-ui/src/components/actions-menu/actions-menu.tsx @@ -36,14 +36,16 @@ interface MinActionsMenuProps { onOpenRunWindow: (taskInstance: any) => void; } -export function handleMenuClick(props: MinActionsMenuProps, params: ClickParam) { +export function handleMenuClick(props: MinActionsMenuProps, params: ClickParam): void { const { taskInstance } = props; const tracker = taskInstance.bugTracker; if (params.keyPath.length !== 2) { switch (params.key) { case 'tracker': { - window.open(`${tracker}`, '_blank') + // false positive eslint(security/detect-non-literal-fs-filename) + // eslint-disable-next-line + window.open(`${tracker}`, '_blank'); return; } case 'auto_annotation': { props.onOpenRunWindow(taskInstance); @@ -57,36 +59,51 @@ export function handleMenuClick(props: MinActionsMenuProps, params: ClickParam) props.onDeleteTask(taskInstance); }, }); - return; + break; } default: { - return; + // do nothing } } } } -export default function ActionsMenuComponent(props: ActionsMenuComponentProps) { - const tracker = props.taskInstance.bugTracker; - const renderModelRunner = props.installedAutoAnnotation || - props.installedTFAnnotation || props.installedTFSegmentation; +export default function ActionsMenuComponent(props: ActionsMenuComponentProps): JSX.Element { + const { + taskInstance, + installedAutoAnnotation, + installedTFAnnotation, + installedTFSegmentation, + dumpers, + loaders, + exporters, + inferenceIsActive, + } = props; + const tracker = taskInstance.bugTracker; + const renderModelRunner = installedAutoAnnotation + || installedTFAnnotation || installedTFSegmentation; return ( - handleMenuClick(props, params) - }> + handleMenuClick(props, params) + } + > { - props.dumpers.map((dumper) => DumperItemComponent({ + dumpers.map((dumper): JSX.Element => DumperItemComponent({ dumper, taskInstance: props.taskInstance, dumpActivity: (props.dumpActivities || []) .filter((_dumper: string) => _dumper === dumper.name)[0] || null, onDumpAnnotation: props.onDumpAnnotation, - } ))} + })) + } { - props.loaders.map((loader) => LoaderItemComponent({ + loaders.map((loader): JSX.Element => LoaderItemComponent({ loader, taskInstance: props.taskInstance, loadActivity: props.loadActivity, @@ -96,7 +113,7 @@ export default function ActionsMenuComponent(props: ActionsMenuComponentProps) { { - props.exporters.map((exporter) => ExportItemComponent({ + exporters.map((exporter): JSX.Element => ExportItemComponent({ exporter, taskInstance: props.taskInstance, exportActivity: (props.exportActivities || []) @@ -107,10 +124,17 @@ export default function ActionsMenuComponent(props: ActionsMenuComponentProps) { {tracker && Open bug tracker} { - renderModelRunner && - Automatic annotation + renderModelRunner + && ( + + Automatic annotation + + ) } -
+
Delete
); diff --git a/cvat-ui/src/components/actions-menu/dumper-item.tsx b/cvat-ui/src/components/actions-menu/dumper-item.tsx index ca862a8ec32755b213c750538b5f22f293e7a4e3..37db1a6c7f8a6791cce17e4474e10c777800fc2f 100644 --- a/cvat-ui/src/components/actions-menu/dumper-item.tsx +++ b/cvat-ui/src/components/actions-menu/dumper-item.tsx @@ -20,25 +20,31 @@ function isDefaultFormat(dumperName: string, taskMode: string): boolean { || (dumperName === 'CVAT XML 1.1 for images' && taskMode === 'annotation'); } -export default function DumperItemComponent(props: DumperItemComponentProps) { - const task = props.taskInstance; - const { mode } = task; +export default function DumperItemComponent(props: DumperItemComponentProps): JSX.Element { + const { + taskInstance, + dumpActivity, + } = props; + const { mode } = taskInstance; const { dumper } = props; - const pending = !!props.dumpActivity; + const pending = !!dumpActivity; return ( - ); } - diff --git a/cvat-ui/src/components/actions-menu/export-item.tsx b/cvat-ui/src/components/actions-menu/export-item.tsx index 157fd495b227ffc1b5e9df50b5415827f6f9b6db..9dba300bdfaa2a90a4ce941ac34b20963aac2867 100644 --- a/cvat-ui/src/components/actions-menu/export-item.tsx +++ b/cvat-ui/src/components/actions-menu/export-item.tsx @@ -15,24 +15,31 @@ interface DumperItemComponentProps { onExportDataset: (task: any, exporter: any) => void; } -export default function DumperItemComponent(props: DumperItemComponentProps) { - const task = props.taskInstance; - const { exporter } = props; - const pending = !!props.exportActivity; +export default function DumperItemComponent(props: DumperItemComponentProps): JSX.Element { + const { + taskInstance, + exporter, + exportActivity, + } = props; + + const pending = !!exportActivity; return ( - ); } - diff --git a/cvat-ui/src/components/actions-menu/loader-item.tsx b/cvat-ui/src/components/actions-menu/loader-item.tsx index f1e4ea46ce2c435a02044048ba418a72d84d2613..5f736c7e870243b818118b2579b03ffb12301c1c 100644 --- a/cvat-ui/src/components/actions-menu/loader-item.tsx +++ b/cvat-ui/src/components/actions-menu/loader-item.tsx @@ -17,12 +17,15 @@ interface LoaderItemComponentProps { onLoadAnnotation: (taskInstance: any, loader: any, file: File) => void; } -export default function LoaderItemComponent(props: LoaderItemComponentProps) { - const { loader } = props; +export default function LoaderItemComponent(props: LoaderItemComponentProps): JSX.Element { + const { + loader, + loadActivity, + } = props; - const loadingWithThisLoader = props.loadActivity - && props.loadActivity === loader.name - ? props.loadActivity : null; + const loadingWithThisLoader = loadActivity + && loadActivity === loader.name + ? loadActivity : null; const pending = !!loadingWithThisLoader; @@ -31,8 +34,8 @@ export default function LoaderItemComponent(props: LoaderItemComponentProps) { { + showUploadList={false} + beforeUpload={(file: RcFile): boolean => { props.onLoadAnnotation( props.taskInstance, loader, @@ -40,13 +43,14 @@ export default function LoaderItemComponent(props: LoaderItemComponentProps) { ); return false; - }}> - ); -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/create-model-page/create-model-content.tsx b/cvat-ui/src/components/create-model-page/create-model-content.tsx index ba40a87050fff16aea125b9da64d8a5c48c34055..228cf9803c646c0011a4c545b1782bbf4e4e2794 100644 --- a/cvat-ui/src/components/create-model-page/create-model-content.tsx +++ b/cvat-ui/src/components/create-model-page/create-model-content.tsx @@ -14,10 +14,10 @@ import { import Text from 'antd/lib/typography/Text'; import CreateModelForm, { - CreateModelForm as WrappedCreateModelForm + CreateModelForm as WrappedCreateModelForm, } from './create-model-form'; import ConnectedFileManager, { - FileManagerContainer + FileManagerContainer, } from '../../containers/file-manager/file-manager'; import { ModelFiles } from '../../reducers/interfaces'; @@ -30,19 +30,32 @@ interface Props { export default class CreateModelContent extends React.PureComponent { private modelForm: WrappedCreateModelForm; private fileManagerContainer: FileManagerContainer; + public constructor(props: Props) { super(props); this.modelForm = null as any as WrappedCreateModelForm; this.fileManagerContainer = null as any as FileManagerContainer; } - private handleSubmitClick = () => { + public componentDidUpdate(prevProps: Props): void { + const { modelCreatingStatus } = this.props; + + if (prevProps.modelCreatingStatus !== 'CREATED' + && modelCreatingStatus === 'CREATED') { + message.success('The model has been uploaded'); + this.modelForm.resetFields(); + this.fileManagerContainer.reset(); + } + } + + private handleSubmitClick = (): void => { + const { createModel } = this.props; this.modelForm.submit() .then((data) => { const { local, share, - } = this.fileManagerContainer.getFiles(); + } = this.fileManagerContainer.getFiles(); const files = local.length ? local : share; const grouppedFiles: ModelFiles = { @@ -65,50 +78,51 @@ export default class CreateModelContent extends React.PureComponent { if (Object.keys(grouppedFiles) .map((key: string) => grouppedFiles[key]) .filter((val) => !!val).length !== 4) { - notification.error({ - message: 'Could not upload a model', - description: 'Please, specify correct files', - }); - } else { - this.props.createModel(data.name, grouppedFiles, data.global); - } + notification.error({ + message: 'Could not upload a model', + description: 'Please, specify correct files', + }); + } else { + createModel(data.name, grouppedFiles, data.global); + } }).catch(() => { notification.error({ message: 'Could not upload a model', description: 'Please, check input fields', }); - }) - } + }); + }; - public componentDidUpdate(prevProps: Props) { - if (prevProps.modelCreatingStatus !== 'CREATED' - && this.props.modelCreatingStatus === 'CREATED') { - message.success('The model has been uploaded'); - this.modelForm.resetFields(); - this.fileManagerContainer.reset(); - } - } - - public render() { - const loading = !!this.props.modelCreatingStatus - && this.props.modelCreatingStatus !== 'CREATED'; - const status = this.props.modelCreatingStatus - && this.props.modelCreatingStatus !== 'CREATED' ? this.props.modelCreatingStatus : ''; + public render(): JSX.Element { + const { + modelCreatingStatus, + } = this.props; + const loading = !!modelCreatingStatus + && modelCreatingStatus !== 'CREATED'; + const status = modelCreatingStatus + && modelCreatingStatus !== 'CREATED' ? modelCreatingStatus : ''; const guideLink = 'https://github.com/opencv/cvat/blob/develop/cvat/apps/auto_annotation/README.md'; return ( - { - window.open(guideLink, '_blank') - }} type='question-circle'/> + { + // false positive + // eslint-disable-next-line + window.open(guideLink, '_blank'); + }} + type='question-circle' + /> this.modelForm = ref + (ref: WrappedCreateModelForm): void => { + this.modelForm = ref; + } } /> @@ -117,13 +131,17 @@ export default class CreateModelContent extends React.PureComponent { Select files: - - this.fileManagerContainer = container - } withRemote={true}/> + { + this.fileManagerContainer = container; + } + } + withRemote={false} + /> - {status && } + {status && } + > + Submit + ); } -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/create-model-page/create-model-form.tsx b/cvat-ui/src/components/create-model-page/create-model-form.tsx index 1a196593346857e493688309537a6c28ee26b366..b33d83b0e756e2a3976674da5921a863286251f1 100644 --- a/cvat-ui/src/components/create-model-page/create-model-form.tsx +++ b/cvat-ui/src/components/create-model-page/create-model-form.tsx @@ -15,13 +15,10 @@ import Text from 'antd/lib/typography/Text'; type Props = FormComponentProps; export class CreateModelForm extends React.PureComponent { - public constructor(props: Props) { - super(props); - } - - public submit(): Promise<{name: string, global: boolean}> { + public submit(): Promise<{name: string; global: boolean}> { + const { form } = this.props; return new Promise((resolve, reject) => { - this.props.form.validateFields((errors, values) => { + form.validateFields((errors, values): void => { if (!errors) { resolve({ name: values.name, @@ -34,15 +31,17 @@ export class CreateModelForm extends React.PureComponent { }); } - public resetFields() { - this.props.form.resetFields(); + public resetFields(): void { + const { form } = this.props; + form.resetFields(); } - public render() { - const { getFieldDecorator } = this.props.form; + public render(): JSX.Element { + const { form } = this.props; + const { getFieldDecorator } = form; return ( -
e.preventDefault()}> + e.preventDefault()}> * @@ -55,7 +54,7 @@ export class CreateModelForm extends React.PureComponent { required: true, message: 'Please, specify a model name', }], - })()} + })()} @@ -64,11 +63,13 @@ export class CreateModelForm extends React.PureComponent { { getFieldDecorator('global', { initialValue: false, valuePropName: 'checked', - })( - - Load globally - - )} + })( + + + Load globally + + , + )} diff --git a/cvat-ui/src/components/create-model-page/create-model-page.tsx b/cvat-ui/src/components/create-model-page/create-model-page.tsx index e58a8ff1c928f4167090bedd3790842c1a01d938..0d9d663dc8460b7f6116898625c68962db3adfda 100644 --- a/cvat-ui/src/components/create-model-page/create-model-page.tsx +++ b/cvat-ui/src/components/create-model-page/create-model-page.tsx @@ -16,17 +16,23 @@ interface Props { modelCreatingStatus: string; } -export default function CreateModelPageComponent(props: Props) { +export default function CreateModelPageComponent(props: Props): JSX.Element { + const { + isAdmin, + modelCreatingStatus, + createModel, + } = props; + return ( Upload a new model ); -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx index 56cc88da846a2c27000ee8d6f611f30d388f5859..c12cf88236d26060ea7b4f0ba2814435c2e8e068 100644 --- a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx +++ b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx @@ -28,19 +28,24 @@ export interface AdvancedConfiguration { } type Props = FormComponentProps & { - onSubmit(values: AdvancedConfiguration): void + onSubmit(values: AdvancedConfiguration): void; installedGit: boolean; }; class AdvancedConfigurationForm extends React.PureComponent { - public submit() { + public submit(): Promise { return new Promise((resolve, reject) => { - this.props.form.validateFields((error, values) => { + const { + form, + onSubmit, + } = this.props; + + form.validateFields((error, values): void => { if (!error) { const filteredValues = { ...values }; delete filteredValues.frameStep; - this.props.onSubmit({ + onSubmit({ ...values, frameFilter: values.frameStep ? `step=${values.frameStep}` : undefined, }); @@ -49,17 +54,19 @@ class AdvancedConfigurationForm extends React.PureComponent { reject(); } }); - }) + }); } - public resetFields() { - this.props.form.resetFields(); + public resetFields(): void { + const { form } = this.props; + form.resetFields(); } - private renderZOrder() { + private renderZOrder(): JSX.Element { + const { form } = this.props; return ( - {this.props.form.getFieldDecorator('zOrder', { + {form.getFieldDecorator('zOrder', { initialValue: false, valuePropName: 'checked', })( @@ -67,21 +74,23 @@ class AdvancedConfigurationForm extends React.PureComponent { Z-order - + , )} ); } - private renderImageQuality() { + private renderImageQuality(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('imageQuality', { + {form.getFieldDecorator('imageQuality', { initialValue: 70, rules: [{ required: true, - message: 'This field is required' + message: 'This field is required', }], })( { type='number' min={5} max={100} - suffix={} - /> + suffix={} + />, )} ); } - private renderOverlap() { + private renderOverlap(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('overlapSize')( - + {form.getFieldDecorator('overlapSize')( + , )} ); } - private renderSegmentSize() { + private renderSegmentSize(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('segmentSize')( - + {form.getFieldDecorator('segmentSize')( + , )} ); } - private renderStartFrame() { + private renderStartFrame(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('startFrame')( + {form.getFieldDecorator('startFrame')( + />, )} ); } - private renderStopFrame() { + private renderStopFrame(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('stopFrame')( + {form.getFieldDecorator('stopFrame')( + />, )} ); } - private renderFrameStep() { + private renderFrameStep(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('frameStep')( + {form.getFieldDecorator('frameStep')( + />, )} ); } - private renderGitLFSBox() { + private renderGitLFSBox(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('lfs', { + {form.getFieldDecorator('lfs', { valuePropName: 'checked', initialValue: false, })( @@ -177,22 +198,24 @@ class AdvancedConfigurationForm extends React.PureComponent { Use LFS (Large File Support): - + , )} ); } - private renderGitRepositoryURL() { + private renderGitRepositoryURL(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('repository', { + {form.getFieldDecorator('repository', { rules: [{ - validator: (_, value, callback) => { + validator: (_, value, callback): void => { if (!value) { callback(); } else { @@ -207,18 +230,19 @@ class AdvancedConfigurationForm extends React.PureComponent { callback(); } - } - }] + }, + }], })( - + />, )} ); } - private renderGit() { + private renderGit(): JSX.Element { return ( <> @@ -235,36 +259,42 @@ class AdvancedConfigurationForm extends React.PureComponent { ); } - private renderBugTracker() { + private renderBugTracker(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('bugTracker', { + {form.getFieldDecorator('bugTracker', { rules: [{ - validator: (_, value, callback) => { + validator: (_, value, callback): void => { if (value && !patterns.validateURL.pattern.test(value)) { callback('Issue tracker must be URL'); } else { callback(); } - } - }] + }, + }], })( - + , )} - ) + ); } - public render() { + public render(): JSX.Element { + const { installedGit } = this.props; + return ( - - {this.renderZOrder()} - + + + {this.renderZOrder()} + + @@ -290,11 +320,11 @@ class AdvancedConfigurationForm extends React.PureComponent { - { this.props.installedGit ? this.renderGit() : null} + { installedGit ? this.renderGit() : null} - {this.renderBugTracker()} + {this.renderBugTracker()} diff --git a/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx b/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx index e0b877a45072c3c104047f2c6e5df63fd7f98b74..7bb56b65c5d21385dc317a03f79edb7a3d1d59ce 100644 --- a/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx +++ b/cvat-ui/src/components/create-task-page/basic-configuration-form.tsx @@ -4,7 +4,6 @@ import { Input, } from 'antd'; -import Text from 'antd/lib/typography/Text'; import Form, { FormComponentProps } from 'antd/lib/form/Form'; export interface BaseConfiguration { @@ -16,11 +15,16 @@ type Props = FormComponentProps & { }; class BasicConfigurationForm extends React.PureComponent { - public submit() { + public submit(): Promise { return new Promise((resolve, reject) => { - this.props.form.validateFields((error, values) => { + const { + form, + onSubmit, + } = this.props; + + form.validateFields((error, values): void => { if (!error) { - this.props.onSubmit({ + onSubmit({ name: values.name, }); resolve(); @@ -28,25 +32,28 @@ class BasicConfigurationForm extends React.PureComponent { reject(); } }); - }) + }); } - public resetFields() { - this.props.form.resetFields(); + public resetFields(): void { + const { form } = this.props; + form.resetFields(); } - public render() { - const { getFieldDecorator } = this.props.form; + public render(): JSX.Element { + const { form } = this.props; + const { getFieldDecorator } = form; + return ( -
e.preventDefault()}> + e.preventDefault()}> { getFieldDecorator('name', { rules: [{ required: true, message: 'Please, specify a name', - }] + }], })( - + , ) }
diff --git a/cvat-ui/src/components/create-task-page/create-task-content.tsx b/cvat-ui/src/components/create-task-page/create-task-content.tsx index 57801a1b8ba87eac9940cd20838caef3f5fc8f22..f7d3347d1512ce7c76eb06e698b6a43869df52ab 100644 --- a/cvat-ui/src/components/create-task-page/create-task-content.tsx +++ b/cvat-ui/src/components/create-task-page/create-task-content.tsx @@ -21,7 +21,7 @@ export interface CreateTaskData { basic: BaseConfiguration; advanced: AdvancedConfiguration; labels: any[]; - files: Files, + files: Files; } interface Props { @@ -58,11 +58,33 @@ export default class CreateTaskContent extends React.PureComponent this.state = { ...defaultState }; } - private validateLabels = () => { - return !!this.state.labels.length; + public componentDidUpdate(prevProps: Props): void { + const { status } = this.props; + + if (status === 'CREATED' && prevProps.status !== 'CREATED') { + notification.info({ + message: 'The task has been created', + }); + + this.basicConfigurationComponent.resetFields(); + if (this.advancedConfigurationComponent) { + this.advancedConfigurationComponent.resetFields(); + } + + this.fileManagerContainer.reset(); + + this.setState({ + ...defaultState, + }); + } } - private validateFiles = () => { + private validateLabels = (): boolean => { + const { labels } = this.state; + return !!labels.length; + }; + + private validateFiles = (): boolean => { const files = this.fileManagerContainer.getFiles(); this.setState({ files, @@ -72,21 +94,21 @@ export default class CreateTaskContent extends React.PureComponent ); return !!totalLen; - } + }; - private handleSubmitBasicConfiguration = (values: BaseConfiguration) => { + private handleSubmitBasicConfiguration = (values: BaseConfiguration): void => { this.setState({ - basic: {...values}, + basic: { ...values }, }); }; - private handleSubmitAdvancedConfiguration = (values: AdvancedConfiguration) => { + private handleSubmitAdvancedConfiguration = (values: AdvancedConfiguration): void => { this.setState({ - advanced: {...values}, + advanced: { ...values }, }); }; - private handleSubmitClick = () => { + private handleSubmitClick = (): void => { if (!this.validateLabels()) { notification.error({ message: 'Could not create a task', @@ -105,44 +127,50 @@ export default class CreateTaskContent extends React.PureComponent this.basicConfigurationComponent.submit() .then(() => { - return this.advancedConfigurationComponent ? - this.advancedConfigurationComponent.submit() : - new Promise((resolve) => { - resolve(); - }) - }) - .then(() => { - this.props.onCreate(this.state); - }) - .catch((_: any) => { + if (this.advancedConfigurationComponent) { + return this.advancedConfigurationComponent.submit(); + } + + return new Promise((resolve): void => { + resolve(); + }); + }).then((): void => { + const { onCreate } = this.props; + onCreate(this.state); + }).catch((): void => { notification.error({ message: 'Could not create a task', description: 'Please, check configuration you specified', }); }); - } + }; - private renderBasicBlock() { + private renderBasicBlock(): JSX.Element { return ( - { this.basicConfigurationComponent = component } - } onSubmit={this.handleSubmitBasicConfiguration}/> + { this.basicConfigurationComponent = component; } + } + onSubmit={this.handleSubmitBasicConfiguration} + /> ); } - private renderLabelsBlock() { + private renderLabelsBlock(): JSX.Element { + const { labels } = this.state; + return ( * Labels: { + (newLabels): void => { this.setState({ - labels, + labels: newLabels, }); } } @@ -151,32 +179,37 @@ export default class CreateTaskContent extends React.PureComponent ); } - private renderFilesBlock() { + private renderFilesBlock(): JSX.Element { return ( * Select files: - - this.fileManagerContainer = container - } withRemote={true}/> + { this.fileManagerContainer = container; } + } + withRemote + /> ); } - private renderAdvancedBlock() { + private renderAdvancedBlock(): JSX.Element { + const { installedGit } = this.props; return ( Advanced configuration - } key='1'> + } + > { - this.advancedConfigurationComponent = component + (component: any): void => { + this.advancedConfigurationComponent = component; } } onSubmit={this.handleSubmitAdvancedConfiguration} @@ -187,29 +220,9 @@ export default class CreateTaskContent extends React.PureComponent ); } - public componentDidUpdate(prevProps: Props) { - if (this.props.status === 'CREATED' && prevProps.status !== 'CREATED') { - notification.info({ - message: 'The task has been created', - }); - - this.basicConfigurationComponent.resetFields(); - if (this.advancedConfigurationComponent) { - this.advancedConfigurationComponent.resetFields(); - } - - this.fileManagerContainer.reset(); - - this.setState({ - ...defaultState, - }); - } - } - - public render() { - const loading = !!this.props.status - && this.props.status !== 'CREATED' - && this.props.status !== 'FAILED'; + public render(): JSX.Element { + const { status } = this.props; + const loading = !!status && status !== 'CREATED' && status !== 'FAILED'; return ( @@ -223,7 +236,7 @@ export default class CreateTaskContent extends React.PureComponent { this.renderAdvancedBlock() } - {loading ? : null} + {loading ? : null} + > + Submit + ); diff --git a/cvat-ui/src/components/create-task-page/create-task-page.tsx b/cvat-ui/src/components/create-task-page/create-task-page.tsx index 2287ab5c51a24c43c474517c2cc0a5738150603c..ad4ae6d723ceb045554542114750494c21104d17 100644 --- a/cvat-ui/src/components/create-task-page/create-task-page.tsx +++ b/cvat-ui/src/components/create-task-page/create-task-page.tsx @@ -15,15 +15,21 @@ interface Props { installedGit: boolean; } -export default function CreateTaskPage(props: Props) { +export default function CreateTaskPage(props: Props): JSX.Element { + const { + status, + onCreate, + installedGit, + } = props; + return ( Create a new task diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index a99e908139ca5a1e4d2062ace9974f7a385330e1..d9dcc8a3821f0988f9b455d96e763aa5d21f81ff 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -46,28 +46,73 @@ type CVATAppProps = { installedTFSegmentation: boolean; notifications: NotificationsState; user: any; -} +}; export default class CVATApplication extends React.PureComponent { - constructor(props: any) { - super(props); + public componentDidMount(): void { + const { verifyAuthorized } = this.props; + verifyAuthorized(); } - private showMessages() { - function showMessage(title: string) { + public componentDidUpdate(): void { + const { + loadFormats, + loadUsers, + initPlugins, + userInitialized, + formatsInitialized, + formatsFetching, + usersInitialized, + usersFetching, + pluginsInitialized, + pluginsFetching, + user, + } = this.props; + + this.showErrors(); + this.showMessages(); + + if (!userInitialized || user == null) { + // not authorized user + return; + } + + if (!formatsInitialized && !formatsFetching) { + loadFormats(); + } + + if (!usersInitialized && !usersFetching) { + loadUsers(); + } + + if (!pluginsInitialized && !pluginsFetching) { + initPlugins(); + } + } + + private showMessages(): void { + function showMessage(title: string): void { notification.info({ message: ( -
+
), duration: null, }); } - const { tasks } = this.props.notifications.messages; - const { models } = this.props.notifications.messages; - let shown = !!tasks.loadingDone || !!models.inferenceDone; + const { + notifications, + resetMessages, + } = this.props; + + const { tasks } = notifications.messages; + const { models } = notifications.messages; + const shown = !!tasks.loadingDone || !!models.inferenceDone; if (tasks.loadingDone) { showMessage(tasks.loadingDone); @@ -77,18 +122,21 @@ export default class CVATApplication extends React.PureComponent { } if (shown) { - this.props.resetMessages(); + resetMessages(); } } - private showErrors() { - function showError(title: string, _error: any) { + private showErrors(): void { + function showError(title: string, _error: any): void { const error = _error.toString(); notification.error({ message: ( -
+
), duration: null, description: error.length > 200 ? '' : error, @@ -97,14 +145,19 @@ export default class CVATApplication extends React.PureComponent { console.error(error); } - const { auth } = this.props.notifications.errors; - const { tasks } = this.props.notifications.errors; - const { formats } = this.props.notifications.errors; - const { users } = this.props.notifications.errors; - const { share } = this.props.notifications.errors; - const { models } = this.props.notifications.errors; + const { + notifications, + resetErrors, + } = this.props; + + const { auth } = notifications.errors; + const { tasks } = notifications.errors; + const { formats } = notifications.errors; + const { users } = notifications.errors; + const { share } = notifications.errors; + const { models } = notifications.errors; - let shown = !!auth.authorized || !!auth.login || !!auth.logout || !!auth.register + const shown = !!auth.authorized || !!auth.login || !!auth.logout || !!auth.register || !!tasks.fetching || !!tasks.updating || !!tasks.dumping || !!tasks.loading || !!tasks.exporting || !!tasks.deleting || !!tasks.creating || !!formats.fetching || !!users.fetching || !!share.fetching || !!models.creating || !!models.starting @@ -169,90 +222,78 @@ export default class CVATApplication extends React.PureComponent { showError(models.metaFetching.message, models.metaFetching.reason); } if (models.inferenceStatusFetching) { - showError(models.inferenceStatusFetching.message, models.inferenceStatusFetching.reason); + showError( + models.inferenceStatusFetching.message, + models.inferenceStatusFetching.reason, + ); } if (shown) { - this.props.resetErrors(); - } - } - - public componentDidMount() { - this.props.verifyAuthorized(); - } - - public componentDidUpdate() { - this.showErrors(); - this.showMessages(); - - if (!this.props.userInitialized || this.props.user == null) { - // not authorized user - return; - } - - if (!this.props.formatsInitialized && !this.props.formatsFetching) { - this.props.loadFormats(); - } - - if (!this.props.usersInitialized && !this.props.usersFetching) { - this.props.loadUsers(); - } - - if (!this.props.pluginsInitialized && !this.props.pluginsFetching) { - this.props.initPlugins(); + resetErrors(); } } // Where you go depends on your URL - public render() { - const readyForRender = - (this.props.userInitialized && this.props.user == null) || - (this.props.userInitialized && this.props.formatsInitialized && - this.props.pluginsInitialized && this.props.usersInitialized); + public render(): JSX.Element { + const { + userInitialized, + usersInitialized, + pluginsInitialized, + formatsInitialized, + installedAutoAnnotation, + installedTFSegmentation, + installedTFAnnotation, + user, + } = this.props; - const withModels = this.props.installedAutoAnnotation - || this.props.installedTFAnnotation || this.props.installedTFSegmentation; + const readyForRender = (userInitialized && user == null) + || (userInitialized && formatsInitialized + && pluginsInitialized && usersInitialized); + + const withModels = installedAutoAnnotation + || installedTFAnnotation || installedTFSegmentation; if (readyForRender) { - if (this.props.user) { + if (user) { return ( - - - - - { withModels && - } - { this.props.installedAutoAnnotation && - } - + + + + + { withModels + && } + { installedAutoAnnotation + && } + - - + + + {/* eslint-disable-next-line */} ); - } else { - return ( - - - - - - - - ); } - } else { + return ( - + + + + + + + ); } + + return ( + + ); } } diff --git a/cvat-ui/src/components/feedback.tsx b/cvat-ui/src/components/feedback.tsx index 475d3ed718845bdffb4136a7c756c08cc23eb62d..ead88caee65c2fba1ed7396e98c61b9b90f96c55 100644 --- a/cvat-ui/src/components/feedback.tsx +++ b/cvat-ui/src/components/feedback.tsx @@ -31,88 +31,87 @@ interface State { active: boolean; } -export default class Feedback extends React.PureComponent<{}, State> { - public constructor(props: {}) { - super(props); - this.state = { - active: false, - } - } +function renderContent(): JSX.Element { + const githubURL = 'https://github.com/opencv/cvat'; + const githubImage = 'https://raw.githubusercontent.com/opencv/' + + 'cvat/develop/cvat/apps/documentation/static/documentation/images/cvat.jpg'; + const questionsURL = 'https://gitter.im/opencv-cvat/public'; + const feedbackURL = 'https://gitter.im/opencv-cvat/public'; - private renderContent() { - const githubURL = 'https://github.com/opencv/cvat'; - const githubImage = 'https://raw.githubusercontent.com/opencv/' - + 'cvat/develop/cvat/apps/documentation/static/documentation/images/cvat.jpg'; - const questionsURL = 'https://gitter.im/opencv-cvat/public'; - const feedbackURL = 'https://gitter.im/opencv-cvat/public'; + return ( + <> + + + Star us on + GitHub + +
+ + + Leave a + feedback + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Do you need help? Contact us on + gitter + + + ); +} - return ( - <> - - - Star us on GitHub - -
- - - Leave a feedback - -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
-
- - Do you need help? Contact us on gitter - - - ); - } +export default function Feedback(): JSX.Element { + const [visible, setVisible] = React.useState(false); - public render() { - return ( - <> - Help to make CVAT better - } - content={this.renderContent()} - visible={this.state.active} + return ( + <> + Help to make CVAT better + } + content={renderContent()} + visible={visible} + > + - - - ); - } -} \ No newline at end of file + { visible ? + : } + + + + ); +} diff --git a/cvat-ui/src/components/file-manager/file-manager.tsx b/cvat-ui/src/components/file-manager/file-manager.tsx index e34aeaba6673039f746fbcf9edb869c1267996e2..1f12d563e6c20a478531e4bde6d21a8b535400ff 100644 --- a/cvat-ui/src/components/file-manager/file-manager.tsx +++ b/cvat-ui/src/components/file-manager/file-manager.tsx @@ -44,35 +44,61 @@ export default class FileManager extends React.PureComponent { }; this.loadData('/'); - }; + } - private loadData = (key: string) => { - const promise = new Promise((resolve, reject) => { - const success = () => resolve(); - const failure = () => reject(); - this.props.onLoadData(key, success, failure); - }); + public getFiles(): Files { + const { + active, + files, + } = this.state; + return { + local: active === 'local' ? files.local : [], + share: active === 'share' ? files.share : [], + remote: active === 'remote' ? files.remote : [], + }; + } + + private loadData = (key: string): Promise => new Promise( + (resolve, reject): void => { + const { onLoadData } = this.props; + + const success = (): void => resolve(); + const failure = (): void => reject(); + onLoadData(key, success, failure); + }, + ); - return promise; + public reset(): void { + this.setState({ + expandedKeys: [], + active: 'local', + files: { + local: [], + share: [], + remote: [], + }, + }); } - private renderLocalSelector() { + private renderLocalSelector(): JSX.Element { + const { files } = this.state; + return ( { + beforeUpload={(_: RcFile, newLocalFiles: RcFile[]): boolean => { this.setState({ files: { - ...this.state.files, - local: files + ...files, + local: newLocalFiles, }, }); return false; - } - }> + }} + >

@@ -81,115 +107,126 @@ export default class FileManager extends React.PureComponent { Support for a bulk images or a single video

- { !!this.state.files.local.length && - <> -
- - {`${this.state.files.local.length} file(s) selected`} - - + { !!files.local.length + && ( + <> +
+ + {`${files.local.length} file(s) selected`} + + + ) }
); } - private renderShareSelector() { - function renderTreeNodes(data: TreeNodeNormal[]) { + private renderShareSelector(): JSX.Element { + function renderTreeNodes(data: TreeNodeNormal[]): JSX.Element[] { return data.map((item: TreeNodeNormal) => { if (item.children) { - return ( - - {renderTreeNodes(item.children)} - - ); + return ( + + {renderTreeNodes(item.children)} + + ); } return ; }); } + const { treeData } = this.props; + const { + expandedKeys, + files, + } = this.state; + return ( - { this.props.treeData.length ? - { - return this.loadData(node.props.dataRef.key); - }} - onExpand={(expandedKeys: string[]) => { - this.setState({ - expandedKeys, - }); - }} - onCheck={(checkedKeys: string[] | {checked: string[], halfChecked: string[]}) => { - const keys = checkedKeys as string[]; - this.setState({ - files: { - ...this.state.files, - share: keys, - }, - }); - }}> - { renderTreeNodes(this.props.treeData) } - : {'No data found'} + { treeData.length + ? ( + => this.loadData( + node.props.dataRef.key, + )} + onExpand={(newExpandedKeys: string[]): void => { + this.setState({ + expandedKeys: newExpandedKeys, + }); + }} + onCheck={ + (checkedKeys: string[] | { + checked: string[]; + halfChecked: string[]; + }): void => { + const keys = checkedKeys as string[]; + this.setState({ + files: { + ...files, + share: keys, + }, + }); + }} + > + { renderTreeNodes(treeData) } + + ) : No data found } ); } - private renderRemoteSelector() { + private renderRemoteSelector(): JSX.Element { + const { files } = this.state; + return ( ) => { + value={[...files.remote].join('\n')} + onChange={(event: React.ChangeEvent): void => { this.setState({ files: { - ...this.state.files, + ...files, remote: event.target.value.split('\n'), }, }); - }}/> + }} + /> ); } - public getFiles(): Files { - return { - local: this.state.active === 'local' ? this.state.files.local : [], - share: this.state.active === 'share' ? this.state.files.share : [], - remote: this.state.active === 'remote' ? this.state.files.remote : [], - }; - } - - public reset() { - this.setState({ - expandedKeys: [], - active: 'local', - files: { - local: [], - share: [], - remote: [], - }, - }); - } + public render(): JSX.Element { + const { withRemote } = this.props; - public render() { return ( <> - this.setState({ - active: activeKey as any, - })}> + this.setState({ + active: activeKey as any, + }) + } + > { this.renderLocalSelector() } { this.renderShareSelector() } - { this.props.withRemote && this.renderRemoteSelector() } + { withRemote && this.renderRemoteSelector() } ); diff --git a/cvat-ui/src/components/header/header.tsx b/cvat-ui/src/components/header/header.tsx index 451d93fd93332e41477cd8b494f508725c2ffc58..707d7de6a48d5b835f62386a962311af248f066b 100644 --- a/cvat-ui/src/components/header/header.tsx +++ b/cvat-ui/src/components/header/header.tsx @@ -15,6 +15,7 @@ import Text from 'antd/lib/typography/Text'; import getCore from '../../core'; const core = getCore(); +const serverHost = core.config.backendAPI.slice(0, -7); interface HeaderContainerProps { onLogout: () => void; @@ -28,66 +29,121 @@ interface HeaderContainerProps { type Props = HeaderContainerProps & RouteComponentProps; -const cvatLogo = () => ; -const userLogo = () => ; +const cvatLogo = (): JSX.Element => ; +const userLogo = (): JSX.Element => ; + +function HeaderContainer(props: Props): JSX.Element { + const { + installedTFSegmentation, + installedAutoAnnotation, + installedTFAnnotation, + installedAnalytics, + username, + onLogout, + logoutFetching, + } = props; + + const renderModels = installedAutoAnnotation + || installedTFAnnotation + || installedTFSegmentation; -function HeaderContainer(props: Props) { - const renderModels = props.installedAutoAnnotation - || props.installedTFAnnotation - || props.installedTFSegmentation; return (
- + - - { renderModels ? - : null + + { renderModels + && ( + + ) } - { props.installedAnalytics ? - : null + { installedAnalytics + && ( + + ) }
- - + } + > + + Help + - - - - - {props.username.length > 14 ? `${props.username.slice(0, 10)} ...` : props.username} - - - - - }> + + + + + {username.length > 14 ? `${username.slice(0, 10)} ...` : username} + + + + + ) + } + > - {props.logoutFetching && } Logout + {logoutFetching && } + Logout diff --git a/cvat-ui/src/components/labels-editor/constructor-creator.tsx b/cvat-ui/src/components/labels-editor/constructor-creator.tsx index f3f189d38ccd2124699550bc770c14e03b3e3cb1..d49c04140aea01bca9ab1e68f95b91c69a43540c 100644 --- a/cvat-ui/src/components/labels-editor/constructor-creator.tsx +++ b/cvat-ui/src/components/labels-editor/constructor-creator.tsx @@ -1,26 +1,17 @@ import React from 'react'; -import LabelForm from './label-form'; -import { Label, Attribute } from './common'; +import LabelForm from './label-form'; +import { Label } from './common'; interface Props { onCreate: (label: Label | null) => void; } -interface State { - attributes: Attribute[]; -} - -export default class ConstructorCreator extends React.PureComponent { - public constructor(props: Props) { - super(props); - } - - public render() { - return ( -
- -
- ); - } +export default function ConstructorCreator(props: Props): JSX.Element { + const { onCreate } = props; + return ( +
+ +
+ ); } diff --git a/cvat-ui/src/components/labels-editor/constructor-updater.tsx b/cvat-ui/src/components/labels-editor/constructor-updater.tsx index c87e3d758ea01d52355971af121f55e7e5f6e943..048b31075c4042d99559881ab7d6fce8b2bbbb70 100644 --- a/cvat-ui/src/components/labels-editor/constructor-updater.tsx +++ b/cvat-ui/src/components/labels-editor/constructor-updater.tsx @@ -1,28 +1,22 @@ import React from 'react'; import LabelForm from './label-form'; -import { Label, Attribute } from './common'; +import { Label } from './common'; interface Props { label: Label; onUpdate: (label: Label | null) => void; } -interface State { - savedAttributes: Attribute[]; - unsavedAttributes: Attribute[]; -} - -export default class ConstructorUpdater extends React.PureComponent { - constructor(props: Props) { - super(props); - } +export default function ConstructorUpdater(props: Props): JSX.Element { + const { + label, + onUpdate, + } = props; - public render() { - return ( -
- -
- ); - } + return ( +
+ +
+ ); } diff --git a/cvat-ui/src/components/labels-editor/constructor-viewer-item.tsx b/cvat-ui/src/components/labels-editor/constructor-viewer-item.tsx index 272b380f30720a7c257e6c245da485ad650333fb..983cf814a3b8d7d9473e75b2f7566759f96acbbb 100644 --- a/cvat-ui/src/components/labels-editor/constructor-viewer-item.tsx +++ b/cvat-ui/src/components/labels-editor/constructor-viewer-item.tsx @@ -16,21 +16,40 @@ interface ConstructorViewerItemProps { onDelete: (label: Label) => void; } -export default function ConstructorViewerItem(props: ConstructorViewerItemProps) { +export default function ConstructorViewerItem(props: ConstructorViewerItemProps): JSX.Element { + const { + color, + label, + onUpdate, + onDelete, + } = props; + return ( -
- { props.label.name } +
+ {label.name} - props.onUpdate(props.label)}> - + onUpdate(label)} + onKeyPress={(): boolean => false} + > + - { props.label.id >= 0 ? null : - - props.onDelete(props.label)}> - - - + { label.id < 0 + && ( + + onDelete(label)} + onKeyPress={(): boolean => false} + > + + + + ) }
); diff --git a/cvat-ui/src/components/labels-editor/constructor-viewer.tsx b/cvat-ui/src/components/labels-editor/constructor-viewer.tsx index 7c12983e590e20b036f0705e0e1903eae9f620c0..138ef61f7d5961686c891a7d2759985c9f507ef8 100644 --- a/cvat-ui/src/components/labels-editor/constructor-viewer.tsx +++ b/cvat-ui/src/components/labels-editor/constructor-viewer.tsx @@ -23,7 +23,7 @@ const colors = [ let currentColor = 0; -function nextColor() { +function nextColor(): string { const color = colors[currentColor]; currentColor += 1; if (currentColor >= colors.length) { @@ -32,12 +32,14 @@ function nextColor() { return color; } -export default function ConstructorViewer(props: ConstructorViewerProps) { +export default function ConstructorViewer(props: ConstructorViewerProps): JSX.Element { + const { onCreate } = props; currentColor = 0; const list = [ - ]; for (const label of props.labels) { list.push( @@ -47,8 +49,8 @@ export default function ConstructorViewer(props: ConstructorViewerProps) { label={label} key={label.id} color={nextColor()} - /> - ) + />, + ); } return ( @@ -56,4 +58,4 @@ export default function ConstructorViewer(props: ConstructorViewerProps) { { list }
); -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/labels-editor/label-form.tsx b/cvat-ui/src/components/labels-editor/label-form.tsx index f9870c653ab358dc018ed1d95262f284684caf34..ff76073f4136efcd524b3a21efc110a29589aeb3 100644 --- a/cvat-ui/src/components/labels-editor/label-form.tsx +++ b/cvat-ui/src/components/labels-editor/label-form.tsx @@ -35,73 +35,76 @@ type Props = FormComponentProps & { onSubmit: (label: Label | null) => void; }; -interface State { - -} - -class LabelForm extends React.PureComponent { +class LabelForm extends React.PureComponent { private continueAfterSubmit: boolean; constructor(props: Props) { super(props); - this.continueAfterSubmit = false; } - private handleSubmit = (e: React.FormEvent) => { + private handleSubmit = (e: React.FormEvent): void => { e.preventDefault(); - this.props.form.validateFields((error, values) => { + + const { + form, + label, + onSubmit, + } = this.props; + + form.validateFields((error, values): void => { if (!error) { - this.props.onSubmit({ + onSubmit({ name: values.labelName, - id: this.props.label ? this.props.label.id : idGenerator(), - attributes: values.keys.map((key: number, index: number) => { - return { + id: label ? label.id : idGenerator(), + attributes: values.keys.map((key: number, index: number): Attribute => ( + { name: values.attrName[key], type: values.type[key], mutable: values.mutable[key], - id: this.props.label && index < this.props.label.attributes.length - ? this.props.label.attributes[index].id : key, + id: label && index < label.attributes.length + ? label.attributes[index].id : key, values: Array.isArray(values.values[key]) - ? values.values[key] : [values.values[key]] - }; - }), + ? values.values[key] : [values.values[key]], + } + )), }); - this.props.form.resetFields(); + form.resetFields(); if (!this.continueAfterSubmit) { - this.props.onSubmit(null); + onSubmit(null); } } }); - } + }; - private addAttribute = () => { + private addAttribute = (): void => { const { form } = this.props; const keys = form.getFieldValue('keys'); const nextKeys = keys.concat(idGenerator()); form.setFieldsValue({ keys: nextKeys, }); - } + }; - private removeAttribute = (key: number) => { + private removeAttribute = (key: number): void => { const { form } = this.props; const keys = form.getFieldValue('keys'); form.setFieldsValue({ keys: keys.filter((_key: number) => _key !== key), }); - } + }; - private renderAttributeNameInput(key: number, attr: Attribute | null) { + private renderAttributeNameInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const value = attr ? attr.name : ''; + const { form } = this.props; return ( - { - this.props.form.getFieldDecorator(`attrName[${key}]`, { + + {form.getFieldDecorator(`attrName[${key}]`, { initialValue: value, rules: [{ required: true, @@ -110,42 +113,54 @@ class LabelForm extends React.PureComponent { pattern: patterns.validateAttributeName.pattern, message: patterns.validateAttributeName.message, }], - })() - } + })()} + ); } - private renderAttributeTypeInput(key: number, attr: Attribute | null) { + private renderAttributeTypeInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const type = attr ? attr.type.toUpperCase() : AttributeType.SELECT; + const { form } = this.props; return ( - {this.props.form.getFieldDecorator(`type[${key}]`, { + { form.getFieldDecorator(`type[${key}]`, { initialValue: type, })( - ) - } + + Select + + + Radio + + + Checkbox + + + Text + + + Number + + , + )} + ); } - private renderAttributeValuesInput(key: number, attr: Attribute | null) { + private renderAttributeValuesInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const existedValues = attr ? attr.values : []; + const { form } = this.props; - const validator = (_: any, values: string[], callback: any) => { + const validator = (_: any, values: string[], callback: any): void => { if (locked && existedValues) { if (!equalArrayHead(existedValues, values)) { callback('You can only append new values'); @@ -159,11 +174,11 @@ class LabelForm extends React.PureComponent { } callback(); - } + }; return ( - { this.props.form.getFieldDecorator(`values[${key}]`, { + { form.getFieldDecorator(`values[${key}]`, { initialValue: existedValues, rules: [{ required: true, @@ -174,37 +189,41 @@ class LabelForm extends React.PureComponent { })( False True - + , )} ); } - private renderNumberRangeInput(key: number, attr: Attribute | null) { + private renderNumberRangeInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const value = attr ? attr.values[0] : ''; + const { form } = this.props; - const validator = (_: any, value: string, callback: any) => { - const numbers = value.split(';').map((number) => Number.parseFloat(number)); + const validator = (_: any, strNumbers: string, callback: any): void => { + const numbers = strNumbers + .split(';') + .map((number): number => Number.parseFloat(number)); if (numbers.length !== 3) { callback('Invalid input'); } @@ -224,58 +243,60 @@ class LabelForm extends React.PureComponent { } callback(); - } + }; return ( - { this.props.form.getFieldDecorator(`values[${key}]`, { + { form.getFieldDecorator(`values[${key}]`, { initialValue: value, rules: [{ required: true, message: 'Please set a range', }, { validator, - }] + }], })( - + , )} ); } - private renderDefaultValueInput(key: number, attr: Attribute | null) { + private renderDefaultValueInput(key: number, attr: Attribute | null): JSX.Element { const value = attr ? attr.values[0] : ''; + const { form } = this.props; return ( - { this.props.form.getFieldDecorator(`values[${key}]`, { + { form.getFieldDecorator(`values[${key}]`, { initialValue: value, })( - + , )} ); } - private renderMutableAttributeInput(key: number, attr: Attribute | null) { + private renderMutableAttributeInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const value = attr ? attr.mutable : false; + const { form } = this.props; return ( - { this.props.form.getFieldDecorator(`mutable[${key}]`, { + { form.getFieldDecorator(`mutable[${key}]`, { initialValue: value, valuePropName: 'checked', })( - Mutable + Mutable , )} ); } - private renderDeleteAttributeButton(key: number, attr: Attribute | null) { + private renderDeleteAttributeButton(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; return ( @@ -285,20 +306,25 @@ class LabelForm extends React.PureComponent { type='link' className='cvat-delete-attribute-button' disabled={locked} - onClick={() => { + onClick={(): void => { this.removeAttribute(key); }} > - + ); } - private renderAttribute = (key: number, index: number) => { - const attr = (this.props.label && index < this.props.label.attributes.length - ? this.props.label.attributes[index] + private renderAttribute = (key: number, index: number): JSX.Element => { + const { + label, + form, + } = this.props; + + const attr = (label && index < label.attributes.length + ? label.attributes[index] : null); return ( @@ -306,23 +332,23 @@ class LabelForm extends React.PureComponent { { this.renderAttributeNameInput(key, attr) } { this.renderAttributeTypeInput(key, attr) } - { - (() => { - const type = this.props.form.getFieldValue(`type[${key}]`); + + {((): JSX.Element => { + const type = form.getFieldValue(`type[${key}]`); let element = null; - - [AttributeType.SELECT, AttributeType.RADIO] - .includes(type) ? - element = this.renderAttributeValuesInput(key, attr) - : type === AttributeType.CHECKBOX ? - element = this.renderBooleanValueInput(key, attr) - : type === AttributeType.NUMBER ? - element = this.renderNumberRangeInput(key, attr) - : element = this.renderDefaultValueInput(key, attr) + if ([AttributeType.SELECT, AttributeType.RADIO].includes(type)) { + element = this.renderAttributeValuesInput(key, attr); + } else if (type === AttributeType.CHECKBOX) { + element = this.renderBooleanValueInput(key, attr); + } else if (type === AttributeType.NUMBER) { + element = this.renderNumberRangeInput(key, attr); + } else { + element = this.renderDefaultValueInput(key, attr); + } return element; - })() - } + })()} + { this.renderMutableAttributeInput(key, attr) } @@ -332,16 +358,20 @@ class LabelForm extends React.PureComponent { ); - } + }; - private renderLabelNameInput() { - const value = this.props.label ? this.props.label.name : ''; - const locked = this.props.label ? this.props.label.id >= 0 : false; + private renderLabelNameInput(): JSX.Element { + const { + label, + form, + } = this.props; + const value = label ? label.name : ''; + const locked = label ? label.id >= 0 : false; return ( - { - this.props.form.getFieldDecorator('labelName', { + + {form.getFieldDecorator('labelName', { initialValue: value, rules: [{ required: true, @@ -349,99 +379,119 @@ class LabelForm extends React.PureComponent { }, { pattern: patterns.validateAttributeName.pattern, message: patterns.validateAttributeName.message, - }] - })() - } + }], + })()} + ); } - private renderNewAttributeButton() { + private renderNewAttributeButton(): JSX.Element { return ( ); } - private renderDoneButton() { + private renderDoneButton(): JSX.Element { return ( + > + Done + ); } - private renderContinueButton() { + private renderContinueButton(): JSX.Element { + const { label } = this.props; + return ( - this.props.label ?
: - - - - - - ); + label ?
+ : ( + + + + + + ) + ); } - private renderCancelButton() { + private renderCancelButton(): JSX.Element { + const { onSubmit } = this.props; + return ( + > + Cancel + ); } - public render() { - this.props.form.getFieldDecorator('keys', { - initialValue: this.props.label - ? this.props.label.attributes.map((attr: Attribute) => attr.id) - : [] + public render(): JSX.Element { + const { + label, + form, + } = this.props; + + form.getFieldDecorator('keys', { + initialValue: label + ? label.attributes.map((attr: Attribute): number => attr.id) + : [], }); - let keys = this.props.form.getFieldValue('keys'); + const keys = form.getFieldValue('keys'); const attributeItems = keys.map(this.renderAttribute); return (
{ this.renderLabelNameInput() } - + { this.renderNewAttributeButton() } - { attributeItems.length > 0 ? - - - Attributes - - : null + { attributeItems.length > 0 + && ( + + + Attributes + + + ) } { attributeItems.reverse() } diff --git a/cvat-ui/src/components/labels-editor/labels-editor.tsx b/cvat-ui/src/components/labels-editor/labels-editor.tsx index f49b2e2236d052d3742ba108e32ed61257e169e4..912caf378c6cfcd26f5b26b194c96e940253dd75 100644 --- a/cvat-ui/src/components/labels-editor/labels-editor.tsx +++ b/cvat-ui/src/components/labels-editor/labels-editor.tsx @@ -39,7 +39,6 @@ interface LabelsEditorState { export default class LabelsEditor extends React.PureComponent { - public constructor(props: LabelsEditortProps) { super(props); @@ -51,37 +50,46 @@ export default class LabelsEditor }; } - private handleSubmit(savedLabels: Label[], unsavedLabels: Label[]) { - function transformLabel(label: Label): any { + public componentDidMount(): void { + // just need performe the same code + this.componentDidUpdate(null as any as LabelsEditortProps); + } + + public componentDidUpdate(prevProps: LabelsEditortProps): void { + function transformLabel(label: any): Label { return { name: label.name, - id: label.id < 0 ? undefined : label.id, - attributes: label.attributes.map((attr: Attribute): any => { - return { + id: label.id || idGenerator(), + attributes: label.attributes.map((attr: any): Attribute => ( + { + id: attr.id || idGenerator(), name: attr.name, - id: attr.id < 0 ? undefined : attr.id, - input_type: attr.type.toLowerCase(), - default_value: attr.values[0], + type: attr.input_type, mutable: attr.mutable, values: [...attr.values], - }; - }), - } + } + )), + }; } - const output = []; - for (const label of savedLabels.concat(unsavedLabels)) { - output.push(transformLabel(label)); - } + const { labels } = this.props; - this.props.onSubmit(output); + if (!prevProps || prevProps.labels !== labels) { + const transformedLabels = labels.map(transformLabel); + this.setState({ + savedLabels: transformedLabels + .filter((label: Label) => label.id >= 0), + unsavedLabels: transformedLabels + .filter((label: Label) => label.id < 0), + }); + } } - private handleRawSubmit = (labels: Label[]) => { + private handleRawSubmit = (labels: Label[]): void => { const unsavedLabels = []; const savedLabels = []; - for (let label of labels) { + for (const label of labels) { if (label.id >= 0) { savedLabels.push(label); } else { @@ -95,29 +103,60 @@ export default class LabelsEditor }); this.handleSubmit(savedLabels, unsavedLabels); - } + }; + + private handleCreate = (label: Label | null): void => { + if (label === null) { + this.setState({ + constructorMode: ConstructorMode.SHOW, + }); + } else { + const { + unsavedLabels, + savedLabels, + } = this.state; + const newUnsavedLabels = [ + ...unsavedLabels, + { + ...label, + id: idGenerator(), + }, + ]; + + this.setState({ + unsavedLabels: newUnsavedLabels, + }); + + this.handleSubmit(savedLabels, newUnsavedLabels); + } + }; + + private handleUpdate = (label: Label | null): void => { + const { + savedLabels, + unsavedLabels, + } = this.state; - private handleUpdate = (label: Label | null) => { if (label) { - const savedLabels = this.state.savedLabels + const filteredSavedLabels = savedLabels .filter((_label: Label) => _label.id !== label.id); - const unsavedLabels = this.state.unsavedLabels + const filteredUnsavedLabels = unsavedLabels .filter((_label: Label) => _label.id !== label.id); if (label.id >= 0) { - savedLabels.push(label); + filteredSavedLabels.push(label); this.setState({ - savedLabels, + savedLabels: filteredSavedLabels, constructorMode: ConstructorMode.SHOW, }); } else { - unsavedLabels.push(label); + filteredUnsavedLabels.push(label); this.setState({ - unsavedLabels, + unsavedLabels: filteredUnsavedLabels, constructorMode: ConstructorMode.SHOW, }); } - this.handleSubmit(savedLabels, unsavedLabels); + this.handleSubmit(filteredSavedLabels, filteredUnsavedLabels); } else { this.setState({ constructorMode: ConstructorMode.SHOW, @@ -125,128 +164,132 @@ export default class LabelsEditor } }; - private handleDelete = (label: Label) => { + private handleDelete = (label: Label): void => { // the label is saved on the server, cannot delete it - if (typeof(label.id) !== 'undefined' && label.id >= 0) { + if (typeof (label.id) !== 'undefined' && label.id >= 0) { notification.error({ message: 'Could not delete the label', description: 'It has been already saved on the server', }); } - const unsavedLabels = this.state.unsavedLabels.filter( - (_label: Label) => _label.id !== label.id + const { + unsavedLabels, + savedLabels, + } = this.state; + + const filteredUnsavedLabels = unsavedLabels.filter( + (_label: Label): boolean => _label.id !== label.id, ); this.setState({ - unsavedLabels: [...unsavedLabels], + unsavedLabels: filteredUnsavedLabels, }); - this.handleSubmit(this.state.savedLabels, unsavedLabels); - }; - - private handleCreate = (label: Label | null) => { - if (label === null) { - this.setState({ - constructorMode: ConstructorMode.SHOW, - }); - } else { - const unsavedLabels = [...this.state.unsavedLabels, - { - ...label, - id: idGenerator() - } - ]; - - this.setState({ - unsavedLabels, - }); - - this.handleSubmit(this.state.savedLabels, unsavedLabels); - } + this.handleSubmit(savedLabels, filteredUnsavedLabels); }; - public componentDidMount() { - this.componentDidUpdate(null as any as LabelsEditortProps); - } - - public componentDidUpdate(prevProps: LabelsEditortProps) { - function transformLabel(label: any): Label { + private handleSubmit(savedLabels: Label[], unsavedLabels: Label[]): void { + function transformLabel(label: Label): any { return { name: label.name, - id: label.id || idGenerator(), - attributes: label.attributes.map((attr: any): Attribute => { - return { - id: attr.id || idGenerator(), + id: label.id < 0 ? undefined : label.id, + attributes: label.attributes.map((attr: Attribute): any => ( + { name: attr.name, - type: attr.input_type, + id: attr.id < 0 ? undefined : attr.id, + input_type: attr.type.toLowerCase(), + default_value: attr.values[0], mutable: attr.mutable, values: [...attr.values], - }; - }), - } + } + )), + }; } - if (!prevProps || prevProps.labels !== this.props.labels) { - const transformedLabels = this.props.labels.map(transformLabel); - this.setState({ - savedLabels: transformedLabels - .filter((label: Label) => label.id >= 0), - unsavedLabels: transformedLabels - .filter((label: Label) => label.id < 0), - }); + const { onSubmit } = this.props; + const output = []; + for (const label of savedLabels.concat(unsavedLabels)) { + output.push(transformLabel(label)); } + + onSubmit(output); } - public render() { + public render(): JSX.Element { + const { + savedLabels, + unsavedLabels, + constructorMode, + labelForUpdate, + } = this.state; + return ( - - - - Raw - - } key='1'> + + + + Raw + + ) + } + key='1' + > - - - Constructor - - } key='2'> + + + Constructor + + ) + } + key='2' + > + { + constructorMode === ConstructorMode.SHOW + && ( + { + this.setState({ + constructorMode: ConstructorMode.UPDATE, + labelForUpdate: label, + }); + }} + onDelete={this.handleDelete} + onCreate={(): void => { + this.setState({ + constructorMode: ConstructorMode.CREATE, + }); + }} + /> + ) + } + { + constructorMode === ConstructorMode.UPDATE + && labelForUpdate !== null && ( + + ) + } { - this.state.constructorMode === ConstructorMode.SHOW ? - { - this.setState({ - constructorMode: ConstructorMode.UPDATE, - labelForUpdate: label, - }); - }} - onDelete={this.handleDelete} - onCreate={() => { - this.setState({ - constructorMode: ConstructorMode.CREATE, - }) - }} - /> : - - this.state.constructorMode === ConstructorMode.UPDATE - && this.state.labelForUpdate !== null ? - : - - + constructorMode === ConstructorMode.CREATE + && ( + + ) } diff --git a/cvat-ui/src/components/labels-editor/raw-viewer.tsx b/cvat-ui/src/components/labels-editor/raw-viewer.tsx index c18f92a234ca0bf64384d44b644c844e1f80b1f8..f6af19673f2fe16979bdb772f97ccc7a74fa8ea6 100644 --- a/cvat-ui/src/components/labels-editor/raw-viewer.tsx +++ b/cvat-ui/src/components/labels-editor/raw-viewer.tsx @@ -13,27 +13,16 @@ import { FormComponentProps } from 'antd/lib/form/Form'; import { Label, + Attribute, } from './common'; type Props = FormComponentProps & { labels: Label[]; onSubmit: (labels: Label[]) => void; -} - -interface State { - valid: boolean; -} - -class RawViewer extends React.PureComponent { - public constructor(props: Props) { - super(props); - - this.state = { - valid: true, - }; - } +}; - private validateLabels = (_: any, value: string, callback: any) => { +class RawViewer extends React.PureComponent { + private validateLabels = (_: any, value: string, callback: any): void => { try { JSON.parse(value); } catch (error) { @@ -41,62 +30,73 @@ class RawViewer extends React.PureComponent { } callback(); - } + }; + + private handleSubmit = (e: React.FormEvent): void => { + const { + form, + onSubmit, + } = this.props; - private handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - this.props.form.validateFields((error, values) => { + form.validateFields((error, values): void => { if (!error) { - this.props.onSubmit(JSON.parse(values.labels)); + onSubmit(JSON.parse(values.labels)); } }); - } + }; - public render() { - const labels = this.props.labels.map((label: any) => { - return { + public render(): JSX.Element { + const { labels } = this.props; + const convertedLabels = labels.map((label: any): Label => ( + { ...label, id: label.id < 0 ? undefined : label.id, - attributes: label.attributes.map((attribute: any) => { - return { + attributes: label.attributes.map((attribute: any): Attribute => ( + { ...attribute, id: attribute.id < 0 ? undefined : attribute.id, - }; - }), - }; - }); + } + )), + } + )); - const textLabels = JSON.stringify(labels, null, 2); + const textLabels = JSON.stringify(convertedLabels, null, 2); + const { form } = this.props; return ( - { - this.props.form.getFieldDecorator('labels', { + + {form.getFieldDecorator('labels', { initialValue: textLabels, rules: [{ validator: this.validateLabels, - }] - })( ) - } + }], + })()} + + > + Done + + > + Reset + diff --git a/cvat-ui/src/components/login-page/login-form.tsx b/cvat-ui/src/components/login-page/login-form.tsx index b3536797b94c9dcd9502355ab7d3c0d5b6f800fa..e2afeef32164389dca77fa8fabd590b71e03926f 100644 --- a/cvat-ui/src/components/login-page/login-form.tsx +++ b/cvat-ui/src/components/login-page/login-form.tsx @@ -18,21 +18,23 @@ type LoginFormProps = { } & FormComponentProps; class LoginFormComponent extends React.PureComponent { - constructor(props: LoginFormProps) { - super(props); - } - - private handleSubmit = (e: React.FormEvent) => { + private handleSubmit = (e: React.FormEvent): void => { e.preventDefault(); - this.props.form.validateFields((error, values) => { + const { + form, + onSubmit, + } = this.props; + + form.validateFields((error, values): void => { if (!error) { - this.props.onSubmit(values); + onSubmit(values); } }); - } + }; - private renderUsernameField() { - const { getFieldDecorator } = this.props.form; + private renderUsernameField(): JSX.Element { + const { form } = this.props; + const { getFieldDecorator } = form; return ( @@ -44,16 +46,17 @@ class LoginFormComponent extends React.PureComponent { })( } + prefix={} placeholder='Username' - /> + />, )} - ) + ); } - private renderPasswordField() { - const { getFieldDecorator } = this.props.form; + private renderPasswordField(): JSX.Element { + const { form } = this.props; + const { getFieldDecorator } = form; return ( @@ -65,16 +68,17 @@ class LoginFormComponent extends React.PureComponent { })( } + prefix={} placeholder='Password' type='password' - /> + />, )} - ) + ); } - public render() { + public render(): JSX.Element { + const { fetching } = this.props; return ( {this.renderUsernameField()} @@ -83,8 +87,8 @@ class LoginFormComponent extends React.PureComponent {
+ { withMapping + && ( +
+ this.setState({ + cleanOut: e.target.checked, + })} + > + Clean old annotations + +
+ ) }
); } - private renderSpin() { - return ( - - ); - } - - public componentDidUpdate(prevProps: Props, prevState: State) { - if (!this.props.modelsInitialized && !this.props.modelsFetching) { - this.props.getModels(); - } - - if (!prevProps.visible && this.props.visible) { - this.setState({ - selectedModel: null, - mapping: {}, - matching: { - model: '', - task: '', - }, - cleanOut: false, - }); - } - - if (this.state.selectedModel && prevState.selectedModel !== this.state.selectedModel) { - const model = this.props.models - .filter((model) => model.name === this.state.selectedModel)[0]; - if (!model.primary) { - let taskLabels: string[] = this.props.taskInstance.labels - .map((label: any) => label.name); - const defaultMapping: StringObject = model.labels - .reduce((acc: StringObject, label) => { - if (taskLabels.includes(label)) { - acc[label] = label; - taskLabels = taskLabels.filter((_label) => _label !== label) - } - - return acc; - }, {}); + public render(): JSX.Element | false { + const { + selectedModel, + mapping, + cleanOut, + } = this.state; - this.setState({ - mapping: defaultMapping, - }); - } - } - } + const { + models, + visible, + taskInstance, + modelsInitialized, + runInference, + closeDialog, + } = this.props; - public render() { - const activeModel = this.props.models.filter( - (model) => model.name === this.state.selectedModel + const activeModel = models.filter( + (model): boolean => model.name === selectedModel, )[0]; - let enabledSubmit = !!activeModel - && activeModel.primary || !!Object.keys(this.state.mapping).length; + const enabledSubmit = (!!activeModel + && activeModel.primary) || !!Object.keys(mapping).length; return ( - this.props.visible && { - this.props.runInference( - this.props.taskInstance, - this.props.models - .filter((model) => model.name === this.state.selectedModel)[0], - this.state.mapping, - this.state.cleanOut, - ); - this.props.closeDialog() - }} - onCancel={() => this.props.closeDialog()} - okButtonProps={{disabled: !enabledSubmit}} - title='Automatic annotation' - visible={true} - > - {!this.props.modelsInitialized && this.renderSpin()} - {this.props.modelsInitialized && this.renderContent()} - + visible && ( + { + runInference( + taskInstance, + models + .filter((model): boolean => model.name === selectedModel)[0], + mapping, + cleanOut, + ); + closeDialog(); + }} + onCancel={(): void => closeDialog()} + okButtonProps={{ disabled: !enabledSubmit }} + title='Automatic annotation' + visible + > + {!modelsInitialized + && } + {modelsInitialized && this.renderContent()} + + ) ); } -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/models-page/built-model-item.tsx b/cvat-ui/src/components/models-page/built-model-item.tsx index 0bfd1bb71501dafa770e827584372f9dbd74cc5e..2e0adfa1c3853e193fc98174983a4d9877336238 100644 --- a/cvat-ui/src/components/models-page/built-model-item.tsx +++ b/cvat-ui/src/components/models-page/built-model-item.tsx @@ -15,7 +15,9 @@ interface Props { model: Model; } -export default function BuiltModelItemComponent(props: Props) { +export default function BuiltModelItemComponent(props: Props): JSX.Element { + const { model } = props; + return ( @@ -23,24 +25,26 @@ export default function BuiltModelItemComponent(props: Props) { - {props.model.name} + {model.name} - + ); } diff --git a/cvat-ui/src/components/models-page/built-models-list.tsx b/cvat-ui/src/components/models-page/built-models-list.tsx index 50e76de58111a1b5920d4a9984cdcbbfc4e34cfd..5ce892e64c8b7ac5a5c21e63d5c055d4744efe11 100644 --- a/cvat-ui/src/components/models-page/built-models-list.tsx +++ b/cvat-ui/src/components/models-page/built-models-list.tsx @@ -14,10 +14,11 @@ interface Props { models: Model[]; } -export default function IntegratedModelsListComponent(props: Props) { - const items = props.models.map((model) => - - ); +export default function IntegratedModelsListComponent(props: Props): JSX.Element { + const { models } = props; + const items = models.map((model): JSX.Element => ( + + )); return ( <> @@ -28,12 +29,12 @@ export default function IntegratedModelsListComponent(props: Props) { - + - {'Framework'} + Framework - {'Name'} + Name Labels diff --git a/cvat-ui/src/components/models-page/empty-list.tsx b/cvat-ui/src/components/models-page/empty-list.tsx index 1746eaa8471a0be92edab26b9e41685d9e6426aa..b48417a03d29764f565b6f2ea89ca7c8ec0ee8ed 100644 --- a/cvat-ui/src/components/models-page/empty-list.tsx +++ b/cvat-ui/src/components/models-page/empty-list.tsx @@ -8,32 +8,31 @@ import { Icon, } from 'antd'; -export default function EmptyListComponent() { - const emptyTasksIcon = () => (); +export default function EmptyListComponent(): JSX.Element { + const emptyTasksIcon = (): JSX.Element => (); return (
- + - {'No models uploaded yet ...'} + No models uploaded yet ... - {'To annotate your tasks automatically'} + To annotate your tasks automatically - + - {'upload a new model'} + upload a new model
- - ) -} \ No newline at end of file + ); +} diff --git a/cvat-ui/src/components/models-page/models-page.tsx b/cvat-ui/src/components/models-page/models-page.tsx index 8db88abedf09eb4ae31854b0de6195f4470e0571..bd26271dc8debaccc055490e1a06dea42550ac68 100644 --- a/cvat-ui/src/components/models-page/models-page.tsx +++ b/cvat-ui/src/components/models-page/models-page.tsx @@ -22,36 +22,48 @@ interface Props { deleteModel(id: number): void; } -export default function ModelsPageComponent(props: Props) { - if (!props.modelsInitialized && !props.modelsFetching) { - props.getModels(); - return ( - - ); - } else { - const uploadedModels = props.models.filter((model) => model.id !== null); - const integratedModels = props.models.filter((model) => model.id === null); +export default function ModelsPageComponent(props: Props): JSX.Element { + const { + installedAutoAnnotation, + installedTFSegmentation, + installedTFAnnotation, + modelsInitialized, + modelsFetching, + registeredUsers, + models, + + deleteModel, + } = props; + if (!modelsInitialized && !modelsFetching) { + props.getModels(); return ( -
- - { !!integratedModels.length && - - } - { !!uploadedModels.length && - - } - { props.installedAutoAnnotation && - !uploadedModels.length && - !props.installedTFAnnotation && - !props.installedTFSegmentation && - - } -
+ ); } + + const uploadedModels = models.filter((model): boolean => model.id !== null); + const integratedModels = models.filter((model): boolean => model.id === null); + + return ( +
+ + { !!integratedModels.length + && + } + { !!uploadedModels.length && ( + + )} + { installedAutoAnnotation + && !uploadedModels.length + && !installedTFAnnotation + && !installedTFSegmentation + && + } +
+ ); } diff --git a/cvat-ui/src/components/models-page/top-bar.tsx b/cvat-ui/src/components/models-page/top-bar.tsx index 2b394cd24c7e9d269387e936a68c22f12193b0b7..b28c7d0247100e62bdac881e84adb5fe6ce617f8 100644 --- a/cvat-ui/src/components/models-page/top-bar.tsx +++ b/cvat-ui/src/components/models-page/top-bar.tsx @@ -14,26 +14,40 @@ type Props = { installedAutoAnnotation: boolean; } & RouteComponentProps; -function TopBarComponent(props: Props) { +function TopBarComponent(props: Props): JSX.Element { + const { + installedAutoAnnotation, + history, + } = props; + return ( Models - { props.installedAutoAnnotation && - + { installedAutoAnnotation + && ( + + ) } - ) + ); } export default withRouter(TopBarComponent); diff --git a/cvat-ui/src/components/models-page/uploaded-model-item.tsx b/cvat-ui/src/components/models-page/uploaded-model-item.tsx index bad3f1a63caee71b379e459a76d73821443e5900..9f93246b47469870d1c4d1a167b20ce2999738d9 100644 --- a/cvat-ui/src/components/models-page/uploaded-model-item.tsx +++ b/cvat-ui/src/components/models-page/uploaded-model-item.tsx @@ -21,8 +21,13 @@ interface Props { onDelete(): void; } -export default function UploadedModelItem(props: Props) { - const subMenuIcon = () => (); +export default function UploadedModelItem(props: Props): JSX.Element { + const subMenuIcon = (): JSX.Element => (); + const { + model, + owner, + onDelete, + } = props; return ( @@ -31,43 +36,52 @@ export default function UploadedModelItem(props: Props) { - {props.model.name} + {model.name} - {props.owner ? props.owner.username : 'undefined'} + {owner ? owner.username : 'undefined'} - {moment(props.model.uploadDate).format('MMMM Do YYYY')} + {moment(model.uploadDate).format('MMMM Do YYYY')} Actions - { - props.onDelete(); - }}key='delete'>Delete + { + onDelete(); + }} + key='delete' + > + Delete +
- }> - + )} + > + diff --git a/cvat-ui/src/components/models-page/uploaded-models-list.tsx b/cvat-ui/src/components/models-page/uploaded-models-list.tsx index 6cc6a1391fb9cabab48e4216943644a956a55291..233359293abcf416eaa8375eec804a52bb105909 100644 --- a/cvat-ui/src/components/models-page/uploaded-models-list.tsx +++ b/cvat-ui/src/components/models-page/uploaded-models-list.tsx @@ -16,15 +16,21 @@ interface Props { deleteModel(id: number): void; } -export default function UploadedModelsListComponent(props: Props) { - const items = props.models.map((model) => { - const owner = props.registeredUsers.filter((user) => user.id === model.ownerID)[0]; +export default function UploadedModelsListComponent(props: Props): JSX.Element { + const { + models, + registeredUsers, + deleteModel, + } = props; + + const items = models.map((model): JSX.Element => { + const owner = registeredUsers.filter((user) => user.id === model.ownerID)[0]; return ( props.deleteModel(model.id as number)} + onDelete={(): void => deleteModel(model.id as number)} /> ); }); @@ -33,17 +39,17 @@ export default function UploadedModelsListComponent(props: Props) { <> - {'Uploaded by a user'} + Uploaded by a user - + - {'Framework'} + Framework - {'Name'} + Name Owner @@ -54,7 +60,7 @@ export default function UploadedModelsListComponent(props: Props) { Labels - + { items } diff --git a/cvat-ui/src/components/register-page/register-form.tsx b/cvat-ui/src/components/register-page/register-form.tsx index 2122aec4b96204d3884154f37f6c4e2d1ff12860..8683b3ce670a2f378a35212c5d3283a29846a923 100644 --- a/cvat-ui/src/components/register-page/register-form.tsx +++ b/cvat-ui/src/components/register-page/register-form.tsx @@ -7,6 +7,8 @@ import { Form, } from 'antd'; +import patterns from '../../utils/validation-patterns'; + export interface RegisterData { username: string; firstName: string; @@ -16,28 +18,22 @@ export interface RegisterData { password2: string; } -import patterns from '../../utils/validation-patterns'; - type RegisterFormProps = { fetching: boolean; onSubmit(registerData: RegisterData): void; } & FormComponentProps; class RegisterFormComponent extends React.PureComponent { - constructor(props: RegisterFormProps) { - super(props); - } - - private validateConfirmation = (rule: any, value: any, callback: any) => { + private validateConfirmation = (rule: any, value: any, callback: any): void => { const { form } = this.props; if (value && value !== form.getFieldValue('password1')) { - callback('Two passwords that you enter is inconsistent!'); + callback('Two passwords that you enter is inconsistent!'); } else { - callback(); + callback(); } - }; + }; - private validatePassword = (_: any, value: any, callback: any) => { + private validatePassword = (_: any, value: any, callback: any): void => { const { form } = this.props; if (!patterns.validatePasswordLength.pattern.test(value)) { callback(patterns.validatePasswordLength.message); @@ -56,12 +52,12 @@ class RegisterFormComponent extends React.PureComponent { } if (value) { - form.validateFields(['password2'], { force: true }); + form.validateFields(['password2'], { force: true }); } callback(); }; - private validateUsername = (_: any, value: any, callback: any) => { + private validateUsername = (_: any, value: any, callback: any): void => { if (!patterns.validateUsernameLength.pattern.test(value)) { callback(patterns.validateUsernameLength.message); } @@ -73,19 +69,26 @@ class RegisterFormComponent extends React.PureComponent { callback(); }; - private handleSubmit = (e: React.FormEvent) => { + private handleSubmit = (e: React.FormEvent): void => { e.preventDefault(); - this.props.form.validateFields((error, values) => { + const { + form, + onSubmit, + } = this.props; + + form.validateFields((error, values): void => { if (!error) { - this.props.onSubmit(values); + onSubmit(values); } }); - } + }; + + private renderFirstNameField(): JSX.Element { + const { form } = this.props; - private renderFirstNameField() { return ( - {this.props.form.getFieldDecorator('firstName', { + {form.getFieldDecorator('firstName', { rules: [{ required: true, message: 'Please specify a first name', @@ -93,18 +96,20 @@ class RegisterFormComponent extends React.PureComponent { }], })( } + prefix={} placeholder='First name' - /> + />, )} - ) + ); } - private renderLastNameField() { + private renderLastNameField(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('lastName', { + {form.getFieldDecorator('lastName', { rules: [{ required: true, message: 'Please specify a last name', @@ -112,18 +117,20 @@ class RegisterFormComponent extends React.PureComponent { }], })( } + prefix={} placeholder='Last name' - /> + />, )} - ) + ); } - private renderUsernameField() { + private renderUsernameField(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('username', { + {form.getFieldDecorator('username', { rules: [{ required: true, message: 'Please specify a username', @@ -132,40 +139,44 @@ class RegisterFormComponent extends React.PureComponent { }], })( } + prefix={} placeholder='Username' - /> + />, )} - ) + ); } - private renderEmailField() { + private renderEmailField(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('email', { + {form.getFieldDecorator('email', { rules: [{ type: 'email', message: 'The input is not valid E-mail!', - }, { + }, { required: true, message: 'Please specify an email address', }], })( } + prefix={} placeholder='Email address' - /> + />, )} - ) + ); } - private renderPasswordField() { + private renderPasswordField(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('password1', { + {form.getFieldDecorator('password1', { rules: [{ required: true, message: 'Please input your password!', @@ -174,17 +185,19 @@ class RegisterFormComponent extends React.PureComponent { }], })(} + prefix={} placeholder='Password' />)} - ) + ); } - private renderPasswordConfirmationField() { + private renderPasswordConfirmationField(): JSX.Element { + const { form } = this.props; + return ( - {this.props.form.getFieldDecorator('password2', { + {form.getFieldDecorator('password2', { rules: [{ required: true, message: 'Please confirm your password!', @@ -193,16 +206,16 @@ class RegisterFormComponent extends React.PureComponent { }], })(} + prefix={} placeholder='Confirm password' />)} - ) + ); } + public render(): JSX.Element { + const { fetching } = this.props; - - public render() { return ( {this.renderFirstNameField()} @@ -217,8 +230,8 @@ class RegisterFormComponent extends React.PureComponent { type='primary' htmlType='submit' className='register-form-button' - loading={this.props.fetching} - disabled={this.props.fetching} + loading={fetching} + disabled={fetching} > Submit @@ -228,4 +241,4 @@ class RegisterFormComponent extends React.PureComponent { } } -export default Form.create()(RegisterFormComponent); \ No newline at end of file +export default Form.create()(RegisterFormComponent); diff --git a/cvat-ui/src/components/register-page/register-page.tsx b/cvat-ui/src/components/register-page/register-page.tsx index 5fd4025e63adba7e844a7d748eeaacd05fb9aa1c..ea82f7995d361b35dfea10fae6845ff3131d1adb 100644 --- a/cvat-ui/src/components/register-page/register-page.tsx +++ b/cvat-ui/src/components/register-page/register-page.tsx @@ -10,7 +10,7 @@ import { Row, } from 'antd'; -import RegisterForm, { RegisterData } from '../../components/register-page/register-form'; +import RegisterForm, { RegisterData } from './register-form'; interface RegisterPageComponentProps { fetching: boolean; @@ -19,33 +19,44 @@ interface RegisterPageComponentProps { password1: string, password2: string) => void; } -function RegisterPageComponent(props: RegisterPageComponentProps & RouteComponentProps) { +function RegisterPageComponent( + props: RegisterPageComponentProps & RouteComponentProps, +): JSX.Element { const sizes = { xs: { span: 14 }, sm: { span: 14 }, md: { span: 10 }, lg: { span: 4 }, xl: { span: 4 }, - } + }; + + const { + fetching, + onRegister, + } = props; return ( Create an account - { - props.onRegister( - registerData.username, - registerData.firstName, - registerData.lastName, - registerData.email, - registerData.password1, - registerData.password2, - ); - }}/> + { + onRegister( + registerData.username, + registerData.firstName, + registerData.lastName, + registerData.email, + registerData.password1, + registerData.password2, + ); + }} + /> - Already have an account? Login + Already have an account? + Login diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx index 355f2e279df91ff9a5eab90a876a468db82d3960..52425be8c7ceee05dc52a008076e3b5329bf9f29 100644 --- a/cvat-ui/src/components/task-page/details.tsx +++ b/cvat-ui/src/components/task-page/details.tsx @@ -55,36 +55,91 @@ export default class DetailsComponent extends React.PureComponent }; } - private renderTaskName() { + public componentDidMount(): void { const { taskInstance } = this.props; + this.mounted = true; + + getReposData(taskInstance.id) + .then((data): void => { + if (data !== null && this.mounted) { + if (data.status.error) { + notification.error({ + message: 'Could not receive repository status', + description: data.status.error, + }); + } else { + this.setState({ + repositoryStatus: data.status.value, + }); + } + + this.setState({ + repository: data.url, + }); + } + }).catch((error): void => { + if (this.mounted) { + notification.error({ + message: 'Could not receive repository status', + description: error.toString(), + }); + } + }); + } + + + public componentDidUpdate(prevProps: Props): void { + const { taskInstance } = this.props; + + if (prevProps !== this.props) { + this.setState({ + name: taskInstance.name, + bugTracker: taskInstance.bugTracker, + }); + } + } + + public componentWillUnmount(): void { + this.mounted = false; + } + + private renderTaskName(): JSX.Element { const { name } = this.state; + const { + taskInstance, + onTaskUpdate, + } = this.props; + return ( { + onChange: (value: string): void => { this.setState({ name: value, }); taskInstance.name = value; - this.props.onTaskUpdate(taskInstance); + onTaskUpdate(taskInstance); }, }} className='cvat-black-color' - >{name} + > + {name} + ); } - private renderPreview() { + private renderPreview(): JSX.Element { + const { previewImage } = this.props; return (
- Preview + Preview
); } - private renderParameters() { + private renderParameters(): JSX.Element { const { taskInstance } = this.props; const { overlap } = taskInstance; const { segmentSize } = taskInstance; @@ -95,25 +150,25 @@ export default class DetailsComponent extends React.PureComponent <> - {'Overlap size'} -
+ Overlap size +
{overlap} - {'Segment size'} -
+ Segment size +
{segmentSize}
- {'Image quality'} -
+ Image quality +
{imageQuality} - {'Z-order'} -
+ Z-order +
{zOrder}
@@ -121,39 +176,47 @@ export default class DetailsComponent extends React.PureComponent ); } - private renderUsers() { - const { taskInstance } = this.props; + private renderUsers(): JSX.Element { + const { + taskInstance, + registeredUsers, + onTaskUpdate, + } = this.props; const owner = taskInstance.owner ? taskInstance.owner.username : null; const assignee = taskInstance.assignee ? taskInstance.assignee.username : null; const created = moment(taskInstance.createdDate).format('MMMM Do YYYY'); - const assigneeSelect = { - let [userInstance] = this.props.registeredUsers - .filter((user: any) => user.username === value); - - if (userInstance === undefined) { - userInstance = null; - } + const assigneeSelect = ( + { + let [userInstance] = registeredUsers + .filter((user: any) => user.username === value); + + if (userInstance === undefined) { + userInstance = null; + } - taskInstance.assignee = userInstance; - this.props.onTaskUpdate(taskInstance); + taskInstance.assignee = userInstance; + onTaskUpdate(taskInstance); + } } - } - /> + /> + ); return ( - { owner ? - Created by {owner} on {created} - : null } + { owner && ( + + {`Created by ${owner} on ${created}`} + + )} - {'Assigned to'} + Assigned to { assigneeSelect } @@ -161,57 +224,89 @@ export default class DetailsComponent extends React.PureComponent ); } - private renderDatasetRepository() { - const { repository } = this.state; - const { repositoryStatus } = this.state; + private renderDatasetRepository(): JSX.Element | boolean { + const { taskInstance } = this.props; + const { + repository, + repositoryStatus, + } = this.state; return ( - repository ? - - - {'Dataset Repository'} -
- {repository} - {repositoryStatus === 'sync' ? - - Synchronized - : repositoryStatus === 'merged' ? - - Merged - : repositoryStatus === 'syncing' ? - - Syncing : - { - this.setState({ - repositoryStatus: 'syncing', - }); - - syncRepos(this.props.taskInstance.id).then(() => { - if (this.mounted) { - this.setState({ - repositoryStatus: 'sync', - }); - } - }).catch(() => { - if (this.mounted) { - this.setState({ - repositoryStatus: '!sync', - }); - } - }); - }}> Synchronize - } - -
: null + !!repository + && ( + + + Dataset Repository +
+ {repository} + {repositoryStatus === 'sync' + && ( + + + Synchronized + + ) + } + {repositoryStatus === 'merged' + && ( + + + Merged + + ) + } + {repositoryStatus === 'syncing' + && ( + + + Syncing + + ) + } + {repositoryStatus === '!sync' + && ( + { + this.setState({ + repositoryStatus: 'syncing', + }); + + syncRepos(taskInstance.id).then((): void => { + if (this.mounted) { + this.setState({ + repositoryStatus: 'sync', + }); + } + }).catch((): void => { + if (this.mounted) { + this.setState({ + repositoryStatus: '!sync', + }); + } + }); + }} + > + + Synchronize + + ) + } + +
+ ) ); } - private renderBugTracker() { - const { taskInstance } = this.props; + private renderBugTracker(): JSX.Element { + const { + taskInstance, + onTaskUpdate, + } = this.props; const { bugTracker } = this.state; let shown = false; - const onChangeValue = (value: string) => { + const onChangeValue = (value: string): void => { if (value && !patterns.validateURL.pattern.test(value)) { if (!shown) { Modal.error({ @@ -229,52 +324,62 @@ export default class DetailsComponent extends React.PureComponent }); taskInstance.bugTracker = value; - this.props.onTaskUpdate(taskInstance); + onTaskUpdate(taskInstance); } - } + }; if (bugTracker) { return ( - {'Issue Tracker'} -
- {bugTracker} - - -
- ); - } else { - return ( - - - {'Issue Tracker'} -
- {'Not specified'} + Issue Tracker +
+ {bugTracker} +
); } + + return ( + + + Issue Tracker +
+ Not specified + +
+ ); } - private renderLabelsEditor() { - const { taskInstance } = this.props; + private renderLabelsEditor(): JSX.Element { + const { + taskInstance, + onTaskUpdate, + } = this.props; return ( label.toJSON() + (label: any): string => label.toJSON(), )} - onSubmit={(labels: any[]) => { - taskInstance.labels = labels.map((labelData) => { - return new core.classes.Label(labelData); - }); - - this.props.onTaskUpdate(taskInstance); + onSubmit={(labels: any[]): void => { + taskInstance.labels = labels + .map((labelData): any => new core.classes.Label(labelData)); + onTaskUpdate(taskInstance); }} /> @@ -282,50 +387,7 @@ export default class DetailsComponent extends React.PureComponent ); } - public componentDidMount() { - this.mounted = true; - getReposData(this.props.taskInstance.id) - .then((data) => { - if (data !== null && this.mounted) { - if (data.status.error) { - notification.error({ - message: 'Could not receive repository status', - description: data.status.error - }); - } else { - this.setState({ - repositoryStatus: data.status.value, - }); - } - - this.setState({ - repository: data.url, - }); - } - }).catch((error) => { - if (this.mounted) { - notification.error({ - message: 'Could not receive repository status', - description: error.toString(), - }); - } - }); - } - - public componentWillUnmount() { - this.mounted = false; - } - - public componentDidUpdate(prevProps: Props) { - if (prevProps !== this.props) { - this.setState({ - name: this.props.taskInstance.name, - bugTracker: this.props.taskInstance.bugTracker, - }); - } - } - - public render() { + public render(): JSX.Element { return (
diff --git a/cvat-ui/src/components/task-page/job-list.tsx b/cvat-ui/src/components/task-page/job-list.tsx index 3f8319fa2a925d7c12eb116fc125bd107a65ce5a..ad13707d2462aa13ea80ce17af2f60a37e7a9ab9 100644 --- a/cvat-ui/src/components/task-page/job-list.tsx +++ b/cvat-ui/src/components/task-page/job-list.tsx @@ -24,17 +24,19 @@ interface Props { onJobUpdate(jobInstance: any): void; } -export default function JobListComponent(props: Props) { - const { jobs } = props.taskInstance; +export default function JobListComponent(props: Props): JSX.Element { + const { + taskInstance, + registeredUsers, + onJobUpdate, + } = props; + + const { jobs } = taskInstance; const columns = [{ title: 'Job', dataIndex: 'job', key: 'job', - render: (id: number) => { - return ( - { `Job #${id++}` } - ); - } + render: (id: number): JSX.Element => ({ `Job #${id}` }), }, { title: 'Frames', dataIndex: 'frames', @@ -44,14 +46,20 @@ export default function JobListComponent(props: Props) { title: 'Status', dataIndex: 'status', key: 'status', - render: (status: string) => { - const progressColor = status === 'completed' ? 'cvat-job-completed-color': - status === 'validation' ? 'cvat-job-validation-color' : 'cvat-job-annotation-color'; + render: (status: string): JSX.Element => { + let progressColor = null; + if (status === 'completed') { + progressColor = 'cvat-job-completed-color'; + } else if (status === 'validation') { + progressColor = 'cvat-job-validation-color'; + } else { + progressColor = 'cvat-job-annotation-color'; + } return ( { status } ); - } + }, }, { title: 'Started on', dataIndex: 'started', @@ -66,22 +74,24 @@ export default function JobListComponent(props: Props) { title: 'Assignee', dataIndex: 'assignee', key: 'assignee', - render: (jobInstance: any) => { - const assignee = jobInstance.assignee ? jobInstance.assignee.username : null + render: (jobInstance: any): JSX.Element => { + const assignee = jobInstance.assignee ? jobInstance.assignee.username : null; + return ( { - let [userInstance] = props.registeredUsers - .filter((user: any) => user.username === value); + onChange={(value: string): void => { + let [userInstance] = [...registeredUsers] + .filter((user: any) => user.username === value); if (userInstance === undefined) { userInstance = null; } + // eslint-disable-next-line jobInstance.assignee = userInstance; - props.onJobUpdate(jobInstance); + onJobUpdate(jobInstance); }} /> ); @@ -129,4 +139,4 @@ export default function JobListComponent(props: Props) { />
); -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/task-page/task-page.tsx b/cvat-ui/src/components/task-page/task-page.tsx index 615788996d824d33153736c4a9788f8b4d71db31..81eda43f1a0995783db7506925199bfd7dd6ab41 100644 --- a/cvat-ui/src/components/task-page/task-page.tsx +++ b/cvat-ui/src/components/task-page/task-page.tsx @@ -25,52 +25,64 @@ interface TaskPageComponentProps { type Props = TaskPageComponentProps & RouteComponentProps<{id: string}>; class TaskPageComponent extends React.PureComponent { - private attempts: number = 0; + private attempts = 0; - public componentDidUpdate() { - if (this.props.deleteActivity) { - this.props.history.replace('/tasks'); + public componentDidUpdate(): void { + const { + deleteActivity, + history, + } = this.props; + + if (deleteActivity) { + history.replace('/tasks'); } - if (this.attempts == 2) { + if (this.attempts === 2) { notification.warning({ message: 'Something wrong with the task. It cannot be fetched from the server', }); } } - public render() { - const { id } = this.props.match.params; - const fetchTask = !this.props.task; + public render(): JSX.Element { + const { + match, + task, + fetching, + onFetchTask, + } = this.props; + const { id } = match.params; + const fetchTask = !task; if (fetchTask) { - if (!this.props.fetching) { + if (!fetching) { if (!this.attempts) { - this.attempts ++; - this.props.onFetchTask(+id); + this.attempts++; + onFetchTask(+id); } else { - this.attempts ++; + this.attempts++; } } return ( - + ); - } else if (typeof(this.props.task) === 'undefined') { + } + + if (typeof (task) === 'undefined') { return (
- ) - } else { - const task = this.props.task as Task; - return ( - - - - - - - ); } + + return ( + + + + + + + + ); } } diff --git a/cvat-ui/src/components/task-page/top-bar.tsx b/cvat-ui/src/components/task-page/top-bar.tsx index 3cbd31393048b15581a4b76ce109828fd8213184..8c368771f764e593993c89e28be2377ea3315924 100644 --- a/cvat-ui/src/components/task-page/top-bar.tsx +++ b/cvat-ui/src/components/task-page/top-bar.tsx @@ -16,10 +16,11 @@ interface DetailsComponentProps { taskInstance: any; } -export default function DetailsComponent(props: DetailsComponentProps) { - const subMenuIcon = () => (); +export default function DetailsComponent(props: DetailsComponentProps): JSX.Element { + const subMenuIcon = (): JSX.Element => (); - const { id } = props.taskInstance; + const { taskInstance } = props; + const { id } = taskInstance; return ( @@ -28,16 +29,18 @@ export default function DetailsComponent(props: DetailsComponentProps) { - }> + ( + + )} + > ); -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/task-page/user-selector.tsx b/cvat-ui/src/components/task-page/user-selector.tsx index b02870faeca3b8793676f95376ffcbdf167d7b7e..b58b85a90c4133c700f27081bb249194a60d48fa 100644 --- a/cvat-ui/src/components/task-page/user-selector.tsx +++ b/cvat-ui/src/components/task-page/user-selector.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { - Icon, Select, } from 'antd'; @@ -11,23 +10,27 @@ interface Props { onChange: (user: string) => void; } -export default function UserSelector(props: Props) { +export default function UserSelector(props: Props): JSX.Element { + const { + value, + users, + onChange, + } = props; + return ( + defaultValue={value || '—'} + size='small' + showSearch + className='cvat-user-selector' + onChange={onChange} + > + — + { users.map((user): JSX.Element => ( + + {user.username} + + ))} + ); -} \ No newline at end of file +} diff --git a/cvat-ui/src/components/tasks-page/empty-list.tsx b/cvat-ui/src/components/tasks-page/empty-list.tsx index 8b631631142a4a6d57e774c17eb129c0608a9f3e..bbc89e00fdd0153100eebcb9233a84d3e47a845f 100644 --- a/cvat-ui/src/components/tasks-page/empty-list.tsx +++ b/cvat-ui/src/components/tasks-page/empty-list.tsx @@ -8,32 +8,31 @@ import { Icon, } from 'antd'; -export default function EmptyListComponent() { - const emptyTasksIcon = () => (); +export default function EmptyListComponent(): JSX.Element { + const emptyTasksIcon = (): JSX.Element => (); return (
- + - {'No tasks created yet ...'} + No tasks created yet ... - {'To get started with your annotation project'} + To get started with your annotation project - + - {'create a new task'} + create a new task
- - ) -} \ No newline at end of file + ); +} diff --git a/cvat-ui/src/components/tasks-page/task-item.tsx b/cvat-ui/src/components/tasks-page/task-item.tsx index 02dd8ba5a219c5caf7d5a567d242404efcac82d3..c7322cf590587e3263d12122132da24332d16d50 100644 --- a/cvat-ui/src/components/tasks-page/task-item.tsx +++ b/cvat-ui/src/components/tasks-page/task-item.tsx @@ -25,71 +25,80 @@ export interface TaskItemProps { } class TaskItemComponent extends React.PureComponent { - constructor(props: TaskItemProps & RouteComponentProps) { - super(props); - } - - private renderPreview() { + private renderPreview(): JSX.Element { + const { previewImage } = this.props; return (
- Preview + Preview
- ) + ); } - private renderDescription() { + private renderDescription(): JSX.Element { // Task info - const task = this.props.taskInstance; - const { id } = task; - const owner = task.owner ? task.owner.username : null; - const updated = moment(task.updatedDate).fromNow(); - const created = moment(task.createdDate).format('MMMM Do YYYY'); + const { taskInstance } = this.props; + const { id } = taskInstance; + const owner = taskInstance.owner ? taskInstance.owner.username : null; + const updated = moment(taskInstance.updatedDate).fromNow(); + const created = moment(taskInstance.createdDate).format('MMMM Do YYYY'); // Get and truncate a task name - const name = `${task.name.substring(0, 70)}${task.name.length > 70 ? '...' : ''}`; + const name = `${taskInstance.name.substring(0, 70)}${taskInstance.name.length > 70 ? '...' : ''}`; return ( - {`${id} ${name}`}
- { owner ? - <> - - Created { owner ? 'by ' + owner : '' } on {created} -
- : null + {`${id} ${name}`} +
+ { owner + && ( + <> + + {`Created ${owner ? `by ${owner}` : ''} on ${created}`} + +
+ + ) } {`Last updated ${updated}`} - ) + ); } - private renderProgress() { - const task = this.props.taskInstance; + private renderProgress(): JSX.Element { + const { + taskInstance, + activeInference, + } = this.props; // Count number of jobs and performed jobs - const numOfJobs = task.jobs.length; - const numOfCompleted = task.jobs.filter( - (job: any) => job.status === 'completed' + const numOfJobs = taskInstance.jobs.length; + const numOfCompleted = taskInstance.jobs.filter( + (job: any): boolean => job.status === 'completed', ).length; // Progress appearence depends on number of jobs - const progressColor = numOfCompleted === numOfJobs ? 'cvat-task-completed-progress': - numOfCompleted ? 'cvat-task-progress-progress' : 'cvat-task-pending-progress'; + let progressColor = null; + let progressText = null; + if (numOfCompleted === numOfJobs) { + progressColor = 'cvat-task-completed-progress'; + progressText = Completed; + } else if (numOfCompleted) { + progressColor = 'cvat-task-progress-progress'; + progressText = In Progress; + } else { + progressColor = 'cvat-task-pending-progress'; + progressText = Pending; + } return ( - + - { numOfCompleted === numOfJobs ? - {'Completed'} - : numOfCompleted ? - {'In Progress'} - : {'Pending'} - } + { progressText } {`${numOfCompleted} of ${numOfJobs} jobs`} @@ -107,78 +116,86 @@ class TaskItemComponent extends React.PureComponent - { this.props.activeInference ? - <> - - - Automatic annotation - - - - - - - - : null + { activeInference + && ( + <> + + + Automatic annotation + + + + + + + + + ) } - ) + ); } - private renderNavigation() { - const subMenuIcon = () => (); - const { id } = this.props.taskInstance; + private renderNavigation(): JSX.Element { + const subMenuIcon = (): JSX.Element => (); + const { + taskInstance, + history, + } = this.props; + const { id } = taskInstance; return ( - + Actions - - }> - + }> + - ) + ); } - public render() { + public render(): JSX.Element { + const { deleted } = this.props; const style = {}; - if (this.props.deleted) { + if (deleted) { (style as any).pointerEvents = 'none'; (style as any).opacity = 0.5; } return ( - + {this.renderPreview()} {this.renderDescription()} {this.renderProgress()} {this.renderNavigation()} - ) - }; + ); + } } export default withRouter(TaskItemComponent); diff --git a/cvat-ui/src/components/tasks-page/task-list.tsx b/cvat-ui/src/components/tasks-page/task-list.tsx index bfce6f0796c373706e50da3d203de50767a059bf..ce83810960041012d86ba136393fb99a1a466b6e 100644 --- a/cvat-ui/src/components/tasks-page/task-list.tsx +++ b/cvat-ui/src/components/tasks-page/task-list.tsx @@ -15,10 +15,15 @@ export interface ContentListProps { numberOfTasks: number; } -export default function TaskListComponent(props: ContentListProps) { - const tasks = props.currentTasksIndexes; - const taskViews = tasks.map( - (tid, id) => +export default function TaskListComponent(props: ContentListProps): JSX.Element { + const { + currentTasksIndexes, + numberOfTasks, + currentPage, + onSwitchPage, + } = props; + const taskViews = currentTasksIndexes.map( + (tid, id): JSX.Element => , ); return ( @@ -32,14 +37,14 @@ export default function TaskListComponent(props: ContentListProps) { - ) + ); } diff --git a/cvat-ui/src/components/tasks-page/tasks-page.tsx b/cvat-ui/src/components/tasks-page/tasks-page.tsx index 2b8da555cc01102ee70acba0e57967bdbcc3c4cc..a0ebf786abbde5f9df777d8dd57ad06675afcc50 100644 --- a/cvat-ui/src/components/tasks-page/tasks-page.tsx +++ b/cvat-ui/src/components/tasks-page/tasks-page.tsx @@ -22,135 +22,155 @@ interface TasksPageProps { onGetTasks: (gettingQuery: TasksQuery) => void; } -class TasksPageComponent extends React.PureComponent { - constructor(props: any) { - super(props); - } +function getSearchField(gettingQuery: TasksQuery): string { + let searchString = ''; + for (const field of Object.keys(gettingQuery)) { + if (gettingQuery[field] !== null && field !== 'page') { + if (field === 'search') { + return (gettingQuery[field] as any) as string; + } - private updateURL(gettingQuery: TasksQuery) { - let queryString = '?'; - for (const field of Object.keys(gettingQuery)) { - if (gettingQuery[field] !== null) { - queryString += `${field}=${gettingQuery[field]}&`; + // not constant condition + // eslint-disable-next-line + if (typeof (gettingQuery[field] === 'number')) { + searchString += `${field}:${gettingQuery[field]} AND `; + } else { + searchString += `${field}:"${gettingQuery[field]}" AND `; } } - this.props.history.replace({ - search: queryString.slice(0, -1), - }); } - private getSearchField(gettingQuery: TasksQuery): string { - let searchString = ''; - for (const field of Object.keys(gettingQuery)) { - if (gettingQuery[field] !== null && field !== 'page') { - if (field === 'search') { - return (gettingQuery[field] as any) as string; - } else { - if (typeof (gettingQuery[field] === 'number')) { - searchString += `${field}:${gettingQuery[field]} AND `; + return searchString.slice(0, -5); +} + +class TasksPageComponent extends React.PureComponent { + public componentDidMount(): void { + const { + gettingQuery, + location, + onGetTasks, + } = this.props; + const params = new URLSearchParams(location.search); + + const query = { ...gettingQuery }; + for (const field of Object.keys(query)) { + if (params.has(field)) { + const value = params.get(field); + if (value) { + if (field === 'id' || field === 'page') { + if (Number.isInteger(+value)) { + query[field] = +value; + } } else { - searchString += `${field}:"${gettingQuery[field]}" AND `; + query[field] = value; } } + } else if (field === 'page') { + query[field] = 1; + } else { + query[field] = null; } } - return searchString.slice(0, -5); + this.updateURL(query); + onGetTasks(query); } private handleSearch = (value: string): void => { - const gettingQuery = { ...this.props.gettingQuery }; + const { + gettingQuery, + onGetTasks, + } = this.props; + + const query = { ...gettingQuery }; const search = value.replace(/\s+/g, ' ').replace(/\s*:+\s*/g, ':').trim(); const fields = ['name', 'mode', 'owner', 'assignee', 'status', 'id']; for (const field of fields) { - gettingQuery[field] = null; + query[field] = null; } - gettingQuery.search = null; + query.search = null; let specificRequest = false; for (const param of search.split(/[\s]+and[\s]+|[\s]+AND[\s]+/)) { if (param.includes(':')) { - const [name, value] = param.split(':'); - if (fields.includes(name) && !!value) { + const [field, fieldValue] = param.split(':'); + if (fields.includes(field) && !!fieldValue) { specificRequest = true; - if (name === 'id') { - if (Number.isInteger(+value)) { - gettingQuery[name] = +value; + if (field === 'id') { + if (Number.isInteger(+fieldValue)) { + query[field] = +fieldValue; } } else { - gettingQuery[name] = value; + query[field] = fieldValue; } } } } - gettingQuery.page = 1; + query.page = 1; if (!specificRequest && value) { // only id - gettingQuery.search = value; + query.search = value; } - this.updateURL(gettingQuery); - this.props.onGetTasks(gettingQuery); - } + this.updateURL(query); + onGetTasks(query); + }; private handlePagination = (page: number): void => { - const gettingQuery = { ...this.props.gettingQuery }; - - gettingQuery.page = page; - this.updateURL(gettingQuery); - this.props.onGetTasks(gettingQuery); - } - - public componentDidMount() { - const gettingQuery = { ...this.props.gettingQuery }; - const params = new URLSearchParams(this.props.location.search); - + const { + gettingQuery, + onGetTasks, + } = this.props; + const query = { ...gettingQuery }; + + query.page = page; + this.updateURL(query); + onGetTasks(query); + }; + + private updateURL(gettingQuery: TasksQuery): void { + const { history } = this.props; + let queryString = '?'; for (const field of Object.keys(gettingQuery)) { - if (params.has(field)) { - const value = params.get(field); - if (value) { - if (field === 'id' || field === 'page') { - if (Number.isInteger(+value)) { - gettingQuery[field] = +value; - } - } else { - gettingQuery[field] = value; - } - } - } else { - if (field === 'page') { - gettingQuery[field] = 1; - } else { - gettingQuery[field] = null; - } + if (gettingQuery[field] !== null) { + queryString += `${field}=${gettingQuery[field]}&`; } } - - this.updateURL(gettingQuery); - this.props.onGetTasks(gettingQuery); + history.replace({ + search: queryString.slice(0, -1), + }); } - public render() { - if (this.props.tasksFetching) { + public render(): JSX.Element { + const { + tasksFetching, + gettingQuery, + numberOfVisibleTasks, + } = this.props; + + if (tasksFetching) { return ( - + ); - } else { - return ( -
- - {this.props.numberOfVisibleTasks ? + } + + return ( +
+ + {numberOfVisibleTasks + ? ( : } -
- ) - } + /> + ) : + } +
+ ); } } -export default withRouter(TasksPageComponent); \ No newline at end of file +export default withRouter(TasksPageComponent); diff --git a/cvat-ui/src/components/tasks-page/top-bar.tsx b/cvat-ui/src/components/tasks-page/top-bar.tsx index b8cfea73f7384d3ce8e12dc46bf0be147ce1bf2d..486e1dbb1f33ec8b750e2c5cdfe13f40feb43c6f 100644 --- a/cvat-ui/src/components/tasks-page/top-bar.tsx +++ b/cvat-ui/src/components/tasks-page/top-bar.tsx @@ -16,35 +16,50 @@ interface VisibleTopBarProps { searchValue: string; } -function TopBarComponent(props: VisibleTopBarProps & RouteComponentProps) { +function TopBarComponent(props: VisibleTopBarProps & RouteComponentProps): JSX.Element { + const { + searchValue, + history, + onSearch, + } = props; + return ( <> - {'Default project'} + Default project Tasks - + md={{ span: 11 }} + lg={{ span: 9 }} + xl={{ span: 8 }} + xxl={{ span: 7 }} + > + - ) + ); } export default withRouter(TopBarComponent); diff --git a/cvat-ui/src/containers/actions-menu/actions-menu.tsx b/cvat-ui/src/containers/actions-menu/actions-menu.tsx index 1fab00afa0f3a9e83ed50d36cfe2efc0c5048148..dc8692c6145465e7587054d9438c628c90f70e30 100644 --- a/cvat-ui/src/containers/actions-menu/actions-menu.tsx +++ b/cvat-ui/src/containers/actions-menu/actions-menu.tsx @@ -4,7 +4,6 @@ import { connect } from 'react-redux'; import ActionsMenuComponent from '../../components/actions-menu/actions-menu'; import { CombinedState, - ActiveInference, } from '../../reducers/interfaces'; import { showRunModelDialog } from '../../actions/models-actions'; @@ -30,7 +29,7 @@ interface StateToProps { installedTFSegmentation: boolean; installedAutoAnnotation: boolean; inferenceIsActive: boolean; -}; +} interface DispatchToProps { onLoadAnnotation: (taskInstance: any, loader: any, file: File) => void; @@ -45,16 +44,16 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { const { activities } = state.tasks; const { dumps } = activities; const { loads } = activities; - const _exports = activities.exports; + const activeExports = activities.exports; const { plugins } = state.plugins; - const id = own.taskInstance.id; + const { id } = own.taskInstance; return { installedTFAnnotation: plugins.TF_ANNOTATION, installedTFSegmentation: plugins.TF_SEGMENTATION, installedAutoAnnotation: plugins.AUTO_ANNOTATION, dumpActivities: dumps.byTask[id] ? dumps.byTask[id] : null, - exportActivities: _exports.byTask[id] ? _exports.byTask[id] : null, + exportActivities: activeExports.byTask[id] ? activeExports.byTask[id] : null, loadActivity: loads.byTask[id] ? loads.byTask[id] : null, loaders: formats.annotationFormats .map((format: any): any[] => format.loaders).flat(), @@ -62,49 +61,32 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { .map((format: any): any[] => format.dumpers).flat(), exporters: formats.datasetFormats, inferenceIsActive: id in state.models.inferences, - }; + }; } function mapDispatchToProps(dispatch: any): DispatchToProps { return { - onLoadAnnotation: (taskInstance: any, loader: any, file: File) => { + onLoadAnnotation: (taskInstance: any, loader: any, file: File): void => { dispatch(loadAnnotationsAsync(taskInstance, loader, file)); }, - onDumpAnnotation: (taskInstance: any, dumper: any) => { + onDumpAnnotation: (taskInstance: any, dumper: any): void => { dispatch(dumpAnnotationsAsync(taskInstance, dumper)); }, - onExportDataset: (taskInstance: any, exporter: any) => { + onExportDataset: (taskInstance: any, exporter: any): void => { dispatch(exportDatasetAsync(taskInstance, exporter)); }, - onDeleteTask: (taskInstance: any) => { + onDeleteTask: (taskInstance: any): void => { dispatch(deleteTaskAsync(taskInstance)); }, - onOpenRunWindow: (taskInstance: any) => { + onOpenRunWindow: (taskInstance: any): void => { dispatch(showRunModelDialog(taskInstance)); - } + }, }; } -function ActionsMenuContainer(props: OwnProps & StateToProps & DispatchToProps) { +function ActionsMenuContainer(props: OwnProps & StateToProps & DispatchToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/annotation-page/annotation-page.tsx b/cvat-ui/src/containers/annotation-page/annotation-page.tsx index d0e482ace13d7f111d4dbbf832e1acabfb061d4c..07f5a7ca9665b1f1478b7822ef9987c8fa61ce33 100644 --- a/cvat-ui/src/containers/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/containers/annotation-page/annotation-page.tsx @@ -1,15 +1,11 @@ import React from 'react'; export default class AnnotationPageContainer extends React.PureComponent { - constructor(props: any) { - super(props); - } - - public render() { + public render(): JSX.Element { return (
- "AnnotationPage" + AnnotationPage
); } -} \ No newline at end of file +} diff --git a/cvat-ui/src/containers/create-model-page/create-model-page.tsx b/cvat-ui/src/containers/create-model-page/create-model-page.tsx index 188589f47ea6d8a07879ca283a0570a643bbf0a9..a20b6fb56283765c9414908da6de42258dcf6dee 100644 --- a/cvat-ui/src/containers/create-model-page/create-model-page.tsx +++ b/cvat-ui/src/containers/create-model-page/create-model-page.tsx @@ -18,7 +18,7 @@ interface DispatchToProps { } function mapStateToProps(state: CombinedState): StateToProps { - const { models} = state; + const { models } = state; return { isAdmin: state.auth.user.isAdmin, @@ -28,19 +28,15 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - createModel(name: string, files: ModelFiles, global: boolean) { + createModel(name: string, files: ModelFiles, global: boolean): void { dispatch(createModelAsync(name, files, global)); }, }; } -function CreateModelPageContainer(props: StateToProps & DispatchToProps) { +function CreateModelPageContainer(props: StateToProps & DispatchToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/create-task-page/create-task-page.tsx b/cvat-ui/src/containers/create-task-page/create-task-page.tsx index a7655dce34ea1b844afe39ca4b26c9e05edda102..aac98fa42ed446cd76717ed7d28620e73a761c8a 100644 --- a/cvat-ui/src/containers/create-task-page/create-task-page.tsx +++ b/cvat-ui/src/containers/create-task-page/create-task-page.tsx @@ -12,12 +12,12 @@ interface StateToProps { } interface DispatchToProps { - create: (data: CreateTaskData) => void; + onCreate: (data: CreateTaskData) => void; } function mapDispatchToProps(dispatch: any): DispatchToProps { return { - create: (data: CreateTaskData) => dispatch(createTaskAsync(data)), + onCreate: (data: CreateTaskData): void => dispatch(createTaskAsync(data)), }; } @@ -29,13 +29,9 @@ function mapStateToProps(state: CombinedState): StateToProps { }; } -function CreateTaskPageContainer(props: StateToProps & DispatchToProps) { +function CreateTaskPageContainer(props: StateToProps & DispatchToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/file-manager/file-manager.tsx b/cvat-ui/src/containers/file-manager/file-manager.tsx index 9cdbd9ed9fd523a45ccba1bedd71fa8f7debdcde..40147d1111e2abcebd0afb38fa08a419441b57bf 100644 --- a/cvat-ui/src/containers/file-manager/file-manager.tsx +++ b/cvat-ui/src/containers/file-manager/file-manager.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { TreeNodeNormal } from 'antd/lib/tree/Tree' +import { TreeNodeNormal } from 'antd/lib/tree/Tree'; import FileManagerComponent, { Files } from '../../components/file-manager/file-manager'; import { loadShareDataAsync } from '../../actions/share-actions'; @@ -44,9 +44,9 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - getTreeData: (key: string, success: () => void, failure: () => void) => { + getTreeData: (key: string, success: () => void, failure: () => void): void => { dispatch(loadShareDataAsync(key, success, failure)); - } + }, }; } @@ -63,13 +63,21 @@ export class FileManagerContainer extends React.PureComponent { return this.managerComponentRef.reset(); } - public render() { + public render(): JSX.Element { + const { + treeData, + getTreeData, + withRemote, + } = this.props; + return ( this.managerComponentRef = component} + treeData={treeData} + onLoadData={getTreeData} + withRemote={withRemote} + ref={(component): void => { + this.managerComponentRef = component; + }} /> ); } diff --git a/cvat-ui/src/containers/header/header.tsx b/cvat-ui/src/containers/header/header.tsx index f3ec07fbe63372bb8f26ded3f7c9dbbc7488ea26..b434c18b6b128df13ed3b7973f89e25cd608ba2a 100644 --- a/cvat-ui/src/containers/header/header.tsx +++ b/cvat-ui/src/containers/header/header.tsx @@ -19,7 +19,7 @@ interface StateToProps { } interface DispatchToProps { - logout(): void; + onLogout(): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -37,21 +37,13 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - logout: () => dispatch(logoutAsync()), - } + onLogout: (): void => dispatch(logoutAsync()), + }; } -function HeaderContainer(props: StateToProps & DispatchToProps) { +function HeaderContainer(props: StateToProps & DispatchToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/login-page/login-page.tsx b/cvat-ui/src/containers/login-page/login-page.tsx index f04e0e3c23920a88633e25847613c690debaa535..b6b9b57dee84ba5901e1f8faaa92d2c189ba9ecc 100644 --- a/cvat-ui/src/containers/login-page/login-page.tsx +++ b/cvat-ui/src/containers/login-page/login-page.tsx @@ -9,7 +9,7 @@ interface StateToProps { } interface DispatchToProps { - login(username: string, password: string): void; + onLogin(username: string, password: string): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -20,16 +20,13 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - login: (...args) => dispatch(loginAsync(...args)), + onLogin: (...args): void => dispatch(loginAsync(...args)), }; } -function LoginPageContainer(props: DispatchToProps & StateToProps) { +function LoginPageContainer(props: DispatchToProps & StateToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx b/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx index 290c4df86e365b8a3add98e1318878623926cd88..1da1386caef25e4a4fbc095093cbba3ae434e9ac 100644 --- a/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx +++ b/cvat-ui/src/containers/model-runner-dialog/model-runner-dialog.tsx @@ -18,18 +18,18 @@ interface StateToProps { modelsInitialized: boolean; models: Model[]; activeProcesses: { - [index: string]: string + [index: string]: string; }; taskInstance: any; visible: boolean; } interface DispatchToProps { - inferModelAsync( + runInference( taskInstance: any, model: Model, mapping: { - [index: string]: string + [index: string]: string; }, cleanOut: boolean, ): void; @@ -52,42 +52,33 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return ({ - inferModelAsync( + runInference( taskInstance: any, model: Model, mapping: { - [index: string]: string + [index: string]: string; }, - cleanOut: boolean): void { - dispatch(inferModelAsync(taskInstance, model, mapping, cleanOut)); + cleanOut: boolean, + ): void { + dispatch(inferModelAsync(taskInstance, model, mapping, cleanOut)); }, getModels(): void { dispatch(getModelsAsync()); }, closeDialog(): void { dispatch(closeRunModelDialog()); - } + }, }); } -function ModelRunnerModalContainer(props: StateToProps & DispatchToProps) { +function ModelRunnerModalContainer(props: StateToProps & DispatchToProps): JSX.Element { return ( - + ); } export default connect( mapStateToProps, mapDispatchToProps, -) (ModelRunnerModalContainer); +)(ModelRunnerModalContainer); diff --git a/cvat-ui/src/containers/models-page/models-page.tsx b/cvat-ui/src/containers/models-page/models-page.tsx index 76c2e7f81b8a1e60945ea541b80e227454f9302c..ba56bb97e255686fa6cd19b354a6759e5f33b103 100644 --- a/cvat-ui/src/containers/models-page/models-page.tsx +++ b/cvat-ui/src/containers/models-page/models-page.tsx @@ -43,33 +43,28 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - getModels() { + getModels(): void { dispatch(getModelsAsync()); }, - deleteModel(id: number) { + deleteModel(id: number): void { dispatch(deleteModelAsync(id)); }, }; } -function ModelsPageContainer(props: DispatchToProps & StateToProps) { - const render = props.installedAutoAnnotation - || props.installedTFAnnotation - || props.installedTFSegmentation; +function ModelsPageContainer(props: DispatchToProps & StateToProps): JSX.Element | null { + const { + installedAutoAnnotation, + installedTFSegmentation, + installedTFAnnotation, + } = props; + + const render = installedAutoAnnotation + || installedTFAnnotation + || installedTFSegmentation; return ( - render ? - : null + render ? : null ); } diff --git a/cvat-ui/src/containers/register-page/register-page.tsx b/cvat-ui/src/containers/register-page/register-page.tsx index f989ece22bc22e8b739f305b93a0c15915fdf991..c11310865444072aee9b611bb5fc1227ec57a902 100644 --- a/cvat-ui/src/containers/register-page/register-page.tsx +++ b/cvat-ui/src/containers/register-page/register-page.tsx @@ -9,7 +9,7 @@ interface StateToProps { } interface DispatchToProps { - register: (username: string, firstName: string, + onRegister: (username: string, firstName: string, lastName: string, email: string, password1: string, password2: string) => void; } @@ -22,16 +22,13 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - register: (...args) => dispatch(registerAsync(...args)) - } + onRegister: (...args): void => dispatch(registerAsync(...args)), + }; } -function RegisterPageContainer(props: StateToProps & DispatchToProps) { +function RegisterPageContainer(props: StateToProps & DispatchToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/task-page/details.tsx b/cvat-ui/src/containers/task-page/details.tsx index 1f5155f34d65ab5295d750262da6f08c7b8d56fc..913bbdf19ddff07dcd07ed9d659360a1d7682ac7 100644 --- a/cvat-ui/src/containers/task-page/details.tsx +++ b/cvat-ui/src/containers/task-page/details.tsx @@ -21,7 +21,7 @@ interface DispatchToProps { onTaskUpdate: (taskInstance: any) => void; } -function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { +function mapStateToProps(state: CombinedState): StateToProps { const { plugins } = state.plugins; return { @@ -33,20 +33,26 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - onTaskUpdate: (taskInstance: any) => - dispatch(updateTaskAsync(taskInstance)) - } + onTaskUpdate: (taskInstance: any): void => dispatch(updateTaskAsync(taskInstance)), + }; } -function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps) { +function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps): JSX.Element { + const { + task, + installedGit, + registeredUsers, + onTaskUpdate, + } = props; + return ( ); } @@ -54,4 +60,4 @@ function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps) { export default connect( mapStateToProps, mapDispatchToProps, -)(TaskPageContainer); \ No newline at end of file +)(TaskPageContainer); diff --git a/cvat-ui/src/containers/task-page/job-list.tsx b/cvat-ui/src/containers/task-page/job-list.tsx index 1714c585bda9eb7e423f64deefd161ba7e0a196a..c7e32c589ccb8bec95e72981a84e52a239a75986 100644 --- a/cvat-ui/src/containers/task-page/job-list.tsx +++ b/cvat-ui/src/containers/task-page/job-list.tsx @@ -28,16 +28,22 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - onJobUpdate: (jobInstance: any) => dispatch(updateJobAsync(jobInstance)), + onJobUpdate: (jobInstance: any): void => dispatch(updateJobAsync(jobInstance)), }; } -function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps) { +function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps): JSX.Element { + const { + task, + registeredUsers, + onJobUpdate, + } = props; + return ( ); } @@ -45,4 +51,4 @@ function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps) { export default connect( mapStateToProps, mapDispatchToProps, -)(TaskPageContainer); \ No newline at end of file +)(TaskPageContainer); diff --git a/cvat-ui/src/containers/task-page/task-page.tsx b/cvat-ui/src/containers/task-page/task-page.tsx index 844df0844ff88ac2072d4ea9ded3d6536c9bfa4b..1133e16620b3320e90aaad7406ba0f0910eb8c85 100644 --- a/cvat-ui/src/containers/task-page/task-page.tsx +++ b/cvat-ui/src/containers/task-page/task-page.tsx @@ -21,7 +21,7 @@ interface StateToProps { } interface DispatchToProps { - fetchTask: (tid: number) => void; + onFetchTask: (tid: number) => void; } function mapStateToProps(state: CombinedState, own: Props): StateToProps { @@ -47,7 +47,7 @@ function mapStateToProps(state: CombinedState, own: Props): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - fetchTask: (tid: number) => { + onFetchTask: (tid: number): void => { dispatch(getTasksAsync({ id: tid, page: 1, @@ -62,15 +62,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { }; } -function TaskPageContainer(props: StateToProps & DispatchToProps) { +function TaskPageContainer(props: StateToProps & DispatchToProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/tasks-page/task-item.tsx b/cvat-ui/src/containers/tasks-page/task-item.tsx index e13390d7e3f5f31b72c3d2040a8c78fa5209b947..38e5c1931167a1da5bb28357f8039ad6b4ce80f8 100644 --- a/cvat-ui/src/containers/tasks-page/task-item.tsx +++ b/cvat-ui/src/containers/tasks-page/task-item.tsx @@ -7,14 +7,14 @@ import { ActiveInference, } from '../../reducers/interfaces'; -import TaskItemComponent from '../../components/tasks-page/task-item' +import TaskItemComponent from '../../components/tasks-page/task-item'; import { getTasksAsync, } from '../../actions/tasks-actions'; interface StateToProps { - deleteActivity: boolean | null; + deleted: boolean; previewImage: string; taskInstance: any; activeInference: ActiveInference | null; @@ -35,7 +35,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { const id = own.taskID; return { - deleteActivity: deletes.byTask[id] ? deletes.byTask[id] : null, + deleted: deletes.byTask[id] ? deletes.byTask[id] === true : false, previewImage: task.preview, taskInstance: task.instance, activeInference: state.models.inferences[id] || null, @@ -47,19 +47,14 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { getTasks: (query: TasksQuery): void => { dispatch(getTasksAsync(query)); }, - } + }; } type TasksItemContainerProps = StateToProps & DispatchToProps & OwnProps; -function TaskItemContainer(props: TasksItemContainerProps) { +function TaskItemContainer(props: TasksItemContainerProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/containers/tasks-page/tasks-list.tsx b/cvat-ui/src/containers/tasks-page/tasks-list.tsx index 8782e3f9fb15816036478b6d047375c066d1697c..7afd17b8de23c5bab7e474b12d4718bc66937b8d 100644 --- a/cvat-ui/src/containers/tasks-page/tasks-list.tsx +++ b/cvat-ui/src/containers/tasks-page/tasks-list.tsx @@ -33,19 +33,26 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - getTasks: (query: TasksQuery) => {dispatch(getTasksAsync(query))} - } + getTasks: (query: TasksQuery): void => { + dispatch(getTasksAsync(query)); + }, + }; } type TasksListContainerProps = StateToProps & DispatchToProps & OwnProps; -function TasksListContainer(props: TasksListContainerProps) { +function TasksListContainer(props: TasksListContainerProps): JSX.Element { + const { + tasks, + onSwitchPage, + } = props; + return ( task.instance.id)} - currentPage={props.tasks.gettingQuery.page} - numberOfTasks={props.tasks.count} + onSwitchPage={onSwitchPage} + currentTasksIndexes={tasks.current.map((task): number => task.instance.id)} + currentPage={tasks.gettingQuery.page} + numberOfTasks={tasks.count} /> ); } diff --git a/cvat-ui/src/containers/tasks-page/tasks-page.tsx b/cvat-ui/src/containers/tasks-page/tasks-page.tsx index 1420ca58927c9c3a506038002824c10bfaaae39c..006e2c7d3e2186c01e60e5c8ac3cd71d3d0f129c 100644 --- a/cvat-ui/src/containers/tasks-page/tasks-page.tsx +++ b/cvat-ui/src/containers/tasks-page/tasks-page.tsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { TasksQuery, - CombinedState + CombinedState, } from '../../reducers/interfaces'; import TasksPageComponent from '../../components/tasks-page/tasks-page'; @@ -18,7 +18,7 @@ interface StateToProps { } interface DispatchToProps { - getTasks: (gettingQuery: TasksQuery) => void; + onGetTasks: (gettingQuery: TasksQuery) => void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -34,21 +34,17 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { - getTasks: (query: TasksQuery) => {dispatch(getTasksAsync(query))} - } + onGetTasks: (query: TasksQuery): void => { + dispatch(getTasksAsync(query)); + }, + }; } type TasksPageContainerProps = StateToProps & DispatchToProps; -function TasksPageContainer(props: TasksPageContainerProps) { +function TasksPageContainer(props: TasksPageContainerProps): JSX.Element { return ( - + ); } diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx index 621c4813a3da6cd243ba71b50d524c87f400b15e..3ef428246f11c7dc0251e85e99f264b59c6a59c1 100644 --- a/cvat-ui/src/index.tsx +++ b/cvat-ui/src/index.tsx @@ -19,7 +19,7 @@ import { import { CombinedState, NotificationsState, - } from './reducers/interfaces'; +} from './reducers/interfaces'; createCVATStore(createRootReducer); const cvatStore = getCVATStore(); @@ -65,7 +65,7 @@ function mapStateToProps(state: CombinedState): StateToProps { installedAutoAnnotation: plugins.plugins.AUTO_ANNOTATION, installedTFSegmentation: plugins.plugins.TF_SEGMENTATION, installedTFAnnotation: plugins.plugins.TF_ANNOTATION, - notifications: {...state.notifications}, + notifications: { ...state.notifications }, user: auth.user, }; } @@ -81,29 +81,10 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { }; } -function reduxAppWrapper(props: StateToProps & DispatchToProps) { +function reduxAppWrapper(props: StateToProps & DispatchToProps): JSX.Element { return ( - - ) + + ); } const ReduxAppWrapper = connect( @@ -114,8 +95,8 @@ const ReduxAppWrapper = connect( ReactDOM.render( ( - + ), - document.getElementById('root') -) + document.getElementById('root'), +); diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index 023aad82cf9de65adf929ced8bf316102afbbc0f..63fecdf67ecec8f6e1023bb9061793b71b97127c 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -18,15 +18,15 @@ export default (state = defaultState, action: AnyAction): AuthState => { user: action.payload.user, }; case AuthActionTypes.AUTHORIZED_FAILED: - return { - ...state, - initialized: true, - }; + return { + ...state, + initialized: true, + }; case AuthActionTypes.LOGIN: return { ...state, fetching: true, - } + }; case AuthActionTypes.LOGIN_SUCCESS: return { ...state, @@ -49,11 +49,6 @@ export default (state = defaultState, action: AnyAction): AuthState => { fetching: false, user: null, }; - case AuthActionTypes.LOGIN_FAILED: - return { - ...state, - fetching: false, - }; case AuthActionTypes.REGISTER: return { ...state, diff --git a/cvat-ui/src/reducers/formats-reducer.ts b/cvat-ui/src/reducers/formats-reducer.ts index 01fa6e43d7164d9db68ef6513489ac5edf06d6de..40fd2a80506fdecc977eef6c3eaf72bea3520b38 100644 --- a/cvat-ui/src/reducers/formats-reducer.ts +++ b/cvat-ui/src/reducers/formats-reducer.ts @@ -37,7 +37,7 @@ export default (state = defaultState, action: AnyAction): FormatsState => { case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: return state; diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index c08af738c5293c72fceb2178a135cf42235dcab0..6a18564671fcd0e74e6d9b06fc16ff29700536a8 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -147,8 +147,8 @@ export interface ModelFiles { } export interface ErrorState { - message: string, - reason: string, + message: string; + reason: string; } export interface NotificationsState { diff --git a/cvat-ui/src/reducers/models-reducer.ts b/cvat-ui/src/reducers/models-reducer.ts index 8636b224ab5fb9c9bfea2bef84d4752d44165043..664622fecffe9bff1cf015d461ec11ed22651a48 100644 --- a/cvat-ui/src/reducers/models-reducer.ts +++ b/cvat-ui/src/reducers/models-reducer.ts @@ -110,7 +110,7 @@ export default function (state = defaultState, action: AnyAction): ModelsState { case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: { return { diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 86074948396a91928f20656bf4f789ce82bc2246..e6fa348738606e103aa7df762338c8d2461a6f12 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -67,7 +67,7 @@ export default function (state = defaultState, action: AnyAction): Notifications authorized: { message: 'Could not check authorization on the server', reason: action.payload.error.toString(), - } + }, }, }, }; @@ -82,7 +82,7 @@ export default function (state = defaultState, action: AnyAction): Notifications login: { message: 'Could not login on the server', reason: action.payload.error.toString(), - } + }, }, }, }; @@ -323,7 +323,7 @@ export default function (state = defaultState, action: AnyAction): Notifications } case ModelsActionTypes.GET_INFERENCE_STATUS_SUCCESS: { if (action.payload.activeInference.status === 'finished') { - const taskID = action.payload.taskID; + const { taskID } = action.payload; return { ...state, messages: { @@ -351,7 +351,7 @@ export default function (state = defaultState, action: AnyAction): Notifications metaFetching: { message: 'Could not fetch models meta information', reason: action.payload.error.toString(), - } + }, }, }, }; @@ -368,7 +368,7 @@ export default function (state = defaultState, action: AnyAction): Notifications message: 'Could not fetch inference status for the ' + `task ${taskID}`, reason: action.payload.error.toString(), - } + }, }, }, }; @@ -400,7 +400,7 @@ export default function (state = defaultState, action: AnyAction): Notifications message: 'Could not infer model for the ' + `task ${taskID}`, reason: action.payload.error.toString(), - } + }, }, }, }; @@ -424,7 +424,7 @@ export default function (state = defaultState, action: AnyAction): Notifications case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: { return { diff --git a/cvat-ui/src/reducers/plugins-reducer.ts b/cvat-ui/src/reducers/plugins-reducer.ts index 29290e18d456c57e1b15aa1a5bb18d87d19bf2bd..1a7e1f16078767dde0b6a12bbc0ca930c5695f1e 100644 --- a/cvat-ui/src/reducers/plugins-reducer.ts +++ b/cvat-ui/src/reducers/plugins-reducer.ts @@ -45,7 +45,7 @@ export default function (state = defaultState, action: AnyAction): PluginsState case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: return { ...state }; diff --git a/cvat-ui/src/reducers/share-reducer.ts b/cvat-ui/src/reducers/share-reducer.ts index 66a989f1970cbb6851892fa166a2ac6507a499be..185bf2565e90256694ab22f3ed2ceb820e0f90e7 100644 --- a/cvat-ui/src/reducers/share-reducer.ts +++ b/cvat-ui/src/reducers/share-reducer.ts @@ -46,7 +46,7 @@ export default function (state = defaultState, action: AnyAction): ShareState { case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: return { diff --git a/cvat-ui/src/reducers/tasks-reducer.ts b/cvat-ui/src/reducers/tasks-reducer.ts index 200d4f4d0d10ef99c6e8834c7ef7fd70ed329175..cb6c975ff81435dd3d35c272165158ac4de2fbb0 100644 --- a/cvat-ui/src/reducers/tasks-reducer.ts +++ b/cvat-ui/src/reducers/tasks-reducer.ts @@ -408,7 +408,7 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: return state; diff --git a/cvat-ui/src/reducers/users-reducer.ts b/cvat-ui/src/reducers/users-reducer.ts index b1f10d42742758f48ebce774773c6b695d9f1020..bb37e6dfc4702fe2e2eda3af1426c3b169ceb09e 100644 --- a/cvat-ui/src/reducers/users-reducer.ts +++ b/cvat-ui/src/reducers/users-reducer.ts @@ -35,7 +35,7 @@ export default function (state: UsersState = defaultState, action: AnyAction): U case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState, - } + }; } default: return { diff --git a/cvat-ui/src/stylesheet.css b/cvat-ui/src/stylesheet.css index 1a2aef1828a4c9cb7cb4a2b66a1bbf934dde7283..4c3c2b34c05fb38de6248b82f5a8fa27c31b03f8 100644 --- a/cvat-ui/src/stylesheet.css +++ b/cvat-ui/src/stylesheet.css @@ -275,6 +275,10 @@ border: 1px solid #40a9ff; } +.cvat-tasks-list-item > div:nth-child(2) { + word-break: break-all; +} + .cvat-tasks-list-item > div:nth-child(4) > div { margin-right: 20px; }