Skip to content

Commit 1d9c313

Browse files
committed
Refactored layout. Added NavBar component.
1 parent fad38dc commit 1d9c313

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+599
-427
lines changed
File renamed without changes.

assets/clock.svg

Lines changed: 1 addition & 0 deletions
Loading

assets/dashboard.svg

Lines changed: 1 addition & 0 deletions
Loading

components/settings/SettingsButton.js renamed to components/buttons/SettingsButton.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useRef, useState } from 'react';
22
import { FormattedMessage } from 'react-intl';
3-
import TimezoneSetting from './TimezoneSetting';
4-
import DateRangeSetting from './DateRangeSetting';
3+
import TimezoneSetting from '../pages/settings/profile/TimezoneSetting';
4+
import DateRangeSetting from '../pages/settings/profile/DateRangeSetting';
55
import { Button, Icon } from 'react-basics';
66
import styles from './SettingsButton.module.css';
77
import Gear from 'assets/gear.svg';
File renamed without changes.

components/common/DateFilter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useIntl, defineMessages } from 'react-intl';
55
import DatePickerForm from 'components/metrics/DatePickerForm';
66
import useLocale from 'hooks/useLocale';
77
import { dateFormat } from 'lib/date';
8-
import Calendar from 'assets/calendar-alt.svg';
8+
import Calendar from 'assets/calendar.svg';
99

1010
const messages = defineMessages({
1111
today: { id: 'label.today', defaultMessage: 'Today' },

components/common/HamburgerButton.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ const menuItems = [
1313
},
1414
{ label: <FormattedMessage id="label.realtime" defaultMessage="Realtime" />, value: '/realtime' },
1515
{
16-
label: <FormattedMessage id="label.settings" defaultMessage="SettingsLayout" />,
17-
value: '/settings',
16+
label: <FormattedMessage id="label.settings" defaultMessage="AppLayout" />,
17+
value: '/buttons',
1818
},
1919
{
2020
label: <FormattedMessage id="label.profile" defaultMessage="Profile" />,
21-
value: '/settings/profile',
21+
value: '/buttons/profile',
2222
},
2323
{ label: <FormattedMessage id="label.logout" defaultMessage="Logout" />, value: '/logout' },
2424
];

components/icons.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Calendar from 'assets/calendar.svg';
2+
import Clock from 'assets/clock.svg';
3+
import Dashboard from 'assets/dashboard.svg';
4+
import Gear from 'assets/gear.svg';
5+
import Globe from 'assets/globe.svg';
6+
import Logo from 'assets/logo.svg';
7+
import Moon from 'assets/moon.svg';
8+
import Profile from 'assets/profile.svg';
9+
import Sun from 'assets/sun.svg';
10+
import User from 'assets/user.svg';
11+
import Users from 'assets/users.svg';
12+
13+
export { Calendar, Clock, Dashboard, Gear, Globe, Logo, Moon, Profile, Sun, User, Users };

components/layout/AppLayout.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import { Container } from 'react-basics';
22
import Head from 'next/head';
3-
import Header from 'components/layout/Header';
4-
import Footer from 'components/layout/Footer';
5-
import useLocale from 'hooks/useLocale';
3+
import NavBar from 'components/layout/NavBar';
64
import useRequireLogin from 'hooks/useRequireLogin';
5+
import styles from './AppLayout.module.css';
76

87
export default function AppLayout({ title, children }) {
98
useRequireLogin();
10-
const { dir } = useLocale();
119

1210
return (
13-
<Container dir={dir} style={{ maxWidth: 1140 }}>
11+
<div className={styles.layout}>
1412
<Head>
1513
<title>{title ? `${title} | umami` : 'umami'}</title>
1614
</Head>
17-
<Header />
18-
<main>{children}</main>
19-
<Footer />
20-
</Container>
15+
<div className={styles.nav}>
16+
<NavBar />
17+
</div>
18+
<div className={styles.body}>
19+
<Container>
20+
<main>{children}</main>
21+
</Container>
22+
</div>
23+
</div>
2124
);
2225
}
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
.layout {
2-
display: flex;
3-
flex-direction: column;
4-
width: 100%;
5-
height: 100%;
2+
display: grid;
3+
grid-template-rows: 1fr;
4+
grid-template-columns: minmax(60px, 200px) 1fr;
5+
height: 100vh;
6+
overflow: hidden;
7+
}
8+
9+
.nav {
10+
grid-row: 1 / 3;
11+
}
12+
13+
.body {
14+
grid-area: 1 / 2;
15+
overflow: auto;
616
}

components/layout/Footer.js

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,38 @@
1+
import { Row, Column } from 'react-basics';
12
import { useRouter } from 'next/router';
23
import Script from 'next/script';
34
import classNames from 'classnames';
4-
import { FormattedMessage } from 'react-intl';
5+
import { useIntl, defineMessages } from 'react-intl';
56
import Link from 'components/common/Link';
6-
import styles from './Footer.module.css';
77
import { CURRENT_VERSION, HOMEPAGE_URL, REPO_URL } from 'lib/constants';
8+
import styles from './Footer.module.css';
9+
10+
const messages = defineMessages({
11+
poweredBy: { id: 'message.powered-by', defaultMessage: 'Powered by {name}' },
12+
});
813

9-
export default function Footer() {
14+
export default function Footer({ className }) {
1015
const { pathname } = useRouter();
16+
const { formatMessage } = useIntl();
1117

1218
return (
13-
<footer className={classNames(styles.footer, 'row')}>
14-
<div className="col-12 col-md-4" />
15-
<div className="col-12 col-md-4">
16-
<FormattedMessage
17-
id="message.powered-by"
18-
defaultMessage="Powered by {name}"
19-
values={{
20-
name: (
21-
<Link href={HOMEPAGE_URL}>
22-
<b>umami</b>
23-
</Link>
24-
),
25-
}}
26-
/>
27-
</div>
28-
<div className={classNames(styles.version, 'col-12 col-md-4')}>
29-
<Link href={REPO_URL}>{`v${CURRENT_VERSION}`}</Link>
30-
</div>
19+
<footer className={classNames(styles.footer, className)}>
20+
<Row>
21+
<Column>
22+
<div>
23+
{formatMessage(messages.poweredBy, {
24+
name: (
25+
<Link href={HOMEPAGE_URL}>
26+
<b>umami</b>
27+
</Link>
28+
),
29+
})}
30+
</div>
31+
</Column>
32+
<Column className={styles.version}>
33+
<Link href={REPO_URL}>{`v${CURRENT_VERSION}`}</Link>
34+
</Column>
35+
</Row>
3136
{!pathname.includes('/share/') && <Script src={`/telemetry.js`} />}
3237
</footer>
3338
);

components/layout/Header.js

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
import Logo from 'assets/logo.svg';
21
import HamburgerButton from 'components/common/HamburgerButton';
3-
import Link from 'components/common/Link';
42
import UpdateNotice from 'components/common/UpdateNotice';
5-
import LanguageButton from 'components/settings/LanguageButton';
6-
import ThemeButton from 'components/settings/ThemeButton';
7-
import UserButton from 'components/settings/UserButton';
3+
import LanguageButton from 'components/buttons/LanguageButton';
4+
import ThemeButton from 'components/buttons/ThemeButton';
5+
import UserButton from 'components/buttons/UserButton';
86
import useConfig from 'hooks/useConfig';
97
import useUser from 'hooks/useUser';
10-
import { HOMEPAGE_URL } from 'lib/constants';
118
import { useRouter } from 'next/router';
12-
import { Column, Icon, Row } from 'react-basics';
13-
import SettingsButton from '../settings/SettingsButton';
9+
import { Column, Row } from 'react-basics';
10+
import SettingsButton from '../buttons/SettingsButton';
1411
import styles from './Header.module.css';
12+
import classNames from 'classnames';
1513

16-
export default function Header() {
14+
export default function Header({ className }) {
1715
const user = useUser();
1816
const { pathname } = useRouter();
1917
const { updatesDisabled, adminDisabled } = useConfig();
@@ -23,14 +21,9 @@ export default function Header() {
2321
return (
2422
<>
2523
{allowUpdate && <UpdateNotice />}
26-
<header className={styles.header}>
24+
<header className={classNames(styles.header, className)}>
2725
<Row>
28-
<Column className={styles.title}>
29-
<Icon size="lg" className={styles.logo}>
30-
<Logo />
31-
</Icon>
32-
<Link href={isSharePage ? HOMEPAGE_URL : '/'}>umami</Link>
33-
</Column>
26+
<Column className={styles.title}></Column>
3427
<HamburgerButton />
3528
<Column className={styles.buttons}>
3629
<ThemeButton />

components/layout/Header.module.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
.header {
22
display: flex;
33
align-items: center;
4-
min-height: 100px;
54
width: 100%;
5+
height: 50px;
6+
border-bottom: 1px solid var(--base300);
67
}
78

89
.title {

components/layout/NavBar.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useState } from 'react';
2+
import { Icon, Text, Icons } from 'react-basics';
3+
import classNames from 'classnames';
4+
import { Dashboard, Logo, Profile, User, Users, Clock, Globe } from 'components/icons';
5+
import NavGroup from './NavGroup';
6+
import styles from './NavBar.module.css';
7+
8+
const { ChevronDown, Search } = Icons;
9+
10+
const analytics = [
11+
{ key: 'dashboard', label: 'Dashboard', url: '/dashboard', icon: <Dashboard /> },
12+
{ key: 'realtime', label: 'Realtime', url: '/realtime', icon: <Clock /> },
13+
{ key: 'queries', label: 'Queries', url: '/queries', icon: <Search /> },
14+
];
15+
16+
const settings = [
17+
{ key: 'websites', label: 'Websites', url: '/settings/websites', icon: <Globe /> },
18+
{ key: 'users', label: 'Users', url: '/settings/users', icon: <User /> },
19+
{ key: 'teams', label: 'Teams', url: '/settings/teams', icon: <Users /> },
20+
];
21+
22+
export default function NavBar() {
23+
const [minimized, setMinimized] = useState(false);
24+
25+
const handleMinimize = () => setMinimized(state => !state);
26+
27+
return (
28+
<div className={classNames(styles.navbar, { [styles.minimized]: minimized })}>
29+
<div className={styles.header} onClick={handleMinimize}>
30+
<Icon size="lg">
31+
<Logo />
32+
</Icon>
33+
<Text className={styles.text}>umami</Text>
34+
<Icon size="sm" rotate={minimized ? -90 : 90} className={styles.icon}>
35+
<ChevronDown />
36+
</Icon>
37+
</div>
38+
<NavGroup title="Analytics" items={analytics} minimized={minimized} />
39+
<NavGroup title="Settings" items={settings} minimized={minimized} />
40+
<div className={styles.footer}>
41+
<Icon>
42+
<Profile />
43+
</Icon>
44+
<Text>Profile</Text>
45+
</div>
46+
</div>
47+
);
48+
}

components/layout/NavBar.module.css

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.navbar {
2+
position: relative;
3+
display: flex;
4+
flex-direction: column;
5+
align-items: center;
6+
background: var(--base75);
7+
height: 100%;
8+
width: 200px;
9+
border-right: 2px solid var(--base200);
10+
}
11+
12+
.header {
13+
display: flex;
14+
align-items: center;
15+
justify-content: center;
16+
gap: 10px;
17+
font-size: 16px;
18+
font-weight: 700;
19+
padding: 20px 0;
20+
cursor: pointer;
21+
}
22+
23+
.header:hover .icon {
24+
visibility: visible;
25+
}
26+
27+
.icon {
28+
position: absolute;
29+
right: 0;
30+
visibility: hidden;
31+
}
32+
33+
.minimized.navbar {
34+
width: 60px;
35+
}
36+
37+
.minimized .text {
38+
display: none;
39+
}
40+
41+
.footer {
42+
flex: 1;
43+
display: flex;
44+
flex-direction: column;
45+
justify-content: flex-end;
46+
padding: 20px;
47+
}

components/layout/NavGroup.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useState } from 'react';
2+
import { Icon, Text, Icons } from 'react-basics';
3+
import classNames from 'classnames';
4+
import { useRouter } from 'next/router';
5+
import Link from 'next/link';
6+
import styles from './NavGroup.module.css';
7+
8+
const { ChevronDown } = Icons;
9+
10+
export default function NavGroup({
11+
title,
12+
items,
13+
defaultExpanded = true,
14+
allowExpand = true,
15+
minimized = false,
16+
}) {
17+
const { pathname } = useRouter();
18+
const [expanded, setExpanded] = useState(defaultExpanded);
19+
20+
const handleExpand = () => setExpanded(state => !state);
21+
22+
return (
23+
<div
24+
className={classNames(styles.group, {
25+
[styles.expanded]: expanded,
26+
[styles.minimized]: minimized,
27+
})}
28+
>
29+
<div className={styles.header} onClick={allowExpand ? handleExpand : undefined}>
30+
<Text>{title}</Text>
31+
<Icon size="sm" rotate={expanded ? 0 : -90}>
32+
<ChevronDown />
33+
</Icon>
34+
</div>
35+
<div className={styles.body}>
36+
{items.map(({ key, label, url, icon }) => {
37+
return (
38+
<Link key={key} href={url}>
39+
<a
40+
className={classNames(styles.item, { [styles.selected]: pathname.startsWith(url) })}
41+
>
42+
<Icon>{icon}</Icon>
43+
<Text className={styles.text}>{label}</Text>
44+
</a>
45+
</Link>
46+
);
47+
})}
48+
</div>
49+
</div>
50+
);
51+
}

0 commit comments

Comments
 (0)