From 31f5cbf33589b1f30fdbe6e4044ed0f1b6a057e7 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 21 Dec 2025 16:03:15 +0200 Subject: [PATCH] fix: accounts suggest field --- packages/webapp/package.json | 2 +- .../Accounts/AccountsMultiSelect.tsx | 2 +- .../Accounts/AccountsSuggestField.tsx | 247 +- .../DataTableCells/AccountsListFieldCell.tsx | 6 +- .../src/components/Forms/BlueprintFormik.tsx | 21 +- .../webapp/src/components/Forms/Select.tsx | 4 +- .../OtherIncome/OtherIncomeFormFields.tsx | 53 +- .../OwnerContributionFormFields.tsx | 49 +- .../MoneyInDialog/TransactionTypeFields.tsx | 46 +- .../TransferFromAccountFormFields.tsx | 59 +- .../OtherExpense/OtherExpnseFormFields.tsx | 48 +- .../OwnerDrawings/OwnerDrawingsFormFields.tsx | 46 +- .../MoneyOutDialog/TransactionTypeFields.tsx | 45 +- .../TransferToAccountFormFields.tsx | 47 +- .../containers/Dialog/withDialogActions.tsx | 27 +- .../BadDebtDialog/BadDebtFormFields.tsx | 116 +- .../InventoryAdjustmentFormDialogFields.tsx | 59 +- .../QuickPaymentMadeFormFields.tsx | 6 +- .../QuickPaymentReceiveFormFields.tsx | 6 +- .../RefundCreditNoteFormFields.tsx | 131 +- .../RefundVendorCreditFormFields.tsx | 63 +- pnpm-lock.yaml | 2330 +++++------------ 22 files changed, 1189 insertions(+), 2224 deletions(-) diff --git a/packages/webapp/package.json b/packages/webapp/package.json index edc6bdd90..506add34a 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -8,7 +8,7 @@ "@bigcapital/utils": "*", "@blueprintjs-formik/core": "^0.3.7", "@blueprintjs-formik/datetime": "^0.4.0", - "@blueprintjs-formik/select": "^0.3.5", + "@blueprintjs-formik/select": "^0.4.5", "@blueprintjs/colors": "4.1.19", "@blueprintjs/core": "^4.20.2", "@blueprintjs/datetime": "^4.4.37", diff --git a/packages/webapp/src/components/Accounts/AccountsMultiSelect.tsx b/packages/webapp/src/components/Accounts/AccountsMultiSelect.tsx index a5929930a..ffc6d88c8 100644 --- a/packages/webapp/src/components/Accounts/AccountsMultiSelect.tsx +++ b/packages/webapp/src/components/Accounts/AccountsMultiSelect.tsx @@ -96,7 +96,7 @@ export function AccountsMultiSelect({ }; return ( - + ; +type AccountItemPredicate = ItemPredicate; // Create new account renderer. -const createNewItemRenderer = (query, active, handleClick) => { +const createNewItemRenderer = ( + query: string, + active: boolean, + handleClick: (event: React.MouseEvent) => void, +): React.ReactElement => { return ( { }; // Create new item from the given query string. -const createNewItemFromQuery = (name) => { +const createNewItemFromQuery = (name: string): Partial => { return { name }; }; // Filters accounts items. -const filterAccountsPredicater = (query, account, _index, exactMatch) => { +const filterAccountsPredicater: AccountItemPredicate = ( + query: string, + account: Account, + _index?: number, + exactMatch?: boolean, +): boolean => { const normalizedTitle = account.name.toLowerCase(); const normalizedQuery = query.toLowerCase(); @@ -45,78 +61,139 @@ const filterAccountsPredicater = (query, account, _index, exactMatch) => { } }; -/** - * Accounts suggest field. - */ -function AccountsSuggestFieldRoot({ - // #withDialogActions - openDialog, - - // #ownProps - accounts, - defaultSelectText = intl.formatMessage({ id: 'select_account' }), - - filterByParentTypes = [], - filterByTypes = [], - filterByNormal, - filterByRootTypes = [], - - allowCreate, - - ...suggestProps -}) { - const flattenAccounts = useMemo( - () => nestedArrayToflatten(accounts), - [accounts], - ); - const filteredAccounts = useMemo( - () => - filterAccountsByQuery(flattenAccounts, { - filterByParentTypes, - filterByTypes, - filterByNormal, - filterByRootTypes, - }), - [ - flattenAccounts, - filterByParentTypes, - filterByTypes, - filterByNormal, - filterByRootTypes, - ], - ); - const handleCreateItemSelect = useCallback( - (item) => { - if (!item.id) { - openDialog(DialogsName.AccountForm); - } - }, - [openDialog], - ); - // Maybe inject new item props to select component. - const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null; - const maybeCreateNewItemFromQuery = allowCreate - ? createNewItemFromQuery - : null; - +// Account item renderer for Suggest (non-Formik) +const accountItemRenderer: AccountItemRenderer = ( + item: Account, + { handleClick, modifiers }, +): React.ReactElement | null => { + if (!modifiers.matchesPredicate) { + return null; + } return ( - ); +}; + +// Input value renderer for Suggest (non-Formik) +const inputValueRenderer = (item: Account | null): string => { + if (item) { + return item.name || ''; + } + return ''; +}; + +// Props specific to the HOC (excluding component's own props) +interface AccountsSuggestFieldOwnProps { + // #withDialogActions + openDialog: (name: string, payload?: any) => void; + + // #ownProps + items: Account[]; + defaultSelectText?: string; + filterByParentTypes?: string[]; + filterByTypes?: string[]; + filterByNormal?: string; + filterByRootTypes?: string[]; + allowCreate?: boolean; } -export const AccountsSuggestField = R.compose(withDialogActions)( - AccountsSuggestFieldRoot, +// Props that the HOC provides to the wrapped component (should be omitted from external props) +type ProvidedSuggestProps = + | 'items' + | 'itemPredicate' + | 'onCreateItemSelect' + | 'valueAccessor' + | 'textAccessor' + | 'labelAccessor' + | 'resetOnClose' + | 'createNewItemRenderer' + | 'createNewItemFromQuery'; + +// Utility type to extract props from a component +type ComponentProps = C extends ComponentType ? P : never; + +/** + * HOC for Accounts Suggest Field logic. + * Returns a component that accepts the wrapped component's props minus the ones provided by the HOC. + */ +function withAccountsSuggestFieldLogic>( + Component: C, +): ComponentType< + AccountsSuggestFieldOwnProps & Omit, ProvidedSuggestProps> +> { + return function AccountsSuggestFieldLogic({ + // #withDialogActions + openDialog, + + // #ownProps + items, + defaultSelectText = intl.formatMessage({ id: 'select_account' }), + + filterByParentTypes = [], + filterByTypes = [], + filterByNormal, + filterByRootTypes = [], + + allowCreate, + + // SuggestProps - props that will be passed to Suggest/FSuggest + ...suggestProps + }: AccountsSuggestFieldOwnProps & + Omit, ProvidedSuggestProps>) { + const filteredAccounts = usePreprocessingAccounts(items, { + filterByParentTypes, + filterByTypes, + filterByNormal: filterByNormal ? [filterByNormal] : [], + filterByRootTypes, + }); + const handleCreateItemSelect = useCallback( + (item: Account | Partial) => { + if (!('id' in item) || !item.id) { + openDialog(DialogsName.AccountForm); + } + }, + [openDialog], + ); + // Maybe inject new item props to select component. + const maybeCreateNewItemRenderer = allowCreate + ? createNewItemRenderer + : undefined; + const maybeCreateNewItemFromQuery = allowCreate + ? createNewItemFromQuery + : undefined; + + // Build the SuggestProps to pass to the component + const processedSuggestProps = { + items: filteredAccounts, + itemPredicate: filterAccountsPredicater, + onCreateItemSelect: handleCreateItemSelect, + valueAccessor: 'id' as const, + textAccessor: 'name' as const, + labelAccessor: 'code' as const, + inputProps: { placeholder: defaultSelectText }, + resetOnClose: true, + popoverProps: { minimal: true, boundary: 'window' as const }, + createNewItemRenderer: maybeCreateNewItemRenderer, + createNewItemFromQuery: maybeCreateNewItemFromQuery, + ...suggestProps, + } as ComponentProps; + + return ; + }; +} +const AccountsSuggestFieldWithLogic = withAccountsSuggestFieldLogic(Suggest); +const FAccountsSuggestFieldWithLogic = withAccountsSuggestFieldLogic(FSuggest); + +export const AccountsSuggestField = withDialogActions( + AccountsSuggestFieldWithLogic, +); +export const FAccountsSuggestField = withDialogActions( + FAccountsSuggestFieldWithLogic, ); diff --git a/packages/webapp/src/components/DataTableCells/AccountsListFieldCell.tsx b/packages/webapp/src/components/DataTableCells/AccountsListFieldCell.tsx index ea401bc84..97b2ec376 100644 --- a/packages/webapp/src/components/DataTableCells/AccountsListFieldCell.tsx +++ b/packages/webapp/src/components/DataTableCells/AccountsListFieldCell.tsx @@ -58,9 +58,9 @@ export default function AccountCellRenderer({ {...formGroupProps} > ); - return