Added Panel component. New color scheme.

This commit is contained in:
Mike Cao 2025-03-20 23:12:56 -07:00
parent a7dad20d8a
commit 5d2c1e27c2
13 changed files with 64 additions and 64 deletions

View file

@ -194,9 +194,6 @@
"esbuild", "esbuild",
"prisma", "prisma",
"sharp" "sharp"
], ]
"overrides": {
"@umami/react-zen": "link:C:/Users/mike/AppData/Local/pnpm/global/5/node_modules/@umami/react-zen"
}
} }
} }

4
pnpm-lock.yaml generated
View file

@ -5,7 +5,6 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides: overrides:
react-zen: link:C:/Users/mike/AppData/Local/pnpm/global/5/node_modules/@umami/react-zen
'@umami/react-zen': link:C:/Users/mike/AppData/Local/pnpm/global/5/node_modules/@umami/react-zen '@umami/react-zen': link:C:/Users/mike/AppData/Local/pnpm/global/5/node_modules/@umami/react-zen
importers: importers:
@ -174,9 +173,6 @@ importers:
react-window: react-window:
specifier: ^1.8.11 specifier: ^1.8.11
version: 1.8.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 1.8.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-zen:
specifier: link:C:/Users/mike/AppData/Local/pnpm/global/5/node_modules/@umami/react-zen
version: link:C:/Users/mike/AppData/Local/pnpm/global/5/node_modules/@umami/react-zen
request-ip: request-ip:
specifier: ^3.3.0 specifier: ^3.3.0
version: 3.3.0 version: 3.3.0

View file

@ -30,25 +30,18 @@ export function App({ children }) {
} }
return ( return (
<Grid <Grid height="100vh" width="100%" columns="auto 1fr" rows="auto 1fr" overflow="hidden">
height="100vh"
width="100%"
columns="auto 1fr"
rows="auto 1fr"
overflow="hidden"
backgroundColor="2"
>
<Nav gridColumn="1 / 2" gridRow="1 / 3" /> <Nav gridColumn="1 / 2" gridRow="1 / 3" />
<NavBar gridColumn="2 / 3" gridRow="1 / 2" /> <NavBar gridColumn="2 / 3" gridRow="1 / 2" />
<Column alignItems="center" overflow="scroll"> <Column gridColumn="2 / 3" gridRow="2 / 3" alignItems="center" overflow="auto">
<Page> <Page>
<UpdateNotice user={user} config={config} />
{children} {children}
{process.env.NODE_ENV === 'production' && !pathname.includes('/share/') && ( {process.env.NODE_ENV === 'production' && !pathname.includes('/share/') && (
<Script src={`${process.env.basePath || ''}/telemetry.js`} /> <Script src={`${process.env.basePath || ''}/telemetry.js`} />
)} )}
</Page> </Page>
</Column> </Column>
<UpdateNotice user={user} config={config} />
</Grid> </Grid>
); );
} }

View file

@ -11,11 +11,10 @@ import {
} from '@umami/react-zen'; } from '@umami/react-zen';
import { Lucide, Icons } from '@/components/icons'; import { Lucide, Icons } from '@/components/icons';
import { useMessages, useTeamUrl } from '@/components/hooks'; import { useMessages, useTeamUrl } from '@/components/hooks';
import type { SideNavProps } from '@umami/react-zen/SideNav';
export function Nav(props: SideNavProps) { export function Nav(props: any) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { renderTeamUrl } = useTeamUrl(); const { renderTeamUrl, pathname } = useTeamUrl();
const [isCollapsed, setCollapsed] = useState(false); const [isCollapsed, setCollapsed] = useState(false);
const links = [ const links = [
@ -47,7 +46,7 @@ export function Nav(props: SideNavProps) {
].filter(n => n); ].filter(n => n);
return ( return (
<SideNav {...props} isCollapsed={isCollapsed} variant="3" showBorder={false}> <SideNav {...props} isCollapsed={isCollapsed} variant="3" showBorder={true}>
<SideNavSection> <SideNavSection>
<SideNavHeader label="umami" icon={<Icons.Logo />} /> <SideNavHeader label="umami" icon={<Icons.Logo />} />
</SideNavSection> </SideNavSection>
@ -55,7 +54,7 @@ export function Nav(props: SideNavProps) {
{links.map(({ href, label, icon }) => { {links.map(({ href, label, icon }) => {
return ( return (
<Link key={href} href={href}> <Link key={href} href={href}>
<SideNavItem label={label} icon={icon} /> <SideNavItem label={label} icon={icon} isSelected={pathname.startsWith(href)} />
</Link> </Link>
); );
})} })}

View file

@ -2,10 +2,18 @@ import { ThemeButton, Row } from '@umami/react-zen';
import { LanguageButton } from '@/components/input/LanguageButton'; import { LanguageButton } from '@/components/input/LanguageButton';
import { ProfileButton } from '@/components/input/ProfileButton'; import { ProfileButton } from '@/components/input/ProfileButton';
import { TeamsButton } from '@/components/input/TeamsButton'; import { TeamsButton } from '@/components/input/TeamsButton';
import type { RowProps } from '@umami/react-zen/Row';
export function NavBar() { export function NavBar(props: RowProps) {
return ( return (
<Row justifyContent="space-between" alignItems="center" paddingY="3"> <Row
{...props}
justifyContent="space-between"
alignItems="center"
paddingY="3"
paddingX="3"
paddingRight="5"
>
<TeamsButton /> <TeamsButton />
<Row justifyContent="flex-end"> <Row justifyContent="flex-end">
<ThemeButton /> <ThemeButton />

View file

@ -8,6 +8,7 @@ import { Column, Tabs, TabList, Tab, TabPanel } from '@umami/react-zen';
import { TeamLeaveButton } from '@/app/(main)/settings/teams/TeamLeaveButton'; import { TeamLeaveButton } from '@/app/(main)/settings/teams/TeamLeaveButton';
import { TeamManage } from './TeamManage'; import { TeamManage } from './TeamManage';
import { TeamEditForm } from './TeamEditForm'; import { TeamEditForm } from './TeamEditForm';
import { Panel } from '@/components/layout/Panel';
export function TeamDetails({ teamId }: { teamId: string }) { export function TeamDetails({ teamId }: { teamId: string }) {
const team = useContext(TeamContext); const team = useContext(TeamContext);
@ -30,18 +31,20 @@ export function TeamDetails({ teamId }: { teamId: string }) {
<PageHeader title={team?.name} icon={<Icons.Users />}> <PageHeader title={team?.name} icon={<Icons.Users />}>
{!isTeamOwner && <TeamLeaveButton teamId={team.id} teamName={team.name} />} {!isTeamOwner && <TeamLeaveButton teamId={team.id} teamName={team.name} />}
</PageHeader> </PageHeader>
<Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}> <Panel>
<TabList> <Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}>
<Tab id="details">{formatMessage(labels.details)}</Tab> <TabList>
{isTeamOwner && <Tab id="manage">{formatMessage(labels.manage)}</Tab>} <Tab id="details">{formatMessage(labels.details)}</Tab>
</TabList> {isTeamOwner && <Tab id="manage">{formatMessage(labels.manage)}</Tab>}
<TabPanel id="details"> </TabList>
<TeamEditForm teamId={teamId} allowEdit={canEdit} /> <TabPanel id="details">
</TabPanel> <TeamEditForm teamId={teamId} allowEdit={canEdit} />
<TabPanel id="manage"> </TabPanel>
<TeamManage teamId={teamId} /> <TabPanel id="manage">
</TabPanel> <TeamManage teamId={teamId} />
</Tabs> </TabPanel>
</Tabs>
</Panel>
</Column> </Column>
); );
} }

View file

@ -3,6 +3,7 @@ import { Loading, SearchField, Row, Column } from '@umami/react-zen';
import { useMessages, useNavigation } from '@/components/hooks'; import { useMessages, useNavigation } from '@/components/hooks';
import { Empty } from '@/components/common/Empty'; import { Empty } from '@/components/common/Empty';
import { Pager } from '@/components/common/Pager'; import { Pager } from '@/components/common/Pager';
import { Panel } from '@/components/layout/Panel';
import { LoadingPanel } from '@/components/common/LoadingPanel'; import { LoadingPanel } from '@/components/common/LoadingPanel';
import { PagedQueryResult } from '@/lib/types'; import { PagedQueryResult } from '@/lib/types';
@ -59,12 +60,14 @@ export function DataGrid({
</Row> </Row>
)} )}
<LoadingPanel data={data} isLoading={isLoading} isFetched={isFetched} error={error}> <LoadingPanel data={data} isLoading={isLoading} isFetched={isFetched} error={error}>
<Column> <Panel>
{hasData ? (typeof children === 'function' ? children(result) : children) : null} <Column>
{isLoading && <Loading position="page" />} {hasData ? (typeof children === 'function' ? children(result) : children) : null}
{!isLoading && !hasData && !search && (renderEmpty ? renderEmpty() : <Empty />)} {isLoading && <Loading position="page" />}
{!isLoading && noResults && <Empty message={formatMessage(messages.noResultsFound)} />} {!isLoading && !hasData && !search && (renderEmpty ? renderEmpty() : <Empty />)}
</Column> {!isLoading && noResults && <Empty message={formatMessage(messages.noResultsFound)} />}
</Column>
</Panel>
{allowPaging && hasData && ( {allowPaging && hasData && (
<Row marginTop="6"> <Row marginTop="6">
<Pager page={page} pageSize={pageSize} count={count} onPageChange={handlePageChange} /> <Pager page={page} pageSize={pageSize} count={count} onPageChange={handlePageChange} />

View file

@ -1,9 +1,6 @@
import { usePathname } from 'next/navigation'; import { usePathname } from 'next/navigation';
export function useTeamUrl(): { export function useTeamUrl() {
teamId?: string;
renderTeamUrl: (url: string) => string;
} {
const pathname = usePathname(); const pathname = usePathname();
const [, teamId] = pathname.match(/^\/teams\/([a-f0-9-]+)/) || []; const [, teamId] = pathname.match(/^\/teams\/([a-f0-9-]+)/) || [];
@ -11,5 +8,5 @@ export function useTeamUrl(): {
return teamId ? `/teams/${teamId}${url}` : url; return teamId ? `/teams/${teamId}${url}` : url;
} }
return { teamId, renderTeamUrl }; return { teamId, renderTeamUrl, pathname };
} }

View file

@ -35,12 +35,6 @@ export function Page({
maxWidth="1320px" maxWidth="1320px"
minHeight="600px" minHeight="600px"
margin="auto" margin="auto"
backgroundColor="1"
overflow="auto"
borderRadius="3"
borderSize="1"
paddingX="8"
paddingY="4"
> >
{children} {children}
</Column> </Column>

View file

@ -0,0 +1,6 @@
import { Box } from '@umami/react-zen';
import type { BoxProps } from '@umami/react-zen/Box';
export function Panel(props: BoxProps) {
return <Box padding="6" borderSize="1" borderRadius="3" backgroundColor="solid" {...props} />;
}

View file

@ -91,7 +91,7 @@ export function MetricsTable({
background: 'var(--background-color)', background: 'var(--background-color)',
border: '1px solid var(--border-color)', border: '1px solid var(--border-color)',
borderRadius: 'var(--border-radius)', borderRadius: 'var(--border-radius)',
padding: '10px', padding: '16px',
}} }}
> >
{error && <ErrorMessage />} {error && <ErrorMessage />}

View file

@ -2,6 +2,7 @@ import { useMemo } from 'react';
import { BarChart, BarChartProps } from '@/components/charts/BarChart'; import { BarChart, BarChartProps } from '@/components/charts/BarChart';
import { useLocale, useTheme, useMessages } from '@/components/hooks'; import { useLocale, useTheme, useMessages } from '@/components/hooks';
import { renderDateLabels } from '@/lib/charts'; import { renderDateLabels } from '@/lib/charts';
import { Panel } from '@/components/layout/Panel';
export interface PageviewsChartProps extends BarChartProps { export interface PageviewsChartProps extends BarChartProps {
data: { data: {
@ -76,13 +77,15 @@ export function PageviewsChart({
}, [data, locale]); }, [data, locale]);
return ( return (
<BarChart <Panel>
{...props} <BarChart
data={chartData} {...props}
unit={unit} data={chartData}
isLoading={isLoading} unit={unit}
isAllTime={isAllTime} isLoading={isLoading}
renderXLabel={renderDateLabels(unit, locale)} isAllTime={isAllTime}
/> renderXLabel={renderDateLabels(unit, locale)}
/>
</Panel>
); );
} }

View file

@ -3,9 +3,10 @@ body {
font-family: var(--font-family), sans-serif; font-family: var(--font-family), sans-serif;
color: var(--font-color); color: var(--font-color);
font-size: var(--font-size); font-size: var(--font-size);
background-color: var(--base-color-1); background-color: var(--base-color-2);
width: 100%; width: 100%;
min-height: 100vh; min-height: 100vh;
overflow: hidden;
} }
a, a,