From 9db03350e08fcd99968edfc49bf431f597f6a44f Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 4 Mar 2024 13:41:15 +0200 Subject: [PATCH] feat(webapp): categorize the cashflow uncategorized transactions --- .../components/ContentTabs/ContentTabs.tsx | 6 +- .../AccountTransactionsFilterTabs.tsx | 10 +- .../AccountTransactionsList.tsx | 46 +++--- .../AccountTransactionsProvider.tsx | 58 +++++++- .../AccountTransactionsUncategorizedTable.tsx | 139 ++++++++++++++++++ .../AccountsTransactionsAll.tsx | 31 ++++ .../AllTransactionsUncategorized.tsx | 29 ++++ .../AccountTransactions/components.tsx | 72 ++++++++- .../CashFlowAccountsActionsBar.tsx | 4 +- .../drawers/CategorizeTransactionDrawer.tsx | 33 +++++ .../src/hooks/query/cashflowAccounts.tsx | 41 +++++- packages/webapp/src/hooks/query/types.tsx | 6 +- 12 files changed, 439 insertions(+), 36 deletions(-) create mode 100644 packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizedTable.tsx create mode 100644 packages/webapp/src/containers/CashFlow/AccountTransactions/AccountsTransactionsAll.tsx create mode 100644 packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx create mode 100644 packages/webapp/src/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer.tsx diff --git a/packages/webapp/src/components/ContentTabs/ContentTabs.tsx b/packages/webapp/src/components/ContentTabs/ContentTabs.tsx index f872430b3..58f844782 100644 --- a/packages/webapp/src/components/ContentTabs/ContentTabs.tsx +++ b/packages/webapp/src/components/ContentTabs/ContentTabs.tsx @@ -6,7 +6,6 @@ import { useUncontrolled } from '@/hooks/useUncontrolled'; const ContentTabsRoot = styled('div')` display: flex; gap: 10px; - `; interface ContentTabItemRootProps { active?: boolean; @@ -62,9 +61,10 @@ const ContentTabsItem = ({ title, description, active, + onClick, }: ContentTabsItemProps) => { return ( - + {title} {description} @@ -84,7 +84,7 @@ export function ContentTabs({ value, onChange, children, - className + className, }: ContentTabsProps) { const [localValue, handleItemChange] = useUncontrolled({ initialValue, diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx index d8a4b8056..aede21571 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsFilterTabs.tsx @@ -1,13 +1,21 @@ +// @ts-nocheck import styled from 'styled-components'; import { ContentTabs } from '@/components/ContentTabs/ContentTabs'; +import { useAccountTransactionsContext } from './AccountTransactionsProvider'; const AccountContentTabs = styled(ContentTabs)` margin: 15px 15px 0 15px; `; export function AccountTransactionsFilterTabs() { + const { filterTab, setFilterTab } = useAccountTransactionsContext(); + + const handleChange = (value) => { + setFilterTab(value); + }; + return ( - + - - - - - - - + }> + + ); @@ -41,14 +39,20 @@ function AccountTransactionsList() { export default AccountTransactionsList; -const CashflowTransactionsTableCard = styled.div` - border: 2px solid #f0f0f0; - border-radius: 10px; - padding: 30px 18px; - background: #fff; - flex: 0 1; -`; +const AccountsTransactionsAll = React.lazy( + () => import('./AccountsTransactionsAll'), +); -const Box = styled.div` - margin: 30px 15px; -`; +const AccountsTransactionsUncategorized = React.lazy( + () => import('./AllTransactionsUncategorized'), +); + +function AccountTransactionsContent() { + const { filterTab } = useAccountTransactionsContext(); + + return filterTab === 'uncategorized' ? ( + + ) : ( + + ); +} diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx index 744863b87..f0aa661e3 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsProvider.tsx @@ -7,7 +7,9 @@ import { useAccountTransactionsInfinity, useCashflowAccounts, useAccount, + useAccountUncategorizedTransactionsInfinity, } from '@/hooks/query'; +import { useAppQueryString } from '@/hooks'; const AccountTransactionsContext = React.createContext(); @@ -15,6 +17,10 @@ function flattenInfinityPages(data) { return flatten(map(data.pages, (page) => page.transactions)); } +function flattenInfinityPagesData(data) { + return flatten(map(data.pages, (page) => page.data)); +} + /** * Account transctions provider. */ @@ -22,6 +28,13 @@ function AccountTransactionsProvider({ query, ...props }) { const { id } = useParams(); const accountId = parseInt(id, 10); + const [locationQuery, setLocationQuery] = useAppQueryString(); + + const filterTab = locationQuery?.filter || 'all'; + const setFilterTab = (value: stirng) => { + setLocationQuery({ filter: value }); + }; + // Fetch cashflow account transactions list const { data: cashflowTransactionsPages, @@ -31,10 +44,32 @@ function AccountTransactionsProvider({ query, ...props }) { fetchNextPage: fetchNextTransactionsPage, isFetchingNextPage, hasNextPage, - } = useAccountTransactionsInfinity(accountId, { - page_size: 50, - account_id: accountId, - }); + } = useAccountTransactionsInfinity( + accountId, + { + page_size: 50, + account_id: accountId, + }, + { + enabled: filterTab === 'all' || filterTab === 'dashboard', + }, + ); + + const { + data: uncategorizedTransactionsPage, + isFetching: isUncategorizedTransactionFetching, + isLoading: isUncategorizedTransactionsLoading, + isSuccess: isUncategorizedTransactionsSuccess, + fetchNextPage: fetchNextUncategorizedTransactionsPage, + } = useAccountUncategorizedTransactionsInfinity( + accountId, + { + page_size: 50, + }, + { + enabled: filterTab === 'uncategorized', + }, + ); // Memorized the cashflow account transactions. const cashflowTransactions = React.useMemo( @@ -45,6 +80,15 @@ function AccountTransactionsProvider({ query, ...props }) { [cashflowTransactionsPages, isCashflowTransactionsSuccess], ); + // Memorized the cashflow account transactions. + const uncategorizedTransactions = React.useMemo( + () => + isUncategorizedTransactionsSuccess + ? flattenInfinityPagesData(uncategorizedTransactionsPage) + : [], + [uncategorizedTransactionsPage, isUncategorizedTransactionsSuccess], + ); + // Fetch cashflow accounts. const { data: cashflowAccounts, @@ -78,6 +122,12 @@ function AccountTransactionsProvider({ query, ...props }) { isCashFlowAccountsLoading, isCurrentAccountFetching, isCurrentAccountLoading, + + filterTab, + setFilterTab, + + uncategorizedTransactions, + isUncategorizedTransactionFetching }; return ( diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizedTable.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizedTable.tsx new file mode 100644 index 000000000..b6a671bb6 --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsUncategorizedTable.tsx @@ -0,0 +1,139 @@ +// @ts-nocheck +import React from 'react'; +import styled from 'styled-components'; + +import { + DataTable, + TableFastCell, + TableSkeletonRows, + TableSkeletonHeader, + TableVirtualizedListRows, + FormattedMessage as T, +} from '@/components'; +import { TABLES } from '@/constants/tables'; + +import withSettings from '@/containers/Settings/withSettings'; +import withAlertsActions from '@/containers/Alert/withAlertActions'; +import withDrawerActions from '@/containers/Drawer/withDrawerActions'; + +import { useMemorizedColumnsWidths } from '@/hooks'; +import { + ActionsMenu, + useAccountUncategorizedTransactionsColumns, +} from './components'; +import { useAccountTransactionsContext } from './AccountTransactionsProvider'; +import { handleCashFlowTransactionType } from './utils'; + +import { compose } from '@/utils'; + +/** + * Account transactions data table. + */ +function AccountTransactionsDataTable({ + // #withSettings + cashflowTansactionsTableSize, + + // #withAlertsActions + openAlert, + + // #withDrawerActions + openDrawer, +}) { + // Retrieve table columns. + const columns = useAccountUncategorizedTransactionsColumns(); + + // Retrieve list context. + const { uncategorizedTransactions, isCashFlowTransactionsLoading } = + useAccountTransactionsContext(); + + // Local storage memorizing columns widths. + const [initialColumnsWidths, , handleColumnResizing] = + useMemorizedColumnsWidths(TABLES.CASHFLOW_Transactions); + + // handle delete transaction + const handleDeleteTransaction = ({ reference_id }) => {}; + + const handleViewDetailCashflowTransaction = (referenceType) => {}; + + // Handle cell click. + const handleCellClick = (cell, event) => {}; + + return ( + } + className="table-constrant" + payload={{ + onViewDetails: handleViewDetailCashflowTransaction, + onDelete: handleDeleteTransaction, + }} + /> + ); +} + +export default compose( + withSettings(({ cashflowTransactionsSettings }) => ({ + cashflowTansactionsTableSize: cashflowTransactionsSettings?.tableSize, + })), + withAlertsActions, + withDrawerActions, +)(AccountTransactionsDataTable); + +const DashboardConstrantTable = styled(DataTable)` + .table { + .thead { + .th { + background: #fff; + } + } + + .tbody { + .tr:last-child .td { + border-bottom: 0; + } + } + } +`; + +const CashflowTransactionsTable = styled(DashboardConstrantTable)` + .table .tbody { + .tbody-inner .tr.no-results { + .td { + padding: 2rem 0; + font-size: 14px; + color: #888; + font-weight: 400; + border-bottom: 0; + } + } + + .tbody-inner { + .tr .td:not(:first-child) { + border-left: 1px solid #e6e6e6; + } + + .td-description { + color: #5F6B7C; + } + } + } +`; diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountsTransactionsAll.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountsTransactionsAll.tsx new file mode 100644 index 000000000..f181983af --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountsTransactionsAll.tsx @@ -0,0 +1,31 @@ +// @ts-nocheck +import styled from 'styled-components'; + +import '@/style/pages/CashFlow/AccountTransactions/List.scss'; + +import AccountTransactionsDataTable from './AccountTransactionsDataTable'; +import { AccountTransactionsUncategorizeFilter } from './AccountTransactionsUncategorizeFilter'; + +const Box = styled.div` + margin: 30px 15px; +`; + +const CashflowTransactionsTableCard = styled.div` + border: 2px solid #f0f0f0; + border-radius: 10px; + padding: 30px 18px; + background: #fff; + flex: 0 1; +`; + +export default function AccountTransactionsAll() { + return ( + + + + + + + + ); +} diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx new file mode 100644 index 000000000..1595b746d --- /dev/null +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx @@ -0,0 +1,29 @@ +// @ts-nocheck +import styled from 'styled-components'; + +import '@/style/pages/CashFlow/AccountTransactions/List.scss'; + +import AccountTransactionsUncategorizedTable from './AccountTransactionsUncategorizedTable'; + +const Box = styled.div` + margin: 30px 15px; +`; + +const CashflowTransactionsTableCard = styled.div` + border: 2px solid #f0f0f0; + border-radius: 10px; + padding: 30px 18px; + background: #fff; + flex: 0 1; +` + + +export default function AllTransactionsUncategorized() { + return ( + + + + + + ) +} \ No newline at end of file diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/components.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/components.tsx index 18e75161e..7d5b17f3a 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/components.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/components.tsx @@ -131,7 +131,75 @@ export function useAccountTransactionsColumns() { * Account transactions progress bar. */ export function AccountTransactionsProgressBar() { - const { isCashFlowTransactionsFetching } = useAccountTransactionsContext(); + const { isCashFlowTransactionsFetching, isUncategorizedTransactionFetching } = + useAccountTransactionsContext(); - return isCashFlowTransactionsFetching ? : null; + return isCashFlowTransactionsFetching || + isUncategorizedTransactionFetching ? ( + + ) : null; +} + +/** + * Retrieve account uncategorized transctions table columns. + */ +export function useAccountUncategorizedTransactionsColumns() { + return React.useMemo( + () => [ + { + id: 'date', + Header: intl.get('date'), + accessor: 'formatted_date', + width: 40, + clickable: true, + textOverview: true, + }, + { + id: 'description', + Header: 'Description', + accessor: 'description', + width: 160, + textOverview: true, + clickable: true, + }, + { + id: 'payee', + Header: 'Payee', + accessor: 'payee', + width: 60, + clickable: true, + textOverview: true, + }, + { + id: 'reference_number', + Header: intl.get('reference_no'), + accessor: 'reference_number', + width: 50, + className: 'reference_number', + clickable: true, + textOverview: true, + }, + { + id: 'deposit', + Header: intl.get('cash_flow.label.deposit'), + accessor: 'formattet_deposit_amount', + width: 40, + className: 'deposit', + textOverview: true, + align: 'right', + clickable: true, + }, + { + id: 'withdrawal', + Header: intl.get('cash_flow.label.withdrawal'), + accessor: 'formatted_withdrawal_amount', + className: 'withdrawal', + width: 40, + textOverview: true, + align: 'right', + clickable: true, + }, + ], + [], + ); } diff --git a/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashFlowAccountsActionsBar.tsx b/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashFlowAccountsActionsBar.tsx index a0028f231..66298dc8b 100644 --- a/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashFlowAccountsActionsBar.tsx +++ b/packages/webapp/src/containers/CashFlow/CashFlowAccounts/CashFlowAccountsActionsBar.tsx @@ -110,12 +110,12 @@ function CashFlowAccountsActionsBar({ - {/*