Updated session profile page. Added TypeIcon component. Added useRegionNames hook.

This commit is contained in:
Mike Cao 2024-07-29 01:38:36 -07:00
parent ac60d08ee5
commit c3c3b46ef6
313 changed files with 197 additions and 94 deletions

View file

@ -7,7 +7,6 @@ import WebsiteExpandedView from './WebsiteExpandedView';
import WebsiteHeader from './WebsiteHeader';
import WebsiteMetricsBar from './WebsiteMetricsBar';
import WebsiteTableView from './WebsiteTableView';
import WebsiteProvider from './WebsiteProvider';
import { FILTER_COLUMNS } from 'lib/constants';
export default function WebsiteDetailsPage({ websiteId }: { websiteId: string }) {
@ -25,13 +24,13 @@ export default function WebsiteDetailsPage({ websiteId }: { websiteId: string })
}, {});
return (
<WebsiteProvider websiteId={websiteId}>
<>
<WebsiteHeader websiteId={websiteId} showLinks={showLinks} />
<FilterTags websiteId={websiteId} params={params} />
<WebsiteMetricsBar websiteId={websiteId} showFilter={true} showChange={true} sticky={true} />
<WebsiteChart websiteId={websiteId} />
{!view && <WebsiteTableView websiteId={websiteId} />}
{view && <WebsiteExpandedView websiteId={websiteId} />}
</WebsiteProvider>
</>
);
}

View file

@ -1,3 +1,4 @@
'use client';
import { createContext, ReactNode, useEffect } from 'react';
import { useModified, useWebsite } from 'components/hooks';
import { Loading } from 'react-basics';

View file

@ -6,7 +6,6 @@ import { useNavigation } from 'components/hooks';
import { FILTER_COLUMNS } from 'lib/constants';
import WebsiteChart from '../WebsiteChart';
import WebsiteCompareTables from './WebsiteCompareTables';
import WebsiteProvider from '../WebsiteProvider';
export function WebsiteComparePage({ websiteId }) {
const { query } = useNavigation();
@ -19,13 +18,13 @@ export function WebsiteComparePage({ websiteId }) {
}, {});
return (
<WebsiteProvider websiteId={websiteId}>
<>
<WebsiteHeader websiteId={websiteId} />
<FilterTags websiteId={websiteId} params={params} />
<WebsiteMetricsBar websiteId={websiteId} compareMode={true} showFilter={true} />
<WebsiteChart websiteId={websiteId} compareMode={true} />
<WebsiteCompareTables websiteId={websiteId} />
</WebsiteProvider>
</>
);
}

View file

@ -0,0 +1,17 @@
import { Metadata } from 'next';
import WebsiteProvider from './WebsiteProvider';
export default function ({ children, params: { websiteId } }) {
if (process.env.cloudMode) {
return null;
}
return <WebsiteProvider websiteId={websiteId}>{children}</WebsiteProvider>;
}
export const metadata: Metadata = {
title: {
template: '%s | Umami',
default: 'Websites | Umami',
},
};

View file

@ -3,19 +3,17 @@ import ListTable from 'components/metrics/ListTable';
import { useLocale, useCountryNames, useMessages } from 'components/hooks';
import classNames from 'classnames';
import styles from './RealtimeCountries.module.css';
import TypeIcon from 'components/common/TypeIcon';
export function RealtimeCountries({ data }) {
const { formatMessage, labels } = useMessages();
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
const { countryNames } = useCountryNames(locale);
const renderCountryName = useCallback(
({ x: code }) => (
<span className={classNames(locale, styles.row)}>
<img
src={`${process.env.basePath || ''}/images/flags/${code?.toLowerCase() || 'xx'}.png`}
alt={code}
/>
<TypeIcon type="country" value={code?.toLowerCase()} />
{countryNames[code]}
</span>
),

View file

@ -32,7 +32,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
const { formatValue } = useFormat();
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
const { countryNames } = useCountryNames(locale);
const [filter, setFilter] = useState(TYPE_ALL);
const buttons = [

View file

@ -10,7 +10,6 @@ import RealtimeHeader from './RealtimeHeader';
import RealtimeUrls from './RealtimeUrls';
import RealtimeCountries from './RealtimeCountries';
import WebsiteHeader from '../WebsiteHeader';
import WebsiteProvider from '../WebsiteProvider';
import { percentFilter } from 'lib/filters';
export function WebsiteRealtimePage({ websiteId }) {
@ -27,7 +26,7 @@ export function WebsiteRealtimePage({ websiteId }) {
);
return (
<WebsiteProvider websiteId={websiteId}>
<>
<WebsiteHeader websiteId={websiteId} />
<RealtimeHeader data={data} />
<RealtimeChart data={data} unit="minute" />
@ -41,7 +40,7 @@ export function WebsiteRealtimePage({ websiteId }) {
<WorldMap data={countries} />
</GridRow>
</Grid>
</WebsiteProvider>
</>
);
}

View file

@ -1,7 +1,6 @@
import Link from 'next/link';
import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
import { useFormat, useMessages } from 'components/hooks';
import { formatDistanceToNow } from 'date-fns';
import Profile from 'components/common/Profile';
export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) {
@ -12,14 +11,10 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean
return (
<GridTable data={data} cardMode={['xs', 'sm', 'md'].includes(breakpoint)}>
<GridColumn name="pic" label="" width="90px">
{row => <Profile seed={row.id} size={64} />}
{row => <Profile key={row.id} seed={row.id} size={64} />}
</GridColumn>
<GridColumn name="id" label="ID">
{row => (
<Link href={`sessions/${row.id}`}>
{row.id} ({row.firstAt !== row.lastAt ? 'YES' : 'NO'})
</Link>
)}
{row => <Link href={`sessions/${row.id}`}>{row.id}</Link>}
</GridColumn>
<GridColumn name="country" label={formatMessage(labels.country)}>
{row => formatValue(row.country, 'country')}
@ -32,8 +27,8 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean
<GridColumn name="device" label={formatMessage(labels.device)}>
{row => formatValue(row.device, 'device')}
</GridColumn>
<GridColumn name="createdAt" label={formatMessage(labels.created)}>
{row => formatDistanceToNow(new Date(row.createdAt))}
<GridColumn name="lastAt" label={formatMessage(labels.lastSeen)}>
{row => row.lastAt}
</GridColumn>
</GridTable>
);

View file

@ -1,3 +1,18 @@
.page {
display: grid;
grid-template-columns: 300px 1fr;
}
.sidebar {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding-right: 20px;
border-right: 1px solid var(--base300);
}
.content {
padding-left: 20px;
}

View file

@ -3,6 +3,7 @@ import WebsiteHeader from '../../WebsiteHeader';
import SessionInfo from './SessionInfo';
import { useSession } from 'components/hooks';
import { Loading } from 'react-basics';
import Profile from 'components/common/Profile';
import styles from './SessionDetailsPage.module.css';
export default function SessionDetailsPage({
@ -19,9 +20,15 @@ export default function SessionDetailsPage({
}
return (
<div className={styles.page}>
<>
<WebsiteHeader websiteId={websiteId} />
<SessionInfo data={data} />
</div>
<div className={styles.page}>
<div className={styles.sidebar}>
<Profile seed={data?.id} />
<SessionInfo data={data} />
</div>
<div className={styles.content}>oh hi.</div>
</div>
</>
);
}

View file

@ -0,0 +1,17 @@
.info {
display: grid;
gap: 10px;
}
.info dt {
color: var(--font-color200);
font-weight: bold;
}
.info dd {
display: flex;
gap: 10px;
align-items: center;
margin: 5px 0 28px;
text-align: left;
}

View file

@ -1,17 +1,62 @@
import Profile from 'components/common/Profile';
import { format } from 'date-fns';
import { useFormat, useLocale, useMessages, useRegionNames } from 'components/hooks';
import TypeIcon from 'components/common/TypeIcon';
import { Icon, CopyIcon } from 'react-basics';
import Icons from 'components/icons';
import styles from './SessionInfo.module.css';
export default function SessionInfo({ data }) {
const { locale } = useLocale();
const { formatMessage, labels } = useMessages();
const { formatValue } = useFormat();
const { getRegionName } = useRegionNames(locale);
return (
<h1>
<Profile seed={data?.id} />
<div className={styles.info}>
<dl>
<dt>ID</dt>
<dd>{data?.id}</dd>
<dt>Country</dt>
<dd>{data?.country}</dd>
<dt>City</dt>
<dd>{data?.city}</dd>
<dd>
{data?.id} <CopyIcon value={data?.id} />
</dd>
<dt>{formatMessage(labels.firstSeen)}</dt>
<dd>{format(new Date(data?.firstAt), 'PPPpp')}</dd>
<dt>{formatMessage(labels.lastSeen)}</dt>
<dd>{format(new Date(data?.lastAt), 'PPPpp')}</dd>
<dt>{formatMessage(labels.country)}</dt>
<dd>
<TypeIcon type="country" value={data?.country} />
{formatValue(data?.country, 'country')}
</dd>
<dt>{formatMessage(labels.region)}</dt>
<dd>
<Icon>
<Icons.Location />
</Icon>
{getRegionName(data?.subdivision1)}
</dd>
<dt>{formatMessage(labels.city)}</dt>
<dd>
<Icon>
<Icons.Location />
</Icon>
{data?.city}
</dd>
<dt>{formatMessage(labels.os)}</dt>
<dd>
<TypeIcon type="os" value={data?.os?.toLowerCase()?.replaceAll(/\W/g, '-')} />
{formatValue(data?.os, 'os')}
</dd>
<dt>{formatMessage(labels.device)}</dt>
<dd>
<TypeIcon type="device" value={data?.device} />
{formatValue(data?.device, 'device')}
</dd>
<dt>{formatMessage(labels.browser)}</dt>
<dd>
<TypeIcon type="browser" value={data?.browser} />
{formatValue(data?.browser, 'browser')}
</dd>
</dl>
</h1>
</div>
);
}