diff --git a/assets/times.svg b/assets/times.svg new file mode 100644 index 00000000..c528bcdd --- /dev/null +++ b/assets/times.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/common/Toast.js b/components/common/Toast.js new file mode 100644 index 00000000..12787985 --- /dev/null +++ b/components/common/Toast.js @@ -0,0 +1,26 @@ +import React, { useEffect } from 'react'; +import ReactDOM from 'react-dom'; +import { useSpring, animated } from 'react-spring'; +import styles from './Toast.module.css'; +import Icon from 'components/common/Icon'; +import Close from 'assets/times.svg'; + +export default function Toast({ message, timeout = 3000, onClose }) { + const props = useSpring({ + opacity: 1, + transform: 'translate3d(0,0px,0)', + from: { opacity: 0, transform: 'translate3d(0,-40px,0)' }, + }); + + useEffect(() => { + setTimeout(onClose, timeout); + }, []); + + return ReactDOM.createPortal( + +
{message}
+ } size="small" /> +
, + document.getElementById('__modals'), + ); +} diff --git a/components/common/Toast.module.css b/components/common/Toast.module.css new file mode 100644 index 00000000..bfcd26bb --- /dev/null +++ b/components/common/Toast.module.css @@ -0,0 +1,25 @@ +.toast { + position: absolute; + top: 30px; + left: 0; + right: 0; + width: 300px; + border-radius: 5px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; + color: var(--gray50); + background: var(--green400); + margin: auto; + z-index: 2; + cursor: pointer; +} + +.message { + font-size: var(--font-size-normal); +} + +.close { + margin-left: 20px; +} diff --git a/components/settings/AccountSettings.js b/components/settings/AccountSettings.js index cde8bedc..cdf55988 100644 --- a/components/settings/AccountSettings.js +++ b/components/settings/AccountSettings.js @@ -14,12 +14,14 @@ import Plus from 'assets/plus.svg'; import Trash from 'assets/trash.svg'; import Check from 'assets/check.svg'; import styles from './AccountSettings.module.css'; +import Toast from '../common/Toast'; export default function AccountSettings() { const [addAccount, setAddAccount] = useState(); const [editAccount, setEditAccount] = useState(); const [deleteAccount, setDeleteAccount] = useState(); const [saved, setSaved] = useState(0); + const [message, setMessage] = useState(); const { data } = useFetch(`/api/accounts`, {}, { update: [saved] }); const Checkmark = ({ is_admin }) => (is_admin ? } size="medium" /> : null); @@ -52,6 +54,7 @@ export default function AccountSettings() { function handleSave() { setSaved(state => state + 1); + setMessage('Saved successfully.'); handleClose(); } @@ -97,6 +100,7 @@ export default function AccountSettings() { /> )} + {message && setMessage(null)} />} ); } diff --git a/components/settings/ProfileSettings.js b/components/settings/ProfileSettings.js index 1aac00af..3c873d73 100644 --- a/components/settings/ProfileSettings.js +++ b/components/settings/ProfileSettings.js @@ -5,12 +5,19 @@ import Button from 'components/common/Button'; import ChangePasswordForm from '../forms/ChangePasswordForm'; import Modal from 'components/common/Modal'; import Dots from 'assets/ellipsis-h.svg'; +import Toast from '../common/Toast'; export default function ProfileSettings() { const user = useSelector(state => state.user); const [changePassword, setChangePassword] = useState(false); + const [message, setMessage] = useState(); const { user_id } = user; + function handleSave() { + setChangePassword(false); + setMessage('Saved successfully.'); + } + return ( <> @@ -27,11 +34,12 @@ export default function ProfileSettings() { setChangePassword(false)} + onSave={handleSave} onClose={() => setChangePassword(false)} /> )} + {message && setMessage(null)} />} ); } diff --git a/components/settings/WebsiteSettings.js b/components/settings/WebsiteSettings.js index a08b61f6..0b6d9a88 100644 --- a/components/settings/WebsiteSettings.js +++ b/components/settings/WebsiteSettings.js @@ -17,6 +17,7 @@ import Code from 'assets/code.svg'; import Link from 'assets/link.svg'; import styles from './WebsiteSettings.module.css'; import useFetch from '../../hooks/useFetch'; +import Toast from '../common/Toast'; export default function WebsiteSettings() { const [editWebsite, setEditWebsite] = useState(); @@ -25,6 +26,7 @@ export default function WebsiteSettings() { const [showCode, setShowCode] = useState(); const [showUrl, setShowUrl] = useState(); const [saved, setSaved] = useState(0); + const [message, setMessage] = useState(); const { data } = useFetch(`/api/websites`, {}, { update: [saved] }); const Buttons = row => ( @@ -66,6 +68,7 @@ export default function WebsiteSettings() { function handleSave() { setSaved(state => state + 1); + setMessage('Saved successfully.'); handleClose(); } @@ -127,6 +130,7 @@ export default function WebsiteSettings() { )} + {message && setMessage(null)} />} ); }