Updated cohort processing.
Some checks are pending
Node.js CI / build (postgresql, 18.18) (push) Waiting to run

This commit is contained in:
Mike Cao 2025-08-27 14:05:46 -07:00
parent c3b62e3a74
commit f9442001e4
8 changed files with 46 additions and 56 deletions

View file

@ -90,7 +90,9 @@ export function CohortEditForm({
<Form <Form
error={error} error={error}
onSubmit={handleSubmit} onSubmit={handleSubmit}
defaultValues={data || { parameters: { filters, dateRange: '30day' } }} defaultValues={
data || { parameters: { filters, dateRange: '30day', action: { type: 'path' } } }
}
> >
<FormField <FormField
name="name" name="name"
@ -104,14 +106,19 @@ export function CohortEditForm({
<Label>{formatMessage(labels.action)}</Label> <Label>{formatMessage(labels.action)}</Label>
<Grid columns="260px 1fr" gap> <Grid columns="260px 1fr" gap>
<Column> <Column>
<Select value={action} onChange={setAction}> <FormField
<ListItem id="path">{formatMessage(labels.viewedPage)}</ListItem> name="parameters.action.type"
<ListItem id="event">{formatMessage(labels.triggeredEvent)}</ListItem> rules={{ required: formatMessage(labels.required) }}
</Select> >
<Select onSelectionChange={(value: any) => setAction(value)}>
<ListItem id="path">{formatMessage(labels.viewedPage)}</ListItem>
<ListItem id="event">{formatMessage(labels.triggeredEvent)}</ListItem>
</Select>
</FormField>
</Column> </Column>
<Column> <Column>
<FormField <FormField
name="parameters.action" name="parameters.action.value"
rules={{ required: formatMessage(labels.required) }} rules={{ required: formatMessage(labels.required) }}
> >
{({ field }) => { {({ field }) => {

View file

@ -1,5 +1,5 @@
import { canViewWebsite } from '@/validations'; import { canViewWebsite } from '@/validations';
import { EVENT_COLUMNS, FILTER_COLUMNS, FILTER_GROUPS, SESSION_COLUMNS } from '@/lib/constants'; import { EVENT_COLUMNS, FILTER_COLUMNS, SEGMENT_TYPES, SESSION_COLUMNS } from '@/lib/constants';
import { getQueryFilters, parseRequest } from '@/lib/request'; import { getQueryFilters, parseRequest } from '@/lib/request';
import { badRequest, json, unauthorized } from '@/lib/response'; import { badRequest, json, unauthorized } from '@/lib/response';
import { getWebsiteSegments, getValues } from '@/queries'; import { getWebsiteSegments, getValues } from '@/queries';
@ -30,14 +30,16 @@ export async function GET(
const { type } = query; const { type } = query;
if (!SESSION_COLUMNS.includes(type) && !EVENT_COLUMNS.includes(type) && !FILTER_GROUPS[type]) { if (!SESSION_COLUMNS.includes(type) && !EVENT_COLUMNS.includes(type) && !SEGMENT_TYPES[type]) {
return badRequest(); return badRequest();
} }
let values; let values: any[];
if (FILTER_GROUPS[type]) { if (SEGMENT_TYPES[type]) {
values = (await getWebsiteSegments(websiteId, type)).map(segment => ({ value: segment.name })); values = (await getWebsiteSegments(websiteId, type))?.data?.map(segment => ({
value: segment.name,
}));
} else { } else {
const filters = await getQueryFilters(query, websiteId); const filters = await getQueryFilters(query, websiteId);
values = await getValues(websiteId, FILTER_COLUMNS[type], filters); values = await getValues(websiteId, FILTER_COLUMNS[type], filters);

View file

@ -92,16 +92,9 @@ export function FilterBar({ websiteId }: { websiteId: string }) {
</TooltipTrigger> </TooltipTrigger>
)} )}
<Modal> <Modal>
<Dialog title={formatMessage(labels.segment)} style={{ width: 400 }}> <Dialog title={formatMessage(labels.segment)} style={{ width: 800, minHeight: 300 }}>
{({ close }) => { {({ close }) => {
return ( return <SegmentEditForm websiteId={websiteId} onClose={close} filters={filters} />;
<SegmentEditForm
websiteId={websiteId}
onClose={close}
filters={filters}
showFilters={false}
/>
);
}} }}
</Dialog> </Dialog>
</Modal> </Modal>

View file

@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Column, Tabs, TabList, Tab, TabPanel, Row, Button } from '@umami/react-zen'; import { Column, Tabs, TabList, Tab, TabPanel, Row, Button } from '@umami/react-zen';
import { useDateRange, useMessages } from '@/components/hooks'; import { useMessages } from '@/components/hooks';
import { FieldFilters } from '@/components/input/FieldFilters'; import { FieldFilters } from '@/components/input/FieldFilters';
import { SegmentFilters } from '@/components/input/SegmentFilters'; import { SegmentFilters } from '@/components/input/SegmentFilters';
@ -23,10 +23,6 @@ export function FilterEditForm({
const [currentFilters, setCurrentFilters] = useState(filters); const [currentFilters, setCurrentFilters] = useState(filters);
const [currentSegment, setCurrentSegment] = useState(segmentId); const [currentSegment, setCurrentSegment] = useState(segmentId);
const {
dateRange: { startDate, endDate },
} = useDateRange(websiteId);
const handleReset = () => { const handleReset = () => {
setCurrentFilters([]); setCurrentFilters([]);
setCurrentSegment(null); setCurrentSegment(null);
@ -49,13 +45,7 @@ export function FilterEditForm({
<Tab id="segments">{formatMessage(labels.segments)}</Tab> <Tab id="segments">{formatMessage(labels.segments)}</Tab>
</TabList> </TabList>
<TabPanel id="fields"> <TabPanel id="fields">
<FieldFilters <FieldFilters websiteId={websiteId} value={currentFilters} onChange={setCurrentFilters} />
websiteId={websiteId}
value={currentFilters}
startDate={startDate}
endDate={endDate}
onSave={setCurrentFilters}
/>
</TabPanel> </TabPanel>
<TabPanel id="segments" style={{ height: 400 }}> <TabPanel id="segments" style={{ height: 400 }}>
<SegmentFilters <SegmentFilters

View file

@ -53,7 +53,7 @@ export const SESSION_COLUMNS = [
'region', 'region',
]; ];
export const FILTER_GROUPS = { export const SEGMENT_TYPES = {
segment: 'segment', segment: 'segment',
cohort: 'cohort', cohort: 'cohort',
}; };

View file

@ -18,7 +18,7 @@ export function isSearchOperator(operator: any) {
return [OPERATORS.contains, OPERATORS.doesNotContain].includes(operator); return [OPERATORS.contains, OPERATORS.doesNotContain].includes(operator);
} }
export function filtersObjectToArray(filters: QueryFilters, options: QueryOptions = {}) { export function filtersObjectToArray(filters: QueryFilters, options: QueryOptions = {}): Filter[] {
if (!filters) { if (!filters) {
return []; return [];
} }

View file

@ -6,6 +6,7 @@ import { badRequest, unauthorized } from '@/lib/response';
import { QueryFilters } from '@/lib/types'; import { QueryFilters } from '@/lib/types';
import { getWebsiteSegment } from '@/queries'; import { getWebsiteSegment } from '@/queries';
import { z } from 'zod/v4'; import { z } from 'zod/v4';
import { filtersArrayToObject } from '@/lib/params';
export async function parseRequest( export async function parseRequest(
request: Request, request: Request,
@ -103,36 +104,31 @@ export async function getQueryFilters(
const segmentParams = (await getWebsiteSegment(websiteId, params.segment)) const segmentParams = (await getWebsiteSegment(websiteId, params.segment))
?.parameters as Record<string, any>; ?.parameters as Record<string, any>;
Object.assign(filters, segmentParams.filters); Object.assign(filters, filtersArrayToObject(segmentParams.filters));
} }
if (params.cohort) { if (params.cohort) {
const cohortParams = (await getWebsiteSegment(websiteId, params.cohort)) const cohortParams = (await getWebsiteSegment(websiteId, params.cohort))
?.parameters as Record<string, any>; ?.parameters as Record<string, any>;
// convert dateRange to startDate and endDate const { startDate, endDate } = parseDateRange(cohortParams.dateRange);
if (cohortParams.dateRange) {
const { startDate, endDate } = parseDateRange(cohortParams.dateRange);
cohortParams.startDate = startDate;
cohortParams.endDate = endDate;
delete cohortParams.dateRange;
}
if (cohortParams.filters) { const cohortFilters = cohortParams.filters.map(({ name, ...props }) => ({
Object.assign(cohortParams, cohortParams.filters); ...props,
delete cohortParams.filters; name: `cohort_${name}`,
} }));
Object.assign( cohortFilters.push({
filters, name: cohortParams.action.type,
Object.fromEntries( operator: 'eq',
Object.entries(cohortParams || {}).map(([key, value]) => value: cohortParams.action.value,
key === 'startDate' || key === 'endDate' });
? [`cohort_${key}`, new Date(value)]
: [`cohort_${key}`, value], Object.assign(filters, {
), ...filtersArrayToObject(cohortFilters),
), cohort_startDate: startDate,
); cohort_endDate: endDate,
});
} }
} }

View file

@ -29,6 +29,8 @@ export interface Filter {
operator: string; operator: string;
value: string; value: string;
type?: string; type?: string;
columns?: string;
prefix?: string;
} }
export interface DateRange { export interface DateRange {