diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..a688bb6
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "trailingComma": "all",
+ "printWidth": 80,
+ "tabWidth": 2,
+ "singleQuote": true
+}
diff --git a/package.json b/package.json
index a9443f6..6b4482a 100644
--- a/package.json
+++ b/package.json
@@ -5,12 +5,19 @@
"dependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
+ "@mui/icons-material": "^5.6.2",
"@mui/material": "^5.0.6",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
+ "chroma-js": "^2.4.2",
+ "cors-anywhere": "^0.4.4",
+ "hoist-non-react-statics": "^3.3.2",
+ "lodash": "^4.17.21",
+ "prettier": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
+ "react-dropzone": "^14.2.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.0.1"
},
diff --git a/public/favicon.ico b/public/favicon.ico
index a11777c..e652531 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/index.html b/public/index.html
index e18ed22..e874064 100644
--- a/public/index.html
+++ b/public/index.html
@@ -25,7 +25,7 @@
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`.
-->
-
React App
+ Hasty Demo App
diff --git a/src/App.js b/src/App.js
index a629449..4a578e8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,177 +1,397 @@
-import logo from "./logo.svg";
-import "./App.css";
-import { useState } from "react";
-import { CardMediaWithAnnotations } from "./ImageWithAnnotations";
+import { useEffect, useRef, useState } from 'react';
+import isEqual from 'lodash/isEqual';
+import './App.css';
+import { CardMediaWithAnnotations } from './components/imageMediaWithAnnotations/ImageWithAnnotations';
import {
- AppBar,
Box,
Button,
+ CircularProgress,
+ Container,
+ Grid,
Stack,
- TextField,
ThemeProvider,
Toolbar,
Typography,
-} from "@mui/material";
-import { theme } from "./theme";
-
-import { ReactComponent as Logo } from "./icons/hog.svg";
-import { getImageSize, readAsArrayBuffer, readAsDataURL } from "./helpers";
-
-const API_BASE_URL = `http://localhost:8080/https://api.hasty.ai`;
-const API_KEY = "";
-const PROJECT_ID = "";
-
-function App() {
- const [key, setKey] = useState(API_KEY);
- const [projectId, setProjectId] = useState(PROJECT_ID);
+} from '@mui/material';
+import { theme } from './theme';
+import { ReactComponent as Logo } from './img/hog.svg';
+import { getImageSize, readAsArrayBuffer, readAsDataURL } from './helpers';
+import {
+ API_BASE_URL,
+ HEADERS,
+ MODEL_STATUS,
+ PROJECT_ID,
+ PROXY,
+} from './constants';
+import { LabelClasses } from './components/labelClasses/LabelClasses';
+import BottomLeftPattern from './img/bottomLeftPattern.svg';
+import Flywheel from './img/flywheel.svg';
+import Success from './img/success.svg';
+import RightPattern from './img/rightPattern.svg';
+import { UploadBox } from './components/uploadBox/UploadBox';
- const [modelStatus, setModelStatus] = useState("Unknown");
+export const App = () => {
+ const [labelClasses, setLabelClasses] = useState([]);
+ const [modelStatus, setModelStatus] = useState(MODEL_STATUS.UNKNOWN);
const [modelId, setModelId] = useState(null);
const [imageUrl, setImageUrl] = useState(null);
const [imageSizes, setImageSizes] = useState({ width: 0, height: 0 });
- const [labels, setLables] = useState([]);
+ const [labels, setLabels] = useState([]);
+ const [model, setModel] = useState('instance_segmentor');
+ const [imageUploading, setImageUploading] = useState(false);
+ const [showPredictions, setShowPredictions] = useState(false);
+ const [imageName, setImageName] = useState('');
+ const [uploadId, setUploadId] = useState();
+ const [threshold, setThreshold] = useState(50);
+ const [confidence, setConfidence] = useState(50);
+ const [predictionsLoading, setPredictionsLoading] = useState(false);
+ const [lastValues, setLastValues] = useState({
+ confidence: 50,
+ threshold: 50,
+ });
+
+ const intervalRef = useRef();
+ const inputRef = useRef();
- const headers = {
- "X-Api-Key": key,
- "content-type": "application/json",
+ const fetchLabels = async (id) => {
+ try {
+ setPredictionsLoading(true);
+
+ const response = await fetch(
+ `${API_BASE_URL}/v1/projects/${PROJECT_ID}/${
+ model === 'instance_segmentor'
+ ? 'instance_segmentation'
+ : 'object_detection'
+ }?include_border_mask=true`,
+ {
+ headers: HEADERS,
+ method: 'POST',
+ body: JSON.stringify({
+ model_id: modelId,
+ upload_id: id,
+ confidence_threshold: confidence / 100,
+ masker_threshold: threshold / 100,
+ }),
+ },
+ );
+
+ const json = await response.json();
+
+ setLabels(
+ json.map((inf) => ({
+ ...inf,
+ x: inf.bbox[0],
+ width: inf.bbox[2] - inf.bbox[0],
+ y: inf.bbox[1],
+ height: inf.bbox[3] - inf.bbox[1],
+ })),
+ );
+ setLastValues({ confidence, threshold });
+ } catch (error) {
+ console.error(error);
+ } finally {
+ setPredictionsLoading(false);
+ }
+ };
+
+ const handleChange = (event) => {
+ setModel(event.target.value);
+ setLabels([]);
+ setImageUrl(null);
+ setImageSizes({ width: 0, height: 0 });
+
+ if (inputRef.current) {
+ inputRef.current.value = null;
+ }
};
const handleModelStatusCheck = async () => {
- setModelStatus("Checking ...");
- const url = `${API_BASE_URL}/v1/projects/${projectId}/instance_segmentor`;
+ setModelStatus('Checking...');
+ const url = `${API_BASE_URL}/v1/projects/${PROJECT_ID}/${model}`;
+
try {
- const response = await fetch(url, { headers });
+ const response = await fetch(url, { headers: HEADERS });
const json = await response.json();
- setModelStatus(json.status || "Error");
+
+ setModelStatus(json.status || MODEL_STATUS.ERROR);
setModelId(json.model_id || null);
} catch (e) {
- setModelStatus("Error");
+ setModelStatus('Error');
+ }
+ };
+
+ const fetchLabelClasses = async () => {
+ const url = `${API_BASE_URL}/v1/projects/${PROJECT_ID}/label_classes`;
+ const labelClassesResponse = await fetch(url, {
+ headers: HEADERS,
+ method: 'GET',
+ });
+ const labelClassesJson = await labelClassesResponse.json();
+
+ setLabelClasses(labelClassesJson.items);
+ };
+
+ const refetch = () => {
+ if (
+ !isEqual(lastValues, { confidence, threshold }) &&
+ confidence !== '' &&
+ threshold !== ''
+ ) {
+ fetchLabels(uploadId);
}
};
const handleUpload = async (e) => {
- const signedUrlsUrl = `${API_BASE_URL}/v1/projects/${projectId}/image_uploads`;
+ const signedUrlsUrl = `${API_BASE_URL}/v1/projects/${PROJECT_ID}/image_uploads`;
+
try {
+ setImageUploading(true);
const signedUrlsResponse = await fetch(signedUrlsUrl, {
- headers,
- method: "POST",
+ headers: HEADERS,
+ method: 'POST',
body: JSON.stringify({ count: 1 }),
});
const urlsJson = await signedUrlsResponse.json();
+
if (urlsJson?.items?.[0]) {
const { id, url } = urlsJson.items[0];
- const data = await readAsArrayBuffer(e.target.files[0]);
+ const data = await readAsArrayBuffer(e[0]);
- await fetch(`http://localhost:8080/${url}`, {
+ await fetch(`${PROXY}${url}`, {
body: data,
- method: "PUT",
+ method: 'PUT',
headers: {
- "content-type": "image/*",
+ 'content-type': 'image/*',
},
});
- const imageUrl = await readAsDataURL(e.target.files[0]);
- const imageSizes = await getImageSize(e.target.files[0]);
+ const imageUrl = await readAsDataURL(e[0]);
+ const imageSizes = await getImageSize(e[0]);
+ setImageName(e[0].name);
setImageUrl(imageUrl);
setImageSizes(imageSizes);
- const response = await fetch(
- `${API_BASE_URL}/v1/projects/${projectId}/instance_segmentation`,
- {
- headers,
- method: "POST",
- body: JSON.stringify({
- // confidence_threshold: 0.5,
- // max_detections_per_image: 10,
- model_id: modelId,
- upload_id: id,
- }),
- }
- );
-
- const json = await response.json();
- setLables(
- json.map((inf) => ({
- ...inf,
- x: inf.bbox[0],
- width: inf.bbox[2] - inf.bbox[0],
- y: inf.bbox[1],
- height: inf.bbox[3] - inf.bbox[1],
- }))
- );
+ setUploadId(id);
+ fetchLabels(id);
+ fetchLabelClasses();
}
} catch (e) {
console.error(e);
+ } finally {
+ setImageUploading(false);
}
};
+ useEffect(() => {
+ if (modelStatus !== MODEL_STATUS.LOADED && !intervalRef.current) {
+ handleModelStatusCheck();
+ intervalRef.current = setInterval(handleModelStatusCheck, 3000);
+ } else if (modelStatus === MODEL_STATUS.LOADED) {
+ clearInterval(intervalRef.current);
+ intervalRef.current = setInterval(handleModelStatusCheck, 30000);
+ }
+ }, [modelStatus, handleModelStatusCheck]);
+
+ useEffect(
+ () => () => {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ }
+ },
+ [],
+ );
+
+ useEffect(() => {
+ setModelStatus(MODEL_STATUS.UNKNOWN);
+ }, [model]);
+
return (
-
-
-
-
-
- Hasty Inference Primer
-
-
-
-
- setKey(e.target.value)}
- size={110}
+
+ {!showPredictions ? (
+
- setProjectId(e.target.value)}
- size={110}
+ ) : null}
+ {showPredictions ? (
+
-
-
- Model status: {modelStatus}
-
-
+ ) : null}
+
+
+
+
+
+ Hasty Inference Primer
+
+
+
+
+ PCB Components
+
+
-
-
-
- {imageUrl && imageSizes.width && (
-
- )}
-
+
+
+
+ {/**/}
+ {/*Model*/}
+ {/**/}
+ {/**/}
+ {!showPredictions ? (
+
+
+ Model status:
+
+
+ {modelStatus}
+
+ {modelStatus === MODEL_STATUS.CHECKING && (
+
+ )}
+
+ ) : null}
+ {!showPredictions ? (
+
+
+
+ Upload your image
+
+
+
+
+
+ File uploaded successfully
+
+
+
+
+
+
+
+
+ ) : null}
+ {showPredictions ? (
+
+
+
+
+
+
+
+ Image: {imageName}
+
+ {labels.length ? (
+
+ {labels.length} predicted{' '}
+ {labels.length === 1 ? 'label' : 'labels'}
+
+ ) : (
+
+ No predicted labels
+
+ )}
+ {labelClasses.length ? (
+
+ ) : null}
+
+
+
+
+ ) : null}
+
+
);
-}
-
-export default App;
+};
diff --git a/src/Camera.jsx b/src/Camera.jsx
new file mode 100644
index 0000000..932b4c1
--- /dev/null
+++ b/src/Camera.jsx
@@ -0,0 +1,20 @@
+import React, { useEffect } from 'react';
+
+export const Camera = () => {
+ async function getMedia(constraints) {
+ let stream = null;
+
+ try {
+ stream = await navigator.mediaDevices.getUserMedia(constraints);
+ /* use the stream */
+ } catch (err) {
+ /* handle the error */
+ }
+ }
+
+ useEffect(() => {
+ // getMedia({ video: true });
+ }, []);
+
+ return null;
+};
diff --git a/src/ImageWithAnnotations.jsx b/src/ImageWithAnnotations.jsx
deleted file mode 100644
index 1395efb..0000000
--- a/src/ImageWithAnnotations.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React, { useCallback, useState } from 'react';
-import { getObjectFitSize } from './helpers';
-
-export const CardMediaWithAnnotations = React.memo(
- ({
- originalImageWidth,
- labels,
- labelTooltip,
- showLabelWhiteBox = true,
- ...rest
- }) => {
- const [offsetX, setOffsetX] = useState(0);
- const [offsetY, setOffsetY] = useState(0);
- const [labelScale, setLabelScale] = useState(0);
-
- const handleLoad = useCallback(
- (e) => {
- const sizes = getObjectFitSize(e.currentTarget);
- setLabelScale(sizes.width / originalImageWidth);
- setOffsetX(sizes.x);
- setOffsetY(sizes.y);
- },
- [originalImageWidth, rest.imageUrl],
- );
-
-
- return (
-
-
![]()
- {labels.map((label, i) => {
- const labelContent = (
-
- );
- if (labelTooltip) {
- return (
- // eslint-disable-next-line react/no-array-index-key
-
- {labelTooltip(labelContent, label, i)}
-
- );
- }
-
- return labelContent;
- })}
-
- );
- },
-);
diff --git a/src/api/api.js b/src/api/api.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/coloredSquare/ColoredSquare.jsx b/src/components/coloredSquare/ColoredSquare.jsx
new file mode 100644
index 0000000..7e7d81e
--- /dev/null
+++ b/src/components/coloredSquare/ColoredSquare.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { getBorderColor } from './helpers';
+
+export const ColoredSquare = ({ color }) => (
+
+);
diff --git a/src/components/coloredSquare/helpers.js b/src/components/coloredSquare/helpers.js
new file mode 100644
index 0000000..800c0ef
--- /dev/null
+++ b/src/components/coloredSquare/helpers.js
@@ -0,0 +1,27 @@
+import chroma from 'chroma-js';
+
+const isEnoughContrast = (color1, color2) =>
+ chroma.contrast(color1, color2) > 2;
+
+export const getBorderColor = (rectColor, background) => {
+ const step = 0.2;
+ const defaultColor = 'white';
+ let currentColor = rectColor;
+ let keepAdjusting;
+
+ do {
+ let chromaColor;
+ try {
+ chromaColor = chroma(currentColor);
+ } catch (e) {
+ chromaColor = chroma(defaultColor);
+ }
+ const brighterColor = chromaColor.brighten(step).hex();
+ keepAdjusting =
+ !isEnoughContrast(background, brighterColor) &&
+ currentColor !== brighterColor;
+ currentColor = brighterColor;
+ } while (keepAdjusting);
+
+ return currentColor;
+};
diff --git a/src/components/imageMediaWithAnnotations/ImageWithAnnotations.jsx b/src/components/imageMediaWithAnnotations/ImageWithAnnotations.jsx
new file mode 100644
index 0000000..3b26e37
--- /dev/null
+++ b/src/components/imageMediaWithAnnotations/ImageWithAnnotations.jsx
@@ -0,0 +1,161 @@
+import React, { useCallback, useMemo, useState } from 'react';
+import chroma from 'chroma-js';
+import { getObjectFitSize } from '../../helpers';
+import {
+ Box,
+ CircularProgress,
+ Paper,
+ Stack,
+ Tooltip,
+ Typography,
+} from '@mui/material';
+import { ColoredSquare } from '../coloredSquare/ColoredSquare';
+
+export const CardMediaWithAnnotations = React.memo(
+ ({
+ originalImageWidth,
+ labels,
+ labelClasses,
+ showLabelWhiteBox = false,
+ predictionsLoading,
+ ...rest
+ }) => {
+ const [offsetX, setOffsetX] = useState(0);
+ const [offsetY, setOffsetY] = useState(0);
+ const [labelScale, setLabelScale] = useState(0);
+
+ const colorsMap = useMemo(() => {
+ return labelClasses.reduce((acc, val) => {
+ acc[val.id] = `rgba(${chroma(val.color).alpha(0.55).rgba()})`;
+
+ return acc;
+ }, {});
+ }, [labelClasses]);
+ const strokeColorsMap = useMemo(() => {
+ return labelClasses.reduce((acc, val) => {
+ acc[val.id] = `rgba(${chroma(val.color).alpha(1).rgba()})`;
+
+ return acc;
+ }, {});
+ }, [labelClasses]);
+ const classNamesMap = useMemo(() => {
+ return labelClasses.reduce((acc, val) => {
+ acc[val.id] = val.name;
+
+ return acc;
+ }, {});
+ }, [labelClasses]);
+
+ const handleLoad = useCallback(
+ (e) => {
+ const sizes = getObjectFitSize(e.currentTarget);
+ setLabelScale(sizes.width / originalImageWidth);
+ setOffsetX(sizes.x);
+ setOffsetY(sizes.y);
+ },
+ [originalImageWidth, rest.imageUrl],
+ );
+
+ return (
+
+ {predictionsLoading && (
+ <>
+
+
+ >
+ )}
+
+ {labels.map((label, i) => {
+ const labelContent = (
+
+
+
+
+
+ {classNamesMap[label.class_id]}
+
+
+
+ Confidence: {label.score.toFixed(2)}
+
+
+ ) : (
+ ''
+ )
+ }
+ followCursor
+ >
+
+
+
+
+ );
+
+ return labelContent;
+ })}
+
+ );
+ },
+);
diff --git a/src/components/labelClasses/LabelClasses.jsx b/src/components/labelClasses/LabelClasses.jsx
new file mode 100644
index 0000000..d1904c0
--- /dev/null
+++ b/src/components/labelClasses/LabelClasses.jsx
@@ -0,0 +1,93 @@
+import React from 'react';
+import { OutlinedInput } from '@mui/material';
+import { Box, FormControl, InputLabel, Stack, Typography } from '@mui/material';
+import { ColoredSquare } from '../coloredSquare/ColoredSquare';
+
+export const LabelClasses = ({
+ labelClasses,
+ labels,
+ setConfidence,
+ setThreshold,
+ confidence,
+ threshold,
+ refetch,
+}) => {
+ const labelsCountMap = labelClasses
+ .map(({ id }) => id)
+ .reduce((acc, val) => {
+ acc[val] = labels.filter(({ class_id }) => class_id === val).length;
+ return acc;
+ }, {});
+
+ return (
+
+
+
+ Confidence
+ {
+ let value = event.target.value;
+
+ if (value > 100) {
+ value = 100;
+ }
+ if (value < 0 && value !== '') {
+ value = 0;
+ }
+
+ setConfidence(value);
+ }}
+ onBlur={refetch}
+ />
+
+
+ Masker threshold
+ {
+ let value = event.target.value;
+
+ if (value > 100) {
+ value = 100;
+ }
+ if (value < 0 && value !== '') {
+ value = 0;
+ }
+
+ setThreshold(+value);
+ }}
+ onBlur={refetch}
+ />
+
+
+ Object classes
+
+ {labelClasses.map((labelClass) => (
+
+
+
+ {labelClass.name}
+
+
+ ({labelsCountMap[labelClass.id]} label
+ {labelsCountMap[labelClass.id] === 1 ? '' : 's'})
+
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/uploadBox/UploadBox.jsx b/src/components/uploadBox/UploadBox.jsx
new file mode 100644
index 0000000..a699a7e
--- /dev/null
+++ b/src/components/uploadBox/UploadBox.jsx
@@ -0,0 +1,111 @@
+import React from 'react';
+import { useDropzone } from 'react-dropzone';
+import Upload from '../../img/upload.svg';
+import { CardActionArea, CircularProgress, Typography } from '@mui/material';
+
+const fileSizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+const step = 1024;
+const formatFileSize = (bytes, decimals = 2) => {
+ if (!bytes) return '0 Bytes';
+
+ const power = Math.floor(Math.log(bytes) / Math.log(step));
+
+ return `${parseFloat((bytes / step ** power).toFixed(decimals))} ${
+ fileSizes[power]
+ }`;
+};
+const acceptedFileTypes = [
+ 'image/jpeg',
+ 'image/jpg',
+ 'image/jpe',
+ 'image/png',
+ 'image/svg',
+ 'image/bmp',
+ 'image/webp',
+ 'image/tiff',
+];
+
+export const UploadBox = ({
+ onDrop,
+ disabled = false,
+ inProgress = false,
+ maxSize,
+ fileTypesText,
+ children,
+}) => {
+ const dropzoneProps = useDropzone({
+ onDrop,
+ accept: acceptedFileTypes,
+ maxSize,
+ disabled,
+ });
+ const {
+ getRootProps,
+ getInputProps,
+ isDragAccept,
+ isDragReject,
+ rejectedFiles,
+ } = dropzoneProps;
+ const { ref, ...rootProps } = getRootProps();
+ const hasRejectedFiles = rejectedFiles?.length > 0;
+
+ return (
+
+
+ {isDragAccept && Release to upload file}
+ {inProgress && (
+ <>
+
+ Uploading
+ >
+ )}
+ {!isDragAccept && !inProgress && !isDragReject && (
+
+ )}
+ {hasRejectedFiles && (
+ <>
+ File rejected
+
+ Please note:
+
+ >
+ )}
+ {(isDragReject || hasRejectedFiles) && (
+ <>
+
+ Supported file types:{' '}
+ {fileTypesText ||
+ acceptedFileTypes
+ .map((fileType) => fileType.split('/')[1])
+ .join(', ')}
+ .
+
+
+ Maximum file size is {formatFileSize(maxSize)}.
+
+ Drag-and-drop of folders is not supported.
+ >
+ )}
+ {!isDragAccept && !inProgress && !isDragReject && !hasRejectedFiles && (
+
+ Drag the image you want to upload here or click to browse files
+
+ )}
+ {children}
+
+ );
+};
diff --git a/src/constants.js b/src/constants.js
new file mode 100644
index 0000000..a004404
--- /dev/null
+++ b/src/constants.js
@@ -0,0 +1,20 @@
+export const PROXY =
+ process.env.NODE_ENV === 'development' ? 'http://localhost:8080/' : '';
+export const API_BASE_URL = `${PROXY}https://api.hasty.ai`;
+export const API_KEY =
+ 'zX0ZBRL7Kml4w2jidFjh917IZX5sdY9YEpm0hY2QZDIg9gLdogniIzNHTfQN2TGET-cO4NK9ry9cRGRQ9-y8WQ';
+// export const API_KEY =
+// '19hkg36JsCn5VlaMHU9xaHpUz2GzPMxp3b5FE6NEyyYzQvuTZn0o67CaXKsPQ2IAj5kvAMXy3inMV-jNV4vJ8Q';
+// export const PROJECT_ID = '8061497a-2eb9-40c1-971c-b05d07f5a7e7';
+export const PROJECT_ID = '8061497a-2eb9-40c1-971c-b05d07f5a7e7';
+export const MODEL_STATUS = {
+ LOADED: 'LOADED',
+ ERROR: 'ERROR',
+ UNKNOWN: 'UNKNOWN',
+ LOADING: 'LOADING',
+ CHECKING: 'Checking...',
+};
+export const HEADERS = {
+ 'X-Api-Key': API_KEY,
+ 'content-type': 'application/json',
+};
diff --git a/src/icons/hog.svg b/src/icons/hog.svg
deleted file mode 100644
index 63a7a92..0000000
--- a/src/icons/hog.svg
+++ /dev/null
@@ -1,57 +0,0 @@
-
diff --git a/src/img/bottomLeftPattern.svg b/src/img/bottomLeftPattern.svg
new file mode 100644
index 0000000..9d2fe97
--- /dev/null
+++ b/src/img/bottomLeftPattern.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/img/flywheel.svg b/src/img/flywheel.svg
new file mode 100644
index 0000000..1cd97c4
--- /dev/null
+++ b/src/img/flywheel.svg
@@ -0,0 +1,529 @@
+
diff --git a/src/img/hog.svg b/src/img/hog.svg
new file mode 100644
index 0000000..9ea5ee9
--- /dev/null
+++ b/src/img/hog.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/img/rightPattern.svg b/src/img/rightPattern.svg
new file mode 100644
index 0000000..e3be922
--- /dev/null
+++ b/src/img/rightPattern.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/img/success.svg b/src/img/success.svg
new file mode 100644
index 0000000..b6cbe50
--- /dev/null
+++ b/src/img/success.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/img/upload.svg b/src/img/upload.svg
new file mode 100644
index 0000000..f392dc9
--- /dev/null
+++ b/src/img/upload.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/index.js b/src/index.js
index ef2edf8..85b7baf 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,14 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
-import App from './App';
+import { App } from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
,
- document.getElementById('root')
+ document.getElementById('root'),
);
// If you want to start measuring performance in your app, pass a function
diff --git a/src/theme.js b/src/theme.js
index ae78751..4537adc 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -1,42 +1,42 @@
-import { createTheme } from "@mui/material";
-import shadows from "@mui/material/styles/shadows";
+import { createTheme } from '@mui/material';
+import shadows from '@mui/material/styles/shadows';
const palette = {
- mode: "dark",
+ mode: 'dark',
primary: {
- main: "#2164aa",
- contrastText: "#dfdfdf",
- light: "#46BFEF",
+ main: '#2164aa',
+ contrastText: '#dfdfdf',
+ light: '#46BFEF',
},
secondary: {
- main: "#33373f",
- contrastText: "#dfdfdf",
+ main: '#33373f',
+ contrastText: '#dfdfdf',
},
interactive: {
- main: "#1e1f28",
+ main: '#1e1f28',
},
success: {
- main: "#00DAA4",
+ main: '#00DAA4',
},
error: {
- main: "#f3325e",
+ main: '#f3325e',
},
neutral: {
- light: "#999999",
- main: "#636363",
+ light: '#999999',
+ main: '#636363',
},
action: {
- hover: "#33373f",
+ hover: '#33373f',
},
text: {
- primary: "#dfdfdf",
- secondary: "#8e9196",
+ primary: '#dfdfdf',
+ secondary: '#8e9196',
},
background: {
- paper: "#34373E",
- default: "#282930",
- menu: "#2d2f36",
- body: "#282930",
+ paper: '#34373E',
+ default: '#282930',
+ menu: '#2d2f36',
+ body: '#EEF2F8',
},
};
@@ -46,46 +46,46 @@ export const theme = createTheme({
fontFamily: "'Maven Pro', sans-serif",
fontSize: 13,
button: {
- textTransform: "none",
+ textTransform: 'none',
fontWeight: 500,
lineHeight: 1,
- fontSize: "1rem",
+ fontSize: '1rem',
},
body1: {
- fontSize: "1rem",
+ fontSize: '1rem',
},
body2: {
- fontSize: "0.92rem",
+ fontSize: '0.92rem',
},
h1: {
fontSize: 22,
- lineHeight: "26px",
+ lineHeight: '26px',
// @ts-ignore
- fontWeight: "bold!important",
+ fontWeight: 'bold!important',
},
h2: {
// @ts-ignore
- fontWeight: "bold!important",
+ fontWeight: 'bold!important',
},
h3: {
// @ts-ignore
- fontWeight: "bold!important",
+ fontWeight: 'bold!important',
},
h4: {
// @ts-ignore
- fontWeight: "bold!important",
+ fontWeight: 'bold!important',
},
h5: {
// @ts-ignore
- fontWeight: "bold!important",
+ fontWeight: 'bold!important',
},
h6: {
fontSize: 16,
lineHeight: 1.7,
- fontWeight: "bold",
+ fontWeight: 'bold',
},
subtitle1: {
- fontSize: "1.3rem",
+ fontSize: '1.3rem',
},
},
shape: {
@@ -106,38 +106,38 @@ export const theme = createTheme({
styleOverrides: {
root: {
borderRadius: 0,
- backgroundImage: "none",
- overflow: "visible!important", // TODO: Revert to auto when fully switched to MUI
+ backgroundImage: 'none',
+ overflow: 'visible!important', // TODO: Revert to auto when fully switched to MUI
},
},
},
MuiButton: {
defaultProps: {
- variant: "contained",
+ variant: 'contained',
disableElevation: true,
},
styleOverrides: {
root: {
borderRadius: 0,
- lineHeight: "1.7rem",
+ lineHeight: '1.7rem',
fontWeight: 700,
- fontSize: "1rem",
- whiteSpace: "nowrap",
+ fontSize: '1rem',
+ whiteSpace: 'nowrap',
},
sizeSmall: {
- fontWeight: "normal",
- fontSize: "0.9rem",
+ fontWeight: 'normal',
+ fontSize: '0.9rem',
},
outlined: {
border: `1px dotted #fff`,
color: palette.text.primary,
background: palette.interactive.main,
- "&:hover": {
+ '&:hover': {
background: palette.neutral.main,
- border: "1px dotted rgba(255, 255, 255, 0.3)",
+ border: '1px dotted rgba(255, 255, 255, 0.3)',
},
- ":disabled": {
- border: "1px dotted rgba(255, 255, 255, 0.3)",
+ ':disabled': {
+ border: '1px dotted rgba(255, 255, 255, 0.3)',
},
},
},
@@ -162,7 +162,7 @@ export const theme = createTheme({
MuiCardContent: {
styleOverrides: {
root: {
- "&:last-child": {
+ '&:last-child': {
paddingBottom: 16,
},
},
@@ -184,34 +184,34 @@ export const theme = createTheme({
},
styleOverrides: {
root: {
- backgroundColor: "#1e1f28",
+ backgroundColor: '#1e1f28',
borderRadius: 0,
// @ts-ignore
- "&.Mui-error .MuiOutlinedInput-notchedOutline": {
- borderStyle: "solid!important",
- borderWidth: "1px!important",
+ '&.Mui-error .MuiOutlinedInput-notchedOutline': {
+ borderStyle: 'solid!important',
+ borderWidth: '1px!important',
borderColor: ({ palette }) =>
// @ts-ignore
palette?.error?.light,
},
},
notchedOutline: {
- border: "none",
+ border: 'none',
},
input: {
- padding: "7.5px 14px",
+ padding: '7.5px 14px',
},
},
},
MuiInputLabel: {
styleOverrides: {
root: {
- transform: "none",
- position: "static",
+ transform: 'none',
+ position: 'static',
paddingBottom: 6,
- color: "#939394",
- display: "flex",
- alignItems: "center",
+ color: '#939394',
+ display: 'flex',
+ alignItems: 'center',
},
},
},
@@ -219,7 +219,7 @@ export const theme = createTheme({
styleOverrides: {
root: {
borderRadius: 0,
- fontSize: "1rem",
+ fontSize: '1rem',
backgroundColor: palette.interactive.main,
},
},
@@ -227,7 +227,7 @@ export const theme = createTheme({
MuiList: {
styleOverrides: {
root: {
- backgroundImage: "none",
+ backgroundImage: 'none',
backgroundColor: palette.background.menu,
},
},
@@ -242,11 +242,11 @@ export const theme = createTheme({
MuiTable: {
styleOverrides: {
root: {
- backgroundColor: "#282930",
+ backgroundColor: '#282930',
},
stickyHeader: {
- ".MuiTableCell-head": {
- backgroundColor: "#282930",
+ '.MuiTableCell-head': {
+ backgroundColor: '#282930',
},
},
},
@@ -254,20 +254,20 @@ export const theme = createTheme({
MuiTableCell: {
styleOverrides: {
root: {
- border: "none",
- fontSize: "1rem",
- padding: "0.8rem 1.2rem",
+ border: 'none',
+ fontSize: '1rem',
+ padding: '0.8rem 1.2rem',
},
head: {
- color: "#888A8E",
- borderBottom: `1px solid ${"#33373f"}`,
- fontWeight: "normal",
- whiteSpace: "nowrap",
+ color: '#888A8E',
+ borderBottom: `1px solid ${'#33373f'}`,
+ fontWeight: 'normal',
+ whiteSpace: 'nowrap',
},
// Our theme was overriding the padding prop passed to TableCell,
// so this over-overrides it back to normal behavior.
paddingNone: {
- padding: "none",
+ padding: 'none',
},
},
},
@@ -277,31 +277,31 @@ export const theme = createTheme({
},
styleOverrides: {
root: {
- "&:nth-of-type(even)": {
- backgroundColor: "#33373f",
- "& > .MuiTableCell-root": {
- backgroundColor: "#33373f",
+ '&:nth-of-type(even)': {
+ backgroundColor: '#33373f',
+ '& > .MuiTableCell-root': {
+ backgroundColor: '#33373f',
},
},
- "&:nth-of-type(odd)": {
- backgroundColor: "#282930",
- "& > .MuiTableCell-root": {
- backgroundColor: "#282930",
+ '&:nth-of-type(odd)': {
+ backgroundColor: '#282930',
+ '& > .MuiTableCell-root': {
+ backgroundColor: '#282930',
},
},
- "&:hover": {
- backgroundColor: `${"#636363"}!important`,
- "& > .MuiTableCell-root": {
- backgroundColor: `${"#636363"}!important`,
+ '&:hover': {
+ backgroundColor: `${'#636363'}!important`,
+ '& > .MuiTableCell-root': {
+ backgroundColor: `${'#636363'}!important`,
},
},
},
head: {
- "&:hover": {
- backgroundColor: "inherit",
+ '&:hover': {
+ backgroundColor: 'inherit',
},
- "&:nth-of-type(even)": {
- backgroundColor: "inherit",
+ '&:nth-of-type(even)': {
+ backgroundColor: 'inherit',
},
},
},
@@ -309,7 +309,9 @@ export const theme = createTheme({
MuiTooltip: {
styleOverrides: {
tooltip: {
- fontSize: "1rem",
+ fontSize: '1rem',
+ padding: 0,
+ borderRadius: '3px',
},
},
},
@@ -329,12 +331,12 @@ export const theme = createTheme({
styleOverrides: {
root: {
backgroundColor: palette.interactive.main,
- padding: "0px 8px",
+ padding: '0px 8px',
minHeight: 36,
},
content: {
margin: 0,
- alignItems: "center",
+ alignItems: 'center',
},
},
},
@@ -348,26 +350,26 @@ export const theme = createTheme({
MuiSelect: {
styleOverrides: {
root: {
- display: "flex",
- alignItems: "center",
+ display: 'flex',
+ alignItems: 'center',
},
filled: {
- padding: "0.6rem",
+ padding: '0.6rem',
},
icon: {
- top: "calc(50% - 9px)",
+ top: 'calc(50% - 9px)',
},
},
},
MuiAutocomplete: {
styleOverrides: {
inputRoot: {
- "&.MuiOutlinedInput-root": {
- padding: "0px 8px",
+ '&.MuiOutlinedInput-root': {
+ padding: '0px 8px',
},
},
endAdornment: {
- top: "calc(50% - 12px)",
+ top: 'calc(50% - 12px)',
},
paper: {
boxShadow: shadows[3],
@@ -384,9 +386,9 @@ export const theme = createTheme({
MuiDialog: {
styleOverrides: {
container: {
- height: "unset",
- marginTop: "4rem",
- marginBottom: "4rem",
+ height: 'unset',
+ marginTop: '4rem',
+ marginBottom: '4rem',
},
paper: {
backgroundColor: palette.background.body,
@@ -398,13 +400,13 @@ export const theme = createTheme({
// @ts-ignore
root: {
backgroundColor: palette.interactive.main,
- padding: "6px 24px",
- "& > .MuiTypography-root": {
+ padding: '6px 24px',
+ '& > .MuiTypography-root': {
marginBottom: 0,
- textTransform: "uppercase",
- color: "#8e9196",
- fontSize: "1.1rem",
- fontWeight: "500!important",
+ textTransform: 'uppercase',
+ color: '#8e9196',
+ fontSize: '1.1rem',
+ fontWeight: '500!important',
},
},
},
@@ -412,30 +414,30 @@ export const theme = createTheme({
MuiDialogContent: {
styleOverrides: {
root: {
- padding: "1.5rem !important",
- overflow: "visible",
+ padding: '1.5rem !important',
+ overflow: 'visible',
},
},
},
MuiDialogActions: {
styleOverrides: {
root: {
- padding: "1rem",
+ padding: '1rem',
},
},
},
MuiModal: {
styleOverrides: {
root: {
- overflow: "auto",
+ overflow: 'auto',
},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
- ":hover": {
- color: "inherit",
+ ':hover': {
+ color: 'inherit',
},
},
},
@@ -459,7 +461,7 @@ export const theme = createTheme({
MuiAlert: {
styleOverrides: {
message: {
- fontSize: "1rem",
+ fontSize: '1rem',
},
},
},
diff --git a/yarn.lock b/yarn.lock
index 45b18dc..055e4db 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1112,6 +1112,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.17.2":
+ version "7.17.9"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
+ integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.3.3":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
@@ -1528,6 +1535,13 @@
prop-types "^15.7.2"
react-is "^17.0.2"
+"@mui/icons-material@^5.6.2":
+ version "5.6.2"
+ resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.6.2.tgz#239c40fc5841dc7c6af7d00e4e988550de170fcd"
+ integrity sha512-9QdI7axKuBAyaGz4mtdi7Uy1j73/thqFmEuxpJHxNC7O8ADEK1Da3t2veK2tgmsXsUlAHcAG63gg+GvWWeQNqQ==
+ dependencies:
+ "@babel/runtime" "^7.17.2"
+
"@mui/material@^5.0.6":
version "5.0.6"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.0.6.tgz#0e688c918fd88a07f59385614c65fce937077a9f"
@@ -2719,6 +2733,11 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+attr-accept@^2.2.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
+ integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
+
autoprefixer@^9.6.1:
version "9.8.6"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
@@ -3433,6 +3452,11 @@ chownr@^2.0.0:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+chroma-js@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-2.4.2.tgz#dffc214ed0c11fa8eefca2c36651d8e57cbfb2b0"
+ integrity sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==
+
chrome-trace-event@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
@@ -3760,6 +3784,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+cors-anywhere@^0.4.4:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/cors-anywhere/-/cors-anywhere-0.4.4.tgz#98892fcab55f408fff13a63e125135c18dc22ca8"
+ integrity sha512-8OBFwnzMgR4mNrAeAyOLB2EruS2z7u02of2bOu7i9kKYlZG+niS7CTHLPgEXKWW2NAOJWRry9RRCaL9lJRjNqg==
+ dependencies:
+ http-proxy "1.11.1"
+ proxy-from-env "0.0.1"
+
cosmiconfig@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
@@ -4933,6 +4965,11 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+eventemitter3@1.x.x:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
+ integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=
+
eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
@@ -5176,6 +5213,13 @@ file-loader@6.1.1:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
+file-selector@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc"
+ integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==
+ dependencies:
+ tslib "^2.4.0"
+
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
@@ -5848,6 +5892,14 @@ http-proxy-middleware@0.19.1:
lodash "^4.17.11"
micromatch "^3.1.10"
+http-proxy@1.11.1:
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.11.1.tgz#71df55757e802d58ea810df2244019dda05ae85d"
+ integrity sha1-cd9VdX6ALVjqgQ3yJEAZ3aBa6F0=
+ dependencies:
+ eventemitter3 "1.x.x"
+ requires-port "0.x.x"
+
http-proxy@^1.17.0:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
@@ -7225,7 +7277,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5:
+"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -8997,6 +9049,11 @@ prepend-http@^1.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
+prettier@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
+ integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
+
pretty-bytes@^5.3.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
@@ -9074,6 +9131,15 @@ prop-types@^15.6.2, prop-types@^15.7.2:
object-assign "^4.1.1"
react-is "^16.8.1"
+prop-types@^15.8.1:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
proxy-addr@~2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
@@ -9082,6 +9148,11 @@ proxy-addr@~2.0.5:
forwarded "~0.1.2"
ipaddr.js "1.9.1"
+proxy-from-env@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-0.0.1.tgz#b27c4946e9e6d5dbadb7598a6435d3014c4cfd49"
+ integrity sha1-snxJRunm1dutt1mKZDXTAUxM/Uk=
+
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
@@ -9280,12 +9351,21 @@ react-dom@^17.0.2:
object-assign "^4.1.1"
scheduler "^0.20.2"
+react-dropzone@^14.2.0:
+ version "14.2.0"
+ resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.0.tgz#97faaaf2e0a76daadfa678232be67d59ce891449"
+ integrity sha512-D7AXPtRba8rd7DBOejh3W2v1Uax6i7XKPYPuMr13XFPfnDcPHHvlEfp3raVpdj3XMHlRfYuf2H5+m8p7mlgKdQ==
+ dependencies:
+ attr-accept "^2.2.2"
+ file-selector "^0.6.0"
+ prop-types "^15.8.1"
+
react-error-overlay@^6.0.9:
version "6.0.9"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
-react-is@^16.7.0, react-is@^16.8.1:
+react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -9645,6 +9725,11 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+requires-port@0.x.x:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-0.0.1.tgz#4b4414411d9df7c855995dd899a8c78a2951c16d"
+ integrity sha1-S0QUQR2d98hVmV3YmajHiilRwW0=
+
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
@@ -10885,6 +10970,11 @@ tslib@^2.0.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
+tslib@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+ integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
tsutils@^3.17.1:
version "3.20.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698"