diff --git a/client/src/common/classes.js b/client/src/common/classes.js index 79e07b132..396700ebb 100644 --- a/client/src/common/classes.js +++ b/client/src/common/classes.js @@ -33,7 +33,7 @@ const CLASSES = { PAGE_FORM_PAYMENT_MADE: 'page-form--payment-made', PAGE_FORM_PAYMENT_RECEIVE: 'page-form--payment-receive', PAGE_FORM_CUSTOMER: 'page-form--customer', - PAGE_FORM_VENDOR: 'page-form--customer', + PAGE_FORM_VENDOR: 'page-form--vendor', PAGE_FORM_ITEM: 'page-form--item', PAGE_FORM_MAKE_JOURNAL: 'page-form--make-journal-entries', PAGE_FORM_EXPENSE: 'page-form--expense', diff --git a/client/src/components/DialogsContainer.js b/client/src/components/DialogsContainer.js index a84df5e5f..c7fb79fff 100644 --- a/client/src/components/DialogsContainer.js +++ b/client/src/components/DialogsContainer.js @@ -19,7 +19,7 @@ import PaymentViaVoucherDialog from 'containers/Dialogs/PaymentViaVoucherDialog' export default function DialogsContainer() { return (
- + {/* */} diff --git a/client/src/containers/Accounting/ManualJournalActionsBar.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalActionsBar.js similarity index 77% rename from client/src/containers/Accounting/ManualJournalActionsBar.js rename to client/src/containers/Accounting/JournalsLanding/ManualJournalActionsBar.js index 2fe0fd561..35f900df7 100644 --- a/client/src/containers/Accounting/ManualJournalActionsBar.js +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalActionsBar.js @@ -13,18 +13,14 @@ import { import classNames from 'classnames'; import { useHistory } from 'react-router-dom'; import { FormattedMessage as T } from 'react-intl'; -import { connect } from 'react-redux'; -import { useManualJournalsContext } from 'containers/Accounting/ManualJournalsListProvider'; +import { useManualJournalsContext } from './ManualJournalsListProvider'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import withDialogActions from 'containers/Dialog/withDialogActions'; import { If, DashboardActionViewsList } from 'components'; -import withResourceDetail from 'containers/Resources/withResourceDetails'; -import withManualJournals from 'containers/Accounting/withManualJournals'; -import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions'; - +import withManualJournalsActions from './withManualJournalsActions'; import { compose } from 'utils'; /** @@ -32,9 +28,12 @@ import { compose } from 'utils'; */ function ManualJournalActionsBar({ // #withManualJournalsActions - addManualJournalsTableQueries, + setManualJournalsTableState, }) { + // History context. const history = useHistory(); + + // Manual journals context. const { journalsViews } = useManualJournalsContext(); // Handle click a new manual journal. @@ -44,13 +43,13 @@ function ManualJournalActionsBar({ // Handle delete button click. const handleBulkDelete = () => { - + }; // Handle tab change. const handleTabChange = (viewId) => { - addManualJournalsTableQueries({ - custom_view_id: viewId.id || null, + setManualJournalsTableState({ + customViewid: viewId.id || null, }); }; @@ -115,20 +114,7 @@ function ManualJournalActionsBar({ ); } -const mapStateToProps = (state, props) => ({ - resourceName: 'manual_journals', -}); - -const withManualJournalsActionsBar = connect(mapStateToProps); - export default compose( - withManualJournalsActionsBar, withDialogActions, - withResourceDetail(({ resourceFields }) => ({ - resourceFields, - })), - withManualJournals(({ manualJournalsViews }) => ({ - manualJournalsViews, - })), withManualJournalsActions, )(ManualJournalActionsBar); diff --git a/client/src/containers/Accounting/ManualJournalsAlerts.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsAlerts.js similarity index 100% rename from client/src/containers/Accounting/ManualJournalsAlerts.js rename to client/src/containers/Accounting/JournalsLanding/ManualJournalsAlerts.js diff --git a/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js new file mode 100644 index 000000000..d9f89d5e1 --- /dev/null +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js @@ -0,0 +1,118 @@ +import React from 'react'; +import classNames from 'classnames'; +import { DataTable, Choose } from 'components'; +import { CLASSES } from 'common/classes'; + +import ManualJournalsEmptyStatus from './ManualJournalsEmptyStatus'; +import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; +import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; + +import withManualJournalsActions from './withManualJournalsActions'; +import withAlertsActions from 'containers/Alert/withAlertActions'; + +import { useManualJournalsContext } from './ManualJournalsListProvider'; +import { useManualJournalsColumns } from './utils'; +import { ActionsMenu } from './components'; + +import { compose } from 'utils'; + +/** + * Manual journals data-table. + */ +function ManualJournalsDataTable({ + // #withManualJournalsActions + setManualJournalsTableState, + + // #withAlertsActions + openAlert, + + // #ownProps + onSelectedRowsChange, +}) { + // Manual journals context. + const { + manualJournals, + pagination, + isManualJournalsLoading, + isManualJournalsFetching, + isEmptyStatus + } = useManualJournalsContext(); + + // Manual journals columns. + const columns = useManualJournalsColumns(); + + // Handles the journal publish action. + const handlePublishJournal = ({ id }) => { + openAlert('journal-publish', { manualJournalId: id }) + }; + + // Handle the journal edit action. + const handleEditJournal = ({ id }) => { + + }; + + // Handle the journal delete action. + const handleDeleteJournal = ({ id }) => { + openAlert('journal-delete', { manualJournalId: id }); + }; + + // Handle fetch data once the page index, size or sort by of the table change. + const handleFetchData = React.useCallback( + ({ pageSize, pageIndex, sortBy }) => { + setManualJournalsTableState({ + pageIndex, + pageSize, + sortBy, + }); + }, + [setManualJournalsTableState], + ); + + return ( +
+ + + + + + + + + +
+ ); +} + +export default compose( + withManualJournalsActions, + withAlertsActions +)(ManualJournalsDataTable); diff --git a/client/src/containers/Accounting/ManualJournalsEmptyStatus.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsEmptyStatus.js similarity index 100% rename from client/src/containers/Accounting/ManualJournalsEmptyStatus.js rename to client/src/containers/Accounting/JournalsLanding/ManualJournalsEmptyStatus.js diff --git a/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js new file mode 100644 index 000000000..b04d2c786 --- /dev/null +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js @@ -0,0 +1,56 @@ +import React, { useEffect } from 'react'; +import { useIntl } from 'react-intl'; + +import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; + +import { ManualJournalsListProvider } from './ManualJournalsListProvider'; +import ManualJournalsAlerts from './ManualJournalsAlerts'; +import ManualJournalsViewTabs from './ManualJournalsViewTabs'; +import ManualJournalsDataTable from './ManualJournalsDataTable'; +import ManualJournalsActionsBar from './ManualJournalActionsBar'; + +import withDashboardActions from 'containers/Dashboard/withDashboardActions'; +import withManualJournals from './withManualJournals'; + +import { transformTableStateToQuery, compose } from 'utils'; + +import 'style/pages/ManualJournal/List.scss'; + +/** + * Manual journals table. + */ +function ManualJournalsTable({ + // #withDashboardActions + changePageTitle, + + // #withManualJournals + journalsTableState, +}) { + const { formatMessage } = useIntl(); + + // Handle update the page title. + useEffect(() => { + changePageTitle(formatMessage({ id: 'manual_journals' })); + }, [changePageTitle, formatMessage]); + + return ( + + + + + + + + + + ); +} + +export default compose( + withDashboardActions, + withManualJournals(({ manualJournalsTableState }) => ({ + journalsTableState: manualJournalsTableState, + })), +)(ManualJournalsTable); diff --git a/client/src/containers/Accounting/ManualJournalsListProvider.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsListProvider.js similarity index 62% rename from client/src/containers/Accounting/ManualJournalsListProvider.js rename to client/src/containers/Accounting/JournalsLanding/ManualJournalsListProvider.js index 6a28387f4..d9f512714 100644 --- a/client/src/containers/Accounting/ManualJournalsListProvider.js +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalsListProvider.js @@ -1,28 +1,39 @@ import React, { createContext } from 'react'; import DashboardInsider from 'components/Dashboard/DashboardInsider'; import { useResourceViews, useJournals } from 'hooks/query'; +import { isTableEmptyStatus } from 'utils'; const ManualJournalsContext = createContext(); function ManualJournalsListProvider({ query, ...props }) { // Fetches accounts resource views and fields. - const { data: journalsViews, isFetching: isViewsLoading } = useResourceViews( + const { data: journalsViews, isLoading: isViewsLoading } = useResourceViews( 'manual_journals', ); // Fetches the manual journals transactions with pagination meta. const { - data: { manualJournals }, - isFetching: isManualJournalsLoading, - } = useJournals(query); + data: { manualJournals, pagination, filterMeta }, + isLoading: isManualJournalsLoading, + isFetching: isManualJournalsFetching + } = useJournals(query, { keepPreviousData: true }); + + // Detarmines the datatable empty status. + const isEmptyStatus = isTableEmptyStatus({ + data: manualJournals, pagination, filterMeta, + }) && !isManualJournalsFetching; // Global state. const state = { manualJournals, + pagination, journalsViews, isManualJournalsLoading, + isManualJournalsFetching, isViewsLoading, + + isEmptyStatus }; return ( diff --git a/client/src/containers/Accounting/ManualJournalsViewTabs.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsViewTabs.js similarity index 72% rename from client/src/containers/Accounting/ManualJournalsViewTabs.js rename to client/src/containers/Accounting/JournalsLanding/ManualJournalsViewTabs.js index 25cba1767..e4b06c652 100644 --- a/client/src/containers/Accounting/ManualJournalsViewTabs.js +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalsViewTabs.js @@ -8,17 +8,21 @@ import { DashboardViewsTabs } from 'components'; import { useManualJournalsContext } from './ManualJournalsListProvider'; import withManualJournalsActions from './withManualJournalsActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; +import withManualJournals from './withManualJournals'; -import { compose, saveInvoke } from 'utils'; +import { compose } from 'utils'; /** * Manual journal views tabs. */ function ManualJournalsViewTabs({ // #withManualJournalsActions - addManualJournalsTableQueries, + setManualJournalsTableState, + + // #withManualJournals + journalsTableState }) { - const { custom_view_id: customViewId } = useParams(); + // Manual journals context. const { journalsViews } = useManualJournalsContext(); const tabs = journalsViews.map((view) => ({ @@ -27,9 +31,10 @@ function ManualJournalsViewTabs({ const handleClickNewView = () => {}; + // Handles the tab change. const handleTabChange = (viewId) => { - addManualJournalsTableQueries({ - custom_view_id: viewId || null, + setManualJournalsTableState({ + customViewId: viewId || null, }); }; @@ -38,7 +43,7 @@ function ManualJournalsViewTabs({ ({ + journalsTableState: manualJournalsTableState, + })), )(ManualJournalsViewTabs); diff --git a/client/src/containers/Accounting/components.js b/client/src/containers/Accounting/JournalsLanding/components.js similarity index 54% rename from client/src/containers/Accounting/components.js rename to client/src/containers/Accounting/JournalsLanding/components.js index e516dbc51..f0d5472ab 100644 --- a/client/src/containers/Accounting/components.js +++ b/client/src/containers/Accounting/JournalsLanding/components.js @@ -6,16 +6,20 @@ import { Position, Tag, Button, + MenuItem, + Menu, + MenuDivider, + Popover, } from '@blueprintjs/core'; import { FormattedMessage as T } from 'react-intl'; import moment from 'moment'; -import { Choose, Money, If, Icon, Hint } from 'components'; +import { Choose, Money, If, Icon } from 'components'; +import { safeCallback } from 'utils'; +import { formatMessage } from 'services/intl'; -import withAccountDetails from 'containers/Accounts/withAccountDetail'; - -import { compose } from 'utils'; - -// Amount accessor. +/** + * Amount accessor. + */ export const AmountAccessor = (r) => ( } @@ -26,15 +30,13 @@ export const AmountAccessor = (r) => ( ); -const AmountPopoverContentLineRender = ({ - journalEntry, - accountId, - - // #withAccountDetail - account, -}) => { +/** + * Amount popover content line. + */ +export const AmountPopoverContentLine = ({ journalEntry }) => { const isCredit = !!journalEntry.credit; const isDebit = !!journalEntry.debit; + const { account } = journalEntry; return ( @@ -55,10 +57,9 @@ const AmountPopoverContentLineRender = ({ ); }; -const AmountPopoverContentLine = compose(withAccountDetails)( - AmountPopoverContentLineRender, -); - +/** + * Amount popover content. + */ export function AmountPopoverContent({ journalEntries }) { const journalLinesProps = journalEntries.map((journalEntry) => ({ journalEntry, @@ -78,7 +79,7 @@ export function AmountPopoverContent({ journalEntries }) { } /** - * publish column accessor. + * Publish column accessor. */ export const StatusAccessor = (row) => { return ( @@ -123,79 +124,52 @@ export function NoteAccessor(row) { ); } -// Contact header cell. -export function ContactHeaderCell() { +/** + * Table actions cell. + */ +export const ActionsCell = (props) => { return ( - <> - - } - position={Position.LEFT_BOTTOM} - /> - + } + position={Position.RIGHT_BOTTOM} + > +
) } \ No newline at end of file diff --git a/client/src/containers/Accounts/AccountsChart.js b/client/src/containers/Accounts/AccountsChart.js index e4f9529b3..75daab70f 100644 --- a/client/src/containers/Accounts/AccountsChart.js +++ b/client/src/containers/Accounts/AccountsChart.js @@ -4,15 +4,17 @@ import { useIntl } from 'react-intl'; import 'style/pages/Accounts/List.scss'; import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; -import { AccountsChartProvider } from 'containers/Accounts/AccountsChartProvider'; -import AccountsViewPage from 'containers/Accounts/AccountsViewPage'; +import { AccountsChartProvider } from './AccountsChartProvider'; + +import AccountsViewsTabs from 'containers/Accounts/AccountsViewsTabs'; import AccountsActionsBar from 'containers/Accounts/AccountsActionsBar'; import AccountsAlerts from './AccountsAlerts'; +import AccountsDataTable from './AccountsDataTable'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withAccounts from 'containers/Accounts/withAccounts'; -import { compose } from 'utils'; +import { transformTableStateToQuery, compose } from 'utils'; /** * Accounts chart list. @@ -22,7 +24,7 @@ function AccountsChart({ changePageTitle, // #withAccounts - accountsTableQuery + accountsTableState, }) { const { formatMessage } = useIntl(); @@ -31,11 +33,14 @@ function AccountsChart({ }, [changePageTitle, formatMessage]); return ( - + - + + @@ -45,7 +50,5 @@ function AccountsChart({ export default compose( withDashboardActions, - withAccounts(({ accountsTableQuery }) => ({ - accountsTableQuery, - })), + withAccounts(({ accountsTableState }) => ({ accountsTableState })), )(AccountsChart); diff --git a/client/src/containers/Accounts/AccountsChartProvider.js b/client/src/containers/Accounts/AccountsChartProvider.js index cb8375f63..ff9985afb 100644 --- a/client/src/containers/Accounts/AccountsChartProvider.js +++ b/client/src/containers/Accounts/AccountsChartProvider.js @@ -9,18 +9,22 @@ const AccountsChartContext = createContext(); */ function AccountsChartProvider({ query, ...props }) { // Fetch accounts resource views and fields. - const { data: resourceViews, isFetching: isViewsLoading } = useResourceViews( + const { data: resourceViews, isLoading: isViewsLoading } = useResourceViews( 'accounts', ); // Fetch the accounts resource fields. const { data: resourceFields, - isFetching: isFieldsLoading, + isLoading: isFieldsLoading, } = useResourceFields('accounts'); // Fetch accounts list according to the given custom view id. - const { data: accounts, isFetching: isAccountsLoading } = useAccounts( + const { + data: accounts, + isFetching: isAccountsFetching, + isLoading: isAccountsLoading + } = useAccounts( query, { keepPreviousData: true } ); @@ -32,6 +36,7 @@ function AccountsChartProvider({ query, ...props }) { resourceViews, isAccountsLoading, + isAccountsFetching, isFieldsLoading, isViewsLoading, }; diff --git a/client/src/containers/Accounts/AccountsDataTable.js b/client/src/containers/Accounts/AccountsDataTable.js index 25d89fc1c..07d9f9ba8 100644 --- a/client/src/containers/Accounts/AccountsDataTable.js +++ b/client/src/containers/Accounts/AccountsDataTable.js @@ -1,127 +1,75 @@ -import React, { useCallback, useMemo } from 'react'; -import { Button, Popover, Position } from '@blueprintjs/core'; -import { useIntl } from 'react-intl'; +import React from 'react'; import classNames from 'classnames'; -import { Icon, DataTable } from 'components'; -import { saveInvoke } from 'utils'; +import { TableFastCell, DataTable } from 'components'; +import { compose } from 'utils'; import { CLASSES } from 'common/classes'; -import { NormalCell, BalanceCell, AccountActionsMenu } from './components'; -import { TableFastCell } from 'components'; +import { useAccountsTableColumns, rowClassNames } from './utils'; +import { ActionsMenu } from './components'; import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; +import { useAccountsChartContext } from './AccountsChartProvider'; + +import withAlertsActions from 'containers/Alert/withAlertActions'; +import withDialogActions from 'containers/Dialog/withDialogActions'; /** * Accounts data-table. */ -export default function AccountsDataTable({ - // #ownProps - accounts, - loading, - onFetchData, - onSelectedRowsChange, - // onDeleteAccount, - // onInactivateAccount, - // onActivateAccount, - // onEditAccount, - // onNewChildAccount, +function AccountsDataTable({ + // #withAlertsDialog + openAlert, + + // #withDial + openDialog, }) { - const { formatMessage } = useIntl(); + const { + isAccountsLoading, + isAccountsFetching, + accounts, + } = useAccountsChartContext(); - const ActionsCell = useMemo( - () => ({ row }) => ( - } - position={Position.RIGHT_TOP} - > -