mirror of
https://github.com/umami-software/umami.git
synced 2026-02-13 17:15:37 +01:00
Updated reports components.
This commit is contained in:
parent
f5bc3dc6c2
commit
0f6cdf8b80
95 changed files with 580 additions and 698 deletions
|
|
@ -1,9 +1,7 @@
|
|||
'use client';
|
||||
import { Column } from '@umami/react-zen';
|
||||
import { Panel } from '@/components/layout/Panel';
|
||||
import { FilterTags } from '@/components/metrics/FilterTags';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { useNavigation } from '@/components/hooks';
|
||||
import { FILTER_COLUMNS } from '@/lib/constants';
|
||||
import { WebsiteChart } from './WebsiteChart';
|
||||
import { WebsiteExpandedView } from './WebsiteExpandedView';
|
||||
import { WebsiteHeader } from './WebsiteHeader';
|
||||
|
|
@ -11,21 +9,16 @@ import { WebsiteMetricsBar } from './WebsiteMetricsBar';
|
|||
import { WebsiteTableView } from './WebsiteTableView';
|
||||
|
||||
export function WebsiteDetailsPage({ websiteId }: { websiteId: string }) {
|
||||
const { query } = useNavigation();
|
||||
const { view } = query;
|
||||
|
||||
const params = Object.keys(query).reduce((obj, key) => {
|
||||
if (FILTER_COLUMNS[key]) {
|
||||
obj[key] = query[key];
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
const {
|
||||
query: { view },
|
||||
} = useNavigation();
|
||||
|
||||
return (
|
||||
<Column gap="3">
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<FilterTags websiteId={websiteId} params={params} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} showFilter={true} showChange={true} />
|
||||
<Panel>
|
||||
<WebsiteMetricsBar websiteId={websiteId} showFilter={true} showChange={true} />
|
||||
</Panel>
|
||||
<Panel>
|
||||
<WebsiteChart websiteId={websiteId} />
|
||||
</Panel>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Icon, Icons, Text, Grid, Column } from '@umami/react-zen';
|
||||
import { LinkButton } from '@/components/common/LinkButton';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { SideBar } from '@/components/layout/SideBar';
|
||||
import { SideBar } from '@/components/common/SideBar';
|
||||
import { BrowsersTable } from '@/components/metrics/BrowsersTable';
|
||||
import { CitiesTable } from '@/components/metrics/CitiesTable';
|
||||
import { CountriesTable } from '@/components/metrics/CountriesTable';
|
||||
|
|
|
|||
|
|
@ -1,31 +1,80 @@
|
|||
import { ReactNode } from 'react';
|
||||
import { Row, Heading } from '@umami/react-zen';
|
||||
import {
|
||||
Column,
|
||||
Row,
|
||||
Heading,
|
||||
MenuTrigger,
|
||||
Button,
|
||||
Icon,
|
||||
Icons,
|
||||
Popover,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuSeparator,
|
||||
} from '@umami/react-zen';
|
||||
import { Favicon } from '@/components/common/Favicon';
|
||||
import { ActiveUsers } from '@/components/metrics/ActiveUsers';
|
||||
import { WebsiteTabs } from '@/app/(main)/websites/[websiteId]/WebsiteTabs';
|
||||
import { useWebsite } from '@/components/hooks/useWebsite';
|
||||
import { WebsiteFilterButton } from '@/app/(main)/websites/[websiteId]/WebsiteFilterButton';
|
||||
import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter';
|
||||
import { FilterTags } from '@/components/metrics/FilterTags';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
||||
export function WebsiteHeader({
|
||||
websiteId,
|
||||
children,
|
||||
showFilter = true,
|
||||
allowEdit = true,
|
||||
compareMode = false,
|
||||
}: {
|
||||
websiteId: string;
|
||||
children?: ReactNode;
|
||||
showFilter?: boolean;
|
||||
allowEdit?: boolean;
|
||||
compareMode?: boolean;
|
||||
}) {
|
||||
const website = useWebsite();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { name, domain } = website || {};
|
||||
|
||||
const items = [
|
||||
{ label: formatMessage(labels.previousPeriod), value: 'prev' },
|
||||
{ label: formatMessage(labels.previousYear), value: 'yoy' },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row alignItems="center" gap="3" marginY="6">
|
||||
<Favicon domain={domain} />
|
||||
<Heading>
|
||||
{name}
|
||||
<ActiveUsers websiteId={websiteId} />
|
||||
</Heading>
|
||||
{children}
|
||||
<Column marginY="6" gap="6">
|
||||
<Row alignItems="center" justifyContent="space-between" gap="3">
|
||||
<Row alignItems="center" gap="3">
|
||||
<Favicon domain={domain} />
|
||||
<Heading>
|
||||
{name}
|
||||
<ActiveUsers websiteId={websiteId} />
|
||||
</Heading>
|
||||
</Row>
|
||||
<Row alignItems="center" gap="3">
|
||||
{showFilter && <WebsiteFilterButton websiteId={websiteId} />}
|
||||
<WebsiteDateFilter websiteId={websiteId} />
|
||||
{allowEdit && (
|
||||
<MenuTrigger>
|
||||
<Button variant="quiet">
|
||||
<Icon>
|
||||
<Icons.More />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popover placement="bottom end">
|
||||
<Menu>
|
||||
<MenuItem>Compare dates</MenuItem>
|
||||
<MenuItem>Share</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem>Settings</MenuItem>
|
||||
</Menu>
|
||||
</Popover>
|
||||
</MenuTrigger>
|
||||
)}
|
||||
</Row>
|
||||
</Row>
|
||||
{compareMode && items.map(item => <div key={item.value}>{item.label}</div>)}
|
||||
<FilterTags websiteId={websiteId} />
|
||||
<WebsiteTabs websiteId={websiteId} />
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,16 @@
|
|||
import { Select, ListItem } from '@umami/react-zen';
|
||||
import classNames from 'classnames';
|
||||
import { useDateRange, useMessages, useSticky } from '@/components/hooks';
|
||||
import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter';
|
||||
import { useDateRange, useMessages } from '@/components/hooks';
|
||||
import { MetricCard } from '@/components/metrics/MetricCard';
|
||||
import { MetricsBar } from '@/components/metrics/MetricsBar';
|
||||
import { formatShortTime, formatLongNumber } from '@/lib/format';
|
||||
import { useWebsiteStatsQuery } from '@/components/hooks/queries/useWebsiteStatsQuery';
|
||||
import { useWebsites, setWebsiteDateCompare } from '@/store/websites';
|
||||
import { WebsiteFilterButton } from './WebsiteFilterButton';
|
||||
import styles from './WebsiteMetricsBar.module.css';
|
||||
import { useWebsites } from '@/store/websites';
|
||||
|
||||
export function WebsiteMetricsBar({
|
||||
websiteId,
|
||||
sticky,
|
||||
showChange = false,
|
||||
compareMode = false,
|
||||
showFilter = false,
|
||||
}: {
|
||||
websiteId: string;
|
||||
sticky?: boolean;
|
||||
showChange?: boolean;
|
||||
compareMode?: boolean;
|
||||
showFilter?: boolean;
|
||||
|
|
@ -26,7 +18,6 @@ export function WebsiteMetricsBar({
|
|||
const { dateRange } = useDateRange(websiteId);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const dateCompare = useWebsites(state => state[websiteId]?.dateCompare);
|
||||
const { ref } = useSticky({ enabled: sticky });
|
||||
const { data, isLoading, isFetched, error } = useWebsiteStatsQuery(
|
||||
websiteId,
|
||||
compareMode && dateCompare,
|
||||
|
|
@ -76,51 +67,23 @@ export function WebsiteMetricsBar({
|
|||
]
|
||||
: [];
|
||||
|
||||
const items = [
|
||||
{ label: formatMessage(labels.previousPeriod), value: 'prev' },
|
||||
{ label: formatMessage(labels.previousYear), value: 'yoy' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div ref={ref} className={classNames(styles.container)}>
|
||||
<div>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
{metrics.map(({ label, value, prev, change, formatValue, reverseColors }) => {
|
||||
return (
|
||||
<MetricCard
|
||||
key={label}
|
||||
value={value}
|
||||
previousValue={prev}
|
||||
label={label}
|
||||
change={change}
|
||||
formatValue={formatValue}
|
||||
reverseColors={reverseColors}
|
||||
showChange={!isAllTime && (compareMode || showChange)}
|
||||
showPrevious={!isAllTime && compareMode}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</MetricsBar>
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
{showFilter && <WebsiteFilterButton websiteId={websiteId} />}
|
||||
<WebsiteDateFilter websiteId={websiteId} showAllTime={!compareMode} />
|
||||
{compareMode && (
|
||||
<div className={styles.vs}>
|
||||
<b>VS</b>
|
||||
<Select
|
||||
className={styles.dropdown}
|
||||
items={items}
|
||||
value={dateCompare || 'prev'}
|
||||
onChange={(value: any) => setWebsiteDateCompare(websiteId, value)}
|
||||
>
|
||||
{items.map(({ label, value }) => (
|
||||
<ListItem key={value}>{label}</ListItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
{metrics.map(({ label, value, prev, change, formatValue, reverseColors }) => {
|
||||
return (
|
||||
<MetricCard
|
||||
key={label}
|
||||
value={value}
|
||||
previousValue={prev}
|
||||
label={label}
|
||||
change={change}
|
||||
formatValue={formatValue}
|
||||
reverseColors={reverseColors}
|
||||
showChange={!isAllTime && (compareMode || showChange)}
|
||||
showPrevious={!isAllTime && compareMode}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</MetricsBar>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Grid } from '@umami/react-zen';
|
||||
import { Panel } from '@/components/layout/Panel';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { PagesTable } from '@/components/metrics/PagesTable';
|
||||
import { ReferrersTable } from '@/components/metrics/ReferrersTable';
|
||||
import { BrowsersTable } from '@/components/metrics/BrowsersTable';
|
||||
|
|
|
|||
|
|
@ -1,28 +1,17 @@
|
|||
'use client';
|
||||
import { Grid } from '@umami/react-zen';
|
||||
import { Panel } from '@/components/layout/Panel';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { WebsiteHeader } from '../WebsiteHeader';
|
||||
import { WebsiteMetricsBar } from '../WebsiteMetricsBar';
|
||||
import { FilterTags } from '@/components/metrics/FilterTags';
|
||||
import { useNavigation } from '@/components/hooks';
|
||||
import { FILTER_COLUMNS } from '@/lib/constants';
|
||||
import { WebsiteChart } from '../WebsiteChart';
|
||||
import { WebsiteCompareTables } from './WebsiteCompareTables';
|
||||
|
||||
export function WebsiteComparePage({ websiteId }) {
|
||||
const { query } = useNavigation();
|
||||
|
||||
const params = Object.keys(query).reduce((obj, key) => {
|
||||
if (FILTER_COLUMNS[key]) {
|
||||
obj[key] = query[key];
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<Grid gap="3">
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<FilterTags websiteId={websiteId} params={params} />
|
||||
<FilterTags websiteId={websiteId} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} compareMode={true} showFilter={true} />
|
||||
<Panel>
|
||||
<WebsiteChart websiteId={websiteId} compareMode={true} />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Grid, Heading, Column } from '@umami/react-zen';
|
||||
import { useDateRange, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { SideBar } from '@/components/layout/SideBar';
|
||||
import { SideBar } from '@/components/common/SideBar';
|
||||
import { BrowsersTable } from '@/components/metrics/BrowsersTable';
|
||||
import { ChangeLabel } from '@/components/metrics/ChangeLabel';
|
||||
import { CitiesTable } from '@/components/metrics/CitiesTable';
|
||||
|
|
|
|||
|
|
@ -1,40 +1,35 @@
|
|||
import { useMessages } from '@/components/hooks';
|
||||
import { useWebsiteSessionStatsQuery } from '@/components/hooks/queries/useWebsiteSessionStatsQuery';
|
||||
import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter';
|
||||
import { MetricCard } from '@/components/metrics/MetricCard';
|
||||
import { MetricsBar } from '@/components/metrics/MetricsBar';
|
||||
import { formatLongNumber } from '@/lib/format';
|
||||
import { Flexbox } from '@umami/react-zen';
|
||||
|
||||
export function EventsMetricsBar({ websiteId }: { websiteId: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { data, isLoading, isFetched, error } = useWebsiteSessionStatsQuery(websiteId);
|
||||
|
||||
return (
|
||||
<Flexbox direction="row" justifyContent="space-between" style={{ minHeight: 120 }}>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
<MetricCard
|
||||
value={data?.visitors?.value}
|
||||
label={formatMessage(labels.visitors)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.visits?.value}
|
||||
label={formatMessage(labels.visits)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.pageviews?.value}
|
||||
label={formatMessage(labels.views)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.events?.value}
|
||||
label={formatMessage(labels.events)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
</MetricsBar>
|
||||
<WebsiteDateFilter websiteId={websiteId} />
|
||||
</Flexbox>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
<MetricCard
|
||||
value={data?.visitors?.value}
|
||||
label={formatMessage(labels.visitors)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.visits?.value}
|
||||
label={formatMessage(labels.visits)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.pageviews?.value}
|
||||
label={formatMessage(labels.views)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.events?.value}
|
||||
label={formatMessage(labels.events)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
</MetricsBar>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
'use client';
|
||||
import { TabList, Tab, Tabs, TabPanel, Grid } from '@umami/react-zen';
|
||||
import { TabList, Tab, Tabs, TabPanel, Column } from '@umami/react-zen';
|
||||
import { useState } from 'react';
|
||||
import { WebsiteHeader } from '../WebsiteHeader';
|
||||
import { EventsDataTable } from './EventsDataTable';
|
||||
import { EventsMetricsBar } from './EventsMetricsBar';
|
||||
import { Panel } from '@/components/layout/Panel';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { EventsChart } from '@/components/metrics/EventsChart';
|
||||
import { GridRow } from '@/components/layout/GridRow';
|
||||
import { GridRow } from '@/components/common/GridRow';
|
||||
import { MetricsTable } from '@/components/metrics/MetricsTable';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { EventProperties } from './EventProperties';
|
||||
|
|
@ -16,9 +16,11 @@ export function EventsPage({ websiteId }) {
|
|||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<Grid gap="3">
|
||||
<Column gap="3">
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<EventsMetricsBar websiteId={websiteId} />
|
||||
<Panel>
|
||||
<EventsMetricsBar websiteId={websiteId} />
|
||||
</Panel>
|
||||
<GridRow layout="two-one">
|
||||
<Panel>
|
||||
<EventsChart websiteId={websiteId} />
|
||||
|
|
@ -46,6 +48,6 @@ export function EventsPage({ websiteId }) {
|
|||
</TabPanel>
|
||||
</Tabs>
|
||||
</Panel>
|
||||
</Grid>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,18 @@
|
|||
import { MetricCard } from '@/components/metrics/MetricCard';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { RealtimeData } from '@/lib/types';
|
||||
import styles from './RealtimeHeader.module.css';
|
||||
import { MetricsBar } from '@/components/metrics/MetricsBar';
|
||||
|
||||
export function RealtimeHeader({ data }: { data: RealtimeData }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { totals }: any = data || {};
|
||||
|
||||
return (
|
||||
<div className={styles.header}>
|
||||
<div className={styles.metrics}>
|
||||
<MetricCard
|
||||
className={styles.card}
|
||||
label={formatMessage(labels.views)}
|
||||
value={totals.views}
|
||||
/>
|
||||
<MetricCard
|
||||
className={styles.card}
|
||||
label={formatMessage(labels.visitors)}
|
||||
value={totals.visitors}
|
||||
/>
|
||||
<MetricCard
|
||||
className={styles.card}
|
||||
label={formatMessage(labels.events)}
|
||||
value={totals.events}
|
||||
/>
|
||||
<MetricCard
|
||||
className={styles.card}
|
||||
label={formatMessage(labels.countries)}
|
||||
value={totals.countries}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<MetricsBar isFetched={true}>
|
||||
<MetricCard label={formatMessage(labels.views)} value={totals.views} />
|
||||
<MetricCard label={formatMessage(labels.visitors)} value={totals.visitors} />
|
||||
<MetricCard label={formatMessage(labels.events)} value={totals.events} />
|
||||
<MetricCard label={formatMessage(labels.countries)} value={totals.countries} />
|
||||
</MetricsBar>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Page } from '@/components/layout/Page';
|
||||
import { PageHeader } from '@/components/layout/PageHeader';
|
||||
import { Page } from '@/components/common/Page';
|
||||
import { PageHeader } from '@/components/common/PageHeader';
|
||||
import { useApi, useMessages } from '@/components/hooks';
|
||||
import { EmptyPlaceholder } from '@/components/common/EmptyPlaceholder';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
'use client';
|
||||
import { firstBy } from 'thenby';
|
||||
import { Grid } from '@umami/react-zen';
|
||||
import { GridRow } from '@/components/layout/GridRow';
|
||||
import { Page } from '@/components/layout/Page';
|
||||
import { Panel } from '@/components/layout/Panel';
|
||||
import { GridRow } from '@/components/common/GridRow';
|
||||
import { Page } from '@/components/common/Page';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { RealtimeChart } from '@/components/metrics/RealtimeChart';
|
||||
import { WorldMap } from '@/components/metrics/WorldMap';
|
||||
import { useRealtimeQuery } from '@/components/hooks';
|
||||
|
|
@ -30,7 +30,9 @@ export function WebsiteRealtimePage({ websiteId }) {
|
|||
return (
|
||||
<Grid gap="3">
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<RealtimeHeader data={data} />
|
||||
<Panel>
|
||||
<RealtimeHeader data={data} />
|
||||
</Panel>
|
||||
<Panel>
|
||||
<RealtimeChart data={data} unit="minute" />
|
||||
</Panel>
|
||||
|
|
|
|||
|
|
@ -1,40 +1,35 @@
|
|||
import { useMessages } from '@/components/hooks';
|
||||
import { useWebsiteSessionStatsQuery } from '@/components/hooks/queries/useWebsiteSessionStatsQuery';
|
||||
import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter';
|
||||
import { MetricCard } from '@/components/metrics/MetricCard';
|
||||
import { MetricsBar } from '@/components/metrics/MetricsBar';
|
||||
import { formatLongNumber } from '@/lib/format';
|
||||
import { Flexbox } from '@umami/react-zen';
|
||||
|
||||
export function SessionsMetricsBar({ websiteId }: { websiteId: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { data, isLoading, isFetched, error } = useWebsiteSessionStatsQuery(websiteId);
|
||||
|
||||
return (
|
||||
<Flexbox direction="row" justifyContent="space-between" style={{ minHeight: 120 }}>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
<MetricCard
|
||||
value={data?.visitors?.value}
|
||||
label={formatMessage(labels.visitors)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.visits?.value}
|
||||
label={formatMessage(labels.visits)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.pageviews?.value}
|
||||
label={formatMessage(labels.views)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.countries?.value}
|
||||
label={formatMessage(labels.countries)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
</MetricsBar>
|
||||
<WebsiteDateFilter websiteId={websiteId} />
|
||||
</Flexbox>
|
||||
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
|
||||
<MetricCard
|
||||
value={data?.visitors?.value}
|
||||
label={formatMessage(labels.visitors)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.visits?.value}
|
||||
label={formatMessage(labels.visits)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.pageviews?.value}
|
||||
label={formatMessage(labels.views)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
<MetricCard
|
||||
value={data?.countries?.value}
|
||||
label={formatMessage(labels.countries)}
|
||||
formatValue={formatLongNumber}
|
||||
/>
|
||||
</MetricsBar>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
'use client';
|
||||
import { useState } from 'react';
|
||||
import { TabList, Tab, Tabs, TabPanel, Column } from '@umami/react-zen';
|
||||
import { WebsiteHeader } from '../WebsiteHeader';
|
||||
import { SessionsDataTable } from './SessionsDataTable';
|
||||
import { SessionsMetricsBar } from './SessionsMetricsBar';
|
||||
import { SessionProperties } from './SessionProperties';
|
||||
import { WorldMap } from '@/components/metrics/WorldMap';
|
||||
import { GridRow } from '@/components/layout/GridRow';
|
||||
import { TabList, Tab, Tabs, TabPanel } from '@umami/react-zen';
|
||||
import { useState } from 'react';
|
||||
import { GridRow } from '@/components/common/GridRow';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { SessionsWeekly } from './SessionsWeekly';
|
||||
import { Panel } from '@/components/layout/Panel';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
|
||||
export function SessionsPage({ websiteId }) {
|
||||
const [tab, setTab] = useState('activity');
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Column gap="3">
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<SessionsMetricsBar websiteId={websiteId} />
|
||||
<Panel>
|
||||
<SessionsMetricsBar websiteId={websiteId} />
|
||||
</Panel>
|
||||
<GridRow layout="two-one">
|
||||
<Panel padding="0">
|
||||
<WorldMap websiteId={websiteId} />
|
||||
|
|
@ -41,6 +43,6 @@ export function SessionsPage({ websiteId }) {
|
|||
</TabPanel>
|
||||
</Tabs>
|
||||
</Panel>
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue