Code cleanup.

This commit is contained in:
Mike Cao 2026-02-05 19:46:16 -08:00
parent a8534a9d4d
commit 8484fd26e1
27 changed files with 183 additions and 162 deletions

View file

@ -39,15 +39,14 @@ export function App({ children }) {
return (
<Grid
columns={{ xs: '1fr', lg: 'auto 1fr' }}
rows={{ xs: 'auto 1fr', lg: '1fr' }}
height={{ xs: 'auto', lg: '100vh' }}
width="100%"
columns={{ base: '1fr', lg: 'auto 1fr' }}
rows={{ base: 'auto 1fr', lg: '1fr' }}
height="screen"
>
<Row display={{ xs: 'flex', lg: 'none' }} alignItems="center" gap padding="3">
<Row display={{ base: 'flex', lg: 'none' }} alignItems="center" gap padding="3">
<MobileNav />
</Row>
<Column display={{ xs: 'none', lg: 'flex' }}>
<Column display={{ base: 'none', lg: 'flex' }}>
<SideNav />
</Column>
<Column alignItems="center" overflowY="auto" overflowX="hidden" position="relative">

View file

@ -1,6 +1,7 @@
import { Grid, IconLabel, NavMenu, NavMenuItem, Row, Text } from '@umami/react-zen';
import { Grid, Row, Text } from '@umami/react-zen';
import Link from 'next/link';
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
import { IconLabel } from '@/components/common/IconLabel';
import { useMessages, useNavigation } from '@/components/hooks';
import { Globe, Grid2x2, LinkIcon } from '@/components/icons';
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
@ -42,18 +43,16 @@ export function MobileNav() {
{({ close }) => {
return (
<>
<NavMenu padding="3" onItemClick={close} border="bottom">
<Row padding="3" onClick={close} border="bottom">
<NavButton />
{links.map(link => {
return (
<Link key={link.id} href={renderUrl(link.path)}>
<NavMenuItem>
<IconLabel icon={link.icon} label={link.label} />
</NavMenuItem>
<IconLabel icon={link.icon} label={link.label} />
</Link>
);
})}
</NavMenu>
</Row>
{websiteId && <WebsiteNav websiteId={websiteId} onItemClick={close} />}
{isAdmin && <AdminNav onItemClick={close} />}
{isSettings && <SettingsNav onItemClick={close} />}

View file

@ -1,22 +1,26 @@
import {
Box,
Button,
type ButtonProps,
Column,
Focusable,
Icon,
Row,
Sidebar,
SidebarHeader,
SidebarItem,
type SidebarProps,
SidebarSection,
Text,
ThemeButton,
Tooltip,
TooltipTrigger,
} from '@umami/react-zen';
import Link from 'next/link';
import type { Key } from 'react';
import { IconLabel } from '@/components/common/IconLabel';
import { useGlobalState, useMessages, useNavigation } from '@/components/hooks';
import { Globe, Grid2x2, LayoutDashboard, LinkIcon, PanelLeft } from '@/components/icons';
import { LanguageButton } from '@/components/input/LanguageButton';
import { NavButton } from '@/components/input/NavButton';
import { PanelButton } from '@/components/input/PanelButton';
import { Logo } from '@/components/svg';
export function SideNav(props: SidebarProps) {
export function SideNav(props: any) {
const { formatMessage, labels } = useMessages();
const { pathname, renderUrl, websiteId, router } = useNavigation();
const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed');
@ -55,39 +59,76 @@ export function SideNav(props: SidebarProps) {
};
return (
<Sidebar {...props} isCollapsed={isCollapsed || hasNav} backgroundColor>
<SidebarSection onClick={() => setIsCollapsed(false)}>
<SidebarHeader
label="umami"
icon={isCollapsed && !hasNav ? <PanelLeft /> : <Logo />}
style={{ maxHeight: 40 }}
>
{!isCollapsed && !hasNav && <PanelButton />}
</SidebarHeader>
</SidebarSection>
<SidebarSection paddingTop="0" paddingBottom="0" justifyContent="center">
<NavButton showText={!hasNav && !isCollapsed} onAction={handleSelect} />
</SidebarSection>
<SidebarSection flexGrow={1}>
{links.map(({ id, path, label, icon }) => {
return (
<Link key={id} href={renderUrl(path, false)} role="button">
<SidebarItem
label={label}
icon={icon}
isSelected={pathname.includes(path)}
role="button"
/>
</Link>
);
})}
</SidebarSection>
<SidebarSection justifyContent="flex-start">
<Row wrap="wrap">
<LanguageButton />
<ThemeButton />
<Column
{...props}
backgroundColor="surface-base"
justifyContent="space-between"
border
borderRadius
paddingX="2"
height="100%"
margin="2"
style={{
width: isCollapsed ? '55px' : '240px',
transition: 'width 0.2s ease-in-out',
}}
>
<Column>
<Row alignItems="center" justifyContent="space-between" height="60px">
<Row paddingX="3" alignItems="center" justifyContent="space-between" flexGrow={1}>
{!isCollapsed && (
<IconLabel icon={<Logo />}>
<Text weight="bold">umami</Text>
</IconLabel>
)}
<PanelButton />
</Row>
</Row>
</SidebarSection>
</Sidebar>
<Row marginBottom="4">
<NavButton showText={!hasNav && !isCollapsed} onAction={handleSelect} />
</Row>
<Column gap="2">
{links.map(({ id, path, label, icon }) => {
return (
<Link key={id} href={renderUrl(path, false)} role="button">
<TooltipTrigger isDisabled={!isCollapsed} delay={0}>
<Focusable>
<Row
alignItems="center"
hover={{ backgroundColor: 'surface-sunken' }}
borderRadius
minHeight="40px"
>
<IconLabel icon={icon} label={isCollapsed ? '' : label} padding />
</Row>
</Focusable>
<Tooltip placement="right">{label}</Tooltip>
</TooltipTrigger>
</Link>
);
})}
</Column>
</Column>
<Row alignItems="center" justifyContent="center" wrap="wrap" marginBottom="4" gap>
<LanguageButton />
<ThemeButton />
</Row>
</Column>
);
}
const PanelButton = (props: ButtonProps) => {
const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed');
return (
<Button
onPress={() => setIsCollapsed(!isCollapsed)}
variant="zero"
{...props}
style={{ padding: 0 }}
>
<Icon strokeColor="muted">
<PanelLeft />
</Icon>
</Button>
);
};

View file

@ -1,5 +1,6 @@
'use client';
import { Column, IconLabel } from '@umami/react-zen';
import { Column } from '@umami/react-zen';
import { IconLabel } from '@/components/common/IconLabel';
import { LinkButton } from '@/components/common/LinkButton';
import { PageBody } from '@/components/common/PageBody';
import { PageHeader } from '@/components/common/PageHeader';

View file

@ -1,4 +1,4 @@
import { IconLabel } from '@umami/react-zen';
import { IconLabel } from '@/components/common/IconLabel';
import { LinkButton } from '@/components/common/LinkButton';
import { PageHeader } from '@/components/common/PageHeader';
import { useLink, useMessages, useSlug } from '@/components/hooks';

View file

@ -1,4 +1,4 @@
import { IconLabel } from '@umami/react-zen';
import { IconLabel } from '@/components/common/IconLabel';
import { LinkButton } from '@/components/common/LinkButton';
import { PageHeader } from '@/components/common/PageHeader';
import { useMessages, usePixel, useSlug } from '@/components/hooks';

View file

@ -4,10 +4,10 @@ import {
FormButtons,
FormField,
FormSubmitButton,
IconLabel,
Row,
TextField,
} from '@umami/react-zen';
import { IconLabel } from '@/components/common/IconLabel';
import { useMessages, useTeam, useUpdateQuery } from '@/components/hooks';
import { RefreshCw } from '@/components/icons';
import { getRandomChars } from '@/lib/generate';

View file

@ -1,6 +1,7 @@
import { IconLabel, Row } from '@umami/react-zen';
import { Row } from '@umami/react-zen';
import { WebsiteShareForm } from '@/app/(main)/websites/[websiteId]/settings/WebsiteShareForm';
import { Favicon } from '@/components/common/Favicon';
import { IconLabel } from '@/components/common/IconLabel';
import { LinkButton } from '@/components/common/LinkButton';
import { PageHeader } from '@/components/common/PageHeader';
import { useMessages, useNavigation, useWebsite } from '@/components/hooks';

View file

@ -161,7 +161,7 @@ export function WebsiteNav({
.find(({ path }) => path && pathname.endsWith(path.split('?')[0]))?.id;
return (
<Column padding="3" position="sticky" top="0" gap>
<Column padding="2" position="sticky" top="0" gap backgroundColor="transparent">
<WebsiteSelect
websiteId={websiteId}
teamId={teamId}

View file

@ -6,7 +6,6 @@ import {
Dialog,
DialogTrigger,
Icon,
IconLabel,
Popover,
Row,
Text,
@ -14,6 +13,7 @@ import {
import Link from 'next/link';
import { Avatar } from '@/components/common/Avatar';
import { DateDistance } from '@/components/common/DateDistance';
import { IconLabel } from '@/components/common/IconLabel';
import { TypeIcon } from '@/components/common/TypeIcon';
import { useFormat, useMessages, useNavigation } from '@/components/hooks';
import { Eye, FileText } from '@/components/icons';

View file

@ -1,5 +1,5 @@
import { IconLabel } from '@umami/react-zen';
import { useCallback } from 'react';
import { IconLabel } from '@/components/common/IconLabel';
import { TypeIcon } from '@/components/common/TypeIcon';
import { useCountryNames, useLocale, useMessages } from '@/components/hooks';
import { ListTable } from '@/components/metrics/ListTable';

View file

@ -1,4 +1,4 @@
import { Column, Heading, IconLabel, Row, SearchField, Text } from '@umami/react-zen';
import { Column, Heading, Row, SearchField, Text } from '@umami/react-zen';
import Link from 'next/link';
import { useMemo, useState } from 'react';
import { FixedSizeList } from 'react-window';
@ -6,6 +6,7 @@ import { SessionModal } from '@/app/(main)/websites/[websiteId]/sessions/Session
import { useFormat } from '@/components//hooks/useFormat';
import { Avatar } from '@/components/common/Avatar';
import { Empty } from '@/components/common/Empty';
import { IconLabel } from '@/components/common/IconLabel';
import {
useCountryNames,
useLocale,

View file

@ -1,5 +1,6 @@
import { IconLabel, Row } from '@umami/react-zen';
import { Row } from '@umami/react-zen';
import Link from 'next/link';
import { IconLabel } from '@/components/common/IconLabel';
import { PageHeader } from '@/components/common/PageHeader';
import { useMessages, useNavigation, useWebsite } from '@/components/hooks';
import { ArrowLeft, Globe } from '@/components/icons';

View file

@ -4,7 +4,6 @@ import {
Form,
FormButtons,
FormSubmitButton,
IconLabel,
Label,
Row,
Switch,
@ -12,6 +11,7 @@ import {
} from '@umami/react-zen';
import { RefreshCcw } from 'lucide-react';
import { useState } from 'react';
import { IconLabel } from '@/components/common/IconLabel';
import { useConfig, useMessages, useUpdateQuery } from '@/components/hooks';
import { getRandomChars } from '@/lib/generate';

View file

@ -1,13 +1,12 @@
:root {
--font-family: var(--font-inter), sans-serif;
--text-primary: oklch(68.5% 0.169 237.323);
}
html,
body {
font-family: var(--font-family), sans-serif;
color: var(--text-base);
font-size: 14px;
color: var(--text-primary);
background-color: var(--surface-raised);
width: 100%;
min-height: 100vh;

View file

@ -2,8 +2,8 @@ import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import { Suspense } from 'react';
import { Providers } from './Providers';
import './global.css';
import '@umami/react-zen/styles.full.css';
import './global.css';
const inter = Inter({
subsets: ['latin'],

View file

@ -0,0 +1,32 @@
import { Icon, type IconProps, Row, type RowProps, Text, type TextProps } from '@umami/react-zen';
import type { ReactNode } from 'react';
interface IconLabelProps extends RowProps {
icon: ReactNode;
label?: ReactNode;
weight?: TextProps['weight'];
iconProps?: Partial<IconProps>;
labelProps?: Partial<TextProps>;
}
export function IconLabel({
icon,
label,
weight,
iconProps,
labelProps,
children,
...props
}: IconLabelProps) {
return (
<Row alignItems="center" gap="2" {...props}>
<Icon {...iconProps}>{icon}</Icon>
{label && (
<Text weight={weight} {...labelProps}>
{label}
</Text>
)}
{children}
</Row>
);
}

View file

@ -1,14 +1,6 @@
import {
Column,
Heading,
IconLabel,
NavMenu,
NavMenuGroup,
NavMenuItem,
type NavMenuProps,
Row,
} from '@umami/react-zen';
import { Column, Heading, Row, Text } from '@umami/react-zen';
import Link from 'next/link';
import { IconLabel } from '@/components/common/IconLabel';
interface SideMenuData {
id: string;
@ -22,7 +14,7 @@ interface SideMenuItems {
items: SideMenuData[];
}
export interface SideMenuProps extends NavMenuProps {
export interface SideMenuProps {
items: SideMenuItems[];
title?: string;
selectedKey?: string;
@ -42,39 +34,47 @@ export function SideMenu({
return (
<Link key={id} href={path}>
<NavMenuItem isSelected={isSelected}>
<IconLabel icon={icon}>{label}</IconLabel>
</NavMenuItem>
<Row padding borderRadius hover={{ backgroundColor: 'surface-raised' }}>
<IconLabel icon={icon}>
<Text weight={isSelected ? 'bold' : 'normal'}>{label}</Text>
</IconLabel>
</Row>
</Link>
);
});
};
return (
<Column gap overflowY="auto" justifyContent="space-between" position="sticky" top="20px">
<Column
gap
overflowY="auto"
justifyContent="space-between"
position="sticky"
backgroundColor="surface-base"
>
{title && (
<Row padding>
<Heading size="1">{title}</Heading>
<Heading size="sm">{title}</Heading>
</Row>
)}
<NavMenu gap="6" {...props}>
<Column gap="6" {...props}>
{items?.map(({ label, items }, index) => {
if (label) {
return (
<NavMenuGroup
<Column
title={label}
key={`${label}${index}`}
gap="1"
allowMinimize={allowMinimize}
marginBottom="3"
minHeight="40px"
>
{renderItems(items)}
</NavMenuGroup>
</Column>
);
}
return null;
})}
</NavMenu>
</Column>
</Column>
);
}

View file

@ -4,10 +4,10 @@ import {
Dialog,
type DialogProps,
DialogTrigger,
IconLabel,
Modal,
} from '@umami/react-zen';
import type { CSSProperties, ReactNode } from 'react';
import { IconLabel } from '@/components/common/IconLabel';
import { useMobile } from '@/components/hooks';
export interface DialogButtonProps extends Omit<ButtonProps, 'children'> {

View file

@ -14,12 +14,12 @@ export function LanguageButton() {
return (
<MenuTrigger key="language">
<Button variant="quiet">
<Icon>
<Icon color="primary">
<Globe />
</Icon>
</Button>
<Popover placement="bottom end">
<Dialog variant="menu">
<Dialog>
<Grid columns="repeat(3, minmax(200px, 1fr))" overflow="hidden">
{items.map(({ value, label }) => {
return (

View file

@ -1,7 +1,6 @@
import {
Column,
Icon,
IconLabel,
Menu,
MenuItem,
MenuSection,
@ -15,6 +14,7 @@ import {
} from '@umami/react-zen';
import { ArrowRight } from 'lucide-react';
import type { Key } from 'react';
import { IconLabel } from '@/components/common/IconLabel';
import {
useConfig,
useLoginQuery,
@ -77,8 +77,8 @@ export function NavButton({ showText = true }: TeamsButtonProps) {
padding
border
borderRadius
shadow="1"
maxHeight="40px"
shadow="sm"
minHeight="40px"
role="button"
style={{ cursor: 'pointer', textWrap: 'nowrap', overflow: 'hidden', outline: 'none' }}
>

View file

@ -1,5 +1,6 @@
import { IconLabel, List, ListItem } from '@umami/react-zen';
import { List, ListItem } from '@umami/react-zen';
import { Empty } from '@/components/common/Empty';
import { IconLabel } from '@/components/common/IconLabel';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { useWebsiteSegmentsQuery } from '@/components/hooks';
import { ChartPie, UserPlus } from '@/components/icons';

View file

@ -111,14 +111,7 @@ const AnimatedRow = ({
});
return (
<Grid
columns="1fr 50px 50px"
paddingLeft="2"
alignItems="center"
hoverBackgroundColor="2"
borderRadius
gap
>
<Grid columns="1fr 50px 50px" paddingLeft="2" alignItems="center" borderRadius gap>
<Row alignItems="center">
<Text truncate={true} style={{ maxWidth: isPhone ? '200px' : '400px' }}>
{label}