@ -0,0 +1,5 @@
|
||||
dist
|
||||
docs
|
||||
node_modules
|
||||
reports
|
||||
|
||||
@ -1,2 +1 @@
|
||||
build
|
||||
node_modules
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
'env': {
|
||||
'node': true,
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
},
|
||||
'parserOptions': {
|
||||
'parser': '@typescript-eslint/parser',
|
||||
'ecmaVersion': 6,
|
||||
},
|
||||
'plugins': [
|
||||
'@typescript-eslint',
|
||||
'import',
|
||||
],
|
||||
'extends': [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'airbnb-typescript/base',
|
||||
'plugin:import/errors',
|
||||
'plugin:import/warnings',
|
||||
'plugin:import/typescript',
|
||||
],
|
||||
'rules': {
|
||||
'@typescript-eslint/indent': ['warn', 4],
|
||||
'@typescript-eslint/no-explicit-any': [0],
|
||||
'no-restricted-syntax': [0, {'selector': 'ForOfStatement'}],
|
||||
},
|
||||
'settings': {
|
||||
'import/resolver': {
|
||||
'node': {
|
||||
'extensions': ['.tsx', '.ts', '.jsx', '.js', '.json'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
const { override, fixBabelImports, addLessLoader } = require('customize-cra');
|
||||
|
||||
module.exports = override(
|
||||
fixBabelImports('import', {
|
||||
libraryName: 'antd',
|
||||
libraryDirectory: 'es',
|
||||
style: true,
|
||||
}),
|
||||
|
||||
addLessLoader({
|
||||
javascriptEnabled: true,
|
||||
// modifyVars: { '@primary-color': '#1DA57A' },
|
||||
}),
|
||||
);
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M25.9 23c-1.73 0-2.561 1-5.4 1-2.839 0-3.664-1-5.4-1-4.472 0-8.1 3.762-8.1 8.4V33c0 1.656 1.296 3 2.893 3h21.214C32.704 36 34 34.656 34 33v-1.6c0-4.637-3.628-8.4-8.1-8.4zm5.207 10H9.893v-1.6c0-2.975 2.338-5.4 5.207-5.4.88 0 2.308 1 5.4 1 3.116 0 4.514-1 5.4-1 2.869 0 5.207 2.425 5.207 5.4V33zM20.5 22c4.791 0 8.679-4.031 8.679-9S25.29 4 20.5 4s-8.679 4.031-8.679 9 3.888 9 8.679 9zm0-15c3.188 0 5.786 2.694 5.786 6s-2.598 6-5.786 6c-3.188 0-5.786-2.694-5.786-6s2.598-6 5.786-6z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 591 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M30.72 25.142h-7.625l4.013 9.562c.28.663-.04 1.406-.679 1.687l-3.534 1.507a1.281 1.281 0 0 1-1.677-.683l-3.813-9.08-6.229 6.267c-.83.835-2.176.192-2.176-.904V3.287c0-1.153 1.432-1.716 2.176-.904l20.442 20.569c.825.786.216 2.19-.898 2.19z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 350 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" stroke="#000" stroke-width="2" fill="none"><path d="M3 9h34v22H3z"/><path d="M33.626 16.983h-2.571v-5.538h-3.858v5.538h-2.571l4.5 6.462z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 235 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M4 11h32v19H4V11zm3.2 3.167h3.84m-3.84-.254v4.18m26.24 9.5H29.6m3.84.254v-4.18" stroke="#000" stroke-width="2" fill="none" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 222 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M5.816 24.607v6.572h6.53V32H5v-7.393h.816zm29.184 0V32h-7.347v-.821h6.53v-6.572H35zM12.347 9v.821h-6.53v6.572H5V9h7.347zM35 9v7.393h-.816V9.82h-6.53V9H35z" stroke="#000" fill="#000" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 281 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path stroke="#4A4A4A" stroke-width="1.473" d="M4.236 9.191h31.527v21.8H4.236z"/><path fill="#4A4A4A" d="M2 7h4.5v4.364H2zM33.5 7H38v4.364h-4.5zM33.5 28.818H38v4.364h-4.5zM2 28.818h4.5v4.364H2z"/><g stroke="#4A4A4A" stroke-width="1.591"><path fill="#4A4A4A" d="M10.295 13.613h13.409v8.591H10.295z"/><path d="M17.795 17.977h13.409v8.591H17.795z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 458 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M20 2c9.941 0 18 8.059 18 18s-8.059 18-18 18S2 29.941 2 20 10.059 2 20 2zm0 2.4C11.384 4.4 4.4 11.384 4.4 20S11.384 35.6 20 35.6 35.6 28.616 35.6 20 28.616 4.4 20 4.4zm1.27 25.2h-2.642V14.147h2.642V29.6zm-2.856-19.552c0-.429.13-.79.393-1.086.261-.295.65-.443 1.164-.443.514 0 .904.148 1.17.443.267.295.4.657.4 1.086 0 .428-.133.785-.4 1.07-.266.286-.656.43-1.17.43-.515 0-.903-.144-1.164-.43-.262-.285-.393-.642-.393-1.07z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 535 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M37.011 17.886L22.114 2.99A3.375 3.375 0 0 0 19.727 2H5.375A3.375 3.375 0 0 0 2 5.375v14.352c0 .895.356 1.754.989 2.387L17.886 37.01a3.375 3.375 0 0 0 4.773 0L37.011 22.66a3.375 3.375 0 0 0 0-4.773zm-1.59 3.182L21.068 35.421a1.125 1.125 0 0 1-1.59 0L4.579 20.522a1.118 1.118 0 0 1-.329-.795V5.375c0-.62.505-1.125 1.125-1.125h14.352c.3 0 .583.117.796.33L35.42 19.476a1.126 1.126 0 0 1 0 1.591zm-23.296-10.35a1.408 1.408 0 0 1 0 2.812c-.775.001-1.406-.63-1.406-1.405s.63-1.406 1.406-1.406zm0-1.968a3.375 3.375 0 1 0 0 6.75 3.375 3.375 0 0 0 0-6.75z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 659 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g transform="translate(2 6)" fill="none" fill-rule="evenodd"><path d="M2.605 0c-1.956 6.618.068 11.128 6.072 13.53 9.005 3.604 14.282.585 19.39 3.156C31.473 18.4 33.85 21.858 35.2 27.06" stroke="#000" stroke-width="2" stroke-linecap="square"/><circle fill="#000" cx="1.956" cy="4.571" r="1.956"/><circle fill="#000" cx="11.733" cy="14.349" r="1.956"/><circle fill="#000" cx="27.378" cy="16.305" r="1.956"/></g></svg>
|
||||
|
After Width: | Height: | Size: 480 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle stroke="#000" stroke-width="2" cx="20" cy="20" r="17"/><circle fill="#000" cx="20" cy="20" r="7.5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 216 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M20 4.236L4.057 15.82l6.09 18.742h19.707l6.09-18.742L20 4.236z" stroke="#3B3B3B" stroke-width="2" fill="none" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 209 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path stroke="#000" stroke-width="2" d="M3 8h34v25H3z" fill="none" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 157 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M5 30h30v-3.333H5V30zm0-8.333h30v-3.334H5v3.334zM5 10v3.333h30V10H5z" fill="#000" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 181 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M37 8H15v9H3v16h22v-9h12V8z" stroke="#000" stroke-width="2" fill="none" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 171 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M36.918 19.593a1.05 1.05 0 0 0-.231-.346l-3.633-3.633a1.064 1.064 0 1 0-1.505 1.505l1.817 1.816H21.062l.001-12.301L22.96 8.53a1.062 1.062 0 0 0 1.505 0 1.064 1.064 0 0 0 0-1.505L20.752 3.31a1.063 1.063 0 0 0-1.16-.23c-.13.054-.246.13-.343.228l-.002.001-3.636 3.634a1.066 1.066 0 0 0 .753 1.818c.272 0 .545-.105.753-.312l1.818-1.817-.001 12.302H6.635l1.816-1.816a1.065 1.065 0 0 0-1.506-1.505l-3.633 3.634a1.059 1.059 0 0 0 0 1.505l3.713 3.713a1.059 1.059 0 0 0 1.506 0 1.065 1.065 0 0 0 0-1.505l-1.898-1.897h12.3v12.3l-1.816-1.815a1.066 1.066 0 0 0-1.506 1.506l3.636 3.633a1.059 1.059 0 0 0 1.504 0l3.714-3.714a1.064 1.064 0 1 0-1.506-1.505l-1.897 1.898V21.064h12.304l-1.896 1.897a1.065 1.065 0 0 0 1.505 1.505l3.713-3.713c.002-.003.003-.007.007-.01a1.064 1.064 0 0 0 .223-1.15z" fill="#000" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 891 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g transform="translate(3 7)" fill="#000" fill-rule="evenodd"><rect x="5.75" y="4.5" width="22.5" height="15.75" rx="2.25"/><path stroke="#F1F1F1" stroke-width="2.25" d="M-1.39 2.72l2.451-3.773 33.967 22.057-2.451 3.774z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 296 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M33.632 2.58a2.058 2.058 0 0 1 2.863 0l1.912 1.868c.79.772.79 2.022.003 2.795L25.42 20l12.99 12.757a1.947 1.947 0 0 1-.003 2.795l-1.912 1.868a2.058 2.058 0 0 1-2.863 0L17.24 21.4a1.947 1.947 0 0 1 0-2.798zm-15.647 0a2.058 2.058 0 0 1 2.863 0l1.912 1.868c.79.772.79 2.022.003 2.795L9.773 20l12.99 12.757a1.947 1.947 0 0 1-.003 2.795l-1.912 1.868a2.058 2.058 0 0 1-2.863 0L1.593 21.4a1.947 1.947 0 0 1 0-2.798z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 521 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M13.762 2v14.774L32 2v36L13.762 23.225V38H7V2h6.762z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 165 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M6.368 2.58a2.058 2.058 0 0 0-2.863 0L1.593 4.448a1.947 1.947 0 0 0-.003 2.795L14.58 20 1.59 32.757a1.947 1.947 0 0 0 .003 2.795l1.912 1.868c.79.773 2.072.773 2.863 0L22.76 21.4c.79-.773.79-2.025 0-2.798zm15.647 0a2.058 2.058 0 0 0-2.863 0L17.24 4.448a1.947 1.947 0 0 0-.003 2.795L30.227 20l-12.99 12.757a1.947 1.947 0 0 0 .003 2.795l1.912 1.868c.79.773 2.072.773 2.863 0L38.407 21.4c.79-.773.79-2.025 0-2.798z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 523 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M25.238 2v14.774L7 2v36l18.238-14.775V38H32V2h-6.762z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 166 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M30.76 21.399L14.368 37.42a2.058 2.058 0 0 1-2.863 0l-1.912-1.868a1.947 1.947 0 0 1-.003-2.795L22.58 20 9.59 7.243a1.947 1.947 0 0 1 .003-2.795l1.912-1.868a2.058 2.058 0 0 1 2.863 0L30.76 18.6c.79.773.79 2.025 0 2.798z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 331 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path fill="#000" fill-rule="nonzero" d="M35.5 20l-30 19.5V.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 134 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M9.593 21.399L25.985 37.42c.79.773 2.073.773 2.863 0l1.912-1.868c.79-.772.79-2.022.003-2.795L17.773 20l12.99-12.757a1.947 1.947 0 0 0-.003-2.795L28.848 2.58a2.058 2.058 0 0 0-2.863 0L9.593 18.6a1.947 1.947 0 0 0 0 2.798z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 333 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M19.258 20.746l.049 13.82a.7.7 0 0 0 .697.698.687.687 0 0 0 .692-.693l-.048-13.924 13.924.049a.687.687 0 0 0 .692-.692.7.7 0 0 0-.698-.698l-13.82-.048-.048-13.83a.7.7 0 0 0-.697-.697.69.69 0 0 0-.692.693l.049 13.933-13.934-.048a.69.69 0 0 0-.692.692.7.7 0 0 0 .697.697l13.83.048z" stroke="#000" stroke-width="2" fill="#000" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 423 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M0 0h34v24H0z"/></defs><g transform="translate(3 8)" fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M28.262 9.331C20.935.18 8.932-.428 1.308 7.643V2.968H0v6.537c0 .45.293.817.654.817h5.232V8.688H2.35c7.125-7.322 18.2-6.683 24.987 1.8 7.14 8.92 7.14 23.437 0 32.357L28.262 44c7.65-9.557 7.65-25.111 0-34.669z" fill="#000" mask="url(#b)" transform="matrix(-1 0 0 1 34 0)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 557 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M5.322 5.322l3.365 3.365A15.947 15.947 0 0 1 20 4c8.817 0 16.006 7.195 16 16.012C35.994 28.843 28.833 36 20 36c-4.124 0-7.884-1.56-10.721-4.123a.776.776 0 0 1-.031-1.125l1.273-1.273a.777.777 0 0 1 1.065-.036A12.6 12.6 0 0 0 20 32.645c6.988 0 12.645-5.655 12.645-12.645 0-6.988-5.655-12.645-12.645-12.645a12.603 12.603 0 0 0-8.943 3.702l3.492 3.492a.774.774 0 0 1-.547 1.322H4.774A.774.774 0 0 1 4 15.097V5.869a.774.774 0 0 1 1.322-.547z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 549 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M29.131 5l5.257 5.257V35H5V5h24.131zm-.866 17.143H10.51v11.633h17.755V22.143zM10.51 6.224H6.224v27.552h3.062V20.918H29.49v12.858h3.673V10.764l-4.539-4.54h-.97v9.796H10.51V6.224zm8.878 20.205a.612.612 0 0 1 .1 1.216l-.1.008h-6.123a.612.612 0 0 1-.1-1.216l.1-.008h6.123zm2.271.177c.11.116.178.276.178.435 0 .159-.068.318-.178.435a.644.644 0 0 1-.435.177.63.63 0 0 1-.434-.177.64.64 0 0 1-.178-.435.64.64 0 0 1 .178-.435.641.641 0 0 1 .87 0zm-4.108-2.626a.612.612 0 0 1 .1 1.216l-.1.008h-4.286a.612.612 0 0 1-.1-1.216l.1-.008h4.286zm8.878-17.756H11.735v8.572h14.694V6.224zM25.51 7.45v6.122h-3.673V7.45h3.673zm-1.224 1.224H23.06v3.674h1.225V8.673z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 756 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M22.147 2C23.169 2 24 2.831 24 3.853h0v3.368c0 .542.298.988.798 1.195a1.257 1.257 0 0 0 1.41-.28h0l2.38-2.381c.701-.702 1.922-.7 2.622 0h0l3.035 3.035c.35.35.543.816.543 1.311s-.193.961-.543 1.311h0l-2.381 2.38c-.382.383-.487.91-.28 1.41.207.5.653.798 1.195.798h3.368c1.022 0 1.853.831 1.853 1.853h0v4.294A1.855 1.855 0 0 1 36.147 24h0-3.368c-.542 0-.989.298-1.196.798a1.26 1.26 0 0 0 .28 1.41h0l2.382 2.38c.35.35.542.816.542 1.31 0 .496-.192.962-.542 1.312h0l-3.036 3.035c-.7.7-1.92.702-2.622 0h0l-2.38-2.381a1.257 1.257 0 0 0-1.41-.28c-.5.207-.798.653-.798 1.195h0v3.368A1.855 1.855 0 0 1 22.146 38h0-4.293A1.855 1.855 0 0 1 16 36.147h0v-3.368c0-.542-.298-.988-.798-1.195a1.258 1.258 0 0 0-1.41.28h0l-2.38 2.381c-.701.702-1.921.701-2.622 0h0L5.755 31.21a1.844 1.844 0 0 1-.543-1.311c0-.495.193-.961.543-1.311h0l2.381-2.38c.382-.383.487-.91.28-1.41A1.257 1.257 0 0 0 7.221 24h0-3.368A1.855 1.855 0 0 1 2 22.146h0v-4.293C2 16.831 2.831 16 3.853 16h3.368c.542 0 .988-.298 1.195-.798a1.26 1.26 0 0 0-.28-1.41h0l-2.381-2.38a1.843 1.843 0 0 1-.543-1.31c0-.496.193-.962.543-1.312h0L8.79 5.755c.7-.7 1.92-.702 2.622 0h0l2.38 2.38c.383.382.911.49 1.41.281.5-.207.798-.653.798-1.195h0V3.853C16 2.831 16.831 2 17.853 2h0zm-.001 1.333h-4.293a.52.52 0 0 0-.52.52h0v3.368a2.584 2.584 0 0 1-1.621 2.426 2.586 2.586 0 0 1-2.863-.569h0l-2.38-2.38a.52.52 0 0 0-.736 0h0L6.697 9.732a.52.52 0 0 0 0 .736h0l2.382 2.38c.766.766.984 1.863.569 2.863a2.586 2.586 0 0 1-2.427 1.621h0-3.368a.52.52 0 0 0-.52.52h0v4.294c0 .286.234.52.52.52h3.368c1.083 0 2.013.621 2.427 1.62.415 1 .196 2.098-.57 2.863h0l-2.38 2.38a.52.52 0 0 0 0 .737h0l3.035 3.035a.52.52 0 0 0 .736 0h0l2.38-2.381a2.59 2.59 0 0 1 2.863-.569 2.586 2.586 0 0 1 1.621 2.427h0v3.368c0 .286.234.52.52.52h4.294a.52.52 0 0 0 .52-.52h0v-3.368c0-1.083.621-2.013 1.621-2.427 1-.413 2.097-.197 2.863.57h0l2.38 2.38a.52.52 0 0 0 .736 0h0l3.036-3.035a.52.52 0 0 0 0-.736h0l-2.382-2.38a2.585 2.585 0 0 1-.569-2.863 2.586 2.586 0 0 1 2.427-1.621h3.368a.52.52 0 0 0 .52-.52h0v-4.294a.52.52 0 0 0-.52-.519h0-3.368a2.586 2.586 0 0 1-2.427-1.621c-.415-1-.196-2.098.57-2.863h0l2.38-2.38a.52.52 0 0 0 0-.737h0l-3.035-3.035a.52.52 0 0 0-.736 0h0l-2.38 2.38a2.585 2.585 0 0 1-2.863.57 2.586 2.586 0 0 1-1.621-2.427h0V3.853a.52.52 0 0 0-.521-.52h0zM20 14c3.309 0 6 2.691 6 6s-2.691 6-6 6-6-2.691-6-6 2.691-6 6-6zm0 1.333A4.673 4.673 0 0 0 15.333 20 4.673 4.673 0 0 0 20 24.667 4.673 4.673 0 0 0 24.667 20 4.673 4.673 0 0 0 20 15.333z" stroke="#000" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" stroke="#000" stroke-width="2" fill="none"><path d="M3 9h34v22H3z"/><path d="M28.5 9H37v22h-8.5z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 195 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M5 10.75h30v19.5H5v-19.5zM20 7v27" stroke="#4A4A4A" stroke-width="2" fill="none" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 180 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M20.5 11c2.475 0 4.5-2.025 4.5-4.5S22.975 2 20.5 2A4.513 4.513 0 0 0 16 6.5c0 2.475 2.025 4.5 4.5 4.5zm0 4.5A4.513 4.513 0 0 0 16 20c0 2.475 2.025 4.5 4.5 4.5S25 22.475 25 20s-2.025-4.5-4.5-4.5zm0 13.5a4.513 4.513 0 0 0-4.5 4.5c0 2.475 2.025 4.5 4.5 4.5s4.5-2.025 4.5-4.5-2.025-4.5-4.5-4.5z" fill="#000" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M0 0h34v24H0z"/></defs><g transform="translate(3 8)" fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M28.262 9.331C20.935.18 8.932-.428 1.308 7.643V2.968H0v6.537c0 .45.293.817.654.817h5.232V8.688H2.35c7.125-7.322 18.2-6.683 24.987 1.8 7.14 8.92 7.14 23.437 0 32.357L28.262 44c7.65-9.557 7.65-25.111 0-34.669z" fill="#000" mask="url(#b)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 523 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g stroke="#4A4A4A" stroke-width="2.25" fill="none" fill-rule="evenodd"><path d="M2 7h27.014v16.766H2V7z" stroke-linecap="square" stroke-dasharray="1.131428450345993,3.394285619258881"/><path d="M31.372 27.628l5.635 6.231m-10.106-4.678c3.394 0 6.146-2.723 6.146-6.082 0-3.36-2.752-6.082-6.146-6.082-3.395 0-6.147 2.723-6.147 6.082s2.752 6.082 6.147 6.082z" fill="#F1F1F1" fill-rule="nonzero" stroke-linecap="round"/></g></svg>
|
||||
|
After Width: | Height: | Size: 489 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M36.889 5.188H29.5L27.389 3H22.11C20.945 3 20 3.98 20 5.188v10.937c0 1.208.945 2.188 2.111 2.188H36.89c1.166 0 2.111-.98 2.111-2.188v-8.75c0-1.208-.945-2.188-2.111-2.188zm0 19.687H29.5l-2.111-2.188H22.11c-1.166 0-2.111.98-2.111 2.188v10.938C20 37.02 20.945 38 22.111 38H36.89C38.055 38 39 37.02 39 35.812v-8.75c0-1.208-.945-2.187-2.111-2.187zM5.222 4.094C5.222 3.49 4.75 3 4.167 3H2.056C1.473 3 1 3.49 1 4.094v27.343c0 1.209.945 2.188 2.111 2.188H17.89V29.25H5.222V13.937H17.89V9.564H5.222v-5.47z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 609 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M35.748 2H4.253C2.254 2 1.246 4.425 2.662 5.841L15.5 18.682v13.13c0 .65.28 1.267.768 1.694l4.5 3.936c1.437 1.258 3.732.259 3.732-1.693V18.682L37.339 5.841C38.752 4.428 37.75 2 35.748 2zM22.25 17.75v18l-4.5-3.937V17.75L4.25 4.25h31.5l-13.5 13.5z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 357 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M20 28.656c-4.267 0-7.72-3.325-8.038-7.54l-5.9-4.591c-.777.98-1.49 2.016-2.066 3.149a1.844 1.844 0 0 0 0 1.653C7.046 27.32 13.085 31.375 20 31.375c1.514 0 2.974-.227 4.381-.593l-2.919-2.274a8.053 8.053 0 0 1-1.462.148zm17.652 3.291l-6.218-4.84a18.74 18.74 0 0 0 4.57-5.78 1.844 1.844 0 0 0 0-1.654C32.954 13.68 26.914 9.625 20 9.625a17.24 17.24 0 0 0-8.287 2.135L4.557 6.191a.896.896 0 0 0-1.263.16L2.189 7.78a.91.91 0 0 0 .159 1.272l33.095 25.756a.896.896 0 0 0 1.263-.16l1.105-1.43a.91.91 0 0 0-.159-1.272zm-10.334-8.043l-2.21-1.72a5.38 5.38 0 0 0-1.812-6.027 5.3 5.3 0 0 0-4.72-.88c.34.463.523 1.023.524 1.598a2.659 2.659 0 0 1-.087.566l-4.14-3.222A7.972 7.972 0 0 1 20 12.344a8.067 8.067 0 0 1 5.729 2.387 8.18 8.18 0 0 1 2.37 5.769c0 1.225-.297 2.367-.781 3.405z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 880 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M33.464 17.75h-1.768v-5.063C31.696 6.795 26.673 2 20.5 2S9.304 6.795 9.304 12.688v5.062H7.536C5.584 17.75 4 19.262 4 21.125v13.5C4 36.488 5.584 38 7.536 38h25.928C35.416 38 37 36.488 37 34.625v-13.5c0-1.863-1.584-3.375-3.536-3.375zm-7.66 0H15.196v-5.063c0-2.79 2.38-5.062 5.304-5.062 2.924 0 5.304 2.271 5.304 5.063v5.062z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 435 B |
@ -0,0 +1 @@
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M20.5 3c4.885 0 8.857 3.812 8.857 8.5 0 4.688-3.972 8.5-8.857 8.5s-8.857-3.812-8.857-8.5c0-4.688 3.972-8.5 8.857-8.5zM36 34.45c0 1.408-1.384 2.55-3.1 2.55H8.1C6.384 37 5 35.858 5 34.45V31.9c0-4.223 4.166-7.65 9.3-7.65h.692a14.802 14.802 0 0 0 11.016 0h.692c5.134 0 9.3 3.427 9.3 7.65v2.55z" fill="#000" fill-rule="nonzero"/></svg>
|
||||
|
After Width: | Height: | Size: 402 B |
@ -1,56 +1,51 @@
|
||||
{
|
||||
"name": "cvat-ui",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@types/jest": "24.0.13",
|
||||
"@types/node": "^12.0.3",
|
||||
"@types/react": "16.8.19",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-redux": "^7.1.1",
|
||||
"@types/react-router-dom": "^4.3.4",
|
||||
"@types/redux-logger": "^3.0.7",
|
||||
"antd": "^3.19.1",
|
||||
"babel-plugin-import": "^1.11.2",
|
||||
"customize-cra": "^0.2.12",
|
||||
"less": "^3.9.0",
|
||||
"less-loader": "^5.0.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"query-string": "^6.8.1",
|
||||
"react": "^16.8.6",
|
||||
"react-app-rewired": "^2.1.3",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-redux": "^7.1.0",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-scripts-ts": "^3.1.0",
|
||||
"redux": "^4.0.4",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"source-map-explorer": "^1.8.0",
|
||||
"typescript": "3.4.5"
|
||||
},
|
||||
"description": "CVAT single-page application",
|
||||
"main": "src/index.tsx",
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "react-app-rewired start",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "react-scripts eject"
|
||||
"build": "webpack --config ./webpack.config.js",
|
||||
"server": "nodemon --watch config --exec 'webpack-dev-server --config ./webpack.config.js --mode=development --open'"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
"author": "Intel",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.0",
|
||||
"@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",
|
||||
"babel": "^6.23.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"css-loader": "^3.2.0",
|
||||
"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",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"nodemon": "^1.19.2",
|
||||
"style-loader": "^1.0.0",
|
||||
"typescript": "^3.6.3",
|
||||
"webpack": "^4.39.3",
|
||||
"webpack-cli": "^3.3.8",
|
||||
"webpack-dev-server": "^3.8.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
"dependencies": {
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-redux": "^7.1.2",
|
||||
"@types/react-router": "^5.0.5",
|
||||
"@types/react-router-dom": "^5.1.0",
|
||||
"antd": "^3.23.2",
|
||||
"dotenv-webpack": "^1.7.0",
|
||||
"moment": "^2.24.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-redux": "^7.1.1",
|
||||
"react-router": "^5.1.0",
|
||||
"react-router-dom": "^5.1.0",
|
||||
"redux": "^4.0.4",
|
||||
"redux-thunk": "^2.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
@ -1 +0,0 @@
|
||||
<svg width="90" height="78" xmlns="http://www.w3.org/2000/svg"><path d="M84.27 0c2.753 0 5.007 2.167 5.12 4.874l.005.215v67.148c0 2.734-2.183 4.972-4.908 5.085l-.217.004H5.123c-2.751 0-5.005-2.167-5.118-4.874L0 72.237V5.09C0 2.355 2.183.117 4.907.004L5.123 0H84.27zm1.58 16.242H3.546v55.995c0 .816.632 1.488 1.434 1.56l.144.007H84.27c.824 0 1.501-.627 1.574-1.424l.007-.143V16.242zM12.658 38.48h4.328c.59 0 1.076.452 1.138 1.031l.007.126v1.03h15.02v-1.03c0-.596.446-1.087 1.02-1.15l.125-.007h4.328c.59 0 1.076.451 1.138 1.03l.006.127v4.372c0 .596-.446 1.087-1.02 1.15l-.124.007h-1.02v10.548h1.019c.59 0 1.077.451 1.139 1.031l.006.126v4.372c0 .596-.446 1.087-1.02 1.15l-.125.007h-4.327a1.15 1.15 0 0 1-1.139-1.03l-.006-.127v-1.03H18.13v1.03c0 .596-.446 1.087-1.02 1.15l-.125.007h-4.328a1.15 1.15 0 0 1-1.138-1.03l-.007-.127v-4.372c0-.596.447-1.087 1.02-1.15l.125-.007h1.019V45.166h-1.02a1.15 1.15 0 0 1-1.138-1.03l-.006-.127v-4.372c0-.596.446-1.087 1.02-1.15l.125-.007h4.328zm3.183 19.548h-2.038v2.058h2.038v-2.058zm21.638 0h-2.039v2.058h2.039v-2.058zM33.15 42.98H18.13v1.03c0 .595-.447 1.087-1.02 1.15l-.125.006h-1.019v10.548h1.019c.59 0 1.076.451 1.138 1.031l.007.126V57.9h15.02V56.87c0-.596.446-1.087 1.02-1.15l.125-.007h1.019V45.166h-1.019a1.15 1.15 0 0 1-1.139-1.031l-.006-.126v-1.03zm21.575-7.62c.398 0 .72.338.72.755v.67h9.458v-.67c0-.417.323-.755.721-.755h2.725c.398 0 .72.338.72.754v2.852c0 .416-.322.754-.72.754h-.641v6.88h.64c.399 0 .722.337.722.754v2.852c0 .416-.323.754-.721.754h-2.725c-.398 0-.721-.338-.721-.754v-.672h-9.457v.672c0 .416-.322.754-.72.754H52c-.398 0-.72-.338-.72-.754v-2.852c0-.416.322-.754.72-.754h.642v-6.88H52c-.398 0-.72-.337-.72-.754v-2.851c0-.417.322-.755.72-.755zm-.72 12.749H52.72v1.342h1.283V48.11zm13.623 0h-1.283v1.342h1.283V48.11zm-2.725-9.814h-9.457v.671c0 .417-.323.755-.721.755h-.641V46.6h.641c.399 0 .721.337.721.754v.671h9.457v-.67c0-.417.323-.755.72-.755h.642v-6.88h-.64c-.4 0-.722-.338-.722-.754v-.671zm-49.063 2.5h-2.038v2.058h2.038v-2.058zm21.638-.001H35.44v2.058h2.038v-2.058zm30.15-3.925h-1.283v1.342h1.283V36.87zm-13.624 0h-1.283v1.343h1.283v-1.343zM9.098 19.76c.93 0 1.692.711 1.766 1.616l.006.145v.118c0 .973-.793 1.761-1.772 1.761-.93 0-1.693-.711-1.767-1.616l-.005-.145v-.118c0-.973.793-1.761 1.772-1.761zm21.65 0c.978 0 1.771.788 1.771 1.76 0 .974-.793 1.762-1.771 1.762H16.423c-.98 0-1.772-.788-1.772-1.761 0-.973.792-1.761 1.772-1.761h14.325zm13.988 0c.98 0 1.772.788 1.772 1.76 0 .974-.792 1.762-1.772 1.762h-5.29a1.768 1.768 0 0 1-1.772-1.761c0-.973.796-1.761 1.772-1.761h5.29zm9.868 0c.977 0 1.773.788 1.773 1.76 0 .974-.796 1.762-1.773 1.762H53.05a1.766 1.766 0 0 1-1.772-1.761c0-.973.793-1.761 1.772-1.761h1.553zm17.107 0c.977 0 1.772.788 1.772 1.76 0 .974-.795 1.762-1.772 1.762h-8.194c-.98 0-1.773-.788-1.773-1.761 0-.973.793-1.761 1.773-1.761h8.194zm12.56-16.238H5.122c-.82 0-1.5.628-1.572 1.424l-.006.143v7.631H85.85V5.09c0-.863-.709-1.567-1.58-1.567zM9.125 6.24c.98 0 1.772.787 1.772 1.76s-.793 1.761-1.772 1.761c-.977 0-1.8-.788-1.8-1.76 0-.974.761-1.761 1.741-1.761h.059zm7.354 0c.979 0 1.772.787 1.772 1.76s-.793 1.761-1.772 1.761c-.977 0-1.829-.788-1.829-1.76 0-.974.734-1.761 1.712-1.761h.117zm7.297 0c.977 0 1.773.787 1.773 1.76s-.796 1.761-1.773 1.761c-.979 0-1.8-.788-1.8-1.76 0-.974.764-1.761 1.743-1.761h.057z" fill="#9B9B9B" fill-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@ -1,40 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>CVAT</title>
|
||||
|
||||
<script src="./cvat-core.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "CVAT",
|
||||
"name": "Computer Vision Annotation Tool",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
import { Dispatch, ActionCreator } from 'redux';
|
||||
|
||||
|
||||
export const dumpAnnotation = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'DUMP_ANNOTATION',
|
||||
});
|
||||
}
|
||||
|
||||
export const dumpAnnotationSuccess = (downloadLink: string) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'DUMP_ANNOTATION_SUCCESS',
|
||||
payload: downloadLink,
|
||||
});
|
||||
}
|
||||
|
||||
export const dumpAnnotationError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'DUMP_ANNOTATION_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const uploadAnnotation = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'UPLOAD_ANNOTATION',
|
||||
});
|
||||
}
|
||||
|
||||
export const uploadAnnotationSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'UPLOAD_ANNOTATION_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const uploadAnnotationError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'UPLOAD_ANNOTATION_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const dumpAnnotationAsync = (task: any, dumper: any) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(dumpAnnotation());
|
||||
|
||||
return task.annotations.dump(task.name, dumper).then(
|
||||
(downloadLink: string) => {
|
||||
dispatch(dumpAnnotationSuccess(downloadLink));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(dumpAnnotationError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const uploadAnnotationAsync = (task: any, file: File, loader: any) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(uploadAnnotation());
|
||||
|
||||
return task.annotations.upload(file, loader).then(
|
||||
(response: any) => {
|
||||
dispatch(uploadAnnotationSuccess());
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(uploadAnnotationError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
import { AnyAction, Dispatch, ActionCreator } from 'redux';
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
|
||||
import getCore from '../core';
|
||||
|
||||
const cvat = getCore();
|
||||
|
||||
export enum AuthActionTypes {
|
||||
AUTHORIZED_SUCCESS = 'AUTHORIZED_SUCCESS',
|
||||
AUTHORIZED_FAILED = 'AUTHORIZED_FAILED',
|
||||
LOGIN_SUCCESS = 'LOGIN_SUCCESS',
|
||||
LOGIN_FAILED = 'LOGIN_FAILED',
|
||||
REGISTER_SUCCESS = 'REGISTER_SUCCESS',
|
||||
REGISTER_FAILED = 'REGISTER_FAILED',
|
||||
LOGOUT_SUCCESS = 'LOGOUT_SUCCESS',
|
||||
LOGOUT_FAILED = 'LOGOUT_FAILED',
|
||||
}
|
||||
|
||||
export function registerSuccess(user: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.REGISTER_SUCCESS,
|
||||
payload: {
|
||||
user,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function registerFailed(registerError: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.REGISTER_FAILED,
|
||||
payload: {
|
||||
registerError,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function loginSuccess(user: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.LOGIN_SUCCESS,
|
||||
payload: {
|
||||
user,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function loginFailed(loginError: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.LOGIN_FAILED,
|
||||
payload: {
|
||||
loginError,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function logoutSuccess(): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.LOGOUT_SUCCESS,
|
||||
payload: {},
|
||||
};
|
||||
}
|
||||
|
||||
export function logoutFailed(logoutError: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.LOGOUT_FAILED,
|
||||
payload: {
|
||||
logoutError,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizedSuccess(user: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.AUTHORIZED_SUCCESS,
|
||||
payload: {
|
||||
user,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizedFailed(authError: any): AnyAction {
|
||||
return {
|
||||
type: AuthActionTypes.AUTHORIZED_FAILED,
|
||||
payload: {
|
||||
authError,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function registerAsync({
|
||||
username,
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password1,
|
||||
password2,
|
||||
}: {
|
||||
username: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
password1: string;
|
||||
password2: string;
|
||||
}): ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||
let users = null;
|
||||
try {
|
||||
await cvat.server.register(username, firstName, lastName,
|
||||
email, password1, password2);
|
||||
users = await cvat.users.get({ self: true });
|
||||
} catch (error) {
|
||||
dispatch(registerFailed(error));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(registerSuccess(users[0]));
|
||||
};
|
||||
}
|
||||
|
||||
export function loginAsync({ username, password }: {username: string; password: string}):
|
||||
ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||
let users = null;
|
||||
try {
|
||||
await cvat.server.login(username, password);
|
||||
users = await cvat.users.get({ self: true });
|
||||
} catch (error) {
|
||||
dispatch(loginFailed(error));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(loginSuccess(users[0]));
|
||||
};
|
||||
}
|
||||
|
||||
export function logoutAsync(): ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||
try {
|
||||
await cvat.server.logout();
|
||||
} catch (error) {
|
||||
dispatch(logoutFailed(error));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(logoutSuccess());
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizedAsync(): ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||
let result = null;
|
||||
try {
|
||||
result = await cvat.server.authorized();
|
||||
} catch (error) {
|
||||
dispatch(authorizedFailed(error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
const userInstance = (await cvat.users.get({ self: true }))[0];
|
||||
dispatch(authorizedSuccess(userInstance));
|
||||
} else {
|
||||
dispatch(authorizedSuccess(null));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
import { History } from 'history';
|
||||
import { Dispatch, ActionCreator } from 'redux';
|
||||
|
||||
|
||||
export const login = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'LOGIN',
|
||||
});
|
||||
}
|
||||
|
||||
export const loginSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'LOGIN_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const loginError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'LOGIN_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const logout = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'LOGOUT',
|
||||
});
|
||||
}
|
||||
|
||||
export const logoutSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'LOGOUT_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const logoutError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'LOGOUT_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const isAuthenticated = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'IS_AUTHENTICATED',
|
||||
});
|
||||
}
|
||||
|
||||
export const isAuthenticatedSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'IS_AUTHENTICATED_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const isAuthenticatedFail = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'IS_AUTHENTICATED_FAIL',
|
||||
});
|
||||
}
|
||||
|
||||
export const isAuthenticatedError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'IS_AUTHENTICATED_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const register = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'REGISTER',
|
||||
});
|
||||
}
|
||||
|
||||
export const registerSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'REGISTER_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const registerError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'REGISTER_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const loginAsync = (username: string, password: string, history: History) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(login());
|
||||
|
||||
return (window as any).cvat.server.login(username, password).then(
|
||||
(loggedIn: any) => {
|
||||
dispatch(loginSuccess());
|
||||
|
||||
history.push(history.location.state ? history.location.state.from : '/tasks');
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(loginError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const logoutAsync = () => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(logout());
|
||||
|
||||
return (window as any).cvat.server.logout().then(
|
||||
(loggedOut: any) => {
|
||||
dispatch(logoutSuccess());
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(logoutError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const isAuthenticatedAsync = () => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(isAuthenticated());
|
||||
|
||||
return (window as any).cvat.server.authorized().then(
|
||||
(isAuthenticated: boolean) => {
|
||||
isAuthenticated ? dispatch(isAuthenticatedSuccess()) : dispatch(isAuthenticatedFail());
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(isAuthenticatedError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const registerAsync = (
|
||||
username: string,
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
email: string,
|
||||
password: string,
|
||||
passwordConfirmation: string,
|
||||
history: History,
|
||||
) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(register());
|
||||
|
||||
return (window as any).cvat.server.register(
|
||||
username,
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password,
|
||||
passwordConfirmation,
|
||||
).then(
|
||||
(registered: any) => {
|
||||
dispatch(registerSuccess());
|
||||
|
||||
history.replace('/login');
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(registerError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
import { Dispatch, ActionCreator } from 'redux';
|
||||
|
||||
|
||||
export const getServerInfo = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_SERVER_INFO',
|
||||
});
|
||||
}
|
||||
|
||||
export const getServerInfoSuccess = (information: null) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_SERVER_INFO_SUCCESS',
|
||||
payload: information,
|
||||
});
|
||||
}
|
||||
|
||||
export const getServerInfoError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_SERVER_INFO_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const getShareFiles = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_SHARE_FILES',
|
||||
});
|
||||
}
|
||||
|
||||
export const getShareFilesSuccess = (files: []) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_SHARE_FILES_SUCCESS',
|
||||
payload: files,
|
||||
});
|
||||
}
|
||||
|
||||
export const getShareFilesError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_SHARE_FILES_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const getAnnotationFormats = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_ANNOTATION_FORMATS',
|
||||
});
|
||||
}
|
||||
|
||||
export const getAnnotationFormatsSuccess = (annotationFormats: []) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_ANNOTATION_FORMATS_SUCCESS',
|
||||
payload: annotationFormats,
|
||||
});
|
||||
}
|
||||
|
||||
export const getAnnotationFormatsError = (error = {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_ANNOTATION_FORMATS_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const getServerInfoAsync = () => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(getServerInfo());
|
||||
|
||||
return (window as any).cvat.server.about().then(
|
||||
(information: any) => {
|
||||
dispatch(getServerInfoSuccess(information));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(getServerInfoError(error));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const getShareFilesAsync = (directory: string) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(getShareFiles());
|
||||
|
||||
return (window as any).cvat.server.share(directory).then(
|
||||
(files: any) => {
|
||||
dispatch(getShareFilesSuccess(files));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(getShareFilesError(error));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const getAnnotationFormatsAsync = () => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(getAnnotationFormats());
|
||||
|
||||
return (window as any).cvat.server.formats().then(
|
||||
(formats: any) => {
|
||||
dispatch(getAnnotationFormatsSuccess(formats));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(getAnnotationFormatsError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
import { AnyAction, Dispatch, ActionCreator } from 'redux';
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
import { TasksQuery } from '../reducers/interfaces';
|
||||
|
||||
import getCore from '../core';
|
||||
|
||||
const cvat = getCore();
|
||||
|
||||
export enum TasksActionTypes {
|
||||
GET_TASKS_SUCCESS = 'GET_TASKS_SUCCESS',
|
||||
GET_TASKS_FAILED = 'GET_TASKS_FAILED',
|
||||
}
|
||||
|
||||
export function getTasksSuccess(array: any[], previews: string[],
|
||||
count: number, query: TasksQuery): AnyAction {
|
||||
const action = {
|
||||
type: TasksActionTypes.GET_TASKS_SUCCESS,
|
||||
payload: {
|
||||
previews,
|
||||
array,
|
||||
count,
|
||||
query,
|
||||
},
|
||||
};
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
export function getTasksFailed(error: any, query: TasksQuery): AnyAction {
|
||||
const action = {
|
||||
type: TasksActionTypes.GET_TASKS_FAILED,
|
||||
payload: {
|
||||
error,
|
||||
query,
|
||||
},
|
||||
};
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
export function getTasksAsync(query: TasksQuery):
|
||||
ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||
// We need remove all keys with null values from query
|
||||
const filteredQuery = { ...query };
|
||||
for (const key in filteredQuery) {
|
||||
if (filteredQuery[key] === null) {
|
||||
delete filteredQuery[key];
|
||||
}
|
||||
}
|
||||
|
||||
let result = null;
|
||||
try {
|
||||
result = await cvat.tasks.get(filteredQuery);
|
||||
} catch (error) {
|
||||
dispatch(getTasksFailed(error, query));
|
||||
return;
|
||||
}
|
||||
|
||||
const array = Array.from(result);
|
||||
const previews = [];
|
||||
const promises = array
|
||||
.map((task): string => (task as any).frames.preview());
|
||||
|
||||
for (const promise of promises) {
|
||||
try {
|
||||
// a tricky moment
|
||||
// await is okay in loop in this case, there aren't any performance bottleneck
|
||||
// because all server requests have been already sent in parallel
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
previews.push(await promise);
|
||||
} catch (error) {
|
||||
previews.push('');
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(getTasksSuccess(array, previews, result.count, query));
|
||||
};
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
|
||||
export const filterTasks = (queryParams: { search?: string, page?: number }) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'FILTER_TASKS',
|
||||
payload: queryParams,
|
||||
});
|
||||
}
|
||||
@ -1,175 +0,0 @@
|
||||
import { History } from 'history';
|
||||
import { Dispatch, ActionCreator } from 'redux';
|
||||
|
||||
import queryString from 'query-string';
|
||||
|
||||
import setQueryObject from '../utils/tasks-filter'
|
||||
|
||||
|
||||
export const getTasks = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_TASKS',
|
||||
});
|
||||
}
|
||||
|
||||
export const getTasksSuccess = (tasks: []) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_TASKS_SUCCESS',
|
||||
payload: tasks,
|
||||
});
|
||||
}
|
||||
|
||||
export const getTasksError = (error: {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_TASKS_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const createTask = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'CREATE_TASK',
|
||||
});
|
||||
}
|
||||
|
||||
export const createTaskSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'CREATE_TASK_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const createTaskError = (error: {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'CREATE_TASK_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const updateTask = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'UPDATE_TASK',
|
||||
});
|
||||
}
|
||||
|
||||
export const updateTaskSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'UPDATE_TASK_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const updateTaskError = (error: {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'UPDATE_TASK_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const deleteTask = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'DELETE_TASK',
|
||||
});
|
||||
}
|
||||
|
||||
export const deleteTaskSuccess = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'DELETE_TASK_SUCCESS',
|
||||
});
|
||||
}
|
||||
|
||||
export const deleteTaskError = (error: {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'DELETE_TASK_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const getTasksAsync = (queryObject = {}) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(getTasks());
|
||||
|
||||
return (window as any).cvat.tasks.get(queryObject).then(
|
||||
(tasks: any) => {
|
||||
dispatch(getTasksSuccess(tasks));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(getTasksError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const createTaskAsync = (task: any) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(createTask());
|
||||
|
||||
return task.save().then(
|
||||
(created: any) => {
|
||||
dispatch(createTaskSuccess());
|
||||
|
||||
return dispatch(getTasksAsync());
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(createTaskError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const updateTaskAsync = (task: any) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(updateTask());
|
||||
|
||||
return task.save().then(
|
||||
(updated: any) => {
|
||||
dispatch(updateTaskSuccess());
|
||||
|
||||
return dispatch(getTasksAsync());
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(updateTaskError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const deleteTaskAsync = (task: any, history: History) => {
|
||||
return (dispatch: ActionCreator<Dispatch>, getState: any) => {
|
||||
dispatch(deleteTask());
|
||||
|
||||
return task.delete().then(
|
||||
(deleted: any) => {
|
||||
dispatch(deleteTaskSuccess());
|
||||
|
||||
const state = getState();
|
||||
|
||||
const queryObject = {
|
||||
page: state.tasksFilter.currentPage,
|
||||
search: state.tasksFilter.searchQuery,
|
||||
}
|
||||
|
||||
if (state.tasks.tasks.length === 1 && state.tasks.tasksCount !== 1) {
|
||||
queryObject.page = queryObject.page - 1;
|
||||
|
||||
history.push({ search: queryString.stringify(queryObject) });
|
||||
} else if (state.tasks.tasksCount === 1) {
|
||||
return dispatch(getTasksAsync());
|
||||
} else {
|
||||
const query = setQueryObject(queryObject);
|
||||
|
||||
return dispatch(getTasksAsync(query));
|
||||
}
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(deleteTaskError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
import { Dispatch, ActionCreator } from 'redux';
|
||||
|
||||
|
||||
export const getUsers = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_USERS',
|
||||
});
|
||||
}
|
||||
|
||||
export const getUsersSuccess = (users: [], isCurrentUser: boolean) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_USERS_SUCCESS',
|
||||
payload: users,
|
||||
currentUser: isCurrentUser ? (users as any)[0] : isCurrentUser,
|
||||
});
|
||||
}
|
||||
|
||||
export const getUsersError = (error: {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_USERS_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const getUsersAsync = (filter = {}) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(getUsers());
|
||||
|
||||
return (window as any).cvat.users.get(filter).then(
|
||||
(users: any) => {
|
||||
dispatch(getUsersSuccess(users, (filter as any).self));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(getUsersError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
.App {
|
||||
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import App from './app';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,65 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import HeaderLayout from '../header-layout/header-layout';
|
||||
|
||||
import TasksPage from '../tasks-page/tasks-page';
|
||||
import LoginPage from '../login-page/login-page';
|
||||
import RegisterPage from '../register-page/register-page';
|
||||
import PageNotFound from '../page-not-found/page-not-found';
|
||||
|
||||
import './app.scss';
|
||||
|
||||
|
||||
const ProtectedRoute = ({ component: Component, ...rest }: any) => {
|
||||
return (
|
||||
<Route
|
||||
{ ...rest }
|
||||
render={ (props) => {
|
||||
return rest.isAuthenticated ? (
|
||||
<>
|
||||
<HeaderLayout />
|
||||
<Component { ...props } />
|
||||
</>
|
||||
) : (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/login',
|
||||
state: {
|
||||
from: props.location,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
class App extends PureComponent<any, any> {
|
||||
componentDidMount() {
|
||||
(window as any).cvat.config.backendAPI = process.env.REACT_APP_API_FULL_URL;
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Router>
|
||||
<Switch>
|
||||
<Redirect path="/" exact to="/tasks" />
|
||||
<ProtectedRoute isAuthenticated={ this.props.isAuthenticated } path="/tasks" component={ TasksPage } />
|
||||
<Route path="/login" component={ LoginPage } />
|
||||
<Route path="/register" component={ RegisterPage } />
|
||||
<Route component={ PageNotFound } />
|
||||
</Switch>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return state.authContext;
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(App);
|
||||
@ -1,66 +0,0 @@
|
||||
.header-layout {
|
||||
min-width: 1024px;
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
line-height: initial;
|
||||
background: #d8d8d8;
|
||||
|
||||
&__logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__menu {
|
||||
.ant-menu {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
background-color: #d8d8d8;
|
||||
line-height: 44px;
|
||||
border-bottom: none;
|
||||
|
||||
.ant-menu-item {
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.last-menu-item {
|
||||
float: right;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.ant-menu-item-selected, .ant-menu-item-active {
|
||||
color: black !important;
|
||||
border-bottom: 3px solid black !important;
|
||||
background-color: #c3c3c3 !important;
|
||||
}
|
||||
|
||||
a, a:hover {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
border-left: 1px solid #c3c3c3;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
|
||||
i:first-child {
|
||||
margin-right: 12px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
i:last-child {
|
||||
margin-left: auto;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import HeaderLayout from './header-layout';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<HeaderLayout />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,87 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { logoutAsync } from '../../actions/auth.actions';
|
||||
|
||||
import { Layout, Row, Col, Menu, Dropdown, Icon } from 'antd';
|
||||
import { ClickParam } from 'antd/lib/menu';
|
||||
|
||||
import './header-layout.scss';
|
||||
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
class HeaderLayout extends PureComponent<any, any> {
|
||||
hostUrl: string | undefined;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedMenuItem: null,
|
||||
};
|
||||
|
||||
this.hostUrl = process.env.REACT_APP_API_HOST_URL;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ selectedMenuItem: this.props.location.pathname.split('/')[1] });
|
||||
}
|
||||
|
||||
render() {
|
||||
const dropdownMenu = (
|
||||
<Menu>
|
||||
<Menu.Item onClick={ this.logout } key="logout">Logout</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Header className="header-layout">
|
||||
<Row type="flex" gutter={24}>
|
||||
<Col className="header-layout__logo" span={2}>
|
||||
<img src="./images/cvat-logo.svg" alt="CVAT logo" />
|
||||
</Col>
|
||||
<Col className="header-layout__menu" span={18}>
|
||||
<Menu onClick={ this.selectMenuItem } selectedKeys={ [this.state.selectedMenuItem] } mode="horizontal">
|
||||
<Menu.Item key="tasks">
|
||||
<Link to="/tasks">Tasks</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item disabled key="models">
|
||||
<Link to="/models">Models</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item disabled key="analitics">
|
||||
<Link to="/analitics">Analitics</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<a className="last-menu-item" href={ `${this.hostUrl}/documentation/user_guide.html` } target="blank">Help</a>
|
||||
</Menu>
|
||||
</Col>
|
||||
<Dropdown className="header-layout__dropdown" overlay={ dropdownMenu } trigger={ ['click'] }>
|
||||
<Col span={4}>
|
||||
<Icon type="user" />
|
||||
{ this.props.currentUser ? <span>{ this.props.currentUser.username }</span> : null }
|
||||
<Icon type="caret-down" />
|
||||
</Col>
|
||||
</Dropdown>
|
||||
</Row>
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
|
||||
private selectMenuItem = (event: ClickParam) => {
|
||||
this.setState({ selectedMenuItem: event.key });
|
||||
}
|
||||
|
||||
private logout = () => {
|
||||
this.props.dispatch(logoutAsync());
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return { ...state.authContext, ...state.users };
|
||||
};
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(HeaderLayout) as any);
|
||||
@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { FormComponentProps } from 'antd/lib/form/Form';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Input,
|
||||
Form,
|
||||
} from 'antd';
|
||||
|
||||
export interface LoginData {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
type LoginFormProps = {
|
||||
onSubmit(loginData: LoginData): void;
|
||||
} & FormComponentProps;
|
||||
|
||||
class LoginForm extends React.PureComponent<LoginFormProps> {
|
||||
constructor(props: LoginFormProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
private handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((error, values) => {
|
||||
if (!error) {
|
||||
this.props.onSubmit(values);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private renderUsernameField() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please specify a username',
|
||||
}],
|
||||
})(
|
||||
<Input
|
||||
autoComplete='username'
|
||||
prefix={<Icon type='user' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Username'
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
private renderPasswordField() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please specify a password',
|
||||
}],
|
||||
})(
|
||||
<Input
|
||||
autoComplete='current-password'
|
||||
prefix={<Icon type='lock' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Password'
|
||||
type='password'
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit.bind(this)} className='login-form'>
|
||||
{this.renderUsernameField()}
|
||||
{this.renderPasswordField()}
|
||||
|
||||
<Form.Item>
|
||||
<Button type='primary' htmlType='submit' className='login-form-button'>
|
||||
Sign in
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create<LoginFormProps>()(LoginForm);
|
||||
@ -1,10 +0,0 @@
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
|
||||
&__title {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Login from './login-page';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Login />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,105 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { loginAsync, isAuthenticatedAsync } from '../../actions/auth.actions';
|
||||
import { getUsersAsync } from '../../actions/users.actions';
|
||||
|
||||
import { Button, Icon, Input, Form, Col, Row, Spin } from 'antd';
|
||||
import Title from 'antd/lib/typography/Title';
|
||||
|
||||
import './login-page.scss';
|
||||
|
||||
|
||||
class LoginForm extends PureComponent<any, any> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = { loading: false };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ loading: true });
|
||||
|
||||
this.props.dispatch(isAuthenticatedAsync()).then(
|
||||
(isAuthenticated: boolean) => {
|
||||
this.setState({ loading: false });
|
||||
|
||||
if (this.props.isAuthenticated) {
|
||||
this.props.dispatch(getUsersAsync({ self: true }));
|
||||
this.props.history.replace(this.props.location.state ? this.props.location.state.from : '/tasks');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Spin wrapperClassName="spinner" size="large" spinning={ this.state.loading }>
|
||||
<Row type="flex" justify="center" align="middle">
|
||||
<Col xs={12} md={10} lg={8} xl={6}>
|
||||
<Form className="login-form" onSubmit={ this.onSubmit }>
|
||||
<Title className="login-form__title">Login</Title>
|
||||
|
||||
<Form.Item>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{ required: true, message: 'Please enter your username!' }],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="user" /> }
|
||||
type="text"
|
||||
name="username"
|
||||
placeholder="Username"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{ required: true, message: 'Please enter your password!' }],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="lock" /> }
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" loading={ this.props.isFetching }>
|
||||
Log in
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
Have not registered yet? <Link to="/register">Register here.</Link>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
|
||||
private onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.form.validateFields((error: any, values: any) => {
|
||||
if (!error) {
|
||||
this.props.dispatch(loginAsync(values.username, values.password, this.props.history)).then(
|
||||
(loggedIn: any) => {
|
||||
this.props.dispatch(getUsersAsync({ self: true }));
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return state.authContext;
|
||||
};
|
||||
|
||||
export default Form.create()(connect(mapStateToProps)(LoginForm));
|
||||
@ -1,8 +0,0 @@
|
||||
.ant-badge {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ant-tree.ant-tree-directory {
|
||||
height: 108px;
|
||||
overflow: auto;
|
||||
}
|
||||
@ -1,352 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Form, Input, Icon, Checkbox, Radio, Upload, Badge, Tree, TreeSelect, InputNumber } from 'antd';
|
||||
import { UploadFile, UploadChangeParam } from 'antd/lib/upload/interface';
|
||||
|
||||
import configureStore from '../../../store';
|
||||
import { getShareFilesAsync } from '../../../actions/server.actions';
|
||||
|
||||
import { validateLabels, FileSource, fileModel } from '../../../utils/tasks-dto';
|
||||
|
||||
import './task-create.scss';
|
||||
|
||||
|
||||
const { TreeNode } = Tree;
|
||||
const { SHOW_PARENT } = TreeSelect;
|
||||
const { Dragger } = Upload;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 8 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 16 },
|
||||
},
|
||||
};
|
||||
|
||||
const formItemTailLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
},
|
||||
};
|
||||
|
||||
class TaskCreateForm extends PureComponent<any, any> {
|
||||
store: any;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.store = configureStore();
|
||||
|
||||
this.state = {
|
||||
confirmDirty: false,
|
||||
selectedFileList: [],
|
||||
filesCounter: 0,
|
||||
treeData: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getSharedFiles('').then(
|
||||
(data: any) => {
|
||||
this.setState({ treeData: fileModel('', this.store.getState().server.files) });
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private renderTreeNodes = (data: any) => {
|
||||
return data.map((item: any) => {
|
||||
if (!item.isLeaf) {
|
||||
return (
|
||||
<TreeNode title={ item.name } key={ item.id } value={ item.id } dataRef={ item }>
|
||||
{ item.children ? this.renderTreeNodes(item.children) : '' }
|
||||
</TreeNode>
|
||||
);
|
||||
}
|
||||
|
||||
return <TreeNode isLeaf title={ item.name } key={ item.id } value={ item.id } dataRef={ item } />;
|
||||
});
|
||||
}
|
||||
|
||||
private renderUploader = () => {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
switch (this.props.form.getFieldValue('source')) {
|
||||
case FileSource.Local:
|
||||
return (
|
||||
<Form.Item
|
||||
{ ...formItemTailLayout }
|
||||
extra='Only one video, archive, pdf or many image, directory can be used simultaneously'>
|
||||
<Badge
|
||||
count={ this.state.filesCounter }
|
||||
overflowCount={999}>
|
||||
<div onClick={ this.resetUploader }>
|
||||
{getFieldDecorator('localUpload', {
|
||||
rules: [{ required: true, message: 'Please, add some files!' }],
|
||||
})(
|
||||
<Dragger
|
||||
multiple
|
||||
showUploadList={ false }
|
||||
fileList={ this.state.selectedFileList }
|
||||
customRequest={ this.simulateRequest }
|
||||
onChange={ this.onUploaderChange }>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<Icon type="inbox" />
|
||||
</p>
|
||||
<p className="ant-upload-text">Click or drag file to this area to upload</p>
|
||||
</Dragger>
|
||||
)}
|
||||
</div>
|
||||
</Badge>
|
||||
</Form.Item>
|
||||
);
|
||||
case FileSource.Remote:
|
||||
return (
|
||||
<Form.Item { ...formItemLayout } label="URLs">
|
||||
{getFieldDecorator('remoteURL', {
|
||||
rules: [],
|
||||
})(
|
||||
<Input.TextArea
|
||||
name="remote-url"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
);
|
||||
case FileSource.Share:
|
||||
return (
|
||||
<Form.Item { ...formItemLayout } label="Shared files"
|
||||
extra='Only one video, archive, pdf or many image, directory can be used simultaneously'>
|
||||
{getFieldDecorator('sharedFiles', {
|
||||
rules: [{ required: true, message: 'Please, add some files!' }],
|
||||
})(
|
||||
<TreeSelect
|
||||
multiple
|
||||
treeCheckable={ true }
|
||||
showCheckedStrategy={ SHOW_PARENT }
|
||||
loadData={ this.onLoadData }>
|
||||
{ this.renderTreeNodes(this.state.treeData) }
|
||||
</TreeSelect>
|
||||
)}
|
||||
</Form.Item>
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Form.Item { ...formItemLayout } label="Name">
|
||||
{getFieldDecorator('name', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: new RegExp('[a-zA-Z0-9_]+'),
|
||||
message: 'Bad task name!',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="profile" /> }
|
||||
type="text"
|
||||
name="name"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Labels">
|
||||
{getFieldDecorator('labels', {
|
||||
rules: [
|
||||
{ required: true, message: 'Please add some labels!' },
|
||||
{ validator: validateLabels, message: 'Bad labels format!' },
|
||||
],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="tag" /> }
|
||||
type="text"
|
||||
name="labels"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Bug tracker">
|
||||
{getFieldDecorator('bugTracker', {
|
||||
rules: [{ type: 'url', message: 'Bad bug tracker link!' }],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="tool" /> }
|
||||
type="text"
|
||||
name="bug-tracker"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Source">
|
||||
{getFieldDecorator('source', {
|
||||
rules: [],
|
||||
initialValue: 1,
|
||||
})(
|
||||
<Radio.Group onChange={ this.resetUploader }>
|
||||
<Radio.Button value={1}>Local</Radio.Button>
|
||||
<Radio.Button value={2}>Remote</Radio.Button>
|
||||
<Radio.Button value={3}>Share</Radio.Button>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Z-Order">
|
||||
{getFieldDecorator('zOrder', {
|
||||
rules: [],
|
||||
initialValue: false,
|
||||
})(
|
||||
<Checkbox
|
||||
name="z-order"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Segment size" hasFeedback>
|
||||
{getFieldDecorator('segmentSize', {
|
||||
rules: [],
|
||||
})(
|
||||
<InputNumber
|
||||
min={100}
|
||||
max={50000}
|
||||
name="segment-size"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Overlap size" hasFeedback>
|
||||
{getFieldDecorator('overlapSize', {
|
||||
rules: [],
|
||||
initialValue: 0,
|
||||
})(
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={ this.props.form.getFieldValue('segmentSize') ? this.props.form.getFieldValue('segmentSize') - 1 : 0 }
|
||||
name="overlap-size"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Image quality">
|
||||
{getFieldDecorator('imageQuality', {
|
||||
rules: [{ required: true }],
|
||||
initialValue: 50,
|
||||
})(
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={95}
|
||||
name="image-quality"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Start frame" hasFeedback>
|
||||
{getFieldDecorator('startFrame', {
|
||||
rules: [],
|
||||
initialValue: 0,
|
||||
})(
|
||||
<InputNumber
|
||||
min={0}
|
||||
name="start-frame"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Stop frame" hasFeedback>
|
||||
{getFieldDecorator('stopFrame', {
|
||||
rules: [],
|
||||
})(
|
||||
<InputNumber
|
||||
min={ this.props.form.getFieldValue('startFrame') }
|
||||
name="stop-frame"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item { ...formItemLayout } label="Frame filter">
|
||||
{getFieldDecorator('frameFilter', {
|
||||
rules: [],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="profile" /> }
|
||||
type="text"
|
||||
name="frame-filter"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{ this.renderUploader() }
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
private onLoadData = (treeNode: any) => {
|
||||
return new Promise<void>(resolve => {
|
||||
if (treeNode.props.children) {
|
||||
resolve();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.getSharedFiles(treeNode.props.dataRef.id).then(
|
||||
(data: any) => {
|
||||
treeNode.props.dataRef.children = fileModel(treeNode, this.store.getState().server.files);
|
||||
|
||||
this.setState({
|
||||
treeData: [...this.state.treeData],
|
||||
});
|
||||
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private getSharedFiles = (directory: string) => {
|
||||
return this.store.dispatch(getShareFilesAsync(directory));
|
||||
}
|
||||
|
||||
private onUploaderChange = (info: UploadChangeParam) => {
|
||||
const nextState: { selectedFileList: UploadFile[], filesCounter: number } = {
|
||||
selectedFileList: this.state.selectedFileList,
|
||||
filesCounter: this.state.filesCounter,
|
||||
};
|
||||
|
||||
switch (info.file.status) {
|
||||
case 'uploading':
|
||||
nextState.selectedFileList.push(info.file);
|
||||
nextState.filesCounter += 1;
|
||||
break;
|
||||
case 'done':
|
||||
break;
|
||||
default:
|
||||
// INFO: error or removed
|
||||
nextState.selectedFileList = info.fileList;
|
||||
}
|
||||
|
||||
this.setState(() => nextState);
|
||||
}
|
||||
|
||||
private resetUploader = () => {
|
||||
this.setState({ selectedFileList: [], filesCounter: 0 });
|
||||
}
|
||||
|
||||
private simulateRequest = ({ file, onSuccess }: any) => {
|
||||
setTimeout(() => {
|
||||
onSuccess(file);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create()(TaskCreateForm);
|
||||
@ -1,51 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Form, Input, Icon } from 'antd';
|
||||
|
||||
import { serializeLabels, validateLabels } from '../../../utils/tasks-dto'
|
||||
|
||||
import './task-update.scss';
|
||||
|
||||
|
||||
class TaskUpdateForm extends PureComponent<any, any> {
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('oldLabels', {
|
||||
rules: [],
|
||||
initialValue: serializeLabels(this.props.task),
|
||||
})(
|
||||
<Input
|
||||
disabled
|
||||
prefix={ <Icon type="tag" /> }
|
||||
type="text"
|
||||
name="oldLabels"
|
||||
placeholder="Old labels"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
{getFieldDecorator('newLabels', {
|
||||
rules: [
|
||||
{ required: true, message: 'Please input new labels!' },
|
||||
{ validator: validateLabels, message: 'Bad labels format!' },
|
||||
],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="tag" /> }
|
||||
type="text"
|
||||
name="new-labels"
|
||||
placeholder="Expand the specification here"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create()(TaskUpdateForm) as any;
|
||||
@ -1,3 +0,0 @@
|
||||
.empty.not-found {
|
||||
height: 100vh;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import NotFound from './page-not-found';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<NotFound />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,22 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Empty } from 'antd';
|
||||
|
||||
import './page-not-found.scss';
|
||||
|
||||
|
||||
class PageNotFound extends PureComponent<any, any> {
|
||||
render() {
|
||||
return(
|
||||
<Empty
|
||||
className="empty not-found"
|
||||
description="Page not found..."
|
||||
image="./images/empty-tasks-icon.svg">
|
||||
<Link to="/tasks">Go back to tasks</Link>
|
||||
</Empty>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PageNotFound;
|
||||
@ -0,0 +1,224 @@
|
||||
import React from 'react';
|
||||
import { FormComponentProps } from 'antd/lib/form/Form';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Input,
|
||||
Form,
|
||||
} from 'antd';
|
||||
|
||||
export interface RegisterData {
|
||||
username: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
password1: string;
|
||||
password2: string;
|
||||
}
|
||||
|
||||
import patterns from '../utils/validation-patterns';
|
||||
|
||||
type RegisterFormProps = {
|
||||
onSubmit(registerData: RegisterData): void;
|
||||
} & FormComponentProps;
|
||||
|
||||
class RegisterForm extends React.PureComponent<RegisterFormProps> {
|
||||
constructor(props: RegisterFormProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
private validateConfirmation(rule: any, value: any, callback: any) {
|
||||
const { form } = this.props;
|
||||
if (value && value !== form.getFieldValue('password1')) {
|
||||
callback('Two passwords that you enter is inconsistent!');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
private validatePassword(_: any, value: any, callback: any) {
|
||||
const { form } = this.props;
|
||||
if (!patterns.validatePasswordLength.pattern.test(value)) {
|
||||
callback(patterns.validatePasswordLength.message);
|
||||
}
|
||||
|
||||
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
|
||||
callback(patterns.passwordContainsNumericCharacters.message);
|
||||
}
|
||||
|
||||
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
|
||||
callback(patterns.passwordContainsUpperCaseCharacter.message);
|
||||
}
|
||||
|
||||
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
|
||||
callback(patterns.passwordContainsLowerCaseCharacter.message);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
form.validateFields(['password2'], { force: true });
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
private validateUsername(_: any, value: any, callback: any) {
|
||||
if (!patterns.validateUsernameLength.pattern.test(value)) {
|
||||
callback(patterns.validateUsernameLength.message);
|
||||
}
|
||||
|
||||
if (!patterns.validateUsernameCharacters.pattern.test(value)) {
|
||||
callback(patterns.validateUsernameCharacters.message);
|
||||
}
|
||||
|
||||
callback();
|
||||
};
|
||||
|
||||
private handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((error, values) => {
|
||||
if (!error) {
|
||||
this.props.onSubmit(values);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private renderFirstNameField() {
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{this.props.form.getFieldDecorator('firstName', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please specify a first name',
|
||||
pattern: patterns.validateName.pattern,
|
||||
}],
|
||||
})(
|
||||
<Input
|
||||
prefix={<Icon type='user-add' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='First name'
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
private renderLastNameField() {
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{this.props.form.getFieldDecorator('lastName', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please specify a last name',
|
||||
pattern: patterns.validateName.pattern,
|
||||
}],
|
||||
})(
|
||||
<Input
|
||||
prefix={<Icon type='user-add' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Last name'
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
private renderUsernameField() {
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{this.props.form.getFieldDecorator('username', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please specify a username',
|
||||
}, {
|
||||
validator: this.validateUsername,
|
||||
}],
|
||||
})(
|
||||
<Input
|
||||
prefix={<Icon type='user-add' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Username'
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
private renderEmailField() {
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{this.props.form.getFieldDecorator('email', {
|
||||
rules: [{
|
||||
type: 'email',
|
||||
message: 'The input is not valid E-mail!',
|
||||
}, {
|
||||
required: true,
|
||||
message: 'Please specify an email address',
|
||||
}],
|
||||
})(
|
||||
<Input
|
||||
autoComplete='email'
|
||||
prefix={<Icon type='mail' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Email address'
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
private renderPasswordField() {
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{this.props.form.getFieldDecorator('password1', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please input your password!',
|
||||
}, {
|
||||
validator: this.validatePassword.bind(this),
|
||||
}],
|
||||
})(<Input.Password
|
||||
autoComplete='new-password'
|
||||
prefix={<Icon type='lock' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Password'
|
||||
/>)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
private renderPasswordConfirmationField() {
|
||||
return (
|
||||
<Form.Item hasFeedback>
|
||||
{this.props.form.getFieldDecorator('password2', {
|
||||
rules: [{
|
||||
required: true,
|
||||
message: 'Please confirm your password!',
|
||||
}, {
|
||||
validator: this.validateConfirmation.bind(this),
|
||||
}],
|
||||
})(<Input.Password
|
||||
autoComplete='new-password'
|
||||
prefix={<Icon type='lock' style={{ color: 'rgba(0,0,0,.25)'}} />}
|
||||
placeholder='Confirm password'
|
||||
/>)}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit.bind(this)} className='login-form'>
|
||||
{this.renderFirstNameField()}
|
||||
{this.renderLastNameField()}
|
||||
{this.renderUsernameField()}
|
||||
{this.renderEmailField()}
|
||||
{this.renderPasswordField()}
|
||||
{this.renderPasswordConfirmationField()}
|
||||
|
||||
<Form.Item>
|
||||
<Button type='primary' htmlType='submit' className='register-form-button'>
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create<RegisterFormProps>()(RegisterForm);
|
||||
@ -1,10 +0,0 @@
|
||||
.register-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
|
||||
&__title {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import RegisterPage from './register-page';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<RegisterPage />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,211 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { registerAsync, isAuthenticatedAsync } from '../../actions/auth.actions';
|
||||
|
||||
import { Button, Icon, Input, Form, Col, Row, Spin } from 'antd';
|
||||
import Title from 'antd/lib/typography/Title';
|
||||
|
||||
import './register-page.scss';
|
||||
|
||||
|
||||
class RegisterForm extends PureComponent<any, any> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = { confirmDirty: false, loading: false };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ loading: true });
|
||||
|
||||
this.props.dispatch(isAuthenticatedAsync()).then(
|
||||
(isAuthenticated: boolean) => {
|
||||
this.setState({ loading: false });
|
||||
|
||||
if (this.props.isAuthenticated) {
|
||||
this.props.history.replace('/tasks');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
return (
|
||||
<Spin wrapperClassName="spinner" size="large" spinning={ this.state.loading }>
|
||||
<Row type="flex" justify="center" align="middle">
|
||||
<Col xs={12} md={10} lg={8} xl={6}>
|
||||
<Form className="register-form" onSubmit={ this.onSubmit }>
|
||||
<Title className="register-form__title">Register</Title>
|
||||
|
||||
<Form.Item>
|
||||
{getFieldDecorator('username', {
|
||||
rules: [{ required: true, message: 'Please enter your username!' }],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="user" /> }
|
||||
type="text"
|
||||
name="username"
|
||||
placeholder="Username"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
{getFieldDecorator('firstName', {
|
||||
rules: [],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="idcard" /> }
|
||||
type="text"
|
||||
name="first-name"
|
||||
placeholder="First name"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
{getFieldDecorator('lastName', {
|
||||
rules: [],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="idcard" /> }
|
||||
type="text"
|
||||
name="last-name"
|
||||
placeholder="Last name"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item hasFeedback>
|
||||
{getFieldDecorator('email', {
|
||||
rules: [
|
||||
{
|
||||
type: 'email',
|
||||
message: 'The input is not valid email!',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input your email!',
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Input
|
||||
prefix={ <Icon type="mail" /> }
|
||||
type="text"
|
||||
name="email"
|
||||
placeholder="Email"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item hasFeedback>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input your password!',
|
||||
},
|
||||
{
|
||||
validator: this.validateToNextPassword,
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Input.Password
|
||||
prefix={ <Icon type="lock" /> }
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item hasFeedback>
|
||||
{getFieldDecorator('passwordConfirmation', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please confirm your password!',
|
||||
},
|
||||
{
|
||||
validator: this.compareToFirstPassword,
|
||||
},
|
||||
],
|
||||
})(
|
||||
<Input.Password
|
||||
onBlur={ this.handleConfirmBlur }
|
||||
prefix={ <Icon type="lock" /> }
|
||||
name="password-confirmation"
|
||||
placeholder="Password confirmation"
|
||||
/>,
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" loading={ this.props.isFetching }>
|
||||
Register
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
Already have an account? <Link to="/login">Login here.</Link>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
|
||||
private handleConfirmBlur = (event: any) => {
|
||||
const { value } = event.target;
|
||||
|
||||
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
|
||||
};
|
||||
|
||||
private compareToFirstPassword = (rule: any, value: string, callback: Function) => {
|
||||
const { form } = this.props;
|
||||
|
||||
if (value && value !== form.getFieldValue('password')) {
|
||||
callback('Two passwords that you enter are inconsistent!');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
private validateToNextPassword = (rule: any, value: string, callback: Function) => {
|
||||
const { form } = this.props;
|
||||
|
||||
if (value && this.state.confirmDirty) {
|
||||
form.validateFields(['passwordConfirmation'], { force: true });
|
||||
}
|
||||
|
||||
callback();
|
||||
};
|
||||
|
||||
private onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.form.validateFields((error: any, values: any) => {
|
||||
if (!error) {
|
||||
this.props.dispatch(
|
||||
registerAsync(
|
||||
values.username,
|
||||
values.firstName,
|
||||
values.lastName,
|
||||
values.email,
|
||||
values.password,
|
||||
values.passwordConfirmation,
|
||||
this.props.history,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return state.authContext;
|
||||
};
|
||||
|
||||
export default Form.create()(connect(mapStateToProps)(RegisterForm));
|
||||
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
import Text from 'antd/lib/typography/Text';
|
||||
import {
|
||||
Col,
|
||||
Row,
|
||||
Icon,
|
||||
} from 'antd';
|
||||
|
||||
export default function EmptyList() {
|
||||
const emptyTasksIcon = () => (<img src='/assets/empty-tasks-icon.svg'/>);
|
||||
|
||||
return (
|
||||
<div className='cvat-empty-task-list'>
|
||||
<Row type='flex' justify='center' align='middle'>
|
||||
<Col>
|
||||
<Icon className='cvat-empty-tasks-icon' component={emptyTasksIcon}/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row type='flex' justify='center' align='middle'>
|
||||
<Col>
|
||||
<Text strong> No tasks created yet ... </Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row type='flex' justify='center' align='middle'>
|
||||
<Col>
|
||||
<Text type='secondary'> To get started with your annotation project </Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row type='flex' justify='center' align='middle'>
|
||||
<Col>
|
||||
<Link to='/tasks/create'> create new task </Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||