Merge branch 'dev' of https://github.com/umami-software/umami into feat/um-376-retention-report

This commit is contained in:
Francis Cao 2023-08-07 14:04:05 -07:00
commit fc5a1f458b
23 changed files with 156 additions and 168 deletions

View file

@ -163,6 +163,13 @@ export const labels = defineMessages({
insights: { id: 'label.insights', defaultMessage: 'Insights' },
retention: { id: 'label.retention', defaultMessage: 'Retention' },
dropoff: { id: 'label.dropoff', defaultMessage: 'Dropoff' },
referrer: { id: 'label.referrer', defaultMessage: 'Referrer' },
country: { id: 'label.country', defaultMessage: 'Country' },
region: { id: 'label.region', defaultMessage: 'Region' },
city: { id: 'label.city', defaultMessage: 'City' },
browser: { id: 'label.browser', defaultMessage: 'Browser' },
device: { id: 'label.device', defaultMessage: 'Device' },
pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' },
});
export const messages = defineMessages({

View file

@ -28,6 +28,11 @@ export function EventDataMetricsBar({ websiteId }) {
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
{!error && isFetched && (
<>
<MetricCard
className={styles.card}
label={formatMessage(labels.events)}
value={data?.events}
/>
<MetricCard
className={styles.card}
label={formatMessage(labels.fields)}

View file

@ -16,7 +16,7 @@ export function EventDataTable({ data = [] }) {
<GridTable data={data}>
<GridColumn name="eventName" label={formatMessage(labels.event)}>
{row => (
<Link href={resolveUrl({ eventName: row.eventName })} shallow={true}>
<Link href={resolveUrl({ event: row.eventName })} shallow={true}>
{row.eventName}
</Link>
)}

View file

@ -6,14 +6,14 @@ import PageHeader from 'components/layout/PageHeader';
import Empty from 'components/common/Empty';
import { DATA_TYPES } from 'lib/constants';
export function EventDataValueTable({ data = [], eventName }) {
export function EventDataValueTable({ data = [], event }) {
const { formatMessage, labels } = useMessages();
const { resolveUrl } = usePageQuery();
const Title = () => {
return (
<>
<Link href={resolveUrl({ eventName: undefined })}>
<Link href={resolveUrl({ event: undefined })}>
<Button>
<Icon rotate={180}>
<Icons.ArrowRight />
@ -21,7 +21,7 @@ export function EventDataValueTable({ data = [], eventName }) {
<Text>{formatMessage(labels.back)}</Text>
</Button>
</Link>
<Text>{eventName}</Text>
<Text>{event}</Text>
</>
);
};

View file

@ -0,0 +1,13 @@
import { useState } from 'react';
import FieldSelectForm from './FieldSelectForm';
import FieldFilterForm from './FieldFilterForm';
export default function FilterSelectForm({ fields, onSelect }) {
const [field, setField] = useState();
if (!field) {
return <FieldSelectForm fields={fields} onSelect={setField} />;
}
return <FieldFilterForm name={field.name} type={field.type} onSelect={onSelect} />;
}

View file

@ -1,10 +1,12 @@
import FunnelReport from './funnel/FunnelReport';
import EventDataReport from './event-data/EventDataReport';
import InsightsReport from './insights/InsightsReport';
import RetentionReport from './retention/RetentionReport';
const reports = {
funnel: FunnelReport,
'event-data': EventDataReport,
insights: InsightsReport,
retention: RetentionReport,
};

View file

@ -7,40 +7,38 @@ import Icons from 'components/icons';
import BaseParameters from '../BaseParameters';
import ParameterList from '../ParameterList';
import styles from './InsightsParameters.module.css';
import FieldSelectForm from '../FieldSelectForm';
import PopupForm from '../PopupForm';
import FieldFilterForm from '../FieldFilterForm';
const fieldOptions = [
{ name: 'url', type: 'string' },
{ name: 'title', type: 'string' },
{ name: 'referrer', type: 'string' },
{ name: 'query', type: 'string' },
{ name: 'browser', type: 'string' },
{ name: 'os', type: 'string' },
{ name: 'device', type: 'string' },
{ name: 'country', type: 'string' },
{ name: 'region', type: 'string' },
{ name: 'city', type: 'string' },
{ name: 'language', type: 'string' },
];
import FilterSelectForm from '../FilterSelectForm';
import FieldSelectForm from '../FieldSelectForm';
export function InsightsParameters() {
const { report, runReport, updateReport, isRunning } = useContext(ReportContext);
const { formatMessage, labels } = useMessages();
const ref = useRef(null);
const { parameters } = report || {};
const { websiteId, dateRange, fields, filters, groups } = parameters || {};
const queryEnabled = websiteId && dateRange && fields?.length;
const { websiteId, dateRange, filters, groups } = parameters || {};
const queryEnabled = websiteId && dateRange && (filters?.length || groups?.length);
const fieldOptions = [
{ name: 'url_path', type: 'string', label: formatMessage(labels.url) },
{ name: 'page_title', type: 'string', label: formatMessage(labels.pageTitle) },
{ name: 'referrer_domain', type: 'string', label: formatMessage(labels.referrer) },
{ name: 'url_query', type: 'string', label: formatMessage(labels.query) },
{ name: 'browser', type: 'string', label: formatMessage(labels.browser) },
{ name: 'os', type: 'string', label: formatMessage(labels.os) },
{ name: 'device', type: 'string', label: formatMessage(labels.device) },
{ name: 'country', type: 'string', label: formatMessage(labels.country) },
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
{ name: 'language', type: 'string', label: formatMessage(labels.language) },
];
const parameterGroups = [
{ label: formatMessage(labels.fields), group: REPORT_PARAMETERS.fields },
{ label: formatMessage(labels.filters), group: REPORT_PARAMETERS.filters },
{ label: formatMessage(labels.breakdown), group: REPORT_PARAMETERS.groups },
{ label: formatMessage(labels.filters), group: REPORT_PARAMETERS.filters },
];
const parameterData = {
fields,
filters,
groups,
};
@ -73,11 +71,11 @@ export function InsightsParameters() {
{(close, element) => {
return (
<PopupForm element={element} onClose={close}>
{group === REPORT_PARAMETERS.fields && (
{group === REPORT_PARAMETERS.groups && (
<FieldSelectForm fields={fieldOptions} onSelect={handleAdd.bind(null, group)} />
)}
{group === REPORT_PARAMETERS.filters && (
<FieldFilterForm fields={fieldOptions} onSelect={handleAdd.bind(null, group)} />
<FilterSelectForm fields={fieldOptions} onSelect={handleAdd.bind(null, group)} />
)}
</PopupForm>
);
@ -97,27 +95,21 @@ export function InsightsParameters() {
items={parameterData[group]}
onRemove={index => handleRemove(group, index)}
>
{({ name, value }) => {
{({ value, label }) => {
return (
<div className={styles.parameter}>
{group === REPORT_PARAMETERS.fields && (
{group === REPORT_PARAMETERS.groups && (
<>
<div>{name}</div>
<div className={styles.op}>{value}</div>
<div>{label}</div>
</>
)}
{group === REPORT_PARAMETERS.filters && (
<>
<div>{name}</div>
<div>{label}</div>
<div className={styles.op}>{value[0]}</div>
<div>{value[1]}</div>
</>
)}
{group === REPORT_PARAMETERS.groups && (
<>
<div>{name}</div>
</>
)}
</div>
);
}}

View file

@ -6,14 +6,15 @@ import { ReportContext } from '../Report';
export function InsightsTable() {
const { report } = useContext(ReportContext);
const { formatMessage, labels } = useMessages();
const { fields = [] } = report?.parameters || {};
const { groups = [] } = report?.parameters || {};
return (
<GridTable data={report?.data || []}>
{fields.map(({ name }) => {
return <GridColumn key={name} name={name} label={name} />;
{groups.map(({ name, label }) => {
return <GridColumn key={name} name={name} label={label} />;
})}
<GridColumn name="total" label={formatMessage(labels.total)} />
<GridColumn name="views" label={formatMessage(labels.views)} width="100px" />
<GridColumn name="visitors" label={formatMessage(labels.visitors)} width="100px" />
</GridTable>
);
}

View file

@ -26,15 +26,15 @@ function useData(websiteId, eventName) {
export default function WebsiteEventData({ websiteId }) {
const {
query: { eventName },
query: { event },
} = usePageQuery();
const { data } = useData(websiteId, eventName);
const { data } = useData(websiteId, event);
return (
<Flexbox className={styles.container} direction="column" gap={20}>
<EventDataMetricsBar websiteId={websiteId} />
{!eventName && <EventDataTable data={data} />}
{eventName && <EventDataValueTable eventName={eventName} data={data} />}
{!event && <EventDataTable data={data} />}
{event && <EventDataValueTable event={event} data={data} />}
</Flexbox>
);
}