re-structure to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 01:02:31 +02:00
parent 8242ec64ba
commit 7a0a13f9d5
10400 changed files with 46966 additions and 17223 deletions

View File

@@ -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,
},
});

View 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);

View 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 },
];

View 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);

View File

@@ -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 };

View 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);

View File

@@ -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);

View 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>
);
}

View 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,
}
}

View 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);
};

View File

@@ -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);