Convert buttons to links.

This commit is contained in:
Mike Cao 2020-09-25 23:38:28 -07:00
parent 4fded49b03
commit 35b921bdb4
21 changed files with 108 additions and 79 deletions

View File

@ -5,7 +5,8 @@ import WebsiteChart from 'components/metrics/WebsiteChart';
import WorldMap from 'components/common/WorldMap';
import Page from 'components/layout/Page';
import MenuLayout from 'components/layout/MenuLayout';
import Button from 'components/common/Button';
import Link from 'components/common/Link';
import Loading from 'components/common/Loading';
import Arrow from 'assets/arrow-right.svg';
import styles from './WebsiteDetails.module.css';
import PagesTable from './metrics/PagesTable';
@ -17,8 +18,7 @@ import CountriesTable from './metrics/CountriesTable';
import EventsTable from './metrics/EventsTable';
import EventsChart from './metrics/EventsChart';
import useFetch from 'hooks/useFetch';
import Loading from 'components/common/Loading';
import usePageQuery from '../hooks/usePageQuery';
import usePageQuery from 'hooks/usePageQuery';
const views = {
url: PagesTable,
@ -36,22 +36,21 @@ export default function WebsiteDetails({ websiteId, token }) {
const [countryData, setCountryData] = useState();
const [eventsData, setEventsData] = useState();
const {
pathname,
resolve,
router,
query: { view },
} = usePageQuery();
const BackButton = () => (
<Button
<Link
key="back-button"
className={styles.backButton}
href="/website/[...id]"
as={resolve({ view: undefined })}
icon={<Arrow />}
size="xsmall"
onClick={() => router.push(pathname)}
size="small"
>
<FormattedMessage id="button.back" defaultMessage="Back" />
</Button>
</Link>
);
const menuOptions = [
@ -93,7 +92,6 @@ export default function WebsiteDetails({ websiteId, token }) {
token,
websiteDomain: data?.domain,
limit: 10,
onExpand: handleExpand,
};
const DetailsComponent = views[view];
@ -104,10 +102,6 @@ export default function WebsiteDetails({ websiteId, token }) {
}
}
function handleExpand(value) {
router.push(resolve({ view: value }));
}
if (!data) {
return null;
}

View File

@ -16,7 +16,6 @@
}
.backButton {
align-self: flex-start;
margin-bottom: 16px;
}

View File

@ -38,7 +38,7 @@ export default function Button({
{...props}
>
{icon && <Icon className={styles.icon} icon={icon} size={size} />}
{children && <div>{children}</div>}
{children && <div className={styles.label}>{children}</div>}
{tooltip && <ReactTooltip id={tooltipId}>{tooltip}</ReactTooltip>}
</button>
);

View File

@ -10,7 +10,6 @@
border: 0;
outline: none;
cursor: pointer;
white-space: nowrap;
position: relative;
}
@ -22,12 +21,15 @@
color: var(--gray900);
}
.large {
font-size: var(--font-size-large);
.label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 300px;
}
.medium {
font-size: var(--font-size-normal);
.large {
font-size: var(--font-size-large);
}
.small {
@ -65,7 +67,7 @@
background: inherit;
}
.button .icon + div {
.button .icon + * {
margin-left: 10px;
}
@ -74,7 +76,7 @@
margin-left: 10px;
}
.button.iconRight .icon + div {
.button.iconRight .icon + * {
margin: 0;
}

View File

@ -1,12 +1,23 @@
import React from 'react';
import classNames from 'classnames';
import NextLink from 'next/link';
import Icon from './Icon';
import styles from './Link.module.css';
export default function Link({ className, children, ...props }) {
export default function Link({ className, icon, children, size, iconRight, ...props }) {
return (
<NextLink {...props}>
<a className={classNames(styles.link, className)}>{children}</a>
<a
className={classNames(styles.link, className, {
[styles.large]: size === 'large',
[styles.small]: size === 'small',
[styles.xsmall]: size === 'xsmall',
[styles.iconRight]: iconRight,
})}
>
{icon && <Icon className={styles.icon} icon={icon} size={size} />}
{children}
</a>
</NextLink>
);
}

View File

@ -4,6 +4,8 @@ a.link:visited {
position: relative;
color: var(--gray900);
text-decoration: none;
display: inline-flex;
align-items: center;
}
a.link:before {
@ -21,3 +23,28 @@ a.link:hover:before {
width: 100%;
transition: width 100ms;
}
a.link.large {
font-size: var(--font-size-large);
}
a.link.small {
font-size: var(--font-size-small);
}
a.link.xsmall {
font-size: var(--font-size-xsmall);
}
a.link .icon + * {
margin-left: 10px;
}
a.link.iconRight .icon {
order: 1;
margin-left: 10px;
}
a.link.iconRight .icon + * {
margin: 0;
}

View File

@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
import { browserFilter } from 'lib/filters';
export default function BrowsersTable({ websiteId, token, limit, onExpand }) {
export default function BrowsersTable({ websiteId, token, limit }) {
return (
<MetricsTable
title={<FormattedMessage id="metrics.browsers" defaultMessage="Browsers" />}
@ -13,7 +13,6 @@ export default function BrowsersTable({ websiteId, token, limit, onExpand }) {
token={token}
limit={limit}
dataFilter={browserFilter}
onExpand={onExpand}
/>
);
}

View File

@ -3,13 +3,7 @@ import MetricsTable from './MetricsTable';
import { countryFilter, percentFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
export default function CountriesTable({
websiteId,
token,
limit,
onDataLoad = () => {},
onExpand,
}) {
export default function CountriesTable({ websiteId, token, limit, onDataLoad = () => {} }) {
return (
<MetricsTable
title={<FormattedMessage id="metrics.countries" defaultMessage="Countries" />}
@ -20,7 +14,6 @@ export default function CountriesTable({
limit={limit}
dataFilter={countryFilter}
onDataLoad={data => onDataLoad(percentFilter(data))}
onExpand={onExpand}
/>
);
}

View File

@ -4,7 +4,7 @@ import { deviceFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
import { getDeviceMessage } from 'components/messages';
export default function DevicesTable({ websiteId, token, limit, onExpand }) {
export default function DevicesTable({ websiteId, token, limit }) {
return (
<MetricsTable
title={<FormattedMessage id="metrics.devices" defaultMessage="Devices" />}
@ -15,7 +15,6 @@ export default function DevicesTable({ websiteId, token, limit, onExpand }) {
limit={limit}
dataFilter={deviceFilter}
renderLabel={({ x }) => getDeviceMessage(x)}
onExpand={onExpand}
/>
);
}

View File

@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
import styles from './EventsTable.module.css';
export default function EventsTable({ websiteId, token, limit, onExpand, onDataLoad }) {
export default function EventsTable({ websiteId, token, limit, onDataLoad }) {
return (
<MetricsTable
title={<FormattedMessage id="metrics.events" defaultMessage="Events" />}
@ -13,7 +13,6 @@ export default function EventsTable({ websiteId, token, limit, onExpand, onDataL
token={token}
limit={limit}
renderLabel={({ x }) => <Label value={x} />}
onExpand={onExpand}
onDataLoad={onDataLoad}
/>
);

View File

@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import { FixedSizeList } from 'react-window';
import { useSpring, animated, config } from 'react-spring';
import classNames from 'classnames';
import Button from 'components/common/Button';
import Link from 'components/common/Link';
import Loading from 'components/common/Loading';
import NoData from 'components/common/NoData';
import useFetch from 'hooks/useFetch';
@ -11,8 +11,8 @@ import Arrow from 'assets/arrow-right.svg';
import { percentFilter } from 'lib/filters';
import { formatNumber, formatLongNumber } from 'lib/format';
import useDateRange from 'hooks/useDateRange';
import usePageQuery from 'hooks/usePageQuery';
import styles from './MetricsTable.module.css';
import usePageQuery from '../../hooks/usePageQuery';
export default function MetricsTable({
websiteId,
@ -27,11 +27,11 @@ export default function MetricsTable({
limit,
renderLabel,
onDataLoad = () => {},
onExpand = () => {},
}) {
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange;
const {
resolve,
query: { url },
} = usePageQuery();
@ -106,9 +106,15 @@ export default function MetricsTable({
</div>
<div className={styles.footer}>
{limit && (
<Button icon={<Arrow />} size="xsmall" onClick={() => onExpand(type)}>
<Link
icon={<Arrow />}
href="/website/[...id]"
as={resolve({ view: type })}
size="small"
iconRight
>
<FormattedMessage id="button.more" defaultMessage="More" />
</Button>
</Link>
)}
</div>
</>

View File

@ -1,6 +1,6 @@
.container {
position: relative;
min-height: 460px;
min-height: 430px;
font-size: var(--font-size-small);
display: flex;
flex-direction: column;
@ -21,6 +21,7 @@
.metric {
font-size: var(--font-size-small);
font-weight: 600;
text-align: center;
width: 100px;
cursor: pointer;
@ -72,8 +73,8 @@
.percent {
position: relative;
width: 50px;
color: #6e6e6e;
border-left: 1px solid var(--gray500);
color: var(--gray600);
border-left: 1px solid var(--gray600);
padding-left: 10px;
z-index: 1;
}

View File

@ -3,7 +3,7 @@ import MetricsTable from './MetricsTable';
import { osFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
export default function OSTable({ websiteId, token, limit, onExpand }) {
export default function OSTable({ websiteId, token, limit }) {
return (
<MetricsTable
title={<FormattedMessage id="metrics.operating-systems" defaultMessage="Operating system" />}
@ -13,7 +13,6 @@ export default function OSTable({ websiteId, token, limit, onExpand }) {
token={token}
limit={limit}
dataFilter={osFilter}
onExpand={onExpand}
/>
);
}

View File

@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import Link from 'next/link';
import ButtonGroup from 'components/common/ButtonGroup';
import ButtonLayout from 'components/layout/ButtonLayout';
@ -7,17 +8,14 @@ import { urlFilter } from 'lib/filters';
import { FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
import usePageQuery from 'hooks/usePageQuery';
import MetricsTable from './MetricsTable';
import styles from './PagesTable.module.css';
export default function PagesTable({
websiteId,
token,
websiteDomain,
limit,
showFilters,
onExpand,
}) {
export default function PagesTable({ websiteId, token, websiteDomain, limit, showFilters }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
const { resolve } = usePageQuery();
const {
resolve,
query: { url },
} = usePageQuery();
const buttons = [
{
@ -30,7 +28,14 @@ export default function PagesTable({
const renderLink = ({ x }) => {
return (
<Link href={resolve({ url: x })} replace={true}>
<a>{decodeURI(x)}</a>
<a
className={classNames({
[styles.inactive]: url && x !== url,
[styles.active]: x === url,
})}
>
{decodeURI(x)}
</a>
</Link>
);
};
@ -48,7 +53,6 @@ export default function PagesTable({
dataFilter={urlFilter}
filterOptions={{ domain: websiteDomain, raw: filter === FILTER_RAW }}
renderLabel={renderLink}
onExpand={onExpand}
/>
</>
);

View File

@ -0,0 +1,8 @@
body .inactive {
color: var(--gray500);
}
body .active {
color: var(--gray900);
font-weight: 600;
}

View File

@ -6,14 +6,7 @@ import ButtonGroup from 'components/common/ButtonGroup';
import { FILTER_DOMAIN_ONLY, FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
import ButtonLayout from '../layout/ButtonLayout';
export default function ReferrersTable({
websiteId,
websiteDomain,
token,
limit,
showFilters,
onExpand = () => {},
}) {
export default function ReferrersTable({ websiteId, websiteDomain, token, limit, showFilters }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
const buttons = [
@ -55,7 +48,6 @@ export default function ReferrersTable({
domainOnly: filter === FILTER_DOMAIN_ONLY,
raw: filter === FILTER_RAW,
}}
onExpand={onExpand}
renderLabel={renderLink}
/>
</>

View File

@ -99,7 +99,7 @@ export default function WebsiteChart({
const PageFilter = ({ url, onClick }) => {
return (
<div className={classNames(styles.url, 'col-12')}>
<Button icon={<Times />} onClick={onClick} variant="action" iconRight={true}>
<Button icon={<Times />} onClick={onClick} variant="action" iconRight>
{url}
</Button>
</div>

View File

@ -4,7 +4,6 @@ import Link from 'components/common/Link';
import PageHeader from 'components/layout/PageHeader';
import RefreshButton from 'components/common/RefreshButton';
import ButtonLayout from 'components/layout/ButtonLayout';
import Icon from 'components/common/Icon';
import ActiveUsers from './ActiveUsers';
import Arrow from 'assets/arrow-right.svg';
import styles from './WebsiteHeader.module.css';
@ -21,9 +20,11 @@ export default function WebsiteHeader({ websiteId, token, title, showLink = fals
href="/website/[...id]"
as={`/website/${websiteId}/${title}`}
className={styles.link}
icon={<Arrow />}
size="small"
iconRight
>
<FormattedMessage id="button.view-details" defaultMessage="View details" />
<Icon icon={<Arrow />} size="small" />
</Link>
)}
</ButtonLayout>

View File

@ -5,14 +5,9 @@
}
.link {
font-size: var(--font-size-small);
font-weight: 600;
}
.link svg {
margin-left: 10px;
}
@media only screen and (max-width: 576px) {
.active {
display: none;

View File

@ -1,6 +1,6 @@
{
"name": "umami",
"version": "0.53.0",
"version": "0.54.0",
"description": "A simple, fast, website analytics alternative to Google Analytics. ",
"author": "Mike Cao <mike@mikecao.com>",
"license": "MIT",

View File

@ -55,7 +55,7 @@ export default async (req, res) => {
getColumn(type),
getTable(type),
domain,
url,
type !== 'url' ? url : undefined,
);
return ok(res, data);