mirror of
https://github.com/umami-software/umami.git
synced 2026-02-11 08:07:12 +01:00
implement UTM filters and fields
This commit is contained in:
parent
7514af4236
commit
49adaa32d0
11 changed files with 336 additions and 55 deletions
|
|
@ -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 { useState } from 'react';
|
||||||
import { useFields, useMessages } from '@/components/hooks';
|
import { type FieldGroup, useFields, useMessages } from '@/components/hooks';
|
||||||
|
|
||||||
export function FieldSelectForm({
|
export function FieldSelectForm({
|
||||||
selectedFields = [],
|
selectedFields = [],
|
||||||
|
|
@ -13,7 +13,7 @@ export function FieldSelectForm({
|
||||||
}) {
|
}) {
|
||||||
const [selected, setSelected] = useState(selectedFields);
|
const [selected, setSelected] = useState(selectedFields);
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { fields } = useFields();
|
const { fields, groupLabels } = useFields();
|
||||||
|
|
||||||
const handleChange = (value: string[]) => {
|
const handleChange = (value: string[]) => {
|
||||||
setSelected(value);
|
setSelected(value);
|
||||||
|
|
@ -24,17 +24,38 @@ export function FieldSelectForm({
|
||||||
onClose();
|
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 (
|
return (
|
||||||
<Column gap="6">
|
<Column gap="6">
|
||||||
<List value={selected} onChange={handleChange} selectionMode="multiple">
|
<Column gap="3" overflowY="auto" maxHeight="400px">
|
||||||
{fields.map(({ name, label }) => {
|
<List value={selected} onChange={handleChange} selectionMode="multiple">
|
||||||
return (
|
{groupLabels.map(({ key: groupKey, label }) => {
|
||||||
<ListItem key={name} id={name}>
|
const groupFields = groupedFields[groupKey];
|
||||||
{label}
|
return (
|
||||||
</ListItem>
|
<ListSection key={groupKey} title={label}>
|
||||||
);
|
{groupFields.map(field => (
|
||||||
})}
|
<ListItem key={field.name} id={field.name}>
|
||||||
</List>
|
{field.filterLabel}
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</ListSection>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</Column>
|
||||||
<Grid columns="1fr 1fr" gap>
|
<Grid columns="1fr 1fr" gap>
|
||||||
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
|
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
|
||||||
<Button onPress={handleApply} variant="primary">
|
<Button onPress={handleApply} variant="primary">
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,14 @@ import {
|
||||||
AppWindow,
|
AppWindow,
|
||||||
Cpu,
|
Cpu,
|
||||||
Earth,
|
Earth,
|
||||||
|
Fingerprint,
|
||||||
Globe,
|
Globe,
|
||||||
|
KeyRound,
|
||||||
Landmark,
|
Landmark,
|
||||||
Languages,
|
Languages,
|
||||||
Laptop,
|
Laptop,
|
||||||
|
Layers,
|
||||||
|
Link2,
|
||||||
LogIn,
|
LogIn,
|
||||||
LogOut,
|
LogOut,
|
||||||
MapPin,
|
MapPin,
|
||||||
|
|
@ -15,9 +19,11 @@ import {
|
||||||
Monitor,
|
Monitor,
|
||||||
Network,
|
Network,
|
||||||
Search,
|
Search,
|
||||||
|
Send,
|
||||||
Share2,
|
Share2,
|
||||||
SquareSlash,
|
SquareSlash,
|
||||||
Tag,
|
Tag,
|
||||||
|
Target,
|
||||||
Type,
|
Type,
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
import { Lightning } from '@/components/svg';
|
import { Lightning } from '@/components/svg';
|
||||||
|
|
@ -154,6 +160,41 @@ export function WebsiteExpandedMenu({
|
||||||
},
|
},
|
||||||
].filter(filterExcluded),
|
].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),
|
label: formatMessage(labels.other),
|
||||||
items: [
|
items: [
|
||||||
|
|
@ -173,7 +214,7 @@ export function WebsiteExpandedMenu({
|
||||||
id: 'distinctId',
|
id: 'distinctId',
|
||||||
label: formatMessage(labels.distinctId),
|
label: formatMessage(labels.distinctId),
|
||||||
path: updateParams({ view: 'distinctId' }),
|
path: updateParams({ view: 'distinctId' }),
|
||||||
icon: <Tag />,
|
icon: <Fingerprint />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tag',
|
id: 'tag',
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,31 @@ export function CompareTables({ websiteId }: { websiteId: string }) {
|
||||||
label: formatMessage(labels.events),
|
label: formatMessage(labels.events),
|
||||||
path: renderPath('event'),
|
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',
|
id: 'hostname',
|
||||||
label: formatMessage(labels.hostname),
|
label: formatMessage(labels.hostname),
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,142 @@
|
||||||
import { useMessages } from './useMessages';
|
import { useMessages } from './useMessages';
|
||||||
|
|
||||||
|
export type FieldGroup = 'url' | 'sources' | 'location' | 'environment' | 'utm' | 'other';
|
||||||
|
|
||||||
|
export interface Field {
|
||||||
|
name: string;
|
||||||
|
filterLabel: string;
|
||||||
|
label: string;
|
||||||
|
group: FieldGroup;
|
||||||
|
}
|
||||||
|
|
||||||
export function useFields() {
|
export function useFields() {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
||||||
const fields = [
|
const fields: Field[] = [
|
||||||
{ name: 'path', type: 'string', label: formatMessage(labels.path) },
|
{
|
||||||
{ name: 'query', type: 'string', label: formatMessage(labels.query) },
|
name: 'path',
|
||||||
{ name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
|
filterLabel: formatMessage(labels.path),
|
||||||
{ name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
|
label: formatMessage(labels.path),
|
||||||
{ name: 'browser', type: 'string', label: formatMessage(labels.browser) },
|
group: 'url',
|
||||||
{ 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: 'query',
|
||||||
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
|
filterLabel: formatMessage(labels.query),
|
||||||
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
|
label: formatMessage(labels.query),
|
||||||
{ name: 'hostname', type: 'string', label: formatMessage(labels.hostname) },
|
group: 'url',
|
||||||
{ name: 'distinctId', type: 'string', label: formatMessage(labels.distinctId) },
|
},
|
||||||
{ name: 'tag', type: 'string', label: formatMessage(labels.tag) },
|
{
|
||||||
{ name: 'event', type: 'string', label: formatMessage(labels.event) },
|
name: 'title',
|
||||||
|
filterLabel: formatMessage(labels.pageTitle),
|
||||||
|
label: formatMessage(labels.pageTitle),
|
||||||
|
group: 'url',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'referrer',
|
||||||
|
filterLabel: formatMessage(labels.referrer),
|
||||||
|
label: formatMessage(labels.referrer),
|
||||||
|
group: 'sources',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'country',
|
||||||
|
filterLabel: formatMessage(labels.country),
|
||||||
|
label: formatMessage(labels.country),
|
||||||
|
group: 'location',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'region',
|
||||||
|
filterLabel: formatMessage(labels.region),
|
||||||
|
label: formatMessage(labels.region),
|
||||||
|
group: 'location',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'city',
|
||||||
|
filterLabel: formatMessage(labels.city),
|
||||||
|
label: formatMessage(labels.city),
|
||||||
|
group: 'location',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'browser',
|
||||||
|
filterLabel: formatMessage(labels.browser),
|
||||||
|
label: formatMessage(labels.browser),
|
||||||
|
group: 'environment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'os',
|
||||||
|
filterLabel: formatMessage(labels.os),
|
||||||
|
label: formatMessage(labels.os),
|
||||||
|
group: 'environment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'device',
|
||||||
|
filterLabel: formatMessage(labels.device),
|
||||||
|
label: formatMessage(labels.device),
|
||||||
|
group: 'environment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'utmSource',
|
||||||
|
filterLabel: formatMessage(labels.source),
|
||||||
|
label: formatMessage(labels.utmSource),
|
||||||
|
group: 'utm',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'utmMedium',
|
||||||
|
filterLabel: formatMessage(labels.medium),
|
||||||
|
label: formatMessage(labels.utmMedium),
|
||||||
|
group: 'utm',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'utmCampaign',
|
||||||
|
filterLabel: formatMessage(labels.campaign),
|
||||||
|
label: formatMessage(labels.utmCampaign),
|
||||||
|
group: 'utm',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'utmContent',
|
||||||
|
filterLabel: formatMessage(labels.content),
|
||||||
|
label: formatMessage(labels.utmContent),
|
||||||
|
group: 'utm',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'utmTerm',
|
||||||
|
filterLabel: formatMessage(labels.term),
|
||||||
|
label: formatMessage(labels.utmTerm),
|
||||||
|
group: 'utm',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'hostname',
|
||||||
|
filterLabel: formatMessage(labels.hostname),
|
||||||
|
label: formatMessage(labels.hostname),
|
||||||
|
group: 'other',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'distinctId',
|
||||||
|
filterLabel: formatMessage(labels.distinctId),
|
||||||
|
label: formatMessage(labels.distinctId),
|
||||||
|
group: 'other',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tag',
|
||||||
|
filterLabel: formatMessage(labels.tag),
|
||||||
|
label: formatMessage(labels.tag),
|
||||||
|
group: 'other',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'event',
|
||||||
|
filterLabel: formatMessage(labels.event),
|
||||||
|
label: formatMessage(labels.event),
|
||||||
|
group: 'other',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return { fields };
|
const groupLabels: { key: FieldGroup; label: string }[] = [
|
||||||
|
{ key: 'url', label: formatMessage(labels.url) },
|
||||||
|
{ key: 'sources', label: formatMessage(labels.sources) },
|
||||||
|
{ key: 'location', label: formatMessage(labels.location) },
|
||||||
|
{ key: 'environment', label: formatMessage(labels.environment) },
|
||||||
|
{ key: 'utm', label: formatMessage(labels.utm) },
|
||||||
|
{ key: 'other', label: formatMessage(labels.other) },
|
||||||
|
];
|
||||||
|
|
||||||
|
return { fields, groupLabels };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ export function useFilterParameters() {
|
||||||
tag,
|
tag,
|
||||||
hostname,
|
hostname,
|
||||||
distinctId,
|
distinctId,
|
||||||
|
utmSource,
|
||||||
|
utmMedium,
|
||||||
|
utmCampaign,
|
||||||
|
utmContent,
|
||||||
|
utmTerm,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
search,
|
search,
|
||||||
|
|
@ -45,6 +50,11 @@ export function useFilterParameters() {
|
||||||
tag,
|
tag,
|
||||||
hostname,
|
hostname,
|
||||||
distinctId,
|
distinctId,
|
||||||
|
utmSource,
|
||||||
|
utmMedium,
|
||||||
|
utmCampaign,
|
||||||
|
utmContent,
|
||||||
|
utmTerm,
|
||||||
search,
|
search,
|
||||||
segment,
|
segment,
|
||||||
cohort,
|
cohort,
|
||||||
|
|
@ -66,6 +76,11 @@ export function useFilterParameters() {
|
||||||
tag,
|
tag,
|
||||||
hostname,
|
hostname,
|
||||||
distinctId,
|
distinctId,
|
||||||
|
utmSource,
|
||||||
|
utmMedium,
|
||||||
|
utmCampaign,
|
||||||
|
utmContent,
|
||||||
|
utmTerm,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
search,
|
search,
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ import {
|
||||||
Icon,
|
Icon,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
|
ListSection,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
|
MenuSection,
|
||||||
MenuTrigger,
|
MenuTrigger,
|
||||||
Popover,
|
Popover,
|
||||||
Row,
|
Row,
|
||||||
|
|
@ -15,7 +17,7 @@ import { endOfDay, subMonths } from 'date-fns';
|
||||||
import type { Key } from 'react';
|
import type { Key } from 'react';
|
||||||
import { Empty } from '@/components/common/Empty';
|
import { Empty } from '@/components/common/Empty';
|
||||||
import { FilterRecord } from '@/components/common/FilterRecord';
|
import { FilterRecord } from '@/components/common/FilterRecord';
|
||||||
import { useFields, useMessages, useMobile } from '@/components/hooks';
|
import { type FieldGroup, useFields, useMessages, useMobile } from '@/components/hooks';
|
||||||
import { Plus } from '@/components/icons';
|
import { Plus } from '@/components/icons';
|
||||||
|
|
||||||
export interface FieldFiltersProps {
|
export interface FieldFiltersProps {
|
||||||
|
|
@ -27,11 +29,25 @@ export interface FieldFiltersProps {
|
||||||
|
|
||||||
export function FieldFilters({ websiteId, value, exclude = [], onChange }: FieldFiltersProps) {
|
export function FieldFilters({ websiteId, value, exclude = [], onChange }: FieldFiltersProps) {
|
||||||
const { formatMessage, messages } = useMessages();
|
const { formatMessage, messages } = useMessages();
|
||||||
const { fields } = useFields();
|
const { fields, groupLabels } = useFields();
|
||||||
const startDate = subMonths(endOfDay(new Date()), 6);
|
const startDate = subMonths(endOfDay(new Date()), 6);
|
||||||
const endDate = endOfDay(new Date());
|
const endDate = endOfDay(new Date());
|
||||||
const { isMobile } = useMobile();
|
const { isMobile } = useMobile();
|
||||||
|
|
||||||
|
const groupedFields = fields
|
||||||
|
.filter(({ name }) => !exclude.includes(name))
|
||||||
|
.reduce(
|
||||||
|
(acc, field) => {
|
||||||
|
const group = field.group;
|
||||||
|
if (!acc[group]) {
|
||||||
|
acc[group] = [];
|
||||||
|
}
|
||||||
|
acc[group].push(field);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<FieldGroup, typeof fields>,
|
||||||
|
);
|
||||||
|
|
||||||
const updateFilter = (name: string, props: Record<string, any>) => {
|
const updateFilter = (name: string, props: Record<string, any>) => {
|
||||||
onChange(value.map(filter => (filter.name === name ? { ...filter, ...props } : filter)));
|
onChange(value.map(filter => (filter.name === name ? { ...filter, ...props } : filter)));
|
||||||
};
|
};
|
||||||
|
|
@ -66,32 +82,44 @@ export function FieldFilters({ websiteId, value, exclude = [], onChange }: Field
|
||||||
onAction={handleAdd}
|
onAction={handleAdd}
|
||||||
style={{ maxHeight: 'calc(100vh - 2rem)', overflowY: 'auto' }}
|
style={{ maxHeight: 'calc(100vh - 2rem)', overflowY: 'auto' }}
|
||||||
>
|
>
|
||||||
{fields
|
{groupLabels.map(({ key: groupKey, label }) => {
|
||||||
.filter(({ name }) => !exclude.includes(name))
|
const groupFields = groupedFields[groupKey];
|
||||||
.map(field => {
|
return (
|
||||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
<MenuSection key={groupKey} title={label}>
|
||||||
return (
|
{groupFields.map(field => {
|
||||||
<MenuItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||||
{field.label}
|
return (
|
||||||
</MenuItem>
|
<MenuItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||||
);
|
{field.filterLabel}
|
||||||
})}
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MenuSection>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Popover>
|
</Popover>
|
||||||
</MenuTrigger>
|
</MenuTrigger>
|
||||||
</Row>
|
</Row>
|
||||||
<Column display={{ xs: 'none', md: 'flex' }} border="right" paddingRight="3" marginRight="6">
|
<Column display={{ xs: 'none', md: 'flex' }} border="right" paddingRight="3" marginRight="6">
|
||||||
<List onAction={handleAdd}>
|
<List onAction={handleAdd}>
|
||||||
{fields
|
{groupLabels.map(({ key: groupKey, label }) => {
|
||||||
.filter(({ name }) => !exclude.includes(name))
|
const groupFields = groupedFields[groupKey];
|
||||||
.map(field => {
|
if (!groupFields || groupFields.length === 0) return null;
|
||||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
|
||||||
return (
|
return (
|
||||||
<ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
<ListSection key={groupKey} title={label}>
|
||||||
{field.label}
|
{groupFields.map(field => {
|
||||||
</ListItem>
|
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||||
);
|
return (
|
||||||
})}
|
<ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||||
|
{field.filterLabel}
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ListSection>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</List>
|
</List>
|
||||||
</Column>
|
</Column>
|
||||||
<Column overflow="auto" gapY="4" style={{ contain: 'layout' }}>
|
<Column overflow="auto" gapY="4" style={{ contain: 'layout' }}>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export const labels = defineMessages({
|
||||||
joinTeam: { id: 'label.join-team', defaultMessage: 'Join team' },
|
joinTeam: { id: 'label.join-team', defaultMessage: 'Join team' },
|
||||||
settings: { id: 'label.settings', defaultMessage: 'Settings' },
|
settings: { id: 'label.settings', defaultMessage: 'Settings' },
|
||||||
owner: { id: 'label.owner', defaultMessage: 'Owner' },
|
owner: { id: 'label.owner', defaultMessage: 'Owner' },
|
||||||
|
url: { id: 'label.url', defaultMessage: 'URL' },
|
||||||
teamOwner: { id: 'label.team-owner', defaultMessage: 'Team owner' },
|
teamOwner: { id: 'label.team-owner', defaultMessage: 'Team owner' },
|
||||||
teamManager: { id: 'label.team-manager', defaultMessage: 'Team manager' },
|
teamManager: { id: 'label.team-manager', defaultMessage: 'Team manager' },
|
||||||
teamMember: { id: 'label.team-member', defaultMessage: 'Team member' },
|
teamMember: { id: 'label.team-member', defaultMessage: 'Team member' },
|
||||||
|
|
@ -245,6 +246,16 @@ export const labels = defineMessages({
|
||||||
device: { id: 'label.device', defaultMessage: 'Device' },
|
device: { id: 'label.device', defaultMessage: 'Device' },
|
||||||
pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' },
|
pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' },
|
||||||
tag: { id: 'label.tag', defaultMessage: 'Tag' },
|
tag: { id: 'label.tag', defaultMessage: 'Tag' },
|
||||||
|
source: { id: 'label.source', defaultMessage: 'Source' },
|
||||||
|
medium: { id: 'label.medium', defaultMessage: 'Medium' },
|
||||||
|
campaign: { id: 'label.campaign', defaultMessage: 'Campaign' },
|
||||||
|
content: { id: 'label.content', defaultMessage: 'Content' },
|
||||||
|
term: { id: 'label.term', defaultMessage: 'Term' },
|
||||||
|
utmSource: { id: 'label.utm-source', defaultMessage: 'UTM source' },
|
||||||
|
utmMedium: { id: 'label.utm-medium', defaultMessage: 'UTM medium' },
|
||||||
|
utmCampaign: { id: 'label.utm-campaign', defaultMessage: 'UTM campaign' },
|
||||||
|
utmContent: { id: 'label.utm-content', defaultMessage: 'UTM content' },
|
||||||
|
utmTerm: { id: 'label.utm-term', defaultMessage: 'UTM term' },
|
||||||
segment: { id: 'label.segment', defaultMessage: 'Segment' },
|
segment: { id: 'label.segment', defaultMessage: 'Segment' },
|
||||||
cohort: { id: 'label.cohort', defaultMessage: 'Cohort' },
|
cohort: { id: 'label.cohort', defaultMessage: 'Cohort' },
|
||||||
minute: { id: 'label.minute', defaultMessage: 'Minute' },
|
minute: { id: 'label.minute', defaultMessage: 'Minute' },
|
||||||
|
|
@ -311,9 +322,7 @@ export const labels = defineMessages({
|
||||||
channel: { id: 'label.channel', defaultMessage: 'Channel' },
|
channel: { id: 'label.channel', defaultMessage: 'Channel' },
|
||||||
channels: { id: 'label.channels', defaultMessage: 'Channels' },
|
channels: { id: 'label.channels', defaultMessage: 'Channels' },
|
||||||
sources: { id: 'label.sources', defaultMessage: 'Sources' },
|
sources: { id: 'label.sources', defaultMessage: 'Sources' },
|
||||||
medium: { id: 'label.medium', defaultMessage: 'Medium' },
|
|
||||||
campaigns: { id: 'label.campaigns', defaultMessage: 'Campaigns' },
|
campaigns: { id: 'label.campaigns', defaultMessage: 'Campaigns' },
|
||||||
content: { id: 'label.content', defaultMessage: 'Content' },
|
|
||||||
terms: { id: 'label.terms', defaultMessage: 'Terms' },
|
terms: { id: 'label.terms', defaultMessage: 'Terms' },
|
||||||
direct: { id: 'label.direct', defaultMessage: 'Direct' },
|
direct: { id: 'label.direct', defaultMessage: 'Direct' },
|
||||||
referral: { id: 'label.referral', defaultMessage: 'Referral' },
|
referral: { id: 'label.referral', defaultMessage: 'Referral' },
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,11 @@ export const EVENT_COLUMNS = [
|
||||||
'event',
|
'event',
|
||||||
'tag',
|
'tag',
|
||||||
'hostname',
|
'hostname',
|
||||||
|
'utmSource',
|
||||||
|
'utmMedium',
|
||||||
|
'utmCampaign',
|
||||||
|
'utmContent',
|
||||||
|
'utmTerm',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SESSION_COLUMNS = [
|
export const SESSION_COLUMNS = [
|
||||||
|
|
@ -83,6 +88,11 @@ export const FILTER_COLUMNS = {
|
||||||
event: 'event_name',
|
event: 'event_name',
|
||||||
tag: 'tag',
|
tag: 'tag',
|
||||||
eventType: 'event_type',
|
eventType: 'event_type',
|
||||||
|
utmSource: 'utm_source',
|
||||||
|
utmMedium: 'utm_medium',
|
||||||
|
utmCampaign: 'utm_campaign',
|
||||||
|
utmContent: 'utm_content',
|
||||||
|
utmTerm: 'utm_term',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const COLLECTION_TYPE = {
|
export const COLLECTION_TYPE = {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ export const filterParams = {
|
||||||
distinctId: z.string().optional(),
|
distinctId: z.string().optional(),
|
||||||
language: z.string().optional(),
|
language: z.string().optional(),
|
||||||
event: z.string().optional(),
|
event: z.string().optional(),
|
||||||
|
utmSource: z.string().optional(),
|
||||||
|
utmMedium: z.string().optional(),
|
||||||
|
utmCampaign: z.string().optional(),
|
||||||
|
utmContent: z.string().optional(),
|
||||||
|
utmTerm: z.string().optional(),
|
||||||
segment: z.uuid().optional(),
|
segment: z.uuid().optional(),
|
||||||
cohort: z.uuid().optional(),
|
cohort: z.uuid().optional(),
|
||||||
eventType: z.coerce.number().int().positive().optional(),
|
eventType: z.coerce.number().int().positive().optional(),
|
||||||
|
|
@ -94,6 +99,11 @@ export const fieldsParam = z.enum([
|
||||||
'distinctId',
|
'distinctId',
|
||||||
'language',
|
'language',
|
||||||
'event',
|
'event',
|
||||||
|
'utmSource',
|
||||||
|
'utmMedium',
|
||||||
|
'utmCampaign',
|
||||||
|
'utmContent',
|
||||||
|
'utmTerm',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const reportTypeParam = z.enum([
|
export const reportTypeParam = z.enum([
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
||||||
${joinSessionQuery}
|
${joinSessionQuery}
|
||||||
where website_event.website_id = {{websiteId::uuid}}
|
where website_event.website_id = {{websiteId::uuid}}
|
||||||
and website_event.created_at between {{startDate}} and {{endDate}}
|
and website_event.created_at between {{startDate}} and {{endDate}}
|
||||||
|
and website_event.event_type != 2
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
group by time
|
group by time
|
||||||
order by 1
|
order by 1
|
||||||
|
|
@ -59,8 +60,10 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
|
||||||
count(distinct session_id) as value
|
count(distinct session_id) as value
|
||||||
from website_event
|
from website_event
|
||||||
${cohortQuery}
|
${cohortQuery}
|
||||||
|
${excludeBounceQuery}
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||||
|
and event_type != 2
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
group by time
|
group by time
|
||||||
order by time
|
order by time
|
||||||
|
|
@ -75,6 +78,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
|
||||||
${excludeBounceQuery}
|
${excludeBounceQuery}
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||||
|
and event_type != 2
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
group by time
|
group by time
|
||||||
order by time
|
order by time
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ async function relationalQuery(
|
||||||
website_event.session_id, website_event.visit_id
|
website_event.session_id, website_event.visit_id
|
||||||
) as t
|
) as t
|
||||||
group by ${parseFieldsByName(fields)}
|
group by ${parseFieldsByName(fields)}
|
||||||
order by 1 desc, 2 desc
|
order by 2 desc, 1 desc
|
||||||
limit 500
|
limit 500
|
||||||
`,
|
`,
|
||||||
queryParams,
|
queryParams,
|
||||||
|
|
@ -119,7 +119,7 @@ async function clickhouseQuery(
|
||||||
session_id, visit_id
|
session_id, visit_id
|
||||||
) as t
|
) as t
|
||||||
group by ${parseFieldsByName(fields)}
|
group by ${parseFieldsByName(fields)}
|
||||||
order by 1 desc, 2 desc
|
order by 2 desc, 1 desc
|
||||||
limit 500
|
limit 500
|
||||||
`,
|
`,
|
||||||
queryParams,
|
queryParams,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue