Skip to content

Commit

Permalink
Refactored layout. Added NavBar component.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikecao committed Jan 18, 2023
1 parent fad38dc commit 1d9c313
Show file tree
Hide file tree
Showing 56 changed files with 599 additions and 427 deletions.
File renamed without changes
1 change: 1 addition & 0 deletions assets/clock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/dashboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRef, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import TimezoneSetting from './TimezoneSetting';
import DateRangeSetting from './DateRangeSetting';
import TimezoneSetting from '../pages/settings/profile/TimezoneSetting';
import DateRangeSetting from '../pages/settings/profile/DateRangeSetting';
import { Button, Icon } from 'react-basics';
import styles from './SettingsButton.module.css';
import Gear from 'assets/gear.svg';
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion components/common/DateFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useIntl, defineMessages } from 'react-intl';
import DatePickerForm from 'components/metrics/DatePickerForm';
import useLocale from 'hooks/useLocale';
import { dateFormat } from 'lib/date';
import Calendar from 'assets/calendar-alt.svg';
import Calendar from 'assets/calendar.svg';

const messages = defineMessages({
today: { id: 'label.today', defaultMessage: 'Today' },
Expand Down
6 changes: 3 additions & 3 deletions components/common/HamburgerButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ const menuItems = [
},
{ label: <FormattedMessage id="label.realtime" defaultMessage="Realtime" />, value: '/realtime' },
{
label: <FormattedMessage id="label.settings" defaultMessage="SettingsLayout" />,
value: '/settings',
label: <FormattedMessage id="label.settings" defaultMessage="AppLayout" />,
value: '/buttons',
},
{
label: <FormattedMessage id="label.profile" defaultMessage="Profile" />,
value: '/settings/profile',
value: '/buttons/profile',
},
{ label: <FormattedMessage id="label.logout" defaultMessage="Logout" />, value: '/logout' },
];
Expand Down
13 changes: 13 additions & 0 deletions components/icons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Calendar from 'assets/calendar.svg';
import Clock from 'assets/clock.svg';
import Dashboard from 'assets/dashboard.svg';
import Gear from 'assets/gear.svg';
import Globe from 'assets/globe.svg';
import Logo from 'assets/logo.svg';
import Moon from 'assets/moon.svg';
import Profile from 'assets/profile.svg';
import Sun from 'assets/sun.svg';
import User from 'assets/user.svg';
import Users from 'assets/users.svg';

export { Calendar, Clock, Dashboard, Gear, Globe, Logo, Moon, Profile, Sun, User, Users };
21 changes: 12 additions & 9 deletions components/layout/AppLayout.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { Container } from 'react-basics';
import Head from 'next/head';
import Header from 'components/layout/Header';
import Footer from 'components/layout/Footer';
import useLocale from 'hooks/useLocale';
import NavBar from 'components/layout/NavBar';
import useRequireLogin from 'hooks/useRequireLogin';
import styles from './AppLayout.module.css';

export default function AppLayout({ title, children }) {
useRequireLogin();
const { dir } = useLocale();

return (
<Container dir={dir} style={{ maxWidth: 1140 }}>
<div className={styles.layout}>
<Head>
<title>{title ? `${title} | umami` : 'umami'}</title>
</Head>
<Header />
<main>{children}</main>
<Footer />
</Container>
<div className={styles.nav}>
<NavBar />
</div>
<div className={styles.body}>
<Container>
<main>{children}</main>
</Container>
</div>
</div>
);
}
18 changes: 14 additions & 4 deletions components/layout/AppLayout.module.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
.layout {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
display: grid;
grid-template-rows: 1fr;
grid-template-columns: minmax(60px, 200px) 1fr;
height: 100vh;
overflow: hidden;
}

.nav {
grid-row: 1 / 3;
}

.body {
grid-area: 1 / 2;
overflow: auto;
}
47 changes: 26 additions & 21 deletions components/layout/Footer.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
import { Row, Column } from 'react-basics';
import { useRouter } from 'next/router';
import Script from 'next/script';
import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import { useIntl, defineMessages } from 'react-intl';
import Link from 'components/common/Link';
import styles from './Footer.module.css';
import { CURRENT_VERSION, HOMEPAGE_URL, REPO_URL } from 'lib/constants';
import styles from './Footer.module.css';

const messages = defineMessages({
poweredBy: { id: 'message.powered-by', defaultMessage: 'Powered by {name}' },
});

export default function Footer() {
export default function Footer({ className }) {
const { pathname } = useRouter();
const { formatMessage } = useIntl();

return (
<footer className={classNames(styles.footer, 'row')}>
<div className="col-12 col-md-4" />
<div className="col-12 col-md-4">
<FormattedMessage
id="message.powered-by"
defaultMessage="Powered by {name}"
values={{
name: (
<Link href={HOMEPAGE_URL}>
<b>umami</b>
</Link>
),
}}
/>
</div>
<div className={classNames(styles.version, 'col-12 col-md-4')}>
<Link href={REPO_URL}>{`v${CURRENT_VERSION}`}</Link>
</div>
<footer className={classNames(styles.footer, className)}>
<Row>
<Column>
<div>
{formatMessage(messages.poweredBy, {
name: (
<Link href={HOMEPAGE_URL}>
<b>umami</b>
</Link>
),
})}
</div>
</Column>
<Column className={styles.version}>
<Link href={REPO_URL}>{`v${CURRENT_VERSION}`}</Link>
</Column>
</Row>
{!pathname.includes('/share/') && <Script src={`/telemetry.js`} />}
</footer>
);
Expand Down
25 changes: 9 additions & 16 deletions components/layout/Header.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import Logo from 'assets/logo.svg';
import HamburgerButton from 'components/common/HamburgerButton';
import Link from 'components/common/Link';
import UpdateNotice from 'components/common/UpdateNotice';
import LanguageButton from 'components/settings/LanguageButton';
import ThemeButton from 'components/settings/ThemeButton';
import UserButton from 'components/settings/UserButton';
import LanguageButton from 'components/buttons/LanguageButton';
import ThemeButton from 'components/buttons/ThemeButton';
import UserButton from 'components/buttons/UserButton';
import useConfig from 'hooks/useConfig';
import useUser from 'hooks/useUser';
import { HOMEPAGE_URL } from 'lib/constants';
import { useRouter } from 'next/router';
import { Column, Icon, Row } from 'react-basics';
import SettingsButton from '../settings/SettingsButton';
import { Column, Row } from 'react-basics';
import SettingsButton from '../buttons/SettingsButton';
import styles from './Header.module.css';
import classNames from 'classnames';

export default function Header() {
export default function Header({ className }) {
const user = useUser();
const { pathname } = useRouter();
const { updatesDisabled, adminDisabled } = useConfig();
Expand All @@ -23,14 +21,9 @@ export default function Header() {
return (
<>
{allowUpdate && <UpdateNotice />}
<header className={styles.header}>
<header className={classNames(styles.header, className)}>
<Row>
<Column className={styles.title}>
<Icon size="lg" className={styles.logo}>
<Logo />
</Icon>
<Link href={isSharePage ? HOMEPAGE_URL : '/'}>umami</Link>
</Column>
<Column className={styles.title}></Column>
<HamburgerButton />
<Column className={styles.buttons}>
<ThemeButton />
Expand Down
3 changes: 2 additions & 1 deletion components/layout/Header.module.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
.header {
display: flex;
align-items: center;
min-height: 100px;
width: 100%;
height: 50px;
border-bottom: 1px solid var(--base300);
}

.title {
Expand Down
48 changes: 48 additions & 0 deletions components/layout/NavBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useState } from 'react';
import { Icon, Text, Icons } from 'react-basics';
import classNames from 'classnames';
import { Dashboard, Logo, Profile, User, Users, Clock, Globe } from 'components/icons';
import NavGroup from './NavGroup';
import styles from './NavBar.module.css';

const { ChevronDown, Search } = Icons;

const analytics = [
{ key: 'dashboard', label: 'Dashboard', url: '/dashboard', icon: <Dashboard /> },
{ key: 'realtime', label: 'Realtime', url: '/realtime', icon: <Clock /> },
{ key: 'queries', label: 'Queries', url: '/queries', icon: <Search /> },
];

const settings = [
{ key: 'websites', label: 'Websites', url: '/settings/websites', icon: <Globe /> },
{ key: 'users', label: 'Users', url: '/settings/users', icon: <User /> },
{ key: 'teams', label: 'Teams', url: '/settings/teams', icon: <Users /> },
];

export default function NavBar() {
const [minimized, setMinimized] = useState(false);

const handleMinimize = () => setMinimized(state => !state);

return (
<div className={classNames(styles.navbar, { [styles.minimized]: minimized })}>
<div className={styles.header} onClick={handleMinimize}>
<Icon size="lg">
<Logo />
</Icon>
<Text className={styles.text}>umami</Text>
<Icon size="sm" rotate={minimized ? -90 : 90} className={styles.icon}>
<ChevronDown />
</Icon>
</div>
<NavGroup title="Analytics" items={analytics} minimized={minimized} />
<NavGroup title="Settings" items={settings} minimized={minimized} />
<div className={styles.footer}>
<Icon>
<Profile />
</Icon>
<Text>Profile</Text>
</div>
</div>
);
}
47 changes: 47 additions & 0 deletions components/layout/NavBar.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.navbar {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
background: var(--base75);
height: 100%;
width: 200px;
border-right: 2px solid var(--base200);
}

.header {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 16px;
font-weight: 700;
padding: 20px 0;
cursor: pointer;
}

.header:hover .icon {
visibility: visible;
}

.icon {
position: absolute;
right: 0;
visibility: hidden;
}

.minimized.navbar {
width: 60px;
}

.minimized .text {
display: none;
}

.footer {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
}
51 changes: 51 additions & 0 deletions components/layout/NavGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useState } from 'react';
import { Icon, Text, Icons } from 'react-basics';
import classNames from 'classnames';
import { useRouter } from 'next/router';
import Link from 'next/link';
import styles from './NavGroup.module.css';

const { ChevronDown } = Icons;

export default function NavGroup({
title,
items,
defaultExpanded = true,
allowExpand = true,
minimized = false,
}) {
const { pathname } = useRouter();
const [expanded, setExpanded] = useState(defaultExpanded);

const handleExpand = () => setExpanded(state => !state);

return (
<div
className={classNames(styles.group, {
[styles.expanded]: expanded,
[styles.minimized]: minimized,
})}
>
<div className={styles.header} onClick={allowExpand ? handleExpand : undefined}>
<Text>{title}</Text>
<Icon size="sm" rotate={expanded ? 0 : -90}>
<ChevronDown />
</Icon>
</div>
<div className={styles.body}>
{items.map(({ key, label, url, icon }) => {
return (
<Link key={key} href={url}>
<a
className={classNames(styles.item, { [styles.selected]: pathname.startsWith(url) })}
>
<Icon>{icon}</Icon>
<Text className={styles.text}>{label}</Text>
</a>
</Link>
);
})}
</div>
</div>
);
}
Loading

0 comments on commit 1d9c313

Please sign in to comment.