diff --git a/webclient/package-lock.json b/webclient/package-lock.json
index d68eb144..cee3e08c 100644
--- a/webclient/package-lock.json
+++ b/webclient/package-lock.json
@@ -22784,6 +22784,18 @@
"@babel/runtime": "^7.4.4"
}
},
+ "@material-ui/lab": {
+ "version": "4.0.0-alpha.60",
+ "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.60.tgz",
+ "integrity": "sha512-fadlYsPJF+0fx2lRuyqAuJj7hAS1tLDdIEEdov5jlrpb5pp4b+mRDUqQTUxi4inRZHS1bEXpU8QWUhO6xX88aA==",
+ "requires": {
+ "@babel/runtime": "^7.4.4",
+ "@material-ui/utils": "^4.11.2",
+ "clsx": "^1.0.4",
+ "prop-types": "^15.7.2",
+ "react-is": "^16.8.0 || ^17.0.0"
+ }
+ },
"@material-ui/styles": {
"version": "4.11.4",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz",
diff --git a/webclient/package.json b/webclient/package.json
index 57c8b4de..5a3ac045 100644
--- a/webclient/package.json
+++ b/webclient/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
+ "@material-ui/lab": "^4.0.0-alpha.60",
"@material-ui/styles": "^4.11.4",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
diff --git a/webclient/src/components/KnownHosts/KnownHosts.tsx b/webclient/src/components/KnownHosts/KnownHosts.tsx
index f9fd5a94..4564900e 100644
--- a/webclient/src/components/KnownHosts/KnownHosts.tsx
+++ b/webclient/src/components/KnownHosts/KnownHosts.tsx
@@ -1,5 +1,4 @@
-// eslint-disable-next-line
-import React, { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useState } from 'react';
import { Select, MenuItem } from '@material-ui/core';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
@@ -14,6 +13,7 @@ import ErrorOutlinedIcon from '@material-ui/icons/ErrorOutlined';
import { KnownHostDialog } from 'dialogs';
import { HostDTO } from 'services';
import { DefaultHosts, Host, getHostPort } from 'types';
+import Toast from 'components/Toast/Toast';
import './KnownHosts.css';
@@ -44,6 +44,10 @@ const KnownHosts = (props) => {
edit: null,
});
+ const [showCreateToast, setShowCreateToast] = useState(false)
+ const [showDeleteToast, setShowDeleteToast] = useState(false)
+ const [showEditToast, setShowEditToast] = useState(false)
+
const loadKnownHosts = useCallback(async () => {
const hosts = await HostDTO.getAll();
@@ -96,6 +100,7 @@ const KnownHosts = (props) => {
closeKnownHostDialog();
HostDTO.delete(id);
+ setShowDeleteToast(true)
};
const handleDialogSubmit = async ({ id, name, host, port }) => {
@@ -111,6 +116,7 @@ const KnownHosts = (props) => {
hosts: s.hosts.map(h => h.id === id ? hostDTO : h),
selectedHost: hostDTO
}));
+ setShowEditToast(true)
} else {
const newHost: Host = { name, host, port, editable: true };
newHost.id = await HostDTO.add(newHost) as number;
@@ -120,6 +126,7 @@ const KnownHosts = (props) => {
hosts: [...s.hosts, newHost],
selectedHost: newHost,
}));
+ setShowCreateToast(true)
}
closeKnownHostDialog();
@@ -206,6 +213,9 @@ const KnownHosts = (props) => {
onSubmit={handleDialogSubmit}
handleClose={closeKnownHostDialog}
/>
+ setShowCreateToast(false)}>Host successfully created.
+ setShowDeleteToast(false)}>Host successfully deleted.
+ setShowEditToast(false)}>Host successfully edited.
)
};
diff --git a/webclient/src/components/Toast/Toast.tsx b/webclient/src/components/Toast/Toast.tsx
new file mode 100644
index 00000000..e4bb6eb5
--- /dev/null
+++ b/webclient/src/components/Toast/Toast.tsx
@@ -0,0 +1,65 @@
+import * as React from 'react'
+import ReactDOM from 'react-dom'
+
+import Alert, { AlertProps } from '@material-ui/lab/Alert';
+import CheckCircleIcon from '@material-ui/icons/CheckCircle';
+import Slide, { SlideProps } from '@material-ui/core/Slide';
+import Snackbar from '@material-ui/core/Snackbar';
+
+const iconMapping = {
+ success:
+}
+
+function Toast(props) {
+ const { open, onClose, severity, autoHideDuration, children } = props
+
+ const rootElemRef = React.useRef(document.createElement('div'));
+
+ React.useEffect(() => {
+ document.body.appendChild(rootElemRef.current)
+ return () => {
+ rootElemRef.current.remove();
+ }
+ }, [rootElemRef])
+
+ const handleClose = (event?: React.SyntheticEvent, reason?: string) => {
+ if (reason === 'clickaway') {
+ return;
+ }
+ onClose(event);
+ };
+
+ const node = (
+
+
+ {children}
+
+
+ )
+ if (!rootElemRef.current) {
+ return null
+ }
+
+ return ReactDOM.createPortal(
+ node,
+ rootElemRef.current
+ );
+}
+
+Toast.defaultProps = {
+ severity: 'success',
+ // 10s wait before automatically dismissing the Toast.
+ autoHideDuration: 10000,
+}
+
+function TransitionLeft(props) {
+ return ;
+}
+
+export default Toast
diff --git a/webclient/src/containers/Login/Login.tsx b/webclient/src/containers/Login/Login.tsx
index dd50a788..f7a75427 100644
--- a/webclient/src/containers/Login/Login.tsx
+++ b/webclient/src/containers/Login/Login.tsx
@@ -1,5 +1,4 @@
-// eslint-disable-next-line
-import React, { useState, useCallback } from "react";
+import { useState, useCallback } from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { makeStyles } from '@material-ui/core/styles';