mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
re-structure to monorepo.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
// @ts-nocheck
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { AbilitySubject, AccountAction } from '@/constants/abilityOption';
|
||||
import { RESOURCES_TYPES } from '@/constants/resourcesTypes';
|
||||
|
||||
function AccountUniversalSearchItemSelectComponent({
|
||||
// #ownProps
|
||||
resourceType,
|
||||
resourceId,
|
||||
onAction,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
if (resourceType === RESOURCES_TYPES.ACCOUNT) {
|
||||
openDrawer('account-drawer', { accountId: resourceId });
|
||||
onAction && onAction();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const AccountUniversalSearchItemSelect = withDrawerActions(
|
||||
AccountUniversalSearchItemSelectComponent,
|
||||
);
|
||||
|
||||
/**
|
||||
* Transformes account item to search item.
|
||||
* @param {*} account
|
||||
* @returns
|
||||
*/
|
||||
const accountToSearch = (account) => ({
|
||||
id: account.id,
|
||||
text: `${account.name} - ${account.code}`,
|
||||
label: account.formatted_amount,
|
||||
reference: account,
|
||||
});
|
||||
|
||||
/**
|
||||
* Binds universal search account configure.
|
||||
*/
|
||||
export const universalSearchAccountBind = () => ({
|
||||
resourceType: RESOURCES_TYPES.ACCOUNT,
|
||||
optionItemLabel: intl.get('accounts'),
|
||||
selectItemAction: AccountUniversalSearchItemSelect,
|
||||
itemSelect: accountToSearch,
|
||||
permission: {
|
||||
ability: AccountAction.View,
|
||||
subject: AbilitySubject.Account,
|
||||
},
|
||||
});
|
||||
225
packages/webapp/src/containers/Accounts/AccountsActionsBar.tsx
Normal file
225
packages/webapp/src/containers/Accounts/AccountsActionsBar.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
Switch,
|
||||
Alignment,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import {
|
||||
AdvancedFilterPopover,
|
||||
If,
|
||||
Can,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
DashboardActionViewsList,
|
||||
DashboardFilterButton,
|
||||
DashboardRowsHeightButton,
|
||||
DashboardActionsBar
|
||||
} from '@/components';
|
||||
|
||||
import { AccountAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
|
||||
import { useRefreshAccounts } from '@/hooks/query/accounts';
|
||||
import { useAccountsChartContext } from './AccountsChartProvider';
|
||||
|
||||
import withAccounts from './withAccounts';
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import withAlertActions from '@/containers/Alert/withAlertActions';
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Accounts actions bar.
|
||||
*/
|
||||
function AccountsActionsBar({
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withAccounts
|
||||
accountsSelectedRows,
|
||||
accountsInactiveMode,
|
||||
accountsFilterConditions,
|
||||
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #withAccountsTableActions
|
||||
setAccountsTableState,
|
||||
|
||||
// #ownProps
|
||||
onFilterChanged,
|
||||
|
||||
// #withSettings
|
||||
accountsTableSize,
|
||||
|
||||
// #withSettingsActions
|
||||
addSetting,
|
||||
}) {
|
||||
const { resourceViews, fields } = useAccountsChartContext();
|
||||
|
||||
const onClickNewAccount = () => {
|
||||
openDialog(DialogsName.AccountForm, {});
|
||||
};
|
||||
|
||||
// Accounts refresh action.
|
||||
const { refresh } = useRefreshAccounts();
|
||||
|
||||
// Handle bulk accounts delete.
|
||||
const handleBulkDelete = () => {
|
||||
openAlert('accounts-bulk-delete', { accountsIds: accountsSelectedRows });
|
||||
};
|
||||
|
||||
// Handle bulk accounts activate.
|
||||
const handelBulkActivate = () => {
|
||||
openAlert('accounts-bulk-activate', { accountsIds: accountsSelectedRows });
|
||||
};
|
||||
|
||||
// Handle bulk accounts inactivate.
|
||||
const handelBulkInactive = () => {
|
||||
openAlert('accounts-bulk-inactivate', {
|
||||
accountsIds: accountsSelectedRows,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle tab changing.
|
||||
const handleTabChange = (view) => {
|
||||
setAccountsTableState({ viewSlug: view ? view.slug : null });
|
||||
};
|
||||
|
||||
// Handle inactive switch changing.
|
||||
const handleInactiveSwitchChange = (event) => {
|
||||
const checked = event.target.checked;
|
||||
setAccountsTableState({ inactiveMode: checked });
|
||||
};
|
||||
|
||||
// Handle click a refresh accounts
|
||||
const handleRefreshBtnClick = () => {
|
||||
refresh();
|
||||
};
|
||||
|
||||
// Handle table row size change.
|
||||
const handleTableRowSizeChange = (size) => {
|
||||
addSetting('accounts', 'tableSize', size);
|
||||
};
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<DashboardActionViewsList
|
||||
resourceName={'accounts'}
|
||||
allMenuItem={true}
|
||||
allMenuItemText={<T id={'all_accounts'} />}
|
||||
views={resourceViews}
|
||||
onChange={handleTabChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Can I={AccountAction.Create} a={AbilitySubject.Account}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="plus" />}
|
||||
text={<T id={'new_account'} />}
|
||||
onClick={onClickNewAccount}
|
||||
/>
|
||||
</Can>
|
||||
<AdvancedFilterPopover
|
||||
advancedFilterProps={{
|
||||
conditions: accountsFilterConditions,
|
||||
defaultFieldKey: 'name',
|
||||
fields: fields,
|
||||
onFilterChange: (filterConditions) => {
|
||||
setAccountsTableState({ filterRoles: filterConditions });
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DashboardFilterButton
|
||||
conditionsCount={accountsFilterConditions.length}
|
||||
/>
|
||||
</AdvancedFilterPopover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<If condition={!isEmpty(accountsSelectedRows)}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="play-16" iconSize={16} />}
|
||||
text={<T id={'activate'} />}
|
||||
onClick={handelBulkActivate}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||
text={<T id={'inactivate'} />}
|
||||
onClick={handelBulkInactive}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleBulkDelete}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={accountsTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Can I={AccountAction.Edit} a={AbilitySubject.Account}>
|
||||
<Switch
|
||||
labelElement={<T id={'inactive'} />}
|
||||
defaultChecked={accountsInactiveMode}
|
||||
onChange={handleInactiveSwitchChange}
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="refresh-16" iconSize={14} />}
|
||||
onClick={handleRefreshBtnClick}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withAlertActions,
|
||||
withSettingsActions,
|
||||
withAccounts(({ accountsSelectedRows, accountsTableState }) => ({
|
||||
accountsSelectedRows,
|
||||
accountsInactiveMode: accountsTableState.inactiveMode,
|
||||
accountsFilterConditions: accountsTableState.filterRoles,
|
||||
})),
|
||||
withSettings(({ accountsSettings }) => ({
|
||||
accountsTableSize: accountsSettings.tableSize,
|
||||
})),
|
||||
withAccountsTableActions,
|
||||
)(AccountsActionsBar);
|
||||
18
packages/webapp/src/containers/Accounts/AccountsAlerts.tsx
Normal file
18
packages/webapp/src/containers/Accounts/AccountsAlerts.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
const AccountDeleteAlert = React.lazy(
|
||||
() => import('@/containers/Alerts/Accounts/AccountDeleteAlert'),
|
||||
);
|
||||
const AccountInactivateAlert = React.lazy(
|
||||
() => import('@/containers/Alerts/Accounts/AccountInactivateAlert'),
|
||||
);
|
||||
const AccountActivateAlert = React.lazy(
|
||||
() => import('@/containers/Alerts/Accounts/AccountDeleteAlert'),
|
||||
);
|
||||
|
||||
export default [
|
||||
{ name: 'account-delete', component: AccountDeleteAlert },
|
||||
{ name: 'account-inactivate', component: AccountInactivateAlert },
|
||||
{ name: 'account-activate', component: AccountActivateAlert },
|
||||
];
|
||||
60
packages/webapp/src/containers/Accounts/AccountsChart.tsx
Normal file
60
packages/webapp/src/containers/Accounts/AccountsChart.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
// @ts-nocheck
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import '@/style/pages/Accounts/List.scss';
|
||||
import { DashboardPageContent, DashboardContentTable } from '@/components';
|
||||
|
||||
import { AccountsChartProvider } from './AccountsChartProvider';
|
||||
import AccountsViewsTabs from './AccountsViewsTabs';
|
||||
import AccountsActionsBar from './AccountsActionsBar';
|
||||
import AccountsDataTable from './AccountsDataTable';
|
||||
|
||||
import withAccounts from '@/containers/Accounts/withAccounts';
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
import { transformAccountsStateToQuery } from './utils';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Accounts chart list.
|
||||
*/
|
||||
function AccountsChart({
|
||||
// #withAccounts
|
||||
accountsTableState,
|
||||
accountsTableStateChanged,
|
||||
|
||||
// #withAccountsActions
|
||||
resetAccountsTableState,
|
||||
}) {
|
||||
// Resets the accounts table state once the page unmount.
|
||||
useEffect(
|
||||
() => () => {
|
||||
resetAccountsTableState();
|
||||
},
|
||||
[resetAccountsTableState],
|
||||
);
|
||||
|
||||
return (
|
||||
<AccountsChartProvider
|
||||
query={transformAccountsStateToQuery(accountsTableState)}
|
||||
tableStateChanged={accountsTableStateChanged}
|
||||
>
|
||||
<AccountsActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<AccountsViewsTabs />
|
||||
|
||||
<DashboardContentTable>
|
||||
<AccountsDataTable />
|
||||
</DashboardContentTable>
|
||||
</DashboardPageContent>
|
||||
</AccountsChartProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAccounts(({ accountsTableState, accountsTableStateChanged }) => ({
|
||||
accountsTableState,
|
||||
accountsTableStateChanged,
|
||||
})),
|
||||
withAccountsTableActions,
|
||||
)(AccountsChart);
|
||||
@@ -0,0 +1,59 @@
|
||||
// @ts-nocheck
|
||||
import React, { createContext } from 'react';
|
||||
import { DashboardInsider } from '@/components';
|
||||
import { useResourceViews, useResourceMeta, useAccounts } from '@/hooks/query';
|
||||
import { getFieldsFromResourceMeta } from '@/utils';
|
||||
|
||||
const AccountsChartContext = createContext();
|
||||
|
||||
/**
|
||||
* Accounts chart data provider.
|
||||
*/
|
||||
function AccountsChartProvider({ query, tableStateChanged, ...props }) {
|
||||
// Fetch accounts resource views and fields.
|
||||
const { data: resourceViews, isLoading: isViewsLoading } =
|
||||
useResourceViews('accounts');
|
||||
|
||||
// Fetch the accounts resource fields.
|
||||
const {
|
||||
data: resourceMeta,
|
||||
isLoading: isResourceMetaLoading,
|
||||
isFetching: isResourceMetaFetching,
|
||||
} = useResourceMeta('accounts');
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const {
|
||||
data: accounts,
|
||||
isFetching: isAccountsFetching,
|
||||
isLoading: isAccountsLoading,
|
||||
} = useAccounts(query, { keepPreviousData: true });
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
accounts,
|
||||
|
||||
resourceMeta,
|
||||
resourceViews,
|
||||
|
||||
fields: getFieldsFromResourceMeta(resourceMeta.fields),
|
||||
|
||||
isAccountsLoading,
|
||||
isAccountsFetching,
|
||||
isResourceMetaFetching,
|
||||
isResourceMetaLoading,
|
||||
isViewsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading || isResourceMetaLoading}
|
||||
name={'accounts-chart'}
|
||||
>
|
||||
<AccountsChartContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useAccountsChartContext = () => React.useContext(AccountsChartContext);
|
||||
|
||||
export { AccountsChartProvider, useAccountsChartContext };
|
||||
142
packages/webapp/src/containers/Accounts/AccountsDataTable.tsx
Normal file
142
packages/webapp/src/containers/Accounts/AccountsDataTable.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
TableFastCell,
|
||||
DataTable,
|
||||
TableSkeletonRows,
|
||||
TableSkeletonHeader,
|
||||
TableVirtualizedListRows,
|
||||
} from '@/components';
|
||||
import { useAccountsTableColumns, rowClassNames } from './utils';
|
||||
import { ActionsMenu } from './components';
|
||||
import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils';
|
||||
|
||||
import { useAccountsChartContext } from './AccountsChartProvider';
|
||||
import { useMemorizedColumnsWidths } from '@/hooks';
|
||||
|
||||
import { TABLES } from '@/constants/tables';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Accounts data-table.
|
||||
*/
|
||||
function AccountsDataTable({
|
||||
// #withAlertsDialog
|
||||
openAlert,
|
||||
|
||||
// #withDial
|
||||
openDialog,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
|
||||
// #withSettings
|
||||
accountsTableSize,
|
||||
}) {
|
||||
const { isAccountsLoading, isAccountsFetching, accounts } =
|
||||
useAccountsChartContext();
|
||||
|
||||
// Retrieve accounts table columns.
|
||||
const columns = useAccountsTableColumns();
|
||||
|
||||
// Handle delete action account.
|
||||
const handleDeleteAccount = (account) => {
|
||||
openAlert('account-delete', { accountId: account.id });
|
||||
};
|
||||
|
||||
// Handle activate action account.
|
||||
const handleActivateAccount = (account) => {
|
||||
openAlert('account-activate', { accountId: account.id });
|
||||
};
|
||||
|
||||
// Handle inactivate action account.
|
||||
const handleInactivateAccount = (account) => {
|
||||
openAlert('account-inactivate', { accountId: account.id });
|
||||
};
|
||||
|
||||
// Handle edit account action.
|
||||
const handleEditAccount = (account) => {
|
||||
openDialog(DialogsName.AccountForm, {
|
||||
action: AccountDialogAction.Edit,
|
||||
accountId: account.id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle view detail account.
|
||||
const handleViewDetailAccount = ({ id }) => {
|
||||
openDrawer('account-drawer', { accountId: id });
|
||||
};
|
||||
|
||||
// Handle new child button click.
|
||||
const handleNewChildAccount = (account) => {
|
||||
openDialog(DialogsName.AccountForm, {
|
||||
action: AccountDialogAction.NewChild,
|
||||
parentAccountId: account.id,
|
||||
accountType: account.account_type,
|
||||
});
|
||||
};
|
||||
// Handle cell click.
|
||||
const handleCellClick = (cell, event) => {
|
||||
openDrawer('account-drawer', { accountId: cell.row.original.id });
|
||||
};
|
||||
// Local storage memorizing columns widths.
|
||||
const [initialColumnsWidths, , handleColumnResizing] =
|
||||
useMemorizedColumnsWidths(TABLES.ACCOUNTS);
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={accounts}
|
||||
selectionColumn={true}
|
||||
expandable={true}
|
||||
sticky={true}
|
||||
loading={isAccountsLoading}
|
||||
headerLoading={isAccountsLoading}
|
||||
progressBarLoading={isAccountsFetching}
|
||||
rowClassNames={rowClassNames}
|
||||
autoResetExpanded={false}
|
||||
autoResetSortBy={false}
|
||||
autoResetSelectedRows={false}
|
||||
expandColumnSpace={1}
|
||||
expandToggleColumn={2}
|
||||
selectionColumnWidth={45}
|
||||
TableCellRenderer={TableFastCell}
|
||||
TableRowsRenderer={TableVirtualizedListRows}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||
ContextMenu={ActionsMenu}
|
||||
// #TableVirtualizedListRows props.
|
||||
vListrowHeight={accountsTableSize == 'small' ? 40 : 42}
|
||||
vListOverscanRowCount={0}
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
size={accountsTableSize}
|
||||
payload={{
|
||||
onEdit: handleEditAccount,
|
||||
onDelete: handleDeleteAccount,
|
||||
onActivate: handleActivateAccount,
|
||||
onInactivate: handleInactivateAccount,
|
||||
onNewChild: handleNewChildAccount,
|
||||
onViewDetails: handleViewDetailAccount,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withDialogActions,
|
||||
withSettings(({ accountsSettings }) => ({
|
||||
accountsTableSize: accountsSettings.tableSize,
|
||||
})),
|
||||
)(AccountsDataTable);
|
||||
@@ -0,0 +1,60 @@
|
||||
// @ts-nocheck
|
||||
import React, { useCallback } from 'react';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { DashboardViewsTabs } from '@/components';
|
||||
import { useAccountsChartContext } from './AccountsChartProvider';
|
||||
|
||||
import withAccounts from './withAccounts';
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
|
||||
import { compose, transfromViewsToTabs } from '@/utils';
|
||||
|
||||
/**
|
||||
* Accounts views tabs.
|
||||
*/
|
||||
function AccountsViewsTabs({
|
||||
// #withAccountsTableActions
|
||||
setAccountsTableState,
|
||||
|
||||
// #withAccounts
|
||||
accountsCurrentView
|
||||
}) {
|
||||
// Accounts chart context.
|
||||
const { resourceViews } = useAccountsChartContext();
|
||||
|
||||
// Handles the tab change.
|
||||
const handleTabChange = useCallback(
|
||||
(viewSlug) => {
|
||||
setAccountsTableState({
|
||||
viewSlug: viewSlug || null,
|
||||
});
|
||||
},
|
||||
[setAccountsTableState],
|
||||
);
|
||||
|
||||
// Transfromes the accounts views to tabs.
|
||||
const tabs = transfromViewsToTabs(resourceViews);
|
||||
|
||||
return (
|
||||
<Navbar className="navbar--dashboard-views">
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
<DashboardViewsTabs
|
||||
defaultTabText={intl.get('all_accounts_')}
|
||||
currentViewSlug={accountsCurrentView}
|
||||
resourceName={'accounts'}
|
||||
onChange={handleTabChange}
|
||||
tabs={tabs}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAccountsTableActions,
|
||||
withAccounts(({ accountsTableState }) => ({
|
||||
accountsCurrentView: accountsTableState.viewSlug
|
||||
}))
|
||||
)(AccountsViewsTabs);
|
||||
118
packages/webapp/src/containers/Accounts/components.tsx
Normal file
118
packages/webapp/src/containers/Accounts/components.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import {
|
||||
Position,
|
||||
Classes,
|
||||
Tooltip,
|
||||
MenuItem,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import { Can, Icon, If } from '@/components';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { AbilitySubject, AccountAction } from '@/constants/abilityOption';
|
||||
|
||||
/**
|
||||
* Accounts table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: {
|
||||
onEdit,
|
||||
onViewDetails,
|
||||
onDelete,
|
||||
onNewChild,
|
||||
onActivate,
|
||||
onInactivate,
|
||||
// onDrawer,
|
||||
},
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="reader-18" />}
|
||||
text={intl.get('view_details')}
|
||||
onClick={safeCallback(onViewDetails, original)}
|
||||
/>
|
||||
<Can I={AccountAction.Edit} a={AbilitySubject.Account}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('edit_account')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
icon={<Icon icon="plus" />}
|
||||
text={intl.get('new_child_account')}
|
||||
onClick={safeCallback(onNewChild, original)}
|
||||
/>
|
||||
<MenuDivider />
|
||||
</Can>
|
||||
<Can I={AccountAction.Edit} a={AbilitySubject.Account}>
|
||||
<If condition={original.active}>
|
||||
<MenuItem
|
||||
text={intl.get('inactivate_account')}
|
||||
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||
onClick={safeCallback(onInactivate, original)}
|
||||
/>
|
||||
</If>
|
||||
<If condition={!original.active}>
|
||||
<MenuItem
|
||||
text={intl.get('activate_account')}
|
||||
icon={<Icon icon="play-16" iconSize={16} />}
|
||||
onClick={safeCallback(onActivate, original)}
|
||||
/>
|
||||
</If>
|
||||
</Can>
|
||||
<Can I={AccountAction.Edit} a={AbilitySubject.Account}>
|
||||
<MenuItem
|
||||
text={intl.get('delete_account')}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal cell.
|
||||
*/
|
||||
export function NormalCell({ cell: { value } }) {
|
||||
const arrowDirection = value === 'credit' ? 'down' : 'up';
|
||||
|
||||
// Can't continue if the value is not `credit` or `debit`.
|
||||
if (['credit', 'debit'].indexOf(value) === -1) {
|
||||
return '';
|
||||
}
|
||||
return (
|
||||
<Tooltip
|
||||
className={Classes.TOOLTIP_INDICATOR}
|
||||
content={intl.get(value)}
|
||||
position={Position.RIGHT}
|
||||
hoverOpenDelay={100}
|
||||
>
|
||||
<Icon icon={`arrow-${arrowDirection}`} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance cell.
|
||||
*/
|
||||
export function BalanceCell({ cell }) {
|
||||
const account = cell.row.original;
|
||||
|
||||
return account.amount !== null ? (
|
||||
<span>
|
||||
{account.formatted_amount}
|
||||
{/* <Money amount={account.amount} currency={account.currency_code} /> */}
|
||||
</span>
|
||||
) : (
|
||||
<span class="placeholder">—</span>
|
||||
);
|
||||
}
|
||||
123
packages/webapp/src/containers/Accounts/utils.tsx
Normal file
123
packages/webapp/src/containers/Accounts/utils.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Tag } from '@blueprintjs/core';
|
||||
|
||||
import { If, AppToaster } from '@/components';
|
||||
import { NormalCell, BalanceCell } from './components';
|
||||
import { transformTableStateToQuery, isBlank } from '@/utils';
|
||||
|
||||
/**
|
||||
* Account name accessor.
|
||||
*/
|
||||
export const accountNameAccessor = (account) => {
|
||||
return (
|
||||
<span>
|
||||
<span class={'account-name'}>{account.name}</span>
|
||||
<If condition={account.description}>
|
||||
<span class={'account-desc'}>{account.description}</span>
|
||||
</If>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle delete errors in bulk and singular.
|
||||
*/
|
||||
export const handleDeleteErrors = (errors) => {
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('you_could_not_delete_predefined_accounts'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('cannot_delete_account_has_associated_transactions'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const AccountCodeAccessor = (row) =>
|
||||
!isBlank(row.code) ? (
|
||||
<Tag minimal={true} round={true} intent={Intent.NONE}>
|
||||
{row.code}
|
||||
</Tag>
|
||||
) : null;
|
||||
|
||||
/**
|
||||
* Accounts table columns.
|
||||
*/
|
||||
export const useAccountsTableColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'name',
|
||||
Header: intl.get('account_name'),
|
||||
accessor: 'name',
|
||||
className: 'account_name',
|
||||
width: 200,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
Header: intl.get('code'),
|
||||
accessor: AccountCodeAccessor,
|
||||
className: 'code',
|
||||
width: 80,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
Header: intl.get('type'),
|
||||
accessor: 'account_type_label',
|
||||
className: 'type',
|
||||
width: 140,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'normal',
|
||||
Header: intl.get('account_normal'),
|
||||
Cell: NormalCell,
|
||||
accessor: 'account_normal',
|
||||
className: 'normal',
|
||||
width: 80,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'currency',
|
||||
Header: intl.get('currency'),
|
||||
accessor: 'currency_code',
|
||||
width: 75,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
Header: intl.get('balance'),
|
||||
accessor: 'amount',
|
||||
Cell: BalanceCell,
|
||||
width: 150,
|
||||
clickable: true,
|
||||
align: 'right',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
export const rowClassNames = (row) => ({
|
||||
inactive: !row.original.active,
|
||||
});
|
||||
|
||||
/**
|
||||
* Transformes the table state to list query.
|
||||
*/
|
||||
export const transformAccountsStateToQuery = (tableState) => {
|
||||
return {
|
||||
...transformTableStateToQuery(tableState),
|
||||
inactive_mode: tableState.inactiveMode,
|
||||
}
|
||||
}
|
||||
21
packages/webapp/src/containers/Accounts/withAccounts.tsx
Normal file
21
packages/webapp/src/containers/Accounts/withAccounts.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
// @ts-nocheck
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getAccountsTableStateFactory,
|
||||
accountsTableStateChangedFactory,
|
||||
} from '@/store/accounts/accounts.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getAccountsTableState = getAccountsTableStateFactory();
|
||||
const accountsTableStateChanged = accountsTableStateChangedFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
accountsTableState: getAccountsTableState(state, props),
|
||||
accountsTableStateChanged: accountsTableStateChanged(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// @ts-nocheck
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
setAccountsTableState,
|
||||
resetAccountsTableState,
|
||||
} from '@/store/accounts/accounts.actions';
|
||||
|
||||
const mapActionsToProps = (dispatch) => ({
|
||||
setAccountsTableState: (queries) => dispatch(setAccountsTableState(queries)),
|
||||
resetAccountsTableState: () => dispatch(resetAccountsTableState()),
|
||||
});
|
||||
|
||||
export default connect(null, mapActionsToProps);
|
||||
Reference in New Issue
Block a user