New menu layout.

This commit is contained in:
Mike Cao 2025-05-14 13:31:07 -07:00
parent 0cfee43814
commit 1c22c18de5
17 changed files with 103 additions and 47 deletions

View file

@ -19,6 +19,7 @@ export function App({ children }) {
if (error) {
window.location.href = `${process.env.basePath || ''}/login`;
return null;
}
if (!user || !config) {
@ -30,7 +31,7 @@ export function App({ children }) {
}
return (
<Grid height="100vh" width="100%" columns="auto 1fr" rows="auto 1fr" overflow="hidden">
<Grid height="100vh" width="100%" columns="auto 1fr" rows="auto 1fr">
<Nav gridColumn="1 / 2" gridRow="1 / 3" />
<MenuBar gridColumn="2 / 3" gridRow="1 / 2" />
<Column
@ -39,6 +40,7 @@ export function App({ children }) {
alignItems="center"
overflow="auto"
backgroundColor="2"
position="relative"
>
<Page>
{children}

View file

@ -38,7 +38,7 @@ export function Nav(props: any) {
].filter(n => n);
return (
<SideNav {...props} isCollapsed={isCollapsed} variant="0" showBorder={true}>
<SideNav {...props} isCollapsed={isCollapsed} variant="2" showBorder={true}>
<SideNavSection>
<SideNavHeader label="umami" icon={<Icons.Logo />} />
</SideNavSection>

View file

@ -4,7 +4,6 @@ import { Panel } from '@/components/common/Panel';
import { useNavigation } from '@/components/hooks';
import { WebsiteChart } from './WebsiteChart';
import { WebsiteExpandedView } from './WebsiteExpandedView';
import { WebsiteHeader } from './WebsiteHeader';
import { WebsiteMetricsBar } from './WebsiteMetricsBar';
import { WebsiteTableView } from './WebsiteTableView';
import { WebsiteCompareTables } from './WebsiteCompareTables';
@ -15,8 +14,7 @@ export function WebsiteDetailsPage({ websiteId }: { websiteId: string }) {
} = useNavigation();
return (
<Column gap="3">
<WebsiteHeader websiteId={websiteId} />
<Column gap>
<Panel>
<WebsiteMetricsBar websiteId={websiteId} showFilter={true} showChange={true} />
</Panel>

View file

@ -0,0 +1,13 @@
'use client';
import { ReactNode } from 'react';
import { WebsiteProvider } from './WebsiteProvider';
import { WebsiteHeader } from '@/app/(main)/websites/[websiteId]/WebsiteHeader';
export function WebsiteLayout({ websiteId, children }: { websiteId: string; children: ReactNode }) {
return (
<WebsiteProvider websiteId={websiteId}>
<WebsiteHeader websiteId={websiteId} />
{children}
</WebsiteProvider>
);
}

View file

@ -1,6 +1,7 @@
import { Tabs, TabList, Tab, Icon, Text, Row } from '@umami/react-zen';
import { Column, Icon, Text, Row } from '@umami/react-zen';
import { Icons } from '@/components/icons';
import { useMessages, useNavigation } from '@/components/hooks';
import Link from 'next/link';
export function WebsiteTabs({ websiteId }: { websiteId: string }) {
const { formatMessage, labels } = useMessages();
@ -31,6 +32,54 @@ export function WebsiteTabs({ websiteId }: { websiteId: string }) {
icon: <Icons.Clock />,
path: '/realtime',
},
{
id: 'insights',
label: formatMessage(labels.insights),
icon: <Icons.Lightbulb />,
path: '/insights',
},
{
id: 'goals',
label: formatMessage(labels.goals),
icon: <Icons.Target />,
path: '/goals',
},
{
id: 'funnel',
label: formatMessage(labels.funnel),
icon: <Icons.Funnel />,
path: '/funnels',
},
{
id: 'journeys',
label: formatMessage(labels.journey),
icon: <Icons.Path />,
path: '/goals',
},
{
id: 'retention',
label: formatMessage(labels.retention),
icon: <Icons.Magnet />,
path: '/funnels',
},
{
id: 'utm',
label: formatMessage(labels.utm),
icon: <Icons.Tag />,
path: '/utm',
},
{
id: 'revenue',
label: formatMessage(labels.revenue),
icon: <Icons.Money />,
path: '/revenue',
},
{
id: 'attribution',
label: formatMessage(labels.attribution),
icon: <Icons.Network />,
path: '/attribution',
},
{
id: 'reports',
label: formatMessage(labels.reports),
@ -39,24 +88,29 @@ export function WebsiteTabs({ websiteId }: { websiteId: string }) {
},
];
const selectedKey = links
const selected = links
? links.find(({ path }) => path && pathname.endsWith(path))?.id
: 'overview';
return (
<Tabs selectedKey={selectedKey}>
<TabList items={links}>
{({ id, label, icon, path }) => {
return (
<Tab id={id} href={renderTeamUrl(`/websites/${websiteId}/${path}`)}>
<Row gap="3" alignItems="center">
<Icon fillColor="currentColor">{icon}</Icon>
<Text>{label}</Text>
</Row>
</Tab>
);
}}
</TabList>
</Tabs>
<Column gap="2" position="absolute" padding="4" style={{ top: 0, left: 0, bottom: 0 }}>
{links.map(({ id, label, icon, path }) => {
return (
<Link key={id} href={renderTeamUrl(`/websites/${websiteId}/${path}`)}>
<Row
alignItems="center"
padding
gap
hoverBackgroundColor="3"
borderRadius
width="160px"
>
<Icon fillColor="currentColor">{icon}</Icon>
<Text weight={selected === id ? 'bold' : undefined}>{label}</Text>
</Row>
</Link>
);
})}
</Column>
);
}

View file

@ -2,7 +2,6 @@
import { TabList, Tab, Tabs, TabPanel, Column } from '@umami/react-zen';
import { EventsTable } from '@/components/metrics/EventsTable';
import { useState } from 'react';
import { WebsiteHeader } from '../WebsiteHeader';
import { EventsDataTable } from './EventsDataTable';
import { EventsMetricsBar } from './EventsMetricsBar';
import { Panel } from '@/components/common/Panel';
@ -22,7 +21,6 @@ export function EventsPage({ websiteId }) {
return (
<Column gap="3">
<WebsiteHeader websiteId={websiteId} />
<Panel>
<EventsMetricsBar websiteId={websiteId} />
</Panel>

View file

@ -1,5 +1,5 @@
import { Metadata } from 'next';
import { WebsiteProvider } from './WebsiteProvider';
import { WebsiteLayout } from '@/app/(main)/websites/[websiteId]/WebsiteLayout';
export default async function ({
children,
@ -10,7 +10,7 @@ export default async function ({
}) {
const { websiteId } = await params;
return <WebsiteProvider websiteId={websiteId}>{children}</WebsiteProvider>;
return <WebsiteLayout websiteId={websiteId}>{children}</WebsiteLayout>;
}
export const metadata: Metadata = {

View file

@ -11,7 +11,6 @@ import { RealtimeLog } from './RealtimeLog';
import { RealtimeHeader } from './RealtimeHeader';
import { RealtimeUrls } from './RealtimeUrls';
import { RealtimeCountries } from './RealtimeCountries';
import { WebsiteHeader } from '../WebsiteHeader';
import { percentFilter } from '@/lib/filters';
export function WebsiteRealtimePage({ websiteId }: { websiteId: string }) {
@ -29,7 +28,6 @@ export function WebsiteRealtimePage({ websiteId }: { websiteId: string }) {
return (
<Grid gap="3">
<WebsiteHeader websiteId={websiteId} />
<Panel>
<RealtimeHeader data={data} />
</Panel>

View file

@ -2,7 +2,6 @@
import Link from 'next/link';
import { Button, Flexbox, Icon, Icons, Text } from '@umami/react-zen';
import { useMessages, useNavigation } from '@/components/hooks';
import { WebsiteHeader } from '../WebsiteHeader';
import { ReportsDataTable } from '@/app/(main)/reports/ReportsDataTable';
export function WebsiteReportsPage({ websiteId }) {
@ -11,7 +10,6 @@ export function WebsiteReportsPage({ websiteId }) {
return (
<>
<WebsiteHeader websiteId={websiteId} />
<Flexbox alignItems="center" justifyContent="end">
<Link href={renderTeamUrl('/reports/create')}>
<Button variant="primary">

View file

@ -1,7 +1,6 @@
'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';
@ -17,7 +16,6 @@ export function SessionsPage({ websiteId }) {
return (
<Column gap="3">
<WebsiteHeader websiteId={websiteId} />
<Panel>
<SessionsMetricsBar websiteId={websiteId} />
</Panel>

View file

@ -3,7 +3,6 @@ import { Grid, Row, Column } from '@umami/react-zen';
import { Avatar } from '@/components/common/Avatar';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { useWebsiteSessionQuery } from '@/components/hooks';
import { WebsiteHeader } from '@/app/(main)/websites/[websiteId]/WebsiteHeader';
import { SessionActivity } from './SessionActivity';
import { SessionData } from './SessionData';
import { SessionInfo } from './SessionInfo';
@ -21,7 +20,6 @@ export function SessionDetailsPage({
return (
<LoadingPanel {...query} loadingIcon="spinner" data={data}>
<WebsiteHeader websiteId={websiteId} />
<Grid
gap
columns={{ xs: '1fr', sm: '1fr', md: '1fr 1fr', lg: '1fr 2fr 1fr', xl: '1fr 2fr 1fr' }}

View file

@ -1,7 +1,7 @@
import { Icon, Row, Text } from '@umami/react-zen';
import { differenceInDays, isSameDay } from 'date-fns';
import { useLocale } from '@/components/hooks';
import { Icons } from '@/components/icons';
import { Lucide } from '@/components/icons';
import { formatDate } from '@/lib/date';
export function DateDisplay({ startDate, endDate }) {
@ -11,7 +11,7 @@ export function DateDisplay({ startDate, endDate }) {
return (
<Row gap="3" alignItems="center" wrap="nowrap">
<Icon>
<Icons.Calendar />
<Lucide.Calendar />
</Icon>
<Text wrap="nowrap">
{isSingleDate ? (

View file

@ -106,6 +106,7 @@ export function DateFilter({
placeholder={formatMessage(labels.selectDate)}
onChange={handleChange}
renderValue={renderValue}
popoverProps={{ style: { width: 200 } }}
>
{options.map(({ label, value, divider }: any) => {
return (

View file

@ -85,8 +85,8 @@ export function WebsiteDateFilter({
/>
{!isAllTime && compare && (
<Row alignItems="center" gap>
<Text>VS</Text>
<Select selectedKey={compare} onSelectionChange={handleSelect} style={{ width: '200px' }}>
<Text weight="bold">VS</Text>
<Select value={compare} onChange={handleSelect} popoverProps={{ style: { width: 200 } }}>
<ListItem id="prev">{formatMessage(labels.previousPeriod)}</ListItem>
<ListItem id="yoy">{formatMessage(labels.previousYear)}</ListItem>
</Select>

View file

@ -26,16 +26,16 @@ export function FilterBar({ websiteId }: { websiteId: string }) {
return (
<Row
gap
backgroundColor="1"
backgroundColor="3"
alignItems="center"
justifyContent="space-between"
paddingY="3"
paddingLeft="5"
paddingY="2"
paddingLeft="3"
paddingRight="2"
border
borderRadius="2"
>
<Row alignItems="center" gap="3" wrap="wrap">
<Row alignItems="center" gap="3" wrap="wrap" paddingX="2">
<Text color="11" weight="bold">
{formatMessage(labels.filters)}
</Text>
@ -61,11 +61,9 @@ export function FilterBar({ websiteId }: { websiteId: string }) {
<Text color="11">{operatorLabels[operator]}</Text>
<Text weight="bold">{paramValue}</Text>
</Row>
<Button variant="quiet" size="xs" style={{ left: '5px' }}>
<Icon onClick={e => handleCloseFilter(name, e)} size="xs">
<Icons.Close />
</Icon>
</Button>
<Icon onClick={e => handleCloseFilter(name, e)} size="xs">
<Icons.Close />
</Icon>
</Row>
</Row>
);

View file

@ -6,3 +6,4 @@ declare module 'fs-extra';
declare module 'jsonwebtoken';
declare module 'md5';
declare module 'prettier';
declare module 'semver';

View file

@ -6,7 +6,6 @@ body {
background-color: var(--background-color);
width: 100%;
min-height: 100vh;
overflow: hidden;
}
a,