-
- umami
-
+
{user ? umami : 'umami'}
{user && (
- Dashboard
+ Dashboard
Settings
- Logout
+
)}
diff --git a/components/Icon.js b/components/Icon.js
index e5e7c82b..c0073ab0 100644
--- a/components/Icon.js
+++ b/components/Icon.js
@@ -2,6 +2,16 @@ import React from 'react';
import classNames from 'classnames';
import styles from './Icon.module.css';
-export default function Icon({ icon, className }) {
- return
{icon}
;
+export default function Icon({ icon, className, size = 'M' }) {
+ return (
+
+ {icon}
+
+ );
}
diff --git a/components/Icon.module.css b/components/Icon.module.css
index 72f4286e..27ba949f 100644
--- a/components/Icon.module.css
+++ b/components/Icon.module.css
@@ -5,7 +5,21 @@
vertical-align: middle;
}
-.icon > svg {
+.icon svg {
+ fill: currentColor;
+}
+
+.large > svg {
+ width: 24px;
+ height: 24px;
+}
+
+.medium > svg {
width: 16px;
height: 16px;
}
+
+.small > svg {
+ width: 12px;
+ height: 12px;
+}
diff --git a/components/Login.module.css b/components/Login.module.css
index 113ec401..db0b1286 100644
--- a/components/Login.module.css
+++ b/components/Login.module.css
@@ -1,6 +1,5 @@
.form {
position: absolute;
- top: 50%;
left: 50%;
- transform: translate(-50%, -50%);
+ transform: translateX(-50%);
}
diff --git a/components/Menu.js b/components/Menu.js
new file mode 100644
index 00000000..861980c5
--- /dev/null
+++ b/components/Menu.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import classNames from 'classnames';
+import styles from './Menu.module.css';
+
+export default function Menu({ options = [], className, align = 'left', onSelect = () => {} }) {
+ return (
+
+ {options.map(({ label, value, className: optionClassName }) => (
+
onSelect(value, e)}
+ >
+ {label}
+
+ ))}
+
+ );
+}
diff --git a/components/Menu.module.css b/components/Menu.module.css
new file mode 100644
index 00000000..ade8a8d0
--- /dev/null
+++ b/components/Menu.module.css
@@ -0,0 +1,31 @@
+.menu {
+ position: absolute;
+ min-width: 100px;
+ top: 100%;
+ margin-top: 4px;
+ border: 1px solid var(--gray500);
+ border-radius: 4px;
+ overflow: hidden;
+ z-index: 2;
+}
+
+.option {
+ font-size: var(--font-size-small);
+ font-weight: normal;
+ background: #fff;
+ padding: 4px 16px;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.option:hover {
+ background: #f5f5f5;
+}
+
+.left {
+ left: 0;
+}
+
+.right {
+ right: 0;
+}
diff --git a/components/UserButton.js b/components/UserButton.js
new file mode 100644
index 00000000..551c452c
--- /dev/null
+++ b/components/UserButton.js
@@ -0,0 +1,56 @@
+import React, { useState, useRef } from 'react';
+import { useSelector } from 'react-redux';
+import { useRouter } from 'next/router';
+import Menu from './Menu';
+import Icon from './Icon';
+import useDocumentClick from 'hooks/useDocumentClick';
+import User from 'assets/user.svg';
+import Chevron from 'assets/chevron-down.svg';
+import styles from './UserButton.module.css';
+
+export default function UserButton() {
+ const [showMenu, setShowMenu] = useState(false);
+ const user = useSelector(state => state.user);
+ const ref = useRef();
+ const router = useRouter();
+
+ const menuOptions = [
+ {
+ label: (
+ <>
+ Logged in as
{user.username}
+ >
+ ),
+ value: 'username',
+ className: styles.username,
+ },
+ { label: 'Account', value: 'account' },
+ { label: 'Logout', value: 'logout' },
+ ];
+
+ function handleSelect(value) {
+ setShowMenu(false);
+
+ if (value === 'account') {
+ router.push('/account');
+ } else if (value === 'logout') {
+ router.push('/logout');
+ }
+ }
+
+ useDocumentClick(e => {
+ if (!ref.current.contains(e.target)) {
+ setShowMenu(false);
+ }
+ });
+
+ return (
+
+
setShowMenu(state => !state)}>
+ } size="L" className={styles.icon} />
+ } size="S" />
+
+ {showMenu &&
}
+
+ );
+}
diff --git a/components/UserButton.module.css b/components/UserButton.module.css
new file mode 100644
index 00000000..bdb0daa5
--- /dev/null
+++ b/components/UserButton.module.css
@@ -0,0 +1,17 @@
+.container {
+ display: flex;
+ position: relative;
+ cursor: pointer;
+}
+
+.icon {
+ margin-right: 8px;
+}
+
+.username {
+ border-bottom: 1px solid var(--gray500);
+}
+
+.username:hover {
+ background: var(--gray50);
+}
diff --git a/hooks/useDocumentClick.js b/hooks/useDocumentClick.js
new file mode 100644
index 00000000..e1baae7e
--- /dev/null
+++ b/hooks/useDocumentClick.js
@@ -0,0 +1,13 @@
+import { useEffect } from 'react';
+
+export default function useDocumentClick(handler) {
+ useEffect(() => {
+ document.addEventListener('click', handler);
+
+ return () => {
+ document.removeEventListener('click', handler);
+ };
+ }, [handler]);
+
+ return null;
+}
diff --git a/hooks/useRequireLogin.js b/hooks/useRequireLogin.js
index 1d9c037b..ffebfca8 100644
--- a/hooks/useRequireLogin.js
+++ b/hooks/useRequireLogin.js
@@ -31,7 +31,7 @@ export default function useRequireLogin() {
return;
}
- await dispatch(updateUser({ user }));
+ await dispatch(updateUser(user));
setUser(user);
setLoading(false);
diff --git a/pages/account.js b/pages/account.js
new file mode 100644
index 00000000..0b56dfdc
--- /dev/null
+++ b/pages/account.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import Layout from 'components/Layout';
+import Account from 'components/Account';
+import useRequireLogin from 'hooks/useRequireLogin';
+
+export default function AccountPage() {
+ const { loading } = useRequireLogin();
+
+ if (loading) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/pages/dashboard.js b/pages/dashboard.js
new file mode 100644
index 00000000..63c5be49
--- /dev/null
+++ b/pages/dashboard.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import Layout from 'components/Layout';
+import WebsiteList from 'components/WebsiteList';
+import useRequireLogin from 'hooks/useRequireLogin';
+
+export default function DashboardPage() {
+ const { loading } = useRequireLogin();
+
+ if (loading) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/pages/index.js b/pages/index.js
index 2c99f2a4..7d93cef1 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -1,18 +1,12 @@
-import React from 'react';
-import Layout from 'components/Layout';
-import WebsiteList from 'components/WebsiteList';
-import useRequireLogin from 'hooks/useRequireLogin';
+import { useEffect } from 'react';
+import { useRouter } from 'next/router';
-export default function HomePage() {
- const { loading } = useRequireLogin();
+export default function DefaultPage() {
+ const router = useRouter();
- if (loading) {
- return null;
- }
+ useEffect(() => {
+ router.push('/dashboard');
+ }, []);
- return (
-
-
-
- );
+ return null;
}
diff --git a/styles/index.css b/styles/index.css
index c5ac90d5..25ee6baf 100644
--- a/styles/index.css
+++ b/styles/index.css
@@ -47,11 +47,13 @@ form label {
min-width: 100px;
}
-form input,
-form textarea {
+input,
+textarea {
padding: 4px 8px;
margin-right: 10px;
margin-bottom: 20px;
+ font-size: var(--font-size-normal);
+ line-height: 1.8;
border: 1px solid var(--gray500);
border-radius: 4px;
outline: none;
@@ -59,7 +61,7 @@ form textarea {
select {
padding: 4px 8px;
- border: 1px solid var(--gray500);
+ border: 1px solid var(--gray500) !important;
border-radius: 4px;
}