mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Merge branch 'dev' into analytics
This commit is contained in:
commit
ac43f55636
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "umami",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.0",
|
||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||
"author": "Mike Cao <mike@mikecao.com>",
|
||||
"license": "MIT",
|
||||
|
@ -29,16 +29,6 @@ const aliasConfig = {
|
||||
customResolver,
|
||||
};
|
||||
|
||||
const external = [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react/jsx-runtime',
|
||||
'react-intl',
|
||||
'react-basics',
|
||||
'classnames',
|
||||
'next',
|
||||
];
|
||||
|
||||
const jsBundle = {
|
||||
input: 'src/index.ts',
|
||||
output: [
|
||||
|
@ -4,7 +4,7 @@ const path = require('path');
|
||||
const https = require('https');
|
||||
const chalk = require('chalk');
|
||||
|
||||
const src = path.resolve(__dirname, '../lang');
|
||||
const src = path.resolve(__dirname, '../src/lang');
|
||||
const dest = path.resolve(__dirname, '../public/intl/country');
|
||||
const files = fs.readdirSync(src);
|
||||
|
||||
|
@ -4,7 +4,7 @@ const path = require('path');
|
||||
const https = require('https');
|
||||
const chalk = require('chalk');
|
||||
|
||||
const src = path.resolve(__dirname, '../lang');
|
||||
const src = path.resolve(__dirname, '../src/lang');
|
||||
const dest = path.resolve(__dirname, '../public/intl/language');
|
||||
const files = fs.readdirSync(src);
|
||||
|
||||
|
@ -3,7 +3,7 @@ const path = require('path');
|
||||
const del = require('del');
|
||||
const prettier = require('prettier');
|
||||
|
||||
const src = path.resolve(__dirname, '../lang');
|
||||
const src = path.resolve(__dirname, '../src/lang');
|
||||
const dest = path.resolve(__dirname, '../build/messages');
|
||||
const files = fs.readdirSync(src);
|
||||
|
||||
@ -17,7 +17,7 @@ async function run() {
|
||||
await fs.ensureDir(dest);
|
||||
|
||||
files.forEach(file => {
|
||||
const lang = require(`../lang/${file}`);
|
||||
const lang = require(`../src/lang/${file}`);
|
||||
const keys = Object.keys(lang).sort();
|
||||
|
||||
const formatted = keys.reduce((obj, key) => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { createPortal } from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import { useRouter } from 'next/router';
|
||||
import Link from 'next/link';
|
||||
@ -28,10 +29,11 @@ export function MobileMenu({ items = [], onClose }) {
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
return createPortal(
|
||||
<div className={classNames(styles.menu)}>
|
||||
<Items items={items} />
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,10 @@ import styles from './WebsiteDateFilter.module.css';
|
||||
export function WebsiteDateFilter({ websiteId }) {
|
||||
const [dateRange, setDateRange] = useDateRange(websiteId);
|
||||
const { value, startDate, endDate, selectedUnit } = dateRange;
|
||||
|
||||
const isFutureDate =
|
||||
value !== 'all' && isAfter(incrementDateRange(dateRange, -1).startDate, new Date());
|
||||
value !== 'all' &&
|
||||
selectedUnit &&
|
||||
isAfter(incrementDateRange(dateRange, -1).startDate, new Date());
|
||||
|
||||
const handleChange = value => {
|
||||
setDateRange(value);
|
||||
@ -21,7 +22,21 @@ export function WebsiteDateFilter({ websiteId }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flexbox justifyContent="center" gap={10}>
|
||||
{value !== 'all' && selectedUnit && (
|
||||
<Flexbox justifyContent="center" className={styles.buttons}>
|
||||
<Button onClick={() => handleIncrement(1)}>
|
||||
<Icon rotate={90}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Button onClick={() => handleIncrement(-1)} disabled={isFutureDate}>
|
||||
<Icon rotate={270}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
</Flexbox>
|
||||
)}
|
||||
<DateFilter
|
||||
className={styles.dropdown}
|
||||
value={value}
|
||||
@ -31,22 +46,7 @@ export function WebsiteDateFilter({ websiteId }) {
|
||||
onChange={handleChange}
|
||||
showAllTime={true}
|
||||
/>
|
||||
{value !== 'all' && (
|
||||
<Flexbox justifyContent="center" gap={10} className={styles.container}>
|
||||
<Button onClick={() => handleIncrement(1)}>
|
||||
<Icon rotate={90}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => handleIncrement(-1)} disabled={isFutureDate}>
|
||||
<Icon rotate={270}>
|
||||
<Icons.ChevronDown />
|
||||
</Icon>
|
||||
</Button>
|
||||
</Flexbox>
|
||||
)}
|
||||
</>
|
||||
</Flexbox>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,14 @@
|
||||
.dropdown {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.buttons button:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.buttons button:last-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid var(--base400) !important;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
width: 100vw;
|
||||
grid-column: 1;
|
||||
grid-row: 1 / 2;
|
||||
z-index: 1;
|
||||
z-index: var(--z-index-popup);
|
||||
}
|
||||
|
||||
.body {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { safeDecodeURI } from 'next-basics';
|
||||
import { Button, Icon, Icons, Text } from 'react-basics';
|
||||
import usePageQuery from 'components/hooks/usePageQuery';
|
||||
import styles from './FilterTags.module.css';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import styles from './FilterTags.module.css';
|
||||
|
||||
export function FilterTags({ params }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
@ -26,6 +26,7 @@ export function FilterTags({ params }) {
|
||||
|
||||
return (
|
||||
<div className={styles.filters}>
|
||||
<div className={styles.label}>{formatMessage(labels.filters)}</div>
|
||||
{Object.keys(params).map(key => {
|
||||
if (!params[key]) {
|
||||
return null;
|
||||
|
@ -4,19 +4,23 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: var(--font-size-sm);
|
||||
border: 1px solid var(--base600);
|
||||
border: 1px solid var(--blue400);
|
||||
border-radius: var(--border-radius);
|
||||
line-height: 30px;
|
||||
padding: 0 8px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
background: var(--blue100);
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
background: var(--base75);
|
||||
background: var(--blue200);
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ export default function WebsiteDetailsPage({ websiteId }) {
|
||||
return (
|
||||
<Page loading={isLoading} error={error}>
|
||||
<WebsiteHeader websiteId={websiteId} showLinks={showLinks} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} sticky={true} />
|
||||
<WebsiteChart websiteId={websiteId} />
|
||||
<FilterTags
|
||||
websiteId={websiteId}
|
||||
params={{ url, referrer, os, browser, device, country, region, city, title }}
|
||||
/>
|
||||
<WebsiteMetricsBar websiteId={websiteId} sticky={true} />
|
||||
<WebsiteChart websiteId={websiteId} />
|
||||
{!website && <Loading icon="dots" style={{ minHeight: 300 }} />}
|
||||
{website && (
|
||||
<>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Flexbox } from 'react-basics';
|
||||
import { Flexbox, Loading } from 'react-basics';
|
||||
import EventDataTable from 'components/pages/event-data/EventDataTable';
|
||||
import EventDataValueTable from 'components/pages/event-data/EventDataValueTable';
|
||||
import { EventDataMetricsBar } from 'components/pages/event-data/EventDataMetricsBar';
|
||||
@ -28,13 +28,14 @@ export default function WebsiteEventData({ websiteId }) {
|
||||
const {
|
||||
query: { event },
|
||||
} = usePageQuery();
|
||||
const { data } = useData(websiteId, event);
|
||||
const { data, isLoading } = useData(websiteId, event);
|
||||
|
||||
return (
|
||||
<Flexbox className={styles.container} direction="column" gap={20}>
|
||||
<EventDataMetricsBar websiteId={websiteId} />
|
||||
{!event && <EventDataTable data={data} />}
|
||||
{event && <EventDataValueTable event={event} data={data} />}
|
||||
{isLoading && <Loading position="page" />}
|
||||
{event && data && <EventDataValueTable event={event} data={data} />}
|
||||
</Flexbox>
|
||||
);
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import MetricsBar from 'components/metrics/MetricsBar';
|
||||
import FilterSelectForm from 'components/pages/reports/FilterSelectForm';
|
||||
import PopupForm from 'components/pages/reports/PopupForm';
|
||||
import { formatShortTime } from 'lib/format';
|
||||
import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row, TooltipPopup } from 'react-basics';
|
||||
import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row } from 'react-basics';
|
||||
import styles from './WebsiteMetricsBar.module.css';
|
||||
|
||||
export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
|
||||
export function WebsiteMetricsBar({ websiteId, showFilter = true, showRefresh = true, sticky }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const { get, useQuery } = useApi();
|
||||
@ -71,14 +71,12 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
|
||||
const WebsiteFilterButton = () => {
|
||||
return (
|
||||
<PopupTrigger>
|
||||
<TooltipPopup label={formatMessage(labels.addFilter)} position="top">
|
||||
<Button>
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
{formatMessage(labels.filter)}
|
||||
</Button>
|
||||
</TooltipPopup>
|
||||
<Button>
|
||||
<Icon>
|
||||
<Icons.Plus />
|
||||
</Icon>
|
||||
{formatMessage(labels.filter)}
|
||||
</Button>
|
||||
<Popup position="bottom" alignment="start" className={styles.popup}>
|
||||
{close => {
|
||||
return (
|
||||
@ -163,8 +161,8 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) {
|
||||
<Column defaultSize={12} xl={4}>
|
||||
<div className={styles.actions}>
|
||||
{showFilter && <WebsiteFilterButton />}
|
||||
{showRefresh && <RefreshButton websiteId={websiteId} />}
|
||||
<WebsiteDateFilter websiteId={websiteId} />
|
||||
<RefreshButton websiteId={websiteId} />
|
||||
</div>
|
||||
</Column>
|
||||
</Row>
|
||||
|
Loading…
Reference in New Issue
Block a user