mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 21:57:16 +01:00
Fixed retention report showing wrong dates. Changed Breakdown field select to modal.
This commit is contained in:
parent
ee8750d9df
commit
ea83afbc13
20 changed files with 108 additions and 277 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -69,7 +69,7 @@ export function GoalEditForm({
|
|||
label={formatMessage(labels.name)}
|
||||
rules={{ required: formatMessage(labels.required) }}
|
||||
>
|
||||
<TextField />
|
||||
<TextField autoFocus />
|
||||
</FormField>
|
||||
<FormField
|
||||
name="type"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue