From 8484fd26e149b37b8199ee61f8216918b0144e38 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 5 Feb 2026 19:46:16 -0800 Subject: [PATCH] Code cleanup. --- CLAUDE.md | 4 + next.config.ts | 1 + package.json | 4 +- pnpm-lock.yaml | 64 +-------- src/app/(main)/App.tsx | 11 +- src/app/(main)/MobileNav.tsx | 11 +- src/app/(main)/SideNav.tsx | 121 ++++++++++++------ src/app/(main)/boards/BoardsPage.tsx | 3 +- src/app/(main)/links/[linkId]/LinkHeader.tsx | 2 +- .../(main)/pixels/[pixelId]/PixelHeader.tsx | 2 +- .../(main)/teams/[teamId]/TeamEditForm.tsx | 2 +- .../websites/[websiteId]/WebsiteHeader.tsx | 3 +- .../websites/[websiteId]/WebsiteNav.tsx | 2 +- .../[websiteId]/events/EventsTable.tsx | 2 +- .../realtime/RealtimeCountries.tsx | 2 +- .../[websiteId]/realtime/RealtimeLog.tsx | 3 +- .../settings/WebsiteSettingsHeader.tsx | 3 +- .../[websiteId]/settings/WebsiteShareForm.tsx | 2 +- src/app/global.css | 3 +- src/app/layout.tsx | 2 +- src/components/common/IconLabel.tsx | 32 +++++ src/components/common/SideMenu.tsx | 42 +++--- src/components/input/DialogButton.tsx | 2 +- src/components/input/LanguageButton.tsx | 4 +- src/components/input/NavButton.tsx | 6 +- src/components/input/SegmentFilters.tsx | 3 +- src/components/metrics/ListTable.tsx | 9 +- 27 files changed, 183 insertions(+), 162 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a7ad68f90..89b043559 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,6 +6,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Umami is a privacy-focused web analytics platform built with Next.js 15, React 19, and TypeScript. It serves as an alternative to Google Analytics, storing data in PostgreSQL (primary) with optional ClickHouse for time-series analytics. +## Development + +Assume a dev server is always running on port 3001. Do not start the dev server. + ## Common Commands ```bash diff --git a/next.config.ts b/next.config.ts index 1a4e2e0ec..c085bc124 100644 --- a/next.config.ts +++ b/next.config.ts @@ -182,6 +182,7 @@ export default { typescript: { ignoreBuildErrors: true, }, + devIndicators: false, async headers() { return headers; }, diff --git a/package.json b/package.json index ebbeea649..a855ecf27 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "type": "module", "scripts": { - "dev": "next dev -p 3001 --turbo", + "dev": "next dev -p 4444 --turbo", "build": "npm-run-all check-env build-db check-db build-tracker build-geo build-app", "start": "next start", "build-docker": "npm-run-all build-db build-tracker build-geo build-app", @@ -72,7 +72,7 @@ "@react-spring/web": "^10.0.3", "@svgr/cli": "^8.1.0", "@tanstack/react-query": "^5.90.17", - "@umami/react-zen": "^0.231.0", + "@umami/react-zen": "^0.239.0", "@umami/redis-client": "^0.30.0", "bcryptjs": "^3.0.2", "chalk": "^5.6.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9bdc058e2..f79688f3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,8 +42,8 @@ importers: specifier: ^5.90.17 version: 5.90.17(react@19.2.3) '@umami/react-zen': - specifier: ^0.231.0 - version: 0.231.0(@types/react@19.2.8)(immer@10.2.0)(react-aria-components@1.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.3)) + specifier: ^0.239.0 + version: 0.239.0(@types/react@19.2.8)(immer@10.2.0)(react-aria-components@1.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.3)) '@umami/redis-client': specifier: ^0.30.0 version: 0.30.0 @@ -331,44 +331,7 @@ importers: specifier: ^5.9.3 version: 5.9.3 - dist: - dependencies: - chart.js: - specifier: ^4.5.0 - version: 4.5.1 - chartjs-adapter-date-fns: - specifier: ^3.0.0 - version: 3.0.0(chart.js@4.5.1)(date-fns@2.30.0) - colord: - specifier: ^2.9.2 - version: 2.9.3 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.3 - lucide-react: - specifier: ^0.542.0 - version: 0.542.0(react@19.2.3) - pure-rand: - specifier: ^7.0.1 - version: 7.0.1 - react-simple-maps: - specifier: ^2.3.0 - version: 2.3.0(prop-types@15.8.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-use-measure: - specifier: ^2.0.4 - version: 2.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-window: - specifier: ^1.8.6 - version: 1.8.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - serialize-error: - specifier: ^12.0.0 - version: 12.0.0 - thenby: - specifier: ^1.3.4 - version: 1.3.4 - uuid: - specifier: ^11.1.0 - version: 11.1.0 + dist: {} packages: @@ -2882,8 +2845,8 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@umami/react-zen@0.231.0': - resolution: {integrity: sha512-Pn3UqMqYkeiL0E8R8EwExth5UylPI6HhgciuFc9Nfkk0S7MWBbOAJ1s3aRuiNVa3eR/r0vw6h4k/5W2fQ7MvTA==} + '@umami/react-zen@0.239.0': + resolution: {integrity: sha512-6CSPYPnpCnau+Ie8nyC1BDVFrNoes5XOuy+b6hb6htDTvqTKIB8nvky0hJHe6kLz6Kp7xTbknjP+PQ5EnLBC6w==} peerDependencies: react: ^18.0.0 || ^19.0.0 react-aria-components: ^1.0.0 @@ -5006,11 +4969,6 @@ packages: resolution: {integrity: sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} - lucide-react@0.542.0: - resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lucide-react@0.543.0: resolution: {integrity: sha512-fpVfuOQO0V3HBaOA1stIiP/A2fPCXHIleRZL16Mx3HmjTYwNSbimhnFBygs2CAfU1geexMX5ItUcWBGUaqw5CA==} peerDependencies: @@ -7010,10 +6968,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - uuid@13.0.0: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true @@ -10071,7 +10025,7 @@ snapshots: '@types/node': 24.10.8 optional: true - '@umami/react-zen@0.231.0(@types/react@19.2.8)(immer@10.2.0)(react-aria-components@1.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.3))': + '@umami/react-zen@0.239.0(@types/react@19.2.8)(immer@10.2.0)(react-aria-components@1.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.3))': dependencies: '@internationalized/date': 3.10.1 '@react-aria/focus': 3.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -12645,10 +12599,6 @@ snapshots: lru.min@1.1.3: {} - lucide-react@0.542.0(react@19.2.3): - dependencies: - react: 19.2.3 - lucide-react@0.543.0(react@19.2.3): dependencies: react: 19.2.3 @@ -14846,8 +14796,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@11.1.0: {} - uuid@13.0.0: {} uuid@8.3.2: {} diff --git a/src/app/(main)/App.tsx b/src/app/(main)/App.tsx index eada680a6..2e236db0f 100644 --- a/src/app/(main)/App.tsx +++ b/src/app/(main)/App.tsx @@ -39,15 +39,14 @@ export function App({ children }) { return ( - + - + diff --git a/src/app/(main)/MobileNav.tsx b/src/app/(main)/MobileNav.tsx index aaa25846c..de12b14c3 100644 --- a/src/app/(main)/MobileNav.tsx +++ b/src/app/(main)/MobileNav.tsx @@ -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 ( <> - + {links.map(link => { return ( - - - + ); })} - + {websiteId && } {isAdmin && } {isSettings && } diff --git a/src/app/(main)/SideNav.tsx b/src/app/(main)/SideNav.tsx index 73d598990..9d9c72bd9 100644 --- a/src/app/(main)/SideNav.tsx +++ b/src/app/(main)/SideNav.tsx @@ -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 ( - - setIsCollapsed(false)}> - : } - style={{ maxHeight: 40 }} - > - {!isCollapsed && !hasNav && } - - - - - - - {links.map(({ id, path, label, icon }) => { - return ( - - - - ); - })} - - - - - + + + + + {!isCollapsed && ( + }> + umami + + )} + + - - + + + + + {links.map(({ id, path, label, icon }) => { + return ( + + + + + + + + {label} + + + ); + })} + + + + + + + ); } + +const PanelButton = (props: ButtonProps) => { + const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed'); + return ( + + ); +}; diff --git a/src/app/(main)/boards/BoardsPage.tsx b/src/app/(main)/boards/BoardsPage.tsx index cca1939fc..126ac494c 100644 --- a/src/app/(main)/boards/BoardsPage.tsx +++ b/src/app/(main)/boards/BoardsPage.tsx @@ -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'; diff --git a/src/app/(main)/links/[linkId]/LinkHeader.tsx b/src/app/(main)/links/[linkId]/LinkHeader.tsx index a84a6260f..da1d75939 100644 --- a/src/app/(main)/links/[linkId]/LinkHeader.tsx +++ b/src/app/(main)/links/[linkId]/LinkHeader.tsx @@ -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'; diff --git a/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx b/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx index c771687f8..3fab6b73f 100644 --- a/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx +++ b/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx @@ -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'; diff --git a/src/app/(main)/teams/[teamId]/TeamEditForm.tsx b/src/app/(main)/teams/[teamId]/TeamEditForm.tsx index 74e038f6a..56789f9ca 100644 --- a/src/app/(main)/teams/[teamId]/TeamEditForm.tsx +++ b/src/app/(main)/teams/[teamId]/TeamEditForm.tsx @@ -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'; diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index da5db0f5f..f836fad1d 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -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'; diff --git a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx index ad05b7065..235d65822 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx @@ -161,7 +161,7 @@ export function WebsiteNav({ .find(({ path }) => path && pathname.endsWith(path.split('?')[0]))?.id; return ( - + ; + labelProps?: Partial; +} + +export function IconLabel({ + icon, + label, + weight, + iconProps, + labelProps, + children, + ...props +}: IconLabelProps) { + return ( + + {icon} + {label && ( + + {label} + + )} + {children} + + ); +} diff --git a/src/components/common/SideMenu.tsx b/src/components/common/SideMenu.tsx index 92ff798a0..6c7f7fc11 100644 --- a/src/components/common/SideMenu.tsx +++ b/src/components/common/SideMenu.tsx @@ -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 ( - - {label} - + + + {label} + + ); }); }; return ( - + {title && ( - {title} + {title} )} - + {items?.map(({ label, items }, index) => { if (label) { return ( - {renderItems(items)} - + ); } return null; })} - + ); } diff --git a/src/components/input/DialogButton.tsx b/src/components/input/DialogButton.tsx index 7527226d0..cfca45631 100644 --- a/src/components/input/DialogButton.tsx +++ b/src/components/input/DialogButton.tsx @@ -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 { diff --git a/src/components/input/LanguageButton.tsx b/src/components/input/LanguageButton.tsx index ac43dcb6b..9ac2ec3fc 100644 --- a/src/components/input/LanguageButton.tsx +++ b/src/components/input/LanguageButton.tsx @@ -14,12 +14,12 @@ export function LanguageButton() { return ( - + {items.map(({ value, label }) => { return ( diff --git a/src/components/input/NavButton.tsx b/src/components/input/NavButton.tsx index ab77ef062..c5eb1e5ba 100644 --- a/src/components/input/NavButton.tsx +++ b/src/components/input/NavButton.tsx @@ -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' }} > diff --git a/src/components/input/SegmentFilters.tsx b/src/components/input/SegmentFilters.tsx index f03a1dead..562537d0b 100644 --- a/src/components/input/SegmentFilters.tsx +++ b/src/components/input/SegmentFilters.tsx @@ -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'; diff --git a/src/components/metrics/ListTable.tsx b/src/components/metrics/ListTable.tsx index f233bfe7e..4babd50b7 100644 --- a/src/components/metrics/ListTable.tsx +++ b/src/components/metrics/ListTable.tsx @@ -111,14 +111,7 @@ const AnimatedRow = ({ }); return ( - + {label}