Cohort selection.

This commit is contained in:
Mike Cao 2025-08-28 23:29:42 -07:00
parent 05f9a67727
commit bab4f8ebcc
32 changed files with 841 additions and 655 deletions

View file

@ -15,10 +15,7 @@ export function CohortAddButton({ websiteId }: { websiteId: string }) {
<Text>{formatMessage(labels.cohort)}</Text>
</Button>
<Modal>
<Dialog
title={formatMessage(labels.cohort)}
style={{ width: 800, minHeight: 300, maxHeight: '90vh' }}
>
<Dialog title={formatMessage(labels.cohort)} style={{ width: 800, minHeight: 300 }}>
{({ close }) => {
return <CohortEditForm websiteId={websiteId} onClose={close} />;
}}

View file

@ -18,10 +18,7 @@ export function CohortEditButton({
return (
<ActionButton title={formatMessage(labels.edit)} icon={<Edit />}>
<Dialog
title={formatMessage(labels.cohort)}
style={{ width: 800, minHeight: 300, maxHeight: '90vh' }}
>
<Dialog title={formatMessage(labels.cohort)} style={{ width: 800, minHeight: 300 }}>
{({ close }) => {
return (
<CohortEditForm

View file

@ -8,23 +8,13 @@ import {
Label,
Loading,
Column,
ComboBox,
Select,
ListItem,
Grid,
useDebounce,
} from '@umami/react-zen';
import {
useMessages,
useUpdateQuery,
useWebsiteCohortQuery,
useWebsiteValuesQuery,
} from '@/components/hooks';
import { useMessages, useUpdateQuery, useWebsiteCohortQuery } from '@/components/hooks';
import { DateFilter } from '@/components/input/DateFilter';
import { FieldFilters } from '@/components/input/FieldFilters';
import { SetStateAction, useMemo, useState } from 'react';
import { endOfDay, subMonths } from 'date-fns';
import { Empty } from '@/components/common/Empty';
import { LookupField } from '@/components/input/LookupField';
import { ActionSelect } from '@/components/input/ActionSelect';
export function CohortEditForm({
cohortId,
@ -40,21 +30,8 @@ export function CohortEditForm({
onSave?: () => void;
onClose?: () => void;
}) {
const [action, setAction] = useState('path');
const [search, setSearch] = useState('');
const searchValue = useDebounce(search, 300);
const { data } = useWebsiteCohortQuery(websiteId, cohortId);
const { formatMessage, labels, messages } = useMessages();
const startDate = subMonths(endOfDay(new Date()), 6);
const endDate = endOfDay(new Date());
const { data: searchResults, isLoading } = useWebsiteValuesQuery({
websiteId,
type: action,
search: searchValue,
startDate,
endDate,
});
const { mutate, error, isPending, touch, toast } = useUpdateQuery(
`/websites/${websiteId}/segments${cohortId ? `/${cohortId}` : ''}`,
@ -63,10 +40,6 @@ export function CohortEditForm({
},
);
const items: string[] = useMemo(() => {
return searchResults?.map(({ value }) => value) || [];
}, [searchResults]);
const handleSubmit = async (formData: any) => {
mutate(formData, {
onSuccess: async () => {
@ -78,105 +51,84 @@ export function CohortEditForm({
});
};
const handleSearch = (value: SetStateAction<string>) => {
setSearch(value);
};
if (cohortId && !data) {
return <Loading position="page" />;
}
const defaultValues = {
parameters: { filters, dateRange: '30day', action: { type: 'path', value: '' } },
};
return (
<Form
error={error}
onSubmit={handleSubmit}
defaultValues={
data || { parameters: { filters, dateRange: '30day', action: { type: 'path' } } }
}
>
<FormField
name="name"
label={formatMessage(labels.name)}
rules={{ required: formatMessage(labels.required) }}
>
<TextField autoFocus />
</FormField>
<Form error={error} onSubmit={handleSubmit} defaultValues={data || defaultValues}>
{({ watch }) => {
const type = watch('parameters.action.type');
<Column>
<Label>{formatMessage(labels.action)}</Label>
<Grid columns="260px 1fr" gap>
<Column>
return (
<>
<FormField
name="parameters.action.type"
name="name"
label={formatMessage(labels.name)}
rules={{ required: formatMessage(labels.required) }}
>
<Select onSelectionChange={(value: any) => setAction(value)}>
<ListItem id="path">{formatMessage(labels.viewedPage)}</ListItem>
<ListItem id="event">{formatMessage(labels.triggeredEvent)}</ListItem>
</Select>
<TextField autoFocus />
</FormField>
</Column>
<Column>
<FormField
name="parameters.action.value"
rules={{ required: formatMessage(labels.required) }}
>
{({ field }) => {
return (
<ComboBox
aria-label="action"
items={items}
inputValue={field?.value}
onInputChange={value => {
handleSearch(value);
field?.onChange?.(value);
}}
formValue="text"
allowsEmptyCollection
allowsCustomValue
renderEmptyState={() =>
isLoading ? (
<Loading position="center" icon="dots" />
) : (
<Empty message={formatMessage(messages.noResultsFound)} />
)
}
<Column>
<Label>{formatMessage(labels.action)}</Label>
<Grid columns="260px 1fr" gap>
<Column>
<FormField
name="parameters.action.type"
rules={{ required: formatMessage(labels.required) }}
>
{items.map(item => (
<ListItem key={item} id={item}>
{item}
</ListItem>
))}
</ComboBox>
);
}}
</FormField>
</Column>
</Grid>
</Column>
<ActionSelect />
</FormField>
</Column>
<Column>
<FormField
name="parameters.action.value"
rules={{ required: formatMessage(labels.required) }}
>
{({ field }) => {
return <LookupField websiteId={websiteId} type={type} {...field} />;
}}
</FormField>
</Column>
</Grid>
</Column>
<Column width="260px">
<Label>{formatMessage(labels.dateRange)}</Label>
<FormField name="parameters.dateRange" rules={{ required: formatMessage(labels.required) }}>
<DateFilter placement="bottom start" />
</FormField>
</Column>
<Column width="260px">
<Label>{formatMessage(labels.dateRange)}</Label>
<FormField
name="parameters.dateRange"
rules={{ required: formatMessage(labels.required) }}
>
<DateFilter placement="bottom start" />
</FormField>
</Column>
<Column>
<Label>{formatMessage(labels.filters)}</Label>
<FormField name="parameters.filters" rules={{ required: formatMessage(labels.required) }}>
<FieldFilters websiteId={websiteId} exclude={['path', 'event']} />
</FormField>
</Column>
<Column>
<Label>{formatMessage(labels.filters)}</Label>
<FormField
name="parameters.filters"
rules={{ required: formatMessage(labels.required) }}
>
<FieldFilters websiteId={websiteId} exclude={['path', 'event']} />
</FormField>
</Column>
<FormButtons>
<Button isDisabled={isPending} onPress={onClose}>
{formatMessage(labels.cancel)}
</Button>
<FormSubmitButton variant="primary" data-test="button-submit" isDisabled={isPending}>
{formatMessage(labels.save)}
</FormSubmitButton>
</FormButtons>
<FormButtons>
<Button isDisabled={isPending} onPress={onClose}>
{formatMessage(labels.cancel)}
</Button>
<FormSubmitButton variant="primary" data-test="button-submit" isDisabled={isPending}>
{formatMessage(labels.save)}
</FormSubmitButton>
</FormButtons>
</>
);
}}
</Form>
);
}