Responsive updates.

This commit is contained in:
Mike Cao 2025-10-09 17:14:09 -07:00
parent e4ce7c9071
commit 8aa4192576
14 changed files with 976 additions and 917 deletions

View file

@ -78,7 +78,7 @@
"@react-spring/web": "^10.0.3", "@react-spring/web": "^10.0.3",
"@svgr/cli": "^8.1.0", "@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.90.2", "@tanstack/react-query": "^5.90.2",
"@umami/react-zen": "^0.187.0", "@umami/react-zen": "^0.189.0",
"@umami/redis-client": "^0.29.0", "@umami/redis-client": "^0.29.0",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
"chalk": "^5.6.2", "chalk": "^5.6.2",

1774
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -55,7 +55,7 @@ export function LinkPanels({ linkId }: { linkId: string }) {
</Panel> </Panel>
</GridRow> </GridRow>
<GridRow layout="two" {...rowProps}> <GridRow layout="two" {...rowProps}>
<Panel noPadding> <Panel padding="0">
<WorldMap websiteId={linkId} /> <WorldMap websiteId={linkId} />
</Panel> </Panel>
<Panel> <Panel>

View file

@ -55,7 +55,7 @@ export function PixelPanels({ pixelId }: { pixelId: string }) {
</Panel> </Panel>
</GridRow> </GridRow>
<GridRow layout="two" {...rowProps}> <GridRow layout="two" {...rowProps}>
<Panel noPadding> <Panel padding="0">
<WorldMap websiteId={pixelId} /> <WorldMap websiteId={pixelId} />
</Panel> </Panel>
<Panel> <Panel>

View file

@ -1,4 +1,4 @@
import { Column, Row } from '@umami/react-zen'; import { Column, Row, Grid } from '@umami/react-zen';
import { WebsiteFilterButton } from '@/components/input/WebsiteFilterButton'; import { WebsiteFilterButton } from '@/components/input/WebsiteFilterButton';
import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter'; import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter';
import { FilterBar } from '@/components/input/FilterBar'; import { FilterBar } from '@/components/input/FilterBar';
@ -22,12 +22,18 @@ export function WebsiteControls({
}) { }) {
return ( return (
<Column gap> <Column gap>
<Row alignItems="center" justifyContent="space-between" gap="3"> <Grid columns={{ xs: '1fr', md: 'auto 1fr' }} gap>
{allowFilter ? <WebsiteFilterButton websiteId={websiteId} /> : <div />} <Row alignItems="center" justifyContent="flex-end">
{allowDateFilter && <WebsiteDateFilter websiteId={websiteId} allowCompare={allowCompare} />} {allowFilter ? <WebsiteFilterButton websiteId={websiteId} /> : <div />}
{allowDownload && <ExportButton websiteId={websiteId} />} </Row>
{allowMonthFilter && <MonthFilter />} <Row alignItems="center" justifyContent="flex-end">
</Row> {allowDateFilter && (
<WebsiteDateFilter websiteId={websiteId} allowCompare={allowCompare} />
)}
{allowDownload && <ExportButton websiteId={websiteId} />}
{allowMonthFilter && <MonthFilter />}
</Row>
</Grid>
{allowFilter && <FilterBar websiteId={websiteId} />} {allowFilter && <FilterBar websiteId={websiteId} />}
</Column> </Column>
); );

View file

@ -22,7 +22,11 @@ export function WebsiteHeader({ showActions }: { showActions?: boolean }) {
<ActiveUsers websiteId={website.id} /> <ActiveUsers websiteId={website.id} />
{showActions && ( {showActions && (
<Row alignItems="center" gap> <Row
display={{ xs: 'none', sm: 'none', md: 'none', lg: 'flex', xl: 'flex' }}
alignItems="center"
gap
>
<ShareButton websiteId={website.id} shareId={website.shareId} /> <ShareButton websiteId={website.id} shareId={website.shareId} />
<LinkButton href={renderUrl(`/websites/${website.id}/settings`, false)}> <LinkButton href={renderUrl(`/websites/${website.id}/settings`, false)}>
<Icon> <Icon>

View file

@ -2,24 +2,23 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { Column, Grid } from '@umami/react-zen'; import { Column, Grid } from '@umami/react-zen';
import { WebsiteProvider } from '@/app/(main)/websites/WebsiteProvider'; import { WebsiteProvider } from '@/app/(main)/websites/WebsiteProvider';
import { useNavigation } from '@/components/hooks';
import { PageBody } from '@/components/common/PageBody'; import { PageBody } from '@/components/common/PageBody';
import { WebsiteHeader } from './WebsiteHeader'; import { WebsiteHeader } from './WebsiteHeader';
import { WebsiteNav } from './WebsiteNav'; import { WebsiteNav } from './WebsiteNav';
export function WebsiteLayout({ websiteId, children }: { websiteId: string; children: ReactNode }) { export function WebsiteLayout({ websiteId, children }: { websiteId: string; children: ReactNode }) {
const { pathname } = useNavigation();
const isSettings = pathname.endsWith('/settings');
return ( return (
<WebsiteProvider websiteId={websiteId}> <WebsiteProvider websiteId={websiteId}>
<Grid columns={isSettings ? '1fr' : 'auto 1fr'} width="100%" height="100%"> <Grid columns={{ xs: '1fr', lg: 'auto 1fr' }} width="100%" height="100%">
{!isSettings && ( <Column
<Column height="100%" border="right" backgroundColor marginRight="2"> display={{ xs: 'none', lg: 'flex' }}
<WebsiteNav websiteId={websiteId} /> height="100%"
</Column> border="right"
)} backgroundColor
marginRight="2"
>
<WebsiteNav websiteId={websiteId} />
</Column>
<PageBody gap> <PageBody gap>
<WebsiteHeader showActions /> <WebsiteHeader showActions />
<Column>{children}</Column> <Column>{children}</Column>

View file

@ -106,7 +106,7 @@ export function WebsitePanels({ websiteId }: { websiteId: string }) {
</GridRow> </GridRow>
<GridRow layout="two-one" {...rowProps}> <GridRow layout="two-one" {...rowProps}>
<Panel gridColumn="span 2" noPadding> <Panel gridColumn={{ xs: 'span 1', md: 'span 2' }} padding="0">
<WorldMap websiteId={websiteId} /> <WorldMap websiteId={websiteId} />
</Panel> </Panel>
@ -130,7 +130,7 @@ export function WebsitePanels({ websiteId }: { websiteId: string }) {
filterLink={false} filterLink={false}
/> />
</Panel> </Panel>
<Panel gridColumn="span 2"> <Panel gridColumn={{ xs: 'span 1', md: 'span 2' }}>
<EventsChart websiteId={websiteId} /> <EventsChart websiteId={websiteId} />
</Panel> </Panel>
</GridRow> </GridRow>

View file

@ -44,7 +44,7 @@ export function RealtimePage({ websiteId }: { websiteId: string }) {
<Panel> <Panel>
<RealtimeCountries data={countries} /> <RealtimeCountries data={countries} />
</Panel> </Panel>
<Panel gridColumn="span 2" noPadding> <Panel gridColumn="span 2" padding="0">
<WorldMap data={countries} /> <WorldMap data={countries} />
</Panel> </Panel>
</GridRow> </GridRow>

View file

@ -14,12 +14,11 @@ const LAYOUTS = {
md: 'repeat(auto-fill, minmax(360px, 1fr))', md: 'repeat(auto-fill, minmax(360px, 1fr))',
}, },
}, },
'one-two': { columns: { xs: '1fr', lg: 'repeat(3, 1fr)' } }, 'one-two': { columns: { xs: '1fr', md: 'repeat(3, 1fr)' } },
'two-one': { columns: { xs: '1fr', lg: 'repeat(3, 1fr)' } }, 'two-one': { columns: { xs: '1fr', md: 'repeat(3, 1fr)' } },
}; };
export function GridRow(props: { export function GridRow(props: {
[x: string]: any;
layout?: 'one' | 'two' | 'three' | 'one-two' | 'two-one' | 'compare'; layout?: 'one' | 'two' | 'three' | 'one-two' | 'two-one' | 'compare';
className?: string; className?: string;
children?: any; children?: any;

View file

@ -28,7 +28,14 @@ export function PageBody({
} }
return ( return (
<Column {...props} width="100%" paddingBottom="9" style={{ margin: '0 auto', maxWidth }}> <Column
{...props}
width="100%"
paddingBottom="9"
maxWidth={maxWidth}
paddingX="4"
style={{ margin: '0 auto' }}
>
{children} {children}
</Column> </Column>
); );

View file

@ -15,7 +15,6 @@ import { useMessages } from '@/components/hooks';
export interface PanelProps extends ColumnProps { export interface PanelProps extends ColumnProps {
title?: string; title?: string;
allowFullscreen?: boolean; allowFullscreen?: boolean;
noPadding?: boolean;
} }
const fullscreenStyles = { const fullscreenStyles = {
@ -28,14 +27,7 @@ const fullscreenStyles = {
zIndex: 9999, zIndex: 9999,
} as any; } as any;
export function Panel({ export function Panel({ title, allowFullscreen, style, children, ...props }: PanelProps) {
title,
allowFullscreen,
noPadding,
style,
children,
...props
}: PanelProps) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const [isFullscreen, setIsFullscreen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false);
@ -45,7 +37,7 @@ export function Panel({
return ( return (
<Column <Column
padding={!noPadding ? '6' : undefined} padding={{ xs: '3', md: '6' }}
border border
borderRadius="3" borderRadius="3"
backgroundColor backgroundColor

View file

@ -7,7 +7,7 @@ export interface MetricsBarProps extends GridProps {
export function MetricsBar({ children, ...props }: MetricsBarProps) { export function MetricsBar({ children, ...props }: MetricsBarProps) {
return ( return (
<Grid columns="repeat(auto-fit, minmax(200px, 1fr))" gap {...props}> <Grid columns="repeat(auto-fit, minmax(170px, 1fr))" gap {...props}>
{children} {children}
</Grid> </Grid>
); );

View file

@ -1,11 +1,11 @@
import { useEffect, useMemo } from 'react';
import { Icon, Text, Row, Grid } from '@umami/react-zen';
import { LinkButton } from '@/components/common/LinkButton'; import { LinkButton } from '@/components/common/LinkButton';
import { LoadingPanel } from '@/components/common/LoadingPanel'; import { LoadingPanel } from '@/components/common/LoadingPanel';
import { useMessages, useNavigation, useWebsiteMetricsQuery } from '@/components/hooks'; import { useMessages, useNavigation, useWebsiteMetricsQuery } from '@/components/hooks';
import { Maximize } from '@/components/icons'; import { Maximize } from '@/components/icons';
import { MetricLabel } from '@/components/metrics/MetricLabel'; import { MetricLabel } from '@/components/metrics/MetricLabel';
import { percentFilter } from '@/lib/filters'; import { percentFilter } from '@/lib/filters';
import { Icon, Row, Text } from '@umami/react-zen';
import { useEffect, useMemo } from 'react';
import { ListTable, ListTableProps } from './ListTable'; import { ListTable, ListTableProps } from './ListTable';
export interface MetricsTableProps extends ListTableProps { export interface MetricsTableProps extends ListTableProps {
@ -77,21 +77,19 @@ export function MetricsTable({
error={error} error={error}
minHeight="400px" minHeight="400px"
> >
<div style={{ display: 'grid', gridTemplateRows: '1fr auto', minHeight: '400px' }}> <Grid>
<div>{data && <ListTable {...props} data={filteredData} renderLabel={renderLabel} />}</div> {data && <ListTable {...props} data={filteredData} renderLabel={renderLabel} />}
<div> {showMore && limit && (
{showMore && limit && ( <Row justifyContent="center" alignItems="flex-end">
<Row justifyContent="center" alignItems="flex-end"> <LinkButton href={updateParams({ view: type })} variant="quiet">
<LinkButton href={updateParams({ view: type })} variant="quiet"> <Icon size="sm">
<Icon size="sm"> <Maximize />
<Maximize /> </Icon>
</Icon> <Text>{formatMessage(labels.more)}</Text>
<Text>{formatMessage(labels.more)}</Text> </LinkButton>
</LinkButton> </Row>
</Row> )}
)} </Grid>
</div>
</div>
</LoadingPanel> </LoadingPanel>
); );
} }