diff --git a/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts b/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts index 0ddf5861a..963e5b061 100644 --- a/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts +++ b/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts @@ -84,6 +84,8 @@ export default class NewCashflowTransactionController extends BaseController { param('id').exists().isNumeric().toInt(), query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), + query('min_date').optional().isISO8601().toDate(), + query('max_date').optional().isISO8601().toDate(), ]; } diff --git a/packages/webapp/src/components/TagsControl/TagsControl.module.scss b/packages/webapp/src/components/TagsControl/TagsControl.module.scss index e548e2ced..0e5f626d6 100644 --- a/packages/webapp/src/components/TagsControl/TagsControl.module.scss +++ b/packages/webapp/src/components/TagsControl/TagsControl.module.scss @@ -2,7 +2,6 @@ display: flex; flex-direction: row; gap: 10px; - margin-bottom: 14px; } .tag{ diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.module.scss b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.module.scss new file mode 100644 index 000000000..80d22ade7 --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.module.scss @@ -0,0 +1,5 @@ + + +.dateFieldGroup{ + margin-bottom: 0; +} \ No newline at end of file diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.tsx index 6ce0e66dd..64eb3760c 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsDateFilter.tsx @@ -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} >
- - - + + + + + new Date(str)} inputProps={{ fill: true, + placeholder: 'MM/DD/YYY', leftElement: , }} - style={{ marginBottom: 0 }} /> - + new Date(str)} inputProps={{ fill: true, + placeholder: 'MM/DD/YYY', leftElement: , }} - style={{ marginBottom: 0 }} /> - - - - + ); } + +function AccountTransactionsDateFilterFooter() { + const { submitForm, setValues } = useFormikContext(); + + const handleFilterBtnClick = () => { + submitForm(); + }; + const handleClearBtnClick = () => { + setValues({ + ...defaultValues, + }); + submitForm(); + }; + + return ( + + + + + + ); +} + +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 ( + + + + ); +} + +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 }; + } +}; diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx index 3de1c7689..ad46e586c 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizeFilter.tsx @@ -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 ( - - + + - } - position={Position.RIGHT} - popoverClassName={Classes.POPOVER_CONTENT_SIZING} - > - - + + + ); } - -export const UncategorizedTransactionsDateFilter = () => { - const initialValues = {}; - const handleSubmit = () => {}; - - return ( - - ); -}; diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorizedBoot.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorizedBoot.tsx index ce57832b3..2ca46e588 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorizedBoot.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorizedBoot.tsx @@ -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); diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/AccountUncategorizedDateFilter.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/AccountUncategorizedDateFilter.tsx new file mode 100644 index 000000000..5de4d073f --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/AccountUncategorizedDateFilter.tsx @@ -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 ( + + + + } + position={Position.RIGHT} + popoverClassName={Classes.POPOVER_CONTENT} + > + }> + {buttonText} + + + ); +} + +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 ( + + ); + }, +); diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/TagButton.module.scss b/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/TagButton.module.scss new file mode 100644 index 000000000..9bbc4f8d7 --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/TagButton.module.scss @@ -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; + } +} \ No newline at end of file diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/TagButton.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/TagButton.tsx new file mode 100644 index 000000000..1b588a771 --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/UncategorizedTransactions/TagButton.tsx @@ -0,0 +1,9 @@ +// @ts-nocheck +import { Button } from "@blueprintjs/core" +import styles from './TagButton.module.scss'; + + + +export const TagButton = (props) => { + return