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';