mirror of
https://github.com/umami-software/umami.git
synced 2026-02-18 03:25:40 +01:00
Updated session and events queries. Added sessions page.
This commit is contained in:
parent
082a751ffe
commit
db36c37d32
39 changed files with 376 additions and 180 deletions
|
|
@ -0,0 +1,26 @@
|
|||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
min-height: 90px;
|
||||
margin-bottom: 20px;
|
||||
background: var(--base50);
|
||||
z-index: var(--z-index-above);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.container {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { useApi, useDateRange, useMessages } from 'components/hooks';
|
||||
import MetricCard from 'components/metrics/MetricCard';
|
||||
import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
|
||||
import MetricsBar from 'components/metrics/MetricsBar';
|
||||
import styles from './EventDataMetricsBar.module.css';
|
||||
|
||||
export function EventDataMetricsBar({ websiteId }: { websiteId: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { get, useQuery } = useApi();
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
const { startDate, endDate } = dateRange;
|
||||
|
||||
const { data, error, isLoading, isFetched } = useQuery({
|
||||
queryKey: ['event-data:stats', { websiteId, startDate, endDate }],
|
||||
queryFn: () =>
|
||||
get(`/event-data/stats`, {
|
||||
websiteId,
|
||||
startAt: +startDate,
|
||||
endAt: +endDate,
|
||||
}),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
<MetricCard label={formatMessage(labels.events)} value={data?.events} />
|
||||
<MetricCard label={formatMessage(labels.fields)} value={data?.fields} />
|
||||
<MetricCard label={formatMessage(labels.totalRecords)} value={data?.records} />
|
||||
</MetricsBar>
|
||||
<div className={styles.actions}>
|
||||
<WebsiteDateFilter websiteId={websiteId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EventDataMetricsBar;
|
||||
12
src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx
Normal file
12
src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
'use client';
|
||||
import WebsiteHeader from '../WebsiteHeader';
|
||||
import WebsiteEventData from './WebsiteEventData';
|
||||
|
||||
export default function EventDataPage({ websiteId }) {
|
||||
return (
|
||||
<>
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<WebsiteEventData websiteId={websiteId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import Link from 'next/link';
|
||||
import { GridTable, GridColumn } from 'react-basics';
|
||||
import { useMessages, useNavigation } from 'components/hooks';
|
||||
import Empty from 'components/common/Empty';
|
||||
import { DATA_TYPES } from 'lib/constants';
|
||||
|
||||
export function EventDataTable({ data = [] }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { renderUrl } = useNavigation();
|
||||
|
||||
if (data.length === 0) {
|
||||
return <Empty />;
|
||||
}
|
||||
|
||||
return (
|
||||
<GridTable data={data}>
|
||||
<GridColumn name="eventName" label={formatMessage(labels.event)}>
|
||||
{row => (
|
||||
<Link href={renderUrl({ event: row.eventName })} shallow={true}>
|
||||
{row.eventName}
|
||||
</Link>
|
||||
)}
|
||||
</GridColumn>
|
||||
<GridColumn name="fieldName" label={formatMessage(labels.field)}>
|
||||
{row => row.fieldName}
|
||||
</GridColumn>
|
||||
<GridColumn name="dataType" label={formatMessage(labels.type)}>
|
||||
{row => DATA_TYPES[row.dataType]}
|
||||
</GridColumn>
|
||||
<GridColumn name="total" label={formatMessage(labels.totalRecords)}>
|
||||
{({ total }) => total.toLocaleString()}
|
||||
</GridColumn>
|
||||
</GridTable>
|
||||
);
|
||||
}
|
||||
|
||||
export default EventDataTable;
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { GridTable, GridColumn } from 'react-basics';
|
||||
import { useMessages } from 'components/hooks';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import Empty from 'components/common/Empty';
|
||||
import { DATA_TYPES } from 'lib/constants';
|
||||
|
||||
export function EventDataValueTable({ data = [], event }: { data: any[]; event: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={event} />
|
||||
{data.length <= 0 && <Empty />}
|
||||
{data.length > 0 && (
|
||||
<GridTable data={data}>
|
||||
<GridColumn name="fieldName" label={formatMessage(labels.field)} />
|
||||
<GridColumn name="dataType" label={formatMessage(labels.type)}>
|
||||
{row => DATA_TYPES[row.dataType]}
|
||||
</GridColumn>
|
||||
<GridColumn name="fieldValue" label={formatMessage(labels.value)} />
|
||||
<GridColumn name="total" label={formatMessage(labels.totalRecords)} width="200px">
|
||||
{({ total }) => total.toLocaleString()}
|
||||
</GridColumn>
|
||||
</GridTable>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default EventDataValueTable;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
.container a {
|
||||
color: var(--font-color100);
|
||||
}
|
||||
|
||||
.container a:hover {
|
||||
color: var(--primary400);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { Flexbox, Loading } from 'react-basics';
|
||||
import EventDataTable from './EventDataTable';
|
||||
import EventDataValueTable from './EventDataValueTable';
|
||||
import { EventDataMetricsBar } from './EventDataMetricsBar';
|
||||
import { useDateRange, useApi, useNavigation } from 'components/hooks';
|
||||
import styles from './WebsiteEventData.module.css';
|
||||
|
||||
function useData(websiteId: string, event: string) {
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
const { startDate, endDate } = dateRange;
|
||||
const { get, useQuery } = useApi();
|
||||
const { data, error, isLoading } = useQuery({
|
||||
queryKey: ['event-data:events', { websiteId, startDate, endDate, event }],
|
||||
queryFn: () =>
|
||||
get('/event-data/events', {
|
||||
websiteId,
|
||||
startAt: +startDate,
|
||||
endAt: +endDate,
|
||||
event,
|
||||
}),
|
||||
enabled: !!(websiteId && startDate && endDate),
|
||||
});
|
||||
|
||||
return { data, error, isLoading };
|
||||
}
|
||||
|
||||
export default function WebsiteEventData({ websiteId }) {
|
||||
const {
|
||||
query: { event },
|
||||
} = useNavigation();
|
||||
const { data, isLoading } = useData(websiteId, event);
|
||||
|
||||
return (
|
||||
<Flexbox className={styles.container} direction="column" gap={20}>
|
||||
<EventDataMetricsBar websiteId={websiteId} />
|
||||
{!event && <EventDataTable data={data} />}
|
||||
{isLoading && <Loading position="page" />}
|
||||
{event && data && <EventDataValueTable event={event} data={data} />}
|
||||
</Flexbox>
|
||||
);
|
||||
}
|
||||
10
src/app/(main)/websites/[websiteId]/events/page.tsx
Normal file
10
src/app/(main)/websites/[websiteId]/events/page.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Metadata } from 'next';
|
||||
import EventDataPage from './EventDataPage';
|
||||
|
||||
export default async function ({ params: { websiteId } }) {
|
||||
return <EventDataPage websiteId={websiteId} />;
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Event Data',
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue