mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 23:30:32 +00:00
Merge pull request #590 from bigcapitalhq/filter-uncategorized-bank-transactions
feat(banking): Filter uncategorized bank transactions by date
This commit is contained in:
@@ -46,6 +46,10 @@ export class ExcludeBankTransactionsController extends BaseController {
|
|||||||
query('account_id').optional().isNumeric().toInt(),
|
query('account_id').optional().isNumeric().toInt(),
|
||||||
query('page').optional().isNumeric().toInt(),
|
query('page').optional().isNumeric().toInt(),
|
||||||
query('page_size').optional().isNumeric().toInt(),
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
|
query('min_date').optional({ nullable: true }).isISO8601().toDate(),
|
||||||
|
query('max_date').optional({ nullable: true }).isISO8601().toDate(),
|
||||||
|
query('min_amount').optional({ nullable: true }).isFloat().toFloat(),
|
||||||
|
query('max_amount').optional({ nullable: true }).isFloat().toFloat(),
|
||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.getExcludedBankTransactions.bind(this)
|
this.getExcludedBankTransactions.bind(this)
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ export class RecognizedTransactionsController extends BaseController {
|
|||||||
query('page').optional().isNumeric().toInt(),
|
query('page').optional().isNumeric().toInt(),
|
||||||
query('page_size').optional().isNumeric().toInt(),
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
query('account_id').optional().isNumeric().toInt(),
|
query('account_id').optional().isNumeric().toInt(),
|
||||||
|
query('min_date').optional({ nullable: true }).isISO8601().toDate(),
|
||||||
|
query('max_date').optional({ nullable: true }).isISO8601().toDate(),
|
||||||
|
query('min_amount').optional({ nullable: true }).isFloat().toFloat(),
|
||||||
|
query('max_amount').optional({ nullable: true }).isFloat().isFloat(),
|
||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.getRecognizedTransactions.bind(this)
|
this.getRecognizedTransactions.bind(this)
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ export default class NewCashflowTransactionController extends BaseController {
|
|||||||
param('id').exists().isNumeric().toInt(),
|
param('id').exists().isNumeric().toInt(),
|
||||||
query('page').optional().isNumeric().toInt(),
|
query('page').optional().isNumeric().toInt(),
|
||||||
query('page_size').optional().isNumeric().toInt(),
|
query('page_size').optional().isNumeric().toInt(),
|
||||||
|
query('min_date').optional({ nullable: true }).isISO8601().toDate(),
|
||||||
|
query('max_date').optional({ nullable: true }).isISO8601().toDate(),
|
||||||
|
query('min_amount').optional({ nullable: true }).isFloat().toFloat(),
|
||||||
|
query('max_amount').optional({ nullable: true }).isFloat().toFloat(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,11 +167,18 @@ export interface CategorizeTransactionAsExpenseDTO {
|
|||||||
export interface IGetUncategorizedTransactionsQuery {
|
export interface IGetUncategorizedTransactionsQuery {
|
||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
minDate?: Date;
|
||||||
|
maxDate?: Date;
|
||||||
|
minAmount?: number;
|
||||||
|
maxAmount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface IGetRecognizedTransactionsQuery {
|
export interface IGetRecognizedTransactionsQuery {
|
||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
accountId?: number;
|
accountId?: number;
|
||||||
}
|
minDate?: Date;
|
||||||
|
maxDate?: Date;
|
||||||
|
minAmount?: number;
|
||||||
|
maxAmount?: number;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
|
import moment from 'moment';
|
||||||
import { Model, mixin } from 'objection';
|
import { Model, mixin } from 'objection';
|
||||||
import TenantModel from 'models/TenantModel';
|
import TenantModel from 'models/TenantModel';
|
||||||
import ModelSettings from './ModelSetting';
|
import ModelSettings from './ModelSetting';
|
||||||
import Account from './Account';
|
|
||||||
import UncategorizedCashflowTransactionMeta from './UncategorizedCashflowTransaction.meta';
|
import UncategorizedCashflowTransactionMeta from './UncategorizedCashflowTransaction.meta';
|
||||||
|
|
||||||
export default class UncategorizedCashflowTransaction extends mixin(
|
export default class UncategorizedCashflowTransaction extends mixin(
|
||||||
@@ -166,6 +166,28 @@ export default class UncategorizedCashflowTransaction extends mixin(
|
|||||||
pending(query) {
|
pending(query) {
|
||||||
query.where('pending', true);
|
query.where('pending', true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
minAmount(query, minAmount) {
|
||||||
|
query.where('amount', '>=', minAmount);
|
||||||
|
},
|
||||||
|
|
||||||
|
maxAmount(query, maxAmount) {
|
||||||
|
query.where('amount', '<=', maxAmount);
|
||||||
|
},
|
||||||
|
|
||||||
|
toDate(query, toDate) {
|
||||||
|
const dateFormat = 'YYYY-MM-DD';
|
||||||
|
const _toDate = moment(toDate).endOf('day').format(dateFormat);
|
||||||
|
|
||||||
|
query.where('date', '<=', _toDate);
|
||||||
|
},
|
||||||
|
|
||||||
|
fromDate(query, fromDate) {
|
||||||
|
const dateFormat = 'YYYY-MM-DD';
|
||||||
|
const _fromDate = moment(fromDate).startOf('day').format(dateFormat);
|
||||||
|
|
||||||
|
query.where('date', '>=', _fromDate);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import moment from 'moment';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { ExcludedBankTransactionsQuery } from './_types';
|
import { ExcludedBankTransactionsQuery } from './_types';
|
||||||
import { UncategorizedTransactionTransformer } from '@/services/Cashflow/UncategorizedTransactionTransformer';
|
import { UncategorizedTransactionTransformer } from '@/services/Cashflow/UncategorizedTransactionTransformer';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
@@ -39,6 +40,18 @@ export class GetExcludedBankTransactionsService {
|
|||||||
if (_query.accountId) {
|
if (_query.accountId) {
|
||||||
q.where('account_id', _query.accountId);
|
q.where('account_id', _query.accountId);
|
||||||
}
|
}
|
||||||
|
if (_query.minDate) {
|
||||||
|
q.modify('fromDate', _query.minDate);
|
||||||
|
}
|
||||||
|
if (_query.maxDate) {
|
||||||
|
q.modify('toDate', _query.maxDate);
|
||||||
|
}
|
||||||
|
if (_query.minAmount) {
|
||||||
|
q.modify('minAmount', _query.minAmount);
|
||||||
|
}
|
||||||
|
if (_query.maxAmount) {
|
||||||
|
q.modify('maxAmount', _query.maxAmount);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.pagination(_query.page - 1, _query.pageSize);
|
.pagination(_query.page - 1, _query.pageSize);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ export interface ExcludedBankTransactionsQuery {
|
|||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
accountId?: number;
|
accountId?: number;
|
||||||
|
minDate?: Date;
|
||||||
|
maxDate?: Date;
|
||||||
|
minAmount?: number;
|
||||||
|
maxAmount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBankTransactionUnexcludingEventPayload {
|
export interface IBankTransactionUnexcludingEventPayload {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class GetRecognizedTransactionsService {
|
|||||||
) {
|
) {
|
||||||
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
|
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const _filter = {
|
const _query = {
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
...filter,
|
...filter,
|
||||||
@@ -41,11 +41,26 @@ export class GetRecognizedTransactionsService {
|
|||||||
// Exclude the pending transactions.
|
// Exclude the pending transactions.
|
||||||
q.modify('notPending');
|
q.modify('notPending');
|
||||||
|
|
||||||
if (_filter.accountId) {
|
if (_query.accountId) {
|
||||||
q.where('accountId', _filter.accountId);
|
q.where('accountId', _query.accountId);
|
||||||
|
}
|
||||||
|
if (_query.minDate) {
|
||||||
|
q.modify('fromDate', _query.minDate);
|
||||||
|
}
|
||||||
|
if (_query.maxDate) {
|
||||||
|
q.modify('toDate', _query.maxDate);
|
||||||
|
}
|
||||||
|
if (_query.minAmount) {
|
||||||
|
q.modify('minAmount', _query.minAmount);
|
||||||
|
}
|
||||||
|
if (_query.maxAmount) {
|
||||||
|
q.modify('maxAmount', _query.maxAmount);
|
||||||
|
}
|
||||||
|
if (_query.accountId) {
|
||||||
|
q.where('accountId', _query.accountId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.pagination(_filter.page - 1, _filter.pageSize);
|
.pagination(_query.page - 1, _query.pageSize);
|
||||||
|
|
||||||
const data = await this.transformer.transform(
|
const data = await this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -62,6 +62,19 @@ export class GetUncategorizedTransactions {
|
|||||||
|
|
||||||
q.whereNull('matchedBankTransactions.id');
|
q.whereNull('matchedBankTransactions.id');
|
||||||
q.orderBy('date', 'DESC');
|
q.orderBy('date', 'DESC');
|
||||||
|
|
||||||
|
if (_query.minDate) {
|
||||||
|
q.modify('fromDate', _query.minDate);
|
||||||
|
}
|
||||||
|
if (_query.maxDate) {
|
||||||
|
q.modify('toDate', _query.maxDate);
|
||||||
|
}
|
||||||
|
if (_query.minAmount) {
|
||||||
|
q.modify('minAmount', _query.minAmount);
|
||||||
|
}
|
||||||
|
if (_query.maxAmount) {
|
||||||
|
q.modify('maxAmount', _query.maxAmount);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.pagination(_query.page - 1, _query.pageSize);
|
.pagination(_query.page - 1, _query.pageSize);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag{
|
.tag{
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.dateFieldGroup{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { Button, FormGroup, Intent, Position } from '@blueprintjs/core';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
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: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
fromDate: Yup.date()
|
||||||
|
.nullable()
|
||||||
|
.required('From Date is required')
|
||||||
|
.max(Yup.ref('toDate'), 'From Date cannot be after To Date'),
|
||||||
|
toDate: Yup.date()
|
||||||
|
.nullable()
|
||||||
|
.required('To Date is required')
|
||||||
|
.min(Yup.ref('fromDate'), 'To Date cannot be before From Date'),
|
||||||
|
});
|
||||||
|
|
||||||
|
interface AccountTransactionsDateFilterFormValues {
|
||||||
|
period: string;
|
||||||
|
fromDate: string;
|
||||||
|
toDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UncategorizedTransactionsDateFilterProps {
|
||||||
|
initialValues?: AccountTransactionsDateFilterFormValues;
|
||||||
|
onSubmit?: FormikConfig<AccountTransactionsDateFilterFormValues>['onSubmit'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccountTransactionsDateFilterForm({
|
||||||
|
initialValues = {},
|
||||||
|
onSubmit,
|
||||||
|
}: UncategorizedTransactionsDateFilterProps) {
|
||||||
|
const handleSubmit = (values, bag) => {
|
||||||
|
return onSubmit && onSubmit(values, bag);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formInitialValues = {
|
||||||
|
...defaultValues,
|
||||||
|
...initialValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
initialValues={formInitialValues}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<Stack spacing={15}>
|
||||||
|
<Group spacing={10}>
|
||||||
|
<AccountTransactionDatePeriodField />
|
||||||
|
|
||||||
|
<FFormGroup
|
||||||
|
name={'fromDate'}
|
||||||
|
label={'From Date'}
|
||||||
|
style={{ marginBottom: 0, flex: '1' }}
|
||||||
|
>
|
||||||
|
<FDateInput
|
||||||
|
name={'fromDate'}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
|
parseDate={(str) => new Date(str)}
|
||||||
|
inputProps={{
|
||||||
|
fill: true,
|
||||||
|
placeholder: 'MM/DD/YYY',
|
||||||
|
leftElement: <Icon icon={'date-range'} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FormGroup
|
||||||
|
label={'To Date'}
|
||||||
|
name={'toDate'}
|
||||||
|
style={{ marginBottom: 0, flex: '1' }}
|
||||||
|
>
|
||||||
|
<FDateInput
|
||||||
|
name={'toDate'}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
|
parseDate={(str) => new Date(str)}
|
||||||
|
inputProps={{
|
||||||
|
fill: true,
|
||||||
|
placeholder: 'MM/DD/YYY',
|
||||||
|
leftElement: <Icon icon={'date-range'} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</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, flex: '0 28%' }}
|
||||||
|
>
|
||||||
|
<FSelect
|
||||||
|
name={'period'}
|
||||||
|
items={periodOptions}
|
||||||
|
onItemSelect={handleItemChange}
|
||||||
|
popoverProps={{ captureDismiss: true }}
|
||||||
|
/>
|
||||||
|
</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 };
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import * as R from 'ramda';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import * as R from 'ramda';
|
||||||
import { useAppQueryString } from '@/hooks';
|
import { useAppQueryString } from '@/hooks';
|
||||||
import { Group } from '@/components';
|
import { Group, Stack, } from '@/components';
|
||||||
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
||||||
import { TagsControl } from '@/components/TagsControl';
|
import { TagsControl } from '@/components/TagsControl';
|
||||||
|
import { AccountUncategorizedDateFilter } from './UncategorizedTransactions/AccountUncategorizedDateFilter';
|
||||||
|
import { Divider } from '@blueprintjs/core';
|
||||||
|
|
||||||
export function AccountTransactionsUncategorizeFilter() {
|
export function AccountTransactionsUncategorizeFilter() {
|
||||||
const { bankAccountMetaSummary } = useAccountTransactionsContext();
|
const { bankAccountMetaSummary } = useAccountTransactionsContext();
|
||||||
@@ -54,12 +56,17 @@ export function AccountTransactionsUncategorizeFilter() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group position={'apart'}>
|
<Group position={'apart'} style={{ marginBottom: 14 }}>
|
||||||
<TagsControl
|
<Group align={'stretch'} spacing={10}>
|
||||||
options={options}
|
<TagsControl
|
||||||
value={locationQuery?.uncategorizedFilter || 'all'}
|
options={options}
|
||||||
onValueChange={handleTabsChange}
|
value={locationQuery?.uncategorizedFilter || 'all'}
|
||||||
/>
|
onValueChange={handleTabsChange}
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
<AccountUncategorizedDateFilter />
|
||||||
|
</Group>
|
||||||
|
|
||||||
<TagsControl
|
<TagsControl
|
||||||
options={[{ value: 'excluded', label: 'Excluded' }]}
|
options={[{ value: 'excluded', label: 'Excluded' }]}
|
||||||
value={locationQuery?.uncategorizedFilter || 'all'}
|
value={locationQuery?.uncategorizedFilter || 'all'}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { flatten, map } from 'lodash';
|
import { flatten, map } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
import { IntersectionObserver } from '@/components';
|
import { IntersectionObserver } from '@/components';
|
||||||
import { useAccountUncategorizedTransactionsInfinity } from '@/hooks/query';
|
import { useAccountUncategorizedTransactionsInfinity } from '@/hooks/query';
|
||||||
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
|
||||||
|
import { withBanking } from '../withBanking';
|
||||||
|
|
||||||
const AccountUncategorizedTransactionsContext = React.createContext();
|
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();
|
const { accountId } = useAccountTransactionsContext();
|
||||||
|
|
||||||
// Fetches the uncategorized transactions.
|
// Fetches the uncategorized transactions.
|
||||||
@@ -29,6 +37,8 @@ function AccountUncategorizedTransactionsBoot({ children }) {
|
|||||||
hasNextPage: hasUncategorizedTransactionsNextPage,
|
hasNextPage: hasUncategorizedTransactionsNextPage,
|
||||||
} = useAccountUncategorizedTransactionsInfinity(accountId, {
|
} = useAccountUncategorizedTransactionsInfinity(accountId, {
|
||||||
page_size: 50,
|
page_size: 50,
|
||||||
|
min_date: uncategorizedTransactionsFilter?.fromDate || null,
|
||||||
|
max_date: uncategorizedTransactionsFilter?.toDate || null,
|
||||||
});
|
});
|
||||||
// Memorized the cashflow account transactions.
|
// Memorized the cashflow account transactions.
|
||||||
const uncategorizedTransactions = React.useMemo(
|
const uncategorizedTransactions = React.useMemo(
|
||||||
@@ -69,6 +79,12 @@ function AccountUncategorizedTransactionsBoot({ children }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AccountUncategorizedTransactionsBoot = R.compose(
|
||||||
|
withBanking(({ uncategorizedTransactionsFilter }) => ({
|
||||||
|
uncategorizedTransactionsFilter,
|
||||||
|
})),
|
||||||
|
)(AccountUncategorizedTransactionsBootRoot);
|
||||||
|
|
||||||
const useAccountUncategorizedTransactionsContext = () =>
|
const useAccountUncategorizedTransactionsContext = () =>
|
||||||
React.useContext(AccountUncategorizedTransactionsContext);
|
React.useContext(AccountUncategorizedTransactionsContext);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { flatten, map } from 'lodash';
|
import { flatten, map } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
import { IntersectionObserver } from '@/components';
|
import { IntersectionObserver } from '@/components';
|
||||||
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||||
import { useExcludedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
import { useExcludedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
||||||
|
import { withBanking } from '../../withBanking';
|
||||||
|
|
||||||
interface ExcludedBankTransactionsContextValue {
|
interface ExcludedBankTransactionsContextValue {
|
||||||
isExcludedTransactionsLoading: boolean;
|
isExcludedTransactionsLoading: boolean;
|
||||||
@@ -27,7 +29,11 @@ interface ExcludedBankTransactionsTableBootProps {
|
|||||||
/**
|
/**
|
||||||
* Account uncategorized transctions provider.
|
* Account uncategorized transctions provider.
|
||||||
*/
|
*/
|
||||||
function ExcludedBankTransactionsTableBoot({
|
function ExcludedBankTransactionsTableBootRoot({
|
||||||
|
// #withBanking
|
||||||
|
uncategorizedTransactionsFilter,
|
||||||
|
|
||||||
|
// #ownProps
|
||||||
children,
|
children,
|
||||||
}: ExcludedBankTransactionsTableBootProps) {
|
}: ExcludedBankTransactionsTableBootProps) {
|
||||||
const { accountId } = useAccountTransactionsContext();
|
const { accountId } = useAccountTransactionsContext();
|
||||||
@@ -44,6 +50,8 @@ function ExcludedBankTransactionsTableBoot({
|
|||||||
} = useExcludedBankTransactionsInfinity({
|
} = useExcludedBankTransactionsInfinity({
|
||||||
page_size: 50,
|
page_size: 50,
|
||||||
account_id: accountId,
|
account_id: accountId,
|
||||||
|
min_date: uncategorizedTransactionsFilter?.fromDate || null,
|
||||||
|
max_date: uncategorizedTransactionsFilter.toDate || null,
|
||||||
});
|
});
|
||||||
// Memorized the cashflow account transactions.
|
// Memorized the cashflow account transactions.
|
||||||
const excludedBankTransactions = React.useMemo(
|
const excludedBankTransactions = React.useMemo(
|
||||||
@@ -84,6 +92,12 @@ function ExcludedBankTransactionsTableBoot({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ExcludedBankTransactionsTableBoot = R.compose(
|
||||||
|
withBanking(({ uncategorizedTransactionsFilter }) => ({
|
||||||
|
uncategorizedTransactionsFilter,
|
||||||
|
})),
|
||||||
|
)(ExcludedBankTransactionsTableBootRoot);
|
||||||
|
|
||||||
const useExcludedTransactionsBoot = () =>
|
const useExcludedTransactionsBoot = () =>
|
||||||
React.useContext(ExcludedTransactionsContext);
|
React.useContext(ExcludedTransactionsContext);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { flatten, map } from 'lodash';
|
import { flatten, map } from 'lodash';
|
||||||
import { IntersectionObserver } from '@/components';
|
import * as R from 'ramda';
|
||||||
|
import { IntersectionObserver, NumericInputCell } from '@/components';
|
||||||
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||||
import { useRecognizedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
import { useRecognizedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
||||||
|
import { withBanking } from '../../withBanking';
|
||||||
|
|
||||||
interface RecognizedTransactionsContextValue {
|
interface RecognizedTransactionsContextValue {
|
||||||
isRecongizedTransactionsLoading: boolean;
|
isRecongizedTransactionsLoading: boolean;
|
||||||
@@ -27,7 +29,10 @@ interface RecognizedTransactionsTableBootProps {
|
|||||||
/**
|
/**
|
||||||
* Account uncategorized transctions provider.
|
* Account uncategorized transctions provider.
|
||||||
*/
|
*/
|
||||||
function RecognizedTransactionsTableBoot({
|
function RecognizedTransactionsTableBootRoot({
|
||||||
|
// #withBanking
|
||||||
|
uncategorizedTransactionsFilter,
|
||||||
|
|
||||||
children,
|
children,
|
||||||
}: RecognizedTransactionsTableBootProps) {
|
}: RecognizedTransactionsTableBootProps) {
|
||||||
const { accountId } = useAccountTransactionsContext();
|
const { accountId } = useAccountTransactionsContext();
|
||||||
@@ -44,6 +49,8 @@ function RecognizedTransactionsTableBoot({
|
|||||||
} = useRecognizedBankTransactionsInfinity({
|
} = useRecognizedBankTransactionsInfinity({
|
||||||
page_size: 50,
|
page_size: 50,
|
||||||
account_id: accountId,
|
account_id: accountId,
|
||||||
|
min_date: uncategorizedTransactionsFilter.fromDate || null,
|
||||||
|
max_date: uncategorizedTransactionsFilter?.toDate || null,
|
||||||
});
|
});
|
||||||
// Memorized the cashflow account transactions.
|
// Memorized the cashflow account transactions.
|
||||||
const recognizedTransactions = React.useMemo(
|
const recognizedTransactions = React.useMemo(
|
||||||
@@ -84,6 +91,12 @@ function RecognizedTransactionsTableBoot({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RecognizedTransactionsTableBoot = R.compose(
|
||||||
|
withBanking(({ uncategorizedTransactionsFilter }) => ({
|
||||||
|
uncategorizedTransactionsFilter,
|
||||||
|
})),
|
||||||
|
)(RecognizedTransactionsTableBootRoot);
|
||||||
|
|
||||||
const useRecognizedTransactionsBoot = () =>
|
const useRecognizedTransactionsBoot = () =>
|
||||||
React.useContext(RecognizedTransactionsContext);
|
React.useContext(RecognizedTransactionsContext);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { useState } from 'react';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Box, Icon } from '@/components';
|
||||||
|
import { 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';
|
||||||
|
|
||||||
|
// Popover open state.
|
||||||
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// Handle the filter form submitting.
|
||||||
|
const handleSubmit = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
content={
|
||||||
|
<Box style={{ padding: 18 }}>
|
||||||
|
<UncategorizedTransactionsDateFilter onSubmit={handleSubmit} />
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
position={Position.RIGHT}
|
||||||
|
popoverClassName={Classes.POPOVER_CONTENT}
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
>
|
||||||
|
<TagButton
|
||||||
|
outline
|
||||||
|
icon={<Icon icon={'date-range'} />}
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
>
|
||||||
|
{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,
|
||||||
|
|
||||||
|
// #ownProps
|
||||||
|
onSubmit,
|
||||||
|
}) => {
|
||||||
|
const initialValues = {
|
||||||
|
...uncategorizedTransactionsFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (values) => {
|
||||||
|
setUncategorizedTransactionsFilter({
|
||||||
|
fromDate: values.fromDate,
|
||||||
|
toDate: values.toDate,
|
||||||
|
});
|
||||||
|
onSubmit && onSubmit(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccountTransactionsDateFilterForm
|
||||||
|
initialValues={initialValues}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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} />
|
||||||
|
}
|
||||||
@@ -25,6 +25,8 @@ export const withBanking = (mapState) => {
|
|||||||
|
|
||||||
categorizedTransactionsSelected:
|
categorizedTransactionsSelected:
|
||||||
state.plaid.categorizedTransactionsSelected,
|
state.plaid.categorizedTransactionsSelected,
|
||||||
|
|
||||||
|
uncategorizedTransactionsFilter: state.plaid.uncategorizedFilter
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
removeTransactionsToCategorizeSelected,
|
removeTransactionsToCategorizeSelected,
|
||||||
setCategorizedTransactionsSelected,
|
setCategorizedTransactionsSelected,
|
||||||
resetCategorizedTransactionsSelected,
|
resetCategorizedTransactionsSelected,
|
||||||
|
setUncategorizedTransactionsFilter,
|
||||||
|
resetUncategorizedTranasctionsFilter,
|
||||||
} from '@/store/banking/banking.reducer';
|
} from '@/store/banking/banking.reducer';
|
||||||
|
|
||||||
export interface WithBankingActionsProps {
|
export interface WithBankingActionsProps {
|
||||||
@@ -40,6 +42,9 @@ export interface WithBankingActionsProps {
|
|||||||
|
|
||||||
setCategorizedTransactionsSelected: (ids: Array<string | number>) => void;
|
setCategorizedTransactionsSelected: (ids: Array<string | number>) => void;
|
||||||
resetCategorizedTransactionsSelected: () => void;
|
resetCategorizedTransactionsSelected: () => void;
|
||||||
|
|
||||||
|
setUncategorizedTransactionsFilter: (filter: any) => void;
|
||||||
|
resetUncategorizedTranasctionsFilter: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
||||||
@@ -138,6 +143,19 @@ const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
|||||||
*/
|
*/
|
||||||
resetCategorizedTransactionsSelected: () =>
|
resetCategorizedTransactionsSelected: () =>
|
||||||
dispatch(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<
|
export const withBankingActions = connect<
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export function useAccountUncategorizedTransactionsInfinity(
|
|||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useInfiniteQuery(
|
return useInfiniteQuery(
|
||||||
[t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY, accountId],
|
[t.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY, accountId, query],
|
||||||
async ({ pageParam = 1 }) => {
|
async ({ pageParam = 1 }) => {
|
||||||
const response = await apiRequest.http({
|
const response = await apiRequest.http({
|
||||||
...axios,
|
...axios,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ interface StorePlaidState {
|
|||||||
enableMultipleCategorization: boolean;
|
enableMultipleCategorization: boolean;
|
||||||
|
|
||||||
categorizedTransactionsSelected: Array<number | string>;
|
categorizedTransactionsSelected: Array<number | string>;
|
||||||
|
|
||||||
|
uncategorizedFilter: { fromDate?: string; toDate?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlaidSlice = createSlice({
|
export const PlaidSlice = createSlice({
|
||||||
@@ -31,6 +33,9 @@ export const PlaidSlice = createSlice({
|
|||||||
transactionsToCategorizeSelected: [],
|
transactionsToCategorizeSelected: [],
|
||||||
enableMultipleCategorization: false,
|
enableMultipleCategorization: false,
|
||||||
categorizedTransactionsSelected: [],
|
categorizedTransactionsSelected: [],
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
uncategorizedFilter: {},
|
||||||
} as StorePlaidState,
|
} as StorePlaidState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
|
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
|
||||||
@@ -199,6 +204,26 @@ export const PlaidSlice = createSlice({
|
|||||||
resetCategorizedTransactionsSelected: (state: StorePlaidState) => {
|
resetCategorizedTransactionsSelected: (state: StorePlaidState) => {
|
||||||
state.categorizedTransactionsSelected = [];
|
state.categorizedTransactionsSelected = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the uncategorized transactions filter.
|
||||||
|
* @param {StorePlaidState} state
|
||||||
|
* @param {PayloadAction<{ filter: any }>} action
|
||||||
|
*/
|
||||||
|
setUncategorizedTransactionsFilter: (
|
||||||
|
state: StorePlaidState,
|
||||||
|
action: PayloadAction<{ filter: any }>,
|
||||||
|
) => {
|
||||||
|
state.uncategorizedFilter = action.payload.filter;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the uncategorized transactions filter.
|
||||||
|
* @param {StorePlaidState} state
|
||||||
|
*/
|
||||||
|
resetUncategorizedTranasctionsFilter: (state: StorePlaidState) => {
|
||||||
|
state.uncategorizedFilter = {};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,6 +245,10 @@ export const {
|
|||||||
enableMultipleCategorization,
|
enableMultipleCategorization,
|
||||||
setCategorizedTransactionsSelected,
|
setCategorizedTransactionsSelected,
|
||||||
resetCategorizedTransactionsSelected,
|
resetCategorizedTransactionsSelected,
|
||||||
|
|
||||||
|
// Uncategorized transactions filter.
|
||||||
|
setUncategorizedTransactionsFilter,
|
||||||
|
resetUncategorizedTranasctionsFilter,
|
||||||
} = PlaidSlice.actions;
|
} = PlaidSlice.actions;
|
||||||
|
|
||||||
export const getPlaidToken = (state: any) => state.plaid.plaidToken;
|
export const getPlaidToken = (state: any) => state.plaid.plaidToken;
|
||||||
@@ -234,3 +263,6 @@ export const isMultipleCategorization = (state: any) =>
|
|||||||
|
|
||||||
export const getTransactionsToCategorizeIdsSelected = (state: any) =>
|
export const getTransactionsToCategorizeIdsSelected = (state: any) =>
|
||||||
state.plaid.transactionsToCategorizeSelected;
|
state.plaid.transactionsToCategorizeSelected;
|
||||||
|
|
||||||
|
export const getUncategorizedTransactionsFilter = (state: any) =>
|
||||||
|
state.uncategorizedFilter;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
&.bp4-small {
|
&.bp4-small {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
min-height: 29px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user