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

@ -7,7 +7,7 @@ import { useDateRange, useMessages } from '@/components/hooks';
export function AttributionPage({ websiteId }: { websiteId: string }) {
const [model, setModel] = useState('first-click');
const [type, setType] = useState('page');
const [type, setType] = useState('path');
const [step, setStep] = useState('/');
const { formatMessage, labels } = useMessages();
const {
@ -36,7 +36,7 @@ export function AttributionPage({ websiteId }: { websiteId: string }) {
defaultValue={type}
onChange={setType}
>
<ListItem id="page">{formatMessage(labels.page)}</ListItem>
<ListItem id="path">{formatMessage(labels.page)}</ListItem>
<ListItem id="event">{formatMessage(labels.event)}</ListItem>
</Select>
</Column>

View file

@ -56,7 +56,7 @@ export function Funnel({ id, name, type, parameters, websiteId }) {
{ type, value, visitors, previous, dropped, dropoff, remaining }: FunnelResult,
index: number,
) => {
const isPage = type === 'page';
const isPage = type === 'path';
return (
<Grid key={index} columns="auto 1fr" gap="6">
<Column alignItems="center" position="relative">
@ -92,7 +92,7 @@ export function Funnel({ id, name, type, parameters, websiteId }) {
</Row>
<Row alignItems="center" justifyContent="space-between" gap>
<Row alignItems="center" gap>
<Icon>{type === 'page' ? <File /> : <Lightning />}</Icon>
<Icon>{type === 'path' ? <File /> : <Lightning />}</Icon>
<Text>{value}</Text>
</Row>
<Row alignItems="center" gap>

View file

@ -55,7 +55,7 @@ export function FunnelEditForm({
const defaultValues = {
name: data?.name || '',
window: data?.parameters?.window || 60,
steps: data?.parameters?.steps || [{ type: 'page', value: '/' }],
steps: data?.parameters?.steps || [{ type: 'path', value: '/' }],
};
return (
@ -91,7 +91,7 @@ export function FunnelEditForm({
onChange={field.onChange}
>
<Grid columns="1fr 1fr" flexGrow={1} gap>
<Radio id="page" value="page">
<Radio id="path" value="path">
<Icon>
<File />
</Icon>
@ -130,7 +130,7 @@ export function FunnelEditForm({
})}
<Row>
<Button
onPress={() => append({ type: 'page', value: '/' })}
onPress={() => append({ type: 'path', value: '/' })}
isDisabled={fields.length >= FUNNEL_STEPS_MAX}
>
<Icon>

View file

@ -30,7 +30,7 @@ export function Goal({ id, name, type, parameters, websiteId, startDate, endDate
endDate,
...parameters,
});
const isPage = parameters?.type === 'page';
const isPage = parameters?.type === 'path';
return (
<LoadingPanel data={data} isLoading={isLoading} isFetching={isFetching} error={error}>
@ -68,7 +68,7 @@ export function Goal({ id, name, type, parameters, websiteId, startDate, endDate
</Row>
<Row alignItems="center" justifyContent="space-between" gap>
<Row alignItems="center" gap>
<Icon>{parameters.type === 'page' ? <File /> : <Lightning />}</Icon>
<Icon>{parameters.type === 'path' ? <File /> : <Lightning />}</Icon>
<Text>{parameters.value}</Text>
</Row>
<Row alignItems="center" gap>

View file

@ -16,9 +16,10 @@ export function GoalAddButton({ websiteId }: { websiteId: string }) {
</Button>
<Modal>
<Dialog
aria-label="add goal"
variant="modal"
title={formatMessage(labels.goal)}
style={{ minHeight: 375, minWidth: 400 }}
style={{ minWidth: 800, minHeight: 300 }}
>
{({ close }) => <GoalEditForm websiteId={websiteId} onClose={close} />}
</Dialog>

View file

@ -6,14 +6,13 @@ import {
FormButtons,
FormSubmitButton,
Button,
RadioGroup,
Radio,
Text,
Icon,
Loading,
Column,
Label,
} from '@umami/react-zen';
import { useMessages, useReportQuery, useUpdateQuery } from '@/components/hooks';
import { File, Lightning } from '@/components/icons';
import { LookupField } from '@/components/input/LookupField';
import { ActionSelect } from '@/components/input/ActionSelect';
export function GoalEditForm({
id,
@ -27,13 +26,12 @@ export function GoalEditForm({
onClose?: () => void;
}) {
const { formatMessage, labels } = useMessages();
const { data } = useReportQuery(id);
const { mutate, error, isPending, touch } = useUpdateQuery(`/reports${id ? `/${id}` : ''}`);
const handleSubmit = async ({ name, ...parameters }) => {
const handleSubmit = async (formData: Record<string, any>) => {
mutate(
{ ...data, id, name, type: 'goal', websiteId, parameters },
{ ...formData, type: 'goal', websiteId },
{
onSuccess: async () => {
if (id) touch(`report:${id}`);
@ -50,15 +48,15 @@ export function GoalEditForm({
}
const defaultValues = {
name: data?.name || '',
type: data?.parameters?.type || 'page',
value: data?.parameters?.value || '',
name: '',
parameters: { type: 'path', value: '' },
};
return (
<Form onSubmit={handleSubmit} error={error?.message} defaultValues={defaultValues}>
<Form onSubmit={handleSubmit} error={error?.message} defaultValues={data || defaultValues}>
{({ watch }) => {
const watchType = watch('type');
const type = watch('parameters.type');
return (
<>
<FormField
@ -68,35 +66,30 @@ export function GoalEditForm({
>
<TextField autoFocus />
</FormField>
<FormField
name="type"
label={formatMessage(labels.type)}
rules={{ required: formatMessage(labels.required) }}
>
<RadioGroup orientation="horizontal" variant="box">
<Grid columns="1fr 1fr" flexGrow={1} gap>
<Radio value="page">
<Icon>
<File />
</Icon>
<Text>{formatMessage(labels.page)}</Text>
</Radio>
<Radio value="event">
<Icon>
<Lightning />
</Icon>
<Text>{formatMessage(labels.event)}</Text>
</Radio>
</Grid>
</RadioGroup>
</FormField>
<FormField
name="value"
label={formatMessage(watchType === 'event' ? labels.eventName : labels.path)}
rules={{ required: formatMessage(labels.required) }}
>
<TextField />
</FormField>
<Column>
<Label>{formatMessage(labels.action)}</Label>
<Grid columns="260px 1fr" gap>
<Column>
<FormField
name="parameters.type"
rules={{ required: formatMessage(labels.required) }}
>
<ActionSelect />
</FormField>
</Column>
<Column>
<FormField
name="parameters.value"
rules={{ required: formatMessage(labels.required) }}
>
{({ field }) => {
return <LookupField websiteId={websiteId} type={type} {...field} />;
}}
</FormField>
</Column>
</Grid>
</Column>
<FormButtons>
<Button onPress={onClose} isDisabled={isPending}>
{formatMessage(labels.cancel)}