fix: wip filter uncategorized transactions by date

This commit is contained in:
Ahmed Bouhuolia
2024-08-23 01:57:52 +02:00
parent 1062b65b5b
commit f6bad8fe30
14 changed files with 344 additions and 46 deletions

View File

@@ -0,0 +1,5 @@
.dateFieldGroup{
margin-bottom: 0;
}

View File

@@ -1,10 +1,19 @@
// @ts-nocheck
import { Button, FormGroup, Intent, Position } from '@blueprintjs/core';
import * as Yup from 'yup';
import { Form, Formik, FormikConfig } from 'formik';
import { FDateInput, FFormGroup, Group, Icon, Stack } from '@/components';
import moment from 'moment';
import { Form, Formik, FormikConfig, useFormikContext } from 'formik';
import {
FDateInput,
FFormGroup,
FSelect,
Group,
Icon,
Stack,
} from '@/components';
const defaultValues = {
period: 'all_dates',
fromDate: '',
toDate: '',
};
@@ -21,6 +30,7 @@ const validationSchema = Yup.object().shape({
});
interface AccountTransactionsDateFilterFormValues {
period: string;
fromDate: string;
toDate: string;
}
@@ -34,8 +44,8 @@ export function AccountTransactionsDateFilterForm({
initialValues = {},
onSubmit,
}: UncategorizedTransactionsDateFilterProps) {
const handleSubmit = () => {
return onSubmit && onSubmit(...arguments);
const handleSubmit = (values, bag) => {
return onSubmit && onSubmit(values, bag);
};
const formInitialValues = {
@@ -50,9 +60,15 @@ export function AccountTransactionsDateFilterForm({
validationSchema={validationSchema}
>
<Form>
<Stack>
<Group>
<FFormGroup name={'fromDate'} label={'From Date'}>
<Stack spacing={15}>
<Group spacing={10}>
<AccountTransactionDatePeriodField />
<FFormGroup
name={'fromDate'}
label={'From Date'}
style={{ marginBottom: 0 }}
>
<FDateInput
name={'fromDate'}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
@@ -60,13 +76,17 @@ export function AccountTransactionsDateFilterForm({
parseDate={(str) => new Date(str)}
inputProps={{
fill: true,
placeholder: 'MM/DD/YYY',
leftElement: <Icon icon={'date-range'} />,
}}
style={{ marginBottom: 0 }}
/>
</FFormGroup>
<FormGroup label={'To Date'} name={'toDate'}>
<FormGroup
label={'To Date'}
name={'toDate'}
style={{ marginBottom: 0 }}
>
<FDateInput
name={'toDate'}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
@@ -74,21 +94,137 @@ export function AccountTransactionsDateFilterForm({
parseDate={(str) => new Date(str)}
inputProps={{
fill: true,
placeholder: 'MM/DD/YYY',
leftElement: <Icon icon={'date-range'} />,
}}
style={{ marginBottom: 0 }}
/>
</FormGroup>
</Group>
<Group spacing={10}>
<Button small intent={Intent.PRIMARY}>
Filter
</Button>
<Button small>Cancel</Button>
</Group>
<AccountTransactionsDateFilterFooter />
</Stack>
</Form>
</Formik>
);
}
function AccountTransactionsDateFilterFooter() {
const { submitForm, setValues } = useFormikContext();
const handleFilterBtnClick = () => {
submitForm();
};
const handleClearBtnClick = () => {
setValues({
...defaultValues,
});
submitForm();
};
return (
<Group spacing={10}>
<Button
small
intent={Intent.PRIMARY}
onClick={handleFilterBtnClick}
style={{ minWidth: 75 }}
>
Filter
</Button>
<Button
intent={Intent.DANGER}
small
onClick={handleClearBtnClick}
minimal
>
Clear
</Button>
</Group>
);
}
function AccountTransactionDatePeriodField() {
const { setFieldValue } = useFormikContext();
const handleItemChange = (item) => {
const { fromDate, toDate } = getDateRangePeriod(item.value);
setFieldValue('fromDate', fromDate);
setFieldValue('toDate', toDate);
setFieldValue('period', item.value);
};
return (
<FFormGroup name={'period'} label={'Date'} style={{ marginBottom: 0 }}>
<FSelect
name={'period'}
items={periodOptions}
onItemSelect={handleItemChange}
/>
</FFormGroup>
);
}
const periodOptions = [
{ text: 'All Dates', value: 'all_dates' },
{ text: 'Custom', value: 'custom' },
{ text: 'Today', value: 'today' },
{ text: 'Yesterday', value: 'yesterday' },
{ text: 'This week', value: 'this_week' },
{ text: 'This year', value: 'this_year' },
{ text: 'This month', value: 'this_month' },
{ text: 'last week', value: 'last_week' },
{ text: 'Last year', value: 'last_year' },
{ text: 'Last month', value: 'last_month' },
{ text: 'Last month', value: 'last_month' },
];
const getDateRangePeriod = (period: string) => {
switch (period) {
case 'today':
return {
fromDate: moment().startOf('day').toDate(),
toDate: moment().endOf('day').toDate(),
};
case 'yesterday':
return {
fromDate: moment().subtract(1, 'days').startOf('day').toDate(),
toDate: moment().subtract(1, 'days').endOf('day').toDate(),
};
case 'this_week':
return {
fromDate: moment().startOf('week').toDate(),
toDate: moment().endOf('week').toDate(),
};
case 'this_month':
return {
fromDate: moment().startOf('month').toDate(),
toDate: moment().endOf('month').toDate(),
};
case 'this_year':
return {
fromDate: moment().startOf('year').toDate(),
toDate: moment().endOf('year').toDate(),
};
case 'last_week':
return {
fromDate: moment().subtract(1, 'weeks').startOf('week').toDate(),
toDate: moment().subtract(1, 'weeks').endOf('week').toDate(),
};
case 'last_month':
return {
fromDate: moment().subtract(1, 'months').startOf('month').toDate(),
toDate: moment().subtract(1, 'months').endOf('month').toDate(),
};
case 'last_year':
return {
fromDate: moment().subtract(1, 'years').startOf('year').toDate(),
toDate: moment().subtract(1, 'years').endOf('year').toDate(),
};
case 'all_dates':
case 'custom':
default:
return { fromDate: null, toDate: null };
}
};

View File

@@ -2,12 +2,11 @@
import { useMemo } from 'react';
import * as R from 'ramda';
import { useAppQueryString } from '@/hooks';
import { Group, Icon } from '@/components';
import { Group, Stack, } from '@/components';
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
import { TagsControl } from '@/components/TagsControl';
import { Button, Classes, Position } from '@blueprintjs/core';
import { AccountTransactionsDateFilterForm } from './AccountTransactionsDateFilter';
import { Popover2 } from '@blueprintjs/popover2';
import { AccountUncategorizedDateFilter } from './UncategorizedTransactions/AccountUncategorizedDateFilter';
import { Divider } from '@blueprintjs/core';
export function AccountTransactionsUncategorizeFilter() {
const { bankAccountMetaSummary } = useAccountTransactionsContext();
@@ -57,21 +56,17 @@ export function AccountTransactionsUncategorizeFilter() {
);
return (
<Group position={'apart'}>
<Group>
<Group position={'apart'} style={{ marginBottom: 14 }}>
<Group align={'stretch'} spacing={10}>
<TagsControl
options={options}
value={locationQuery?.uncategorizedFilter || 'all'}
onValueChange={handleTabsChange}
/>
<Popover2
content={<UncategorizedTransactionsDateFilter />}
position={Position.RIGHT}
popoverClassName={Classes.POPOVER_CONTENT_SIZING}
>
<Button icon={<Icon icon={'date-range'} />}>Date Filter</Button>
</Popover2>
<Divider />
<AccountUncategorizedDateFilter />
</Group>
<TagsControl
options={[{ value: 'excluded', label: 'Excluded' }]}
value={locationQuery?.uncategorizedFilter || 'all'}
@@ -80,15 +75,3 @@ export function AccountTransactionsUncategorizeFilter() {
</Group>
);
}
export const UncategorizedTransactionsDateFilter = () => {
const initialValues = {};
const handleSubmit = () => {};
return (
<AccountTransactionsDateFilterForm
initialValues={initialValues}
onSubmit={handleSubmit}
/>
);
};

View File

@@ -2,9 +2,11 @@
import React from 'react';
import { flatten, map } from 'lodash';
import * as R from 'ramda';
import { IntersectionObserver } from '@/components';
import { useAccountUncategorizedTransactionsInfinity } from '@/hooks/query';
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
import { withBanking } from '../withBanking';
const AccountUncategorizedTransactionsContext = React.createContext();
@@ -13,9 +15,15 @@ function flattenInfinityPagesData(data) {
}
/**
* Account uncategorized transctions provider.
* Account un-categorized transactions provider.
*/
function AccountUncategorizedTransactionsBoot({ children }) {
function AccountUncategorizedTransactionsBootRoot({
// #withBanking
uncategorizedTransactionsFilter,
// #ownProps
children,
}) {
const { accountId } = useAccountTransactionsContext();
// Fetches the uncategorized transactions.
@@ -29,6 +37,8 @@ function AccountUncategorizedTransactionsBoot({ children }) {
hasNextPage: hasUncategorizedTransactionsNextPage,
} = useAccountUncategorizedTransactionsInfinity(accountId, {
page_size: 50,
min_date: uncategorizedTransactionsFilter?.fromDate,
max_date: uncategorizedTransactionsFilter?.toDate,
});
// Memorized the cashflow account transactions.
const uncategorizedTransactions = React.useMemo(
@@ -69,6 +79,12 @@ function AccountUncategorizedTransactionsBoot({ children }) {
);
}
const AccountUncategorizedTransactionsBoot = R.compose(
withBanking(({ uncategorizedTransactionsFilter }) => ({
uncategorizedTransactionsFilter,
})),
)(AccountUncategorizedTransactionsBootRoot);
const useAccountUncategorizedTransactionsContext = () =>
React.useContext(AccountUncategorizedTransactionsContext);

View File

@@ -0,0 +1,86 @@
// @ts-nocheck
import * as R from 'ramda';
import moment from 'moment';
import { Box, Icon } from '@/components';
import { Button, Classes, Popover, Position } from '@blueprintjs/core';
import { withBankingActions } from '../../withBankingActions';
import { withBanking } from '../../withBanking';
import { AccountTransactionsDateFilterForm } from '../AccountTransactionsDateFilter';
import { TagButton } from './TagButton';
function AccountUncategorizedDateFilterRoot({
uncategorizedTransactionsFilter,
}) {
const fromDate = uncategorizedTransactionsFilter?.fromDate;
const toDate = uncategorizedTransactionsFilter?.toDate;
const fromDateFormatted = moment(fromDate).isSame(
moment().format('YYYY'),
'year',
)
? moment(fromDate).format('MMM, DD')
: moment(fromDate).format('MMM, DD, YYYY');
const toDateFormatted = moment(toDate).isSame(moment().format('YYYY'), 'year')
? moment(toDate).format('MMM, DD')
: moment(toDate).format('MMM, DD, YYYY');
const buttonText =
fromDate && toDate
? `Date: ${fromDateFormatted}${toDateFormatted}`
: 'Date Filter';
return (
<Popover
content={
<Box style={{ padding: 18 }}>
<UncategorizedTransactionsDateFilter />
</Box>
}
position={Position.RIGHT}
popoverClassName={Classes.POPOVER_CONTENT}
>
<TagButton outline icon={<Icon icon={'date-range'} />}>
{buttonText}
</TagButton>
</Popover>
);
}
export const AccountUncategorizedDateFilter = R.compose(
withBanking(({ uncategorizedTransactionsFilter }) => ({
uncategorizedTransactionsFilter,
})),
)(AccountUncategorizedDateFilterRoot);
export const UncategorizedTransactionsDateFilter = R.compose(
withBankingActions,
withBanking(({ uncategorizedTransactionsFilter }) => ({
uncategorizedTransactionsFilter,
})),
)(
({
// #withBankingActions
setUncategorizedTransactionsFilter,
// #withBanking
uncategorizedTransactionsFilter,
}) => {
const initialValues = {
...uncategorizedTransactionsFilter,
};
const handleSubmit = (values) => {
setUncategorizedTransactionsFilter({
fromDate: values.fromDate,
toDate: values.toDate,
});
};
return (
<AccountTransactionsDateFilterForm
initialValues={initialValues}
onSubmit={handleSubmit}
/>
);
},
);

View File

@@ -0,0 +1,11 @@
.root{
min-height: 26px;
border-radius: 15px;
font-size: 13px;
padding: 0 10px;
&:global(.bp4-button:not([class*=bp4-intent-]):not(.bp4-minimal)) {
background: #fff;
border: 1px solid #e1e2e8;
}
}

View File

@@ -0,0 +1,9 @@
// @ts-nocheck
import { Button } from "@blueprintjs/core"
import styles from './TagButton.module.scss';
export const TagButton = (props) => {
return <Button {...props} className={styles.root} />
}

View File

@@ -25,6 +25,8 @@ export const withBanking = (mapState) => {
categorizedTransactionsSelected:
state.plaid.categorizedTransactionsSelected,
uncategorizedTransactionsFilter: state.plaid.uncategorizedFilter
};
return mapState ? mapState(mapped, state, props) : mapped;
};

View File

@@ -15,6 +15,8 @@ import {
removeTransactionsToCategorizeSelected,
setCategorizedTransactionsSelected,
resetCategorizedTransactionsSelected,
setUncategorizedTransactionsFilter,
resetUncategorizedTranasctionsFilter,
} from '@/store/banking/banking.reducer';
export interface WithBankingActionsProps {
@@ -40,6 +42,9 @@ export interface WithBankingActionsProps {
setCategorizedTransactionsSelected: (ids: Array<string | number>) => void;
resetCategorizedTransactionsSelected: () => void;
setUncategorizedTransactionsFilter: (filter: any) => void;
resetUncategorizedTranasctionsFilter: () => void;
}
const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
@@ -138,6 +143,19 @@ const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
*/
resetCategorizedTransactionsSelected: () =>
dispatch(resetCategorizedTransactionsSelected()),
/**
* Sets the uncategorized transactions filter.
* @param {any} filter -
*/
setUncategorizedTransactionsFilter: (filter: any) =>
dispatch(setUncategorizedTransactionsFilter({ filter })),
/**
* Resets the uncategorized transactions filter.
*/
resetUncategorizedTranasctionsFilter: () =>
dispatch(resetUncategorizedTranasctionsFilter()),
});
export const withBankingActions = connect<