Skip to content

Commit

Permalink
feat: date range selector
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Dec 17, 2024
1 parent 3696420 commit 8080ff6
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 3 deletions.
71 changes: 71 additions & 0 deletions frontend/src/component/common/FilterDateItem/DateRangePresets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Box, List, ListItem, ListItemButton, Typography } from '@mui/material';
import type { FilterItemParams } from '../../filter/FilterItem/FilterItem';
import type { FC } from 'react';
import { calculateDateRange, type RangeType } from './calculateDateRange';

export const DateRangePresets: FC<{
onRangeChange: (value: {
from: FilterItemParams;
to: FilterItemParams;
}) => void;
}> = ({ onRangeChange }) => {
const rangeChangeHandler = (rangeType: RangeType) => () => {
const [start, end] = calculateDateRange(rangeType);
onRangeChange({
from: {
operator: 'IS',
values: [start],
},
to: {
operator: 'IS',
values: [end],
},
});
};

return (
<Box>
<Typography variant='h3' sx={{ pb: 1, pl: 2 }}>
Presets
</Typography>
<List disablePadding sx={{ pb: 2 }}>
<ListItem disablePadding>
<ListItemButton onClick={rangeChangeHandler('thisMonth')}>
This month
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton
onClick={rangeChangeHandler('previousMonth')}
>
Previous month
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton onClick={rangeChangeHandler('thisQuarter')}>
This quarter
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton
onClick={rangeChangeHandler('previousQuarter')}
>
Previous quarter
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton onClick={rangeChangeHandler('thisYear')}>
This year
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton
onClick={rangeChangeHandler('previousYear')}
>
Previous year
</ListItemButton>
</ListItem>
</List>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ import { format } from 'date-fns';
import { useLocationSettings } from 'hooks/useLocationSettings';
import { getLocalizedDateString } from '../util';
import type { FilterItemParams } from 'component/filter/FilterItem/FilterItem';
import { DateRangePresets } from './DateRangePresets';

export interface IFilterDateItemProps {
name: string;
label: ReactNode;
onChange: (value: FilterItemParams) => void;
onRangeChange?: (value: {
from: FilterItemParams;
to: FilterItemParams;
}) => void;
onChipClose: () => void;
state: FilterItemParams | null | undefined;
operators: [string, ...string[]];
Expand All @@ -22,6 +27,7 @@ export const FilterDateItem: FC<IFilterDateItemProps> = ({
name,
label,
onChange,
onRangeChange,
onChipClose,
state,
operators,
Expand Down Expand Up @@ -115,6 +121,9 @@ export const FilterDateItem: FC<IFilterDateItemProps> = ({
});
}}
/>
{onRangeChange && (
<DateRangePresets onRangeChange={onRangeChange} />
)}
</LocalizationProvider>
</StyledPopover>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { calculateDateRange, type RangeType } from './calculateDateRange';

describe('calculateDateRange', () => {
const fixedDate = new Date('2024-06-16');

test.each<[RangeType, string, string]>([
['thisMonth', '2024-06-01', '2024-06-30'],
['previousMonth', '2024-05-01', '2024-05-31'],
['thisQuarter', '2024-04-01', '2024-06-30'],
['previousQuarter', '2024-01-01', '2024-03-31'],
['thisYear', '2024-01-01', '2024-12-31'],
['previousYear', '2023-01-01', '2023-12-31'],
])(
'should return correct range for %s',
(rangeType, expectedStart, expectedEnd) => {
const [start, end] = calculateDateRange(rangeType, fixedDate);
expect(start).toBe(expectedStart);
expect(end).toBe(expectedEnd);
},
);

test('should default to previousMonth if rangeType is invalid', () => {
const [start, end] = calculateDateRange(
'invalidRange' as RangeType,
fixedDate,
);
expect(start).toBe('2024-05-01');
expect(end).toBe('2024-05-31');
});

test('should handle edge case for previousMonth at year boundary', () => {
const yearBoundaryDate = new Date('2024-01-15');
const [start, end] = calculateDateRange(
'previousMonth',
yearBoundaryDate,
);
expect(start).toBe('2023-12-01');
expect(end).toBe('2023-12-31');
});
});
66 changes: 66 additions & 0 deletions frontend/src/component/common/FilterDateItem/calculateDateRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
endOfMonth,
endOfQuarter,
endOfYear,
format,
startOfMonth,
startOfQuarter,
startOfYear,
subMonths,
subQuarters,
subYears,
} from 'date-fns';

export type RangeType =
| 'thisMonth'
| 'previousMonth'
| 'thisQuarter'
| 'previousQuarter'
| 'thisYear'
| 'previousYear';

export const calculateDateRange = (
rangeType: RangeType,
today = new Date(),
): [string, string] => {
let start: Date;
let end: Date;

switch (rangeType) {
case 'thisMonth': {
start = startOfMonth(today);
end = endOfMonth(today);
break;
}
case 'thisQuarter': {
start = startOfQuarter(today);
end = endOfQuarter(today);
break;
}
case 'previousQuarter': {
const previousQuarter = subQuarters(today, 1);
start = startOfQuarter(previousQuarter);
end = endOfQuarter(previousQuarter);
break;
}
case 'thisYear': {
start = startOfYear(today);
end = endOfYear(today);
break;
}
case 'previousYear': {
const lastYear = subYears(today, 1);
start = startOfYear(lastYear);
end = endOfYear(lastYear);
break;
}

default: {
const lastMonth = subMonths(today, 1);
start = startOfMonth(lastMonth);
end = endOfMonth(lastMonth);
}
}

return [format(start, 'yyyy-MM-dd'), format(end, 'yyyy-MM-dd')];
};
4 changes: 4 additions & 0 deletions frontend/src/component/events/EventLog/EventLogFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,17 @@ export const useEventLogFilters = (
options: [],
filterKey: 'from',
dateOperators: ['IS'],
fromFilterKey: 'from',
toFilterKey: 'to',
},
{
label: 'Date To',
icon: 'today',
options: [],
filterKey: 'to',
dateOperators: ['IS'],
fromFilterKey: 'from',
toFilterKey: 'to',
},
{
label: 'Created by',
Expand Down
25 changes: 22 additions & 3 deletions frontend/src/component/filter/Filters/Filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type ITextFilterItem = IBaseFilterItem & {

type IDateFilterItem = IBaseFilterItem & {
dateOperators: [string, ...string[]];
fromFilterKey?: string;
toFilterKey?: string;
};

export type IFilterItem = ITextFilterItem | IDateFilterItem;
Expand Down Expand Up @@ -116,6 +118,22 @@ export const Filters: FC<IFilterProps> = ({
}, [JSON.stringify(state), JSON.stringify(availableFilters)]);

const hasAvailableFilters = unselectedFilters.length > 0;

const rangeChangeHandler = (filter: IDateFilterItem) => {
const fromKey = filter.fromFilterKey;
const toKey = filter.toFilterKey;
if (fromKey && toKey) {
return (value: {
from: FilterItemParams;
to: FilterItemParams;
}) => {
onChange({ [fromKey]: value.from });
onChange({ [toKey]: value.to });
};
}
return undefined;
};

return (
<StyledBox className={className}>
{selectedFilters.map((selectedFilter) => {
Expand Down Expand Up @@ -143,9 +161,10 @@ export const Filters: FC<IFilterProps> = ({
label={label}
name={filter.label}
state={state[filter.filterKey]}
onChange={(value) =>
onChange({ [filter.filterKey]: value })
}
onChange={(value) => {
onChange({ [filter.filterKey]: value });
}}
onRangeChange={rangeChangeHandler(filter)}
operators={filter.dateOperators}
onChipClose={() => deselectFilter(filter.label)}
/>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/component/insights/InsightsFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@ export const InsightsFilters: FC<IFeatureToggleFiltersProps> = ({
options: [],
filterKey: 'from',
dateOperators: ['IS'],
fromFilterKey: 'from',
toFilterKey: 'to',
},
{
label: 'Date To',
icon: 'today',
options: [],
filterKey: 'to',
dateOperators: ['IS'],
fromFilterKey: 'from',
toFilterKey: 'to',
},
...(hasMultipleProjects
? ([
Expand Down

0 comments on commit 8080ff6

Please sign in to comment.