implement UTM filters and fields

This commit is contained in:
Francis Cao 2026-02-05 16:30:46 -08:00
parent 7514af4236
commit 49adaa32d0
11 changed files with 336 additions and 55 deletions

View file

@ -1,6 +1,6 @@
import { Button, Column, Grid, List, ListItem } from '@umami/react-zen';
import { Button, Column, Grid, List, ListItem, ListSection } from '@umami/react-zen';
import { useState } from 'react';
import { useFields, useMessages } from '@/components/hooks';
import { type FieldGroup, useFields, useMessages } from '@/components/hooks';
export function FieldSelectForm({
selectedFields = [],
@ -13,7 +13,7 @@ export function FieldSelectForm({
}) {
const [selected, setSelected] = useState(selectedFields);
const { formatMessage, labels } = useMessages();
const { fields } = useFields();
const { fields, groupLabels } = useFields();
const handleChange = (value: string[]) => {
setSelected(value);
@ -24,17 +24,38 @@ export function FieldSelectForm({
onClose();
};
const groupedFields = fields
.filter(field => field.name !== 'event')
.reduce(
(acc, field) => {
const group = field.group;
if (!acc[group]) {
acc[group] = [];
}
acc[group].push(field);
return acc;
},
{} as Record<FieldGroup, typeof fields>,
);
return (
<Column gap="6">
<List value={selected} onChange={handleChange} selectionMode="multiple">
{fields.map(({ name, label }) => {
return (
<ListItem key={name} id={name}>
{label}
</ListItem>
);
})}
</List>
<Column gap="3" overflowY="auto" maxHeight="400px">
<List value={selected} onChange={handleChange} selectionMode="multiple">
{groupLabels.map(({ key: groupKey, label }) => {
const groupFields = groupedFields[groupKey];
return (
<ListSection key={groupKey} title={label}>
{groupFields.map(field => (
<ListItem key={field.name} id={field.name}>
{field.filterLabel}
</ListItem>
))}
</ListSection>
);
})}
</List>
</Column>
<Grid columns="1fr 1fr" gap>
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
<Button onPress={handleApply} variant="primary">

View file

@ -4,10 +4,14 @@ import {
AppWindow,
Cpu,
Earth,
Fingerprint,
Globe,
KeyRound,
Landmark,
Languages,
Laptop,
Layers,
Link2,
LogIn,
LogOut,
MapPin,
@ -15,9 +19,11 @@ import {
Monitor,
Network,
Search,
Send,
Share2,
SquareSlash,
Tag,
Target,
Type,
} from '@/components/icons';
import { Lightning } from '@/components/svg';
@ -154,6 +160,41 @@ export function WebsiteExpandedMenu({
},
].filter(filterExcluded),
},
{
label: formatMessage(labels.utm),
items: [
{
id: 'utmSource',
label: formatMessage(labels.source),
path: updateParams({ view: 'utmSource' }),
icon: <Link2 />,
},
{
id: 'utmMedium',
label: formatMessage(labels.medium),
path: updateParams({ view: 'utmMedium' }),
icon: <Send />,
},
{
id: 'utmCampaign',
label: formatMessage(labels.campaign),
path: updateParams({ view: 'utmCampaign' }),
icon: <Target />,
},
{
id: 'utmContent',
label: formatMessage(labels.content),
path: updateParams({ view: 'utmContent' }),
icon: <Layers />,
},
{
id: 'utmTerm',
label: formatMessage(labels.term),
path: updateParams({ view: 'utmTerm' }),
icon: <KeyRound />,
},
].filter(filterExcluded),
},
{
label: formatMessage(labels.other),
items: [
@ -173,7 +214,7 @@ export function WebsiteExpandedMenu({
id: 'distinctId',
label: formatMessage(labels.distinctId),
path: updateParams({ view: 'distinctId' }),
icon: <Tag />,
icon: <Fingerprint />,
},
{
id: 'tag',

View file

@ -88,6 +88,31 @@ export function CompareTables({ websiteId }: { websiteId: string }) {
label: formatMessage(labels.events),
path: renderPath('event'),
},
{
id: 'utmSource',
label: formatMessage(labels.utmSource),
path: renderPath('utmSource'),
},
{
id: 'utmMedium',
label: formatMessage(labels.utmMedium),
path: renderPath('utmMedium'),
},
{
id: 'utmCampaign',
label: formatMessage(labels.utmCampaign),
path: renderPath('utmCampaign'),
},
{
id: 'utmContent',
label: formatMessage(labels.utmContent),
path: renderPath('utmContent'),
},
{
id: 'utmTerm',
label: formatMessage(labels.utmTerm),
path: renderPath('utmTerm'),
},
{
id: 'hostname',
label: formatMessage(labels.hostname),