Cohorts editing.

This commit is contained in:
Mike Cao 2025-08-26 23:55:57 -07:00
parent 07665f4824
commit 8c8e36c63b
26 changed files with 1066 additions and 985 deletions

View file

@ -4,31 +4,31 @@ import { endOfYear } from 'date-fns';
import { DatePickerForm } from '@/components/metrics/DatePickerForm';
import { useMessages } from '@/components/hooks';
import { DateDisplay } from '@/components/common/DateDisplay';
import { parseDateRange } from '@/lib/date';
export interface DateFilterProps {
value: string;
startDate: Date;
endDate: Date;
value?: string;
onChange?: (value: string) => void;
showAllTime?: boolean;
renderDate?: boolean;
placement?: string;
}
export function DateFilter({
value,
startDate,
endDate,
onChange,
showAllTime,
renderDate,
placement = 'bottom',
}: DateFilterProps) {
const { formatMessage, labels } = useMessages();
const [showPicker, setShowPicker] = useState(false);
const { startDate, endDate } = parseDateRange(value) || {};
const options = [
{ label: formatMessage(labels.today), value: '0day' },
{
label: formatMessage(labels.lastHours, { x: 24 }),
label: formatMessage(labels.lastHours, { x: '24' }),
value: '24hour',
},
{
@ -37,7 +37,7 @@ export function DateFilter({
divider: true,
},
{
label: formatMessage(labels.lastDays, { x: 7 }),
label: formatMessage(labels.lastDays, { x: '7' }),
value: '7day',
},
{
@ -46,21 +46,21 @@ export function DateFilter({
divider: true,
},
{
label: formatMessage(labels.lastDays, { x: 30 }),
label: formatMessage(labels.lastDays, { x: '30' }),
value: '30day',
},
{
label: formatMessage(labels.lastDays, { x: 90 }),
label: formatMessage(labels.lastDays, { x: '90' }),
value: '90day',
},
{ label: formatMessage(labels.thisYear), value: '0year' },
{
label: formatMessage(labels.lastMonths, { x: 6 }),
label: formatMessage(labels.lastMonths, { x: '6' }),
value: '6month',
divider: true,
},
{
label: formatMessage(labels.lastMonths, { x: 12 }),
label: formatMessage(labels.lastMonths, { x: '12' }),
value: '12month',
},
showAllTime && {
@ -105,7 +105,7 @@ export function DateFilter({
placeholder={formatMessage(labels.selectDate)}
onChange={handleChange}
renderValue={renderValue}
popoverProps={{ placement: 'bottom' }}
popoverProps={{ placement: placement as any }}
>
{options.map(({ label, value, divider }: any) => {
return (

View file

@ -1,4 +1,5 @@
import { Key } from 'react';
import { subMonths, endOfDay } from 'date-fns';
import { Grid, Column, List, ListItem } from '@umami/react-zen';
import { useFields, useMessages } from '@/components/hooks';
import { FilterRecord } from '@/components/common/FilterRecord';
@ -6,28 +7,23 @@ import { Empty } from '@/components/common/Empty';
export interface FieldFiltersProps {
websiteId: string;
filters: { name: string; operator: string; value: string }[];
startDate: Date;
endDate: Date;
onSave?: (data: any) => void;
value?: { name: string; operator: string; value: string }[];
exclude?: string[];
onChange?: (data: any) => void;
}
export function FieldFilters({
websiteId,
filters,
startDate,
endDate,
onSave,
}: FieldFiltersProps) {
export function FieldFilters({ websiteId, value, exclude = [], onChange }: FieldFiltersProps) {
const { formatMessage, messages } = useMessages();
const { fields } = useFields();
const startDate = subMonths(endOfDay(new Date()), 6);
const endDate = endOfDay(new Date());
const updateFilter = (name: string, props: Record<string, any>) => {
onSave(filters.map(filter => (filter.name === name ? { ...filter, ...props } : filter)));
onChange(value.map(filter => (filter.name === name ? { ...filter, ...props } : filter)));
};
const handleAdd = (name: Key) => {
onSave(filters.concat({ name: name.toString(), operator: 'eq', value: '' }));
onChange(value.concat({ name: name.toString(), operator: 'eq', value: '' }));
};
const handleChange = (name: string, value: Key) => {
@ -39,25 +35,27 @@ export function FieldFilters({
};
const handleRemove = (name: string) => {
onSave(filters.filter(filter => filter.name !== name));
onChange(value.filter(filter => filter.name !== name));
};
return (
<Grid columns="160px 1fr" overflow="hidden" gapY="6">
<Column border="right" paddingRight="3">
<List onAction={handleAdd}>
{fields.map((field: any) => {
const isDisabled = !!filters.find(({ name }) => name === field.name);
return (
<ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
{field.label}
</ListItem>
);
})}
{fields
.filter(({ name }) => !exclude.includes(name))
.map(field => {
const isDisabled = !!value.find(({ name }) => name === field.name);
return (
<ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
{field.label}
</ListItem>
);
})}
</List>
</Column>
<Column paddingLeft="6" overflow="auto" gapY="4" height="500px" style={{ contain: 'layout' }}>
{filters.map(filter => {
{value.map(filter => {
return (
<FilterRecord
key={filter.name}
@ -72,7 +70,7 @@ export function FieldFilters({
/>
);
})}
{!filters.length && <Empty message={formatMessage(messages.nothingSelected)} />}
{!value.length && <Empty message={formatMessage(messages.nothingSelected)} />}
</Column>
</Grid>
);

View file

@ -72,7 +72,7 @@ export function FilterBar({ websiteId }: { websiteId: string }) {
label={label}
operator={operatorLabels[operator]}
value={paramValue}
onRemove={name => handleCloseFilter(name)}
onRemove={(name: string) => handleCloseFilter(name)}
/>
);
})}
@ -143,7 +143,7 @@ const FilterItem = ({ name, label, operator, value, onRemove }) => {
{value}
</Text>
</Row>
<Icon onClick={() => onRemove(name)} size="xs">
<Icon onClick={() => onRemove(name)} size="xs" style={{ cursor: 'pointer' }}>
<Close />
</Icon>
</Row>

View file

@ -51,7 +51,7 @@ export function FilterEditForm({
<TabPanel id="fields">
<FieldFilters
websiteId={websiteId}
filters={currentFilters}
value={currentFilters}
startDate={startDate}
endDate={endDate}
onSave={setCurrentFilters}

View file

@ -38,8 +38,8 @@ export function TeamsButton({ showText = true }: { showText?: boolean }) {
return (
<MenuTrigger>
<Pressable>
<Row width="100%" backgroundColor="2" border borderRadius>
<SidebarItem label={label} icon={teamId ? <Users /> : <User />}>
<Row role="button" width="100%" backgroundColor="2" border borderRadius>
<SidebarItem role="button" label={label} icon={teamId ? <Users /> : <User />}>
{showText && (
<Icon rotate={90} size="sm">
<Chevron />

View file

@ -19,7 +19,7 @@ export function WebsiteDateFilter({
allowCompare,
}: WebsiteDateFilterProps) {
const { dateRange } = useDateRange(websiteId);
const { value, startDate, endDate } = dateRange;
const { value, endDate } = dateRange;
const { formatMessage, labels } = useMessages();
const {
router,
@ -61,8 +61,6 @@ export function WebsiteDateFilter({
)}
<DateFilter
value={value}
startDate={startDate}
endDate={endDate}
onChange={handleChange}
showAllTime={showAllTime}
renderDate={+offset !== 0}