Fixed retention report showing wrong dates. Changed Breakdown field select to modal.

This commit is contained in:
Mike Cao 2025-06-29 15:36:43 -07:00
parent ee8750d9df
commit ea83afbc13
20 changed files with 108 additions and 277 deletions

View file

@ -24,7 +24,7 @@ import { Panel } from '@/components/common/Panel';
import { DateDisplay } from '@/components/common/DateDisplay';
const views = {
url: PagesTable,
path: PagesTable,
title: PagesTable,
referrer: ReferrersTable,
browser: BrowsersTable,

View file

@ -20,7 +20,7 @@ export function WebsiteControls({
return (
<Column gap>
<Row alignItems="center" justifyContent="space-between" gap="3">
{allowFilter && <WebsiteFilterButton websiteId={websiteId} />}
{allowFilter ? <WebsiteFilterButton websiteId={websiteId} /> : <div />}
{allowDateFilter && <WebsiteDateFilter websiteId={websiteId} allowCompare={allowCompare} />}
{allowMonthFilter && <WebsiteMonthSelect websiteId={websiteId} />}
</Row>

View file

@ -30,9 +30,9 @@ export function WebsiteMetricsBar({
const metrics = data
? [
{
value: pageviews,
label: formatMessage(labels.views),
change: pageviews - previous.pageviews,
value: visitors,
label: formatMessage(labels.visitors),
change: visitors - previous.visitors,
formatValue: formatLongNumber,
},
{
@ -42,9 +42,9 @@ export function WebsiteMetricsBar({
formatValue: formatLongNumber,
},
{
value: visitors,
label: formatMessage(labels.visitors),
change: visitors - previous.visitors,
value: pageviews,
label: formatMessage(labels.views),
change: pageviews - previous.pageviews,
formatValue: formatLongNumber,
},
{

View file

@ -46,14 +46,14 @@ export function Breakdown({ websiteId, parameters, startDate, endDate }: Breakdo
</DataColumn>
);
})}
<DataColumn id="views" label={formatMessage(labels.views)} align="end">
{row => row?.['views']?.toLocaleString()}
<DataColumn id="visitors" label={formatMessage(labels.visitors)} align="end">
{row => row?.['visitors']?.toLocaleString()}
</DataColumn>
<DataColumn id="visits" label={formatMessage(labels.visits)} align="end">
{row => row?.['visits']?.toLocaleString()}
</DataColumn>
<DataColumn id="visitors" label={formatMessage(labels.visitors)} align="end">
{row => row?.['visitors']?.toLocaleString()}
<DataColumn id="views" label={formatMessage(labels.views)} align="end">
{row => row?.['views']?.toLocaleString()}
</DataColumn>
<DataColumn id="bounceRate" label={formatMessage(labels.bounceRate)} align="end">
{row => {

View file

@ -1,22 +1,12 @@
'use client';
import { useState } from 'react';
import {
List,
ListItem,
Button,
Column,
Box,
Grid,
Text,
Icon,
Popover,
DialogTrigger,
} from '@umami/react-zen';
import { useDateRange, useMessages, useFields } from '@/components/hooks';
import { SquarePlus, Chevron } from '@/components/icons';
import { Button, Column, Box, Text, Icon, DialogTrigger, Modal, Dialog } from '@umami/react-zen';
import { useDateRange, useMessages } from '@/components/hooks';
import { ListCheck } from '@/components/icons';
import { Panel } from '@/components/common/Panel';
import { Breakdown } from './Breakdown';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
import { FieldSelectForm } from '@/app/(main)/websites/[websiteId]/reports/breakdown/FieldSelectForm';
export function BreakdownPage({ websiteId }: { websiteId: string }) {
const {
@ -27,9 +17,7 @@ export function BreakdownPage({ websiteId }: { websiteId: string }) {
return (
<Column gap>
<WebsiteControls websiteId={websiteId} />
<Box>
<FieldsButton value={fields} onChange={setFields} />
</Box>
<FieldsButton value={fields} onChange={setFields} />
<Panel height="900px" overflow="auto" allowFullscreen>
<Breakdown
websiteId={websiteId}
@ -43,55 +31,25 @@ export function BreakdownPage({ websiteId }: { websiteId: string }) {
}
const FieldsButton = ({ value, onChange }) => {
const [selected, setSelected] = useState(value);
const [isOpen, setIsOpen] = useState(false);
const { formatMessage, labels } = useMessages();
const { fields } = useFields();
const handleChange = value => {
setSelected(value);
};
const handleApply = () => {
setIsOpen(false);
onChange?.(selected);
};
const handleClose = () => {
setIsOpen(false);
setSelected(value);
};
return (
<DialogTrigger>
<Button variant="quiet" onPress={() => setIsOpen(!isOpen)}>
<Icon>
<SquarePlus />
</Icon>
<Text>Fields</Text>
<Icon rotate={90}>
<Chevron />
</Icon>
</Button>
<Popover placement="bottom start" isOpen={isOpen}>
<Column width="300px" padding="2" border borderRadius shadow="3" backgroundColor gap>
<List value={selected} onChange={handleChange} selectionMode="multiple">
{fields.map(({ name, label }) => {
return (
<ListItem key={name} id={name}>
{label}
</ListItem>
);
})}
</List>
<Grid columns="1fr 1fr" gap>
<Button onPress={handleClose}>{formatMessage(labels.cancel)}</Button>
<Button onPress={handleApply} variant="primary">
{formatMessage(labels.apply)}
</Button>
</Grid>
</Column>
</Popover>
</DialogTrigger>
<Box>
<DialogTrigger>
<Button>
<Icon>
<ListCheck />
</Icon>
<Text>Fields</Text>
</Button>
<Modal>
<Dialog title={formatMessage(labels.fields)}>
{({ close }) => (
<FieldSelectForm selectedFields={value} onChange={onChange} onClose={close} />
)}
</Dialog>
</Modal>
</DialogTrigger>
</Box>
);
};

View file

@ -0,0 +1,46 @@
import { Column, List, ListItem, Grid, Button } from '@umami/react-zen';
import { useFields, useMessages } from '@/components/hooks';
import { useState } from 'react';
export function FieldSelectForm({
selectedFields = [],
onChange,
onClose,
}: {
selectedFields?: string[];
onChange: (values: string[]) => void;
onClose?: () => void;
}) {
const [selected, setSelected] = useState(selectedFields);
const { formatMessage, labels } = useMessages();
const { fields } = useFields();
const handleChange = (value: string[]) => {
setSelected(value);
};
const handleApply = () => {
onChange?.(selected);
onClose();
};
return (
<Column width="300px" gap="6">
<List value={selected} onChange={handleChange} selectionMode="multiple">
{fields.map(({ name, label }) => {
return (
<ListItem key={name} id={name}>
{label}
</ListItem>
);
})}
</List>
<Grid columns="1fr 1fr" gap>
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
<Button onPress={handleApply} variant="primary">
{formatMessage(labels.apply)}
</Button>
</Grid>
</Column>
);
}

View file

@ -69,7 +69,7 @@ export function GoalEditForm({
label={formatMessage(labels.name)}
rules={{ required: formatMessage(labels.required) }}
>
<TextField />
<TextField autoFocus />
</FormField>
<FormField
name="type"

View file

@ -22,10 +22,10 @@ export function Retention({ websiteId, days = DAYS, startDate, endDate }: Retent
const { timezone } = useTimezone();
const { data, error, isLoading } = useResultQuery<any>('retention', {
websiteId,
timezone,
dateRange: {
startDate,
endDate,
timezone,
},
});

View file

@ -15,6 +15,7 @@ export async function POST(request: Request) {
websiteId,
dateRange: { startDate, endDate },
parameters: { fields },
filters,
} = body;
if (!(await canViewWebsite(auth, websiteId))) {
@ -24,6 +25,7 @@ export async function POST(request: Request) {
const data = await getBreakdown(websiteId, fields, {
startDate: new Date(startDate),
endDate: new Date(endDate),
...filters,
});
return json(data);

View file

@ -13,8 +13,7 @@ export async function POST(request: Request) {
const {
websiteId,
dateRange: { startDate, endDate },
timezone,
dateRange: { startDate, endDate, timezone },
} = body;
if (!(await canViewWebsite(auth, websiteId))) {