feat(webapp): refactor customer and vendor select component

This commit is contained in:
Ahmed Bouhuolia
2023-06-22 17:26:33 +02:00
parent ca4d543482
commit eadaac30d6
16 changed files with 495 additions and 607 deletions

View File

@@ -1,119 +0,0 @@
// @ts-nocheck
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { FormattedMessage as T } from '@/components';
import intl from 'react-intl-universal';
import * as R from 'ramda';
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
itemPredicate,
handleContactRenderer,
createNewItemRenderer,
createNewItemFromQuery,
} from './utils';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
function CustomerSelectFieldRoot({
// #withDrawerActions
openDrawer,
// #ownProps
contacts,
initialContactId,
selectedContactId,
defaultSelectText = <T id={'select_contact'} />,
onContactSelected,
popoverFill = false,
disabled = false,
allowCreate,
buttonProps,
...restProps
}) {
const localContacts = useMemo(
() =>
contacts.map((contact) => ({
...contact,
_id: `${contact.id}_${contact.contact_type}`,
})),
[contacts],
);
const initialContact = useMemo(
() => contacts.find((a) => a.id === initialContactId),
[initialContactId, contacts],
);
const [selecetedContact, setSelectedContact] = useState(
initialContact || null,
);
useEffect(() => {
if (typeof selectedContactId !== 'undefined') {
const account = selectedContactId
? contacts.find((a) => a.id === selectedContactId)
: null;
setSelectedContact(account);
}
}, [selectedContactId, contacts, setSelectedContact]);
const handleContactSelect = useCallback(
(contact) => {
if (contact.id) {
setSelectedContact({ ...contact });
onContactSelected && onContactSelected(contact);
} else {
openDrawer(DRAWERS.QUICK_CREATE_CUSTOMER);
}
},
[setSelectedContact, onContactSelected, openDrawer],
);
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
return (
<Select
items={localContacts}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
itemRenderer={handleContactRenderer}
itemPredicate={itemPredicate}
filterable={true}
disabled={disabled}
onItemSelect={handleContactSelect}
popoverProps={{ minimal: true, usePortal: !popoverFill }}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}
inputProps={{
placeholder: intl.get('filter_'),
}}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
createNewItemPosition={'top'}
{...restProps}
>
<Button
disabled={disabled}
text={
selecetedContact ? selecetedContact.display_name : defaultSelectText
}
{...buttonProps}
/>
</Select>
);
}
export const CustomerSelectField = R.compose(withDrawerActions)(
CustomerSelectFieldRoot,
);

View File

@@ -0,0 +1,48 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import { createNewItemFromQuery, createNewItemRenderer } from './utils';
import { FSelect } from '../Forms';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
/**
* Customer select field.
* @returns {React.ReactNode}
*/
function CustomerSelectRoot({
// #withDrawerActions
openDrawer,
// #ownProps
items,
allowCreate,
...props
}) {
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
// Handles the create item click.
const handleCreateItemClick = () => {
openDrawer(DRAWERS.QUICK_CREATE_CUSTOMER);
};
return (
<FSelect
items={items}
textAccessor={'display_name'}
labelAccessor={'code'}
valueAccessor={'id'}
popoverProps={{ minimal: true, usePortal: true, inline: false }}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
onCreateItemSelect={handleCreateItemClick}
{...props}
/>
);
}
export const CustomersSelect = R.compose(withDrawerActions)(CustomerSelectRoot);

View File

@@ -1,3 +1,3 @@
// @ts-nocheck
export * from './CustomerSelectField';
export * from './CustomerDrawerLink';
export * from './CustomersSelect';

View File

@@ -1,118 +0,0 @@
// @ts-nocheck
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { FormattedMessage as T } from '@/components';
import intl from 'react-intl-universal';
import * as R from 'ramda';
import { MenuItem, Button } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
itemPredicate,
handleContactRenderer,
createNewItemFromQuery,
createNewItemRenderer,
} from './utils';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
function VendorSelectFieldRoot({
// #withDrawerActions
openDrawer,
// #ownProps
contacts,
initialContactId,
selectedContactId,
defaultSelectText = <T id={'select_contact'} />,
onContactSelected,
popoverFill = false,
disabled = false,
allowCreate,
buttonProps,
...restProps
}) {
const localContacts = useMemo(
() =>
contacts.map((contact) => ({
...contact,
_id: `${contact.id}_${contact.contact_type}`,
})),
[contacts],
);
const initialContact = useMemo(
() => contacts.find((a) => a.id === initialContactId),
[initialContactId, contacts],
);
const [selecetedContact, setSelectedContact] = useState(
initialContact || null,
);
useEffect(() => {
if (typeof selectedContactId !== 'undefined') {
const account = selectedContactId
? contacts.find((a) => a.id === selectedContactId)
: null;
setSelectedContact(account);
}
}, [selectedContactId, contacts, setSelectedContact]);
const handleContactSelect = useCallback(
(contact) => {
if (contact.id) {
setSelectedContact({ ...contact });
onContactSelected && onContactSelected(contact);
} else {
openDrawer(DRAWERS.QUICK_WRITE_VENDOR);
}
},
[setSelectedContact, onContactSelected, openDrawer],
);
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
return (
<Select
items={localContacts}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
itemRenderer={handleContactRenderer}
itemPredicate={itemPredicate}
filterable={true}
disabled={disabled}
onItemSelect={handleContactSelect}
popoverProps={{ minimal: true, usePortal: !popoverFill }}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
})}
inputProps={{
placeholder: intl.get('filter_'),
}}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
createNewItemPosition={'top'}
{...restProps}
>
<Button
disabled={disabled}
text={
selecetedContact ? selecetedContact.display_name : defaultSelectText
}
{...buttonProps}
/>
</Select>
);
}
export const VendorSelectField = R.compose(withDrawerActions)(
VendorSelectFieldRoot,
);

View File

@@ -0,0 +1,49 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { createNewItemFromQuery, createNewItemRenderer } from './utils';
import { FSelect } from '../Forms';
import { DRAWERS } from '@/constants/drawers';
/**
* Vendor select.
* @returns {React.ReactNode}
*/
function VendorsSelectRoot({
// #withDrawerActions
openDrawer,
// #ownProps
items,
allowCreate,
...restProps
}) {
// Maybe inject create new item props to suggest component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
// Handles the create item click.
const handleCreateItemClick = () => {
openDrawer(DRAWERS.QUICK_WRITE_VENDOR);
};
return (
<FSelect
items={items}
textAccessor={'display_name'}
labelAccessor={'code'}
valueAccessor={'id'}
popoverProps={{ minimal: true, usePortal: true, inline: false }}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
onCreateItemSelect={handleCreateItemClick}
{...restProps}
/>
);
}
export const VendorsSelect = R.compose(withDrawerActions)(VendorsSelectRoot);

View File

@@ -1,3 +1,3 @@
// @ts-nocheck
export * from './VendorDrawerLink'
export * from './VendorSelectField'
export * from './VendorsSelect';

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FormattedMessage as T } from '@/components';
import { CustomersSelect, FormattedMessage as T } from '@/components';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import {
@@ -121,33 +121,35 @@ export default function ExpenseFormHeader() {
)}
</FastField>
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer'} />}
className={classNames('form-group--select-list', Classes.FILL)}
labelInfo={<Hint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'assign_to_customer'} />}
inline={true}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
}}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
)}
</FastField>
{/* ----------- Customer ----------- */}
<ExpenseFormCustomerSelect />
</div>
);
}
/**
* Customer select field of expense form.
* @returns {React.ReactNode}
*/
function ExpenseFormCustomerSelect() {
const { customers } = useExpenseFormContext();
return (
<FormGroup
label={<T id={'customer'} />}
labelInfo={<Hint />}
inline={true}
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
);
}

View File

@@ -15,11 +15,10 @@ import {
FMoneyInputGroup,
InputPrependText,
FormattedMessage as T,
FieldRequiredHint,
CustomerSelectField,
Stack,
CustomersSelect,
} from '@/components';
import { inputIntent, momentFormatter } from '@/utils';
import { momentFormatter } from '@/utils';
import { useProjectFormContext } from './ProjectFormProvider';
/**
@@ -27,9 +26,6 @@ import { useProjectFormContext } from './ProjectFormProvider';
* @returns
*/
function ProjectFormFields() {
// project form dialog context.
const { customers } = useProjectFormContext();
// Formik context.
const { values } = useFormikContext();
@@ -37,26 +33,7 @@ function ProjectFormFields() {
<div className={Classes.DIALOG_BODY}>
<Stack spacing={25}>
{/*------------ Contact -----------*/}
<FastField name={'contact_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={intl.get('projects.dialog.contact')}
className={classNames('form-group--select-list', Classes.FILL)}
intent={inputIntent({ error, touched })}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={'Find or create a customer'}
onContactSelected={(customer) => {
form.setFieldValue('contact_id', customer.id);
}}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
)}
</FastField>
<ProjectFormCustomerSelect />
{/*------------ Project Name -----------*/}
<FFormGroup
@@ -113,4 +90,21 @@ function ProjectFormFields() {
);
}
function ProjectFormCustomerSelect() {
// project form dialog context.
const { customers } = useProjectFormContext();
return (
<FormGroup name={'contact_id'} label={intl.get('projects.dialog.contact')}>
<CustomersSelect
name={'contact_id'}
items={customers}
placeholder={'Find or create a customer'}
allowCreate={true}
popoverFill={true}
/>
</FormGroup>
);
}
export default ProjectFormFields;

View File

@@ -3,17 +3,17 @@ import React from 'react';
import styled from 'styled-components';
import classNames from 'classnames';
import { FormGroup, InputGroup, Classes, Position } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { DateInput } from '@blueprintjs/datetime';
import { FeatureCan, FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes';
import {
FFormGroup,
VendorSelectField,
FieldRequiredHint,
Icon,
VendorDrawerLink,
VendorsSelect,
} from '@/components';
import { useBillFormContext } from './BillFormProvider';
@@ -43,43 +43,7 @@ function BillFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------- Vendor name ------ */}
<FastField
name={'vendor_id'}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
className={classNames(
'form-group--vendor-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />}
>
<VendorSelectField
contacts={vendors}
selectedContactId={value}
defaultSelectText={<T id={'select_vender_account'} />}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
form.setFieldValue('currency_code', contact?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<VendorButtonLink vendorId={value}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
)}
</FastField>
<BillFormVendorField />
{/* ----------- Exchange rate ----------- */}
<BillExchangeRateInputField
@@ -190,6 +154,43 @@ function BillFormHeader() {
);
}
/**
* Vendor select field of bill form.
* @returns {JSX.Element}
*/
function BillFormVendorField() {
const { values, setFieldValue } = useFormikContext();
const { vendors } = useBillFormContext();
return (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
vendors={vendors}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
>
<VendorsSelect
name={'vendor_id'}
items={vendors}
placeholder={<T id={'select_vender_account'} />}
onItemChange={(contact) => {
setFieldValue('vendor_id', contact.id);
setFieldValue('currency_code', contact?.currency_code);
}}
allowCreate={true}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(withDialogActions)(BillFormHeader);
const VendorButtonLink = styled(VendorDrawerLink)`

View File

@@ -9,17 +9,17 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes';
import {
FFormGroup,
VendorSelectField,
FieldRequiredHint,
InputPrependButton,
Icon,
FormattedMessage as T,
VendorDrawerLink,
VendorsSelect,
} from '@/components';
import {
vendorsFieldShouldUpdate,
@@ -51,9 +51,6 @@ function VendorCreditNoteFormHeaderFields({
vendorcreditNumberPrefix,
vendorcreditNextNumber,
}) {
// Vendor Credit form context.
const { vendors } = useVendorCreditNoteFormContext();
// Handle vendor credit number changing.
const handleVendorCreditNumberChange = () => {
openDialog('vendor-credit-form');
@@ -81,39 +78,7 @@ function VendorCreditNoteFormHeaderFields({
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Vendor name ----------- */}
<FastField
name={'vendor_id'}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
className={classNames(CLASSES.FILL, 'form-group--vendor')}
labelInfo={<FieldRequiredHint />}
>
<VendorSelectField
contacts={vendors}
selectedContactId={value}
defaultSelectText={<T id={'select_vender_account'} />}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
form.setFieldValue('currency_code', contact?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<VendorButtonLink vendorId={value}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
)}
</FastField>
<VendorCreditFormVendorSelect />
{/* ----------- Exchange rate ----------- */}
<VendorCreditNoteExchangeRateInputField
@@ -205,6 +170,45 @@ function VendorCreditNoteFormHeaderFields({
);
}
/**
* Vendor select field of vendor credit form.
* @returns {React.ReactNode}
*/
function VendorCreditFormVendorSelect() {
const { values, setFieldValue } = useFormikContext();
// Vendor Credit form context.
const { vendors } = useVendorCreditNoteFormContext();
return (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
labelInfo={<FieldRequiredHint />}
>
<VendorsSelect
name={'vendor_id'}
items={vendors}
placeholder={<T id={'select_vender_account'} />}
onItemChange={(contact) => {
setFieldValue('vendor_id', contact.id);
setFieldValue('currency_code', contact?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(
withDialogActions,
withSettings(({ vendorsCreditNoteSetting }) => ({

View File

@@ -12,14 +12,13 @@ import {
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import { FormattedMessage as T } from '@/components';
import { FormattedMessage as T, VendorsSelect } from '@/components';
import { toSafeInteger } from 'lodash';
import { CLASSES } from '@/constants/classes';
import {
FFormGroup,
AccountsSelect,
VendorSelectField,
FieldRequiredHint,
InputPrependText,
Money,
@@ -55,8 +54,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
} = useFormikContext();
// Payment made form context.
const { vendors, accounts, isNewMode, setPaymentVendorId } =
usePaymentMadeFormContext();
const { accounts } = usePaymentMadeFormContext();
// Sumation of payable full-amount.
const payableFullAmount = useMemo(
@@ -82,41 +80,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------ Vendor name ------------ */}
<FastField
name={'vendor_id'}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
className={classNames('form-group--select-list', Classes.FILL)}
labelInfo={<FieldRequiredHint />}
>
<VendorSelectField
contacts={vendors}
selectedContactId={value}
defaultSelectText={<T id={'select_vender_account'} />}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
form.setFieldValue('currency_code', contact?.currency_code);
setPaymentVendorId(contact.id);
}}
disabled={!isNewMode}
popoverFill={true}
allowCreate={true}
/>
{value && (
<VendorButtonLink vendorId={value}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
)}
</FastField>
<PaymentFormVendorSelect />
{/* ----------- Exchange rate ----------- */}
<PaymentMadeExchangeRateInputField
@@ -258,6 +222,48 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
);
}
/**
* Vendor select field of payment receive form.
* @returns {React.ReactNode}
*/
function PaymentFormVendorSelect() {
// Formik form context.
const { values, setFieldValue } = useFormikContext();
// Payment made form context.
const { vendors, isNewMode, setPaymentVendorId } =
usePaymentMadeFormContext();
return (
<FFormGroup
name={'vendor_id'}
label={<T id={'vendor_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
vendors={vendors}
shouldUpdate={vendorsFieldShouldUpdate}
>
<VendorsSelect
name={'vendor_id'}
items={vendors}
placeholder={<T id={'select_vender_account'} />}
onItemChange={(contact) => {
setFieldValue('vendor_id', contact.id);
setFieldValue('currency_code', contact?.currency_code);
setPaymentVendorId(contact.id);
}}
disabled={!isNewMode}
allowCreate={true}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(withCurrentOrganization())(PaymentMadeFormHeaderFields);
const VendorButtonLink = styled(VendorDrawerLink)`

View File

@@ -22,6 +22,7 @@ import {
CustomerDrawerLink,
FFormGroup,
FInputGroup,
CustomersSelect,
} from '@/components';
import { customerNameFieldShouldUpdate } from './utils';
@@ -121,49 +122,10 @@ const CreditNoteTransactionNoField = R.compose(
* Credit note form header fields.
*/
export default function CreditNoteFormHeaderFields({}) {
// Credit note form context.
const { customers } = useCreditNoteFormContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customerNameFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames(
'form-group--customer-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<CreditNoteCustomersSelect />
{/* ----------- Exchange rate ----------- */}
<CreditNoteExchangeRateInputField
@@ -216,6 +178,45 @@ export default function CreditNoteFormHeaderFields({}) {
);
}
/**
* Customer select field of credit note form.
* @returns {React.ReactNode}
*/
function CreditNoteCustomersSelect() {
// Credit note form context.
const { customers } = useCreditNoteFormContext();
const { setFieldValue, values } = useFormikContext();
return (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
customers={customers}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -11,18 +11,18 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FastField, ErrorMessage, useFormikContext, useFormik } from 'formik';
import {
FeatureCan,
FFormGroup,
FInputGroup,
FormattedMessage as T,
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
CustomerDrawerLink,
CustomersSelect,
} from '@/components';
import {
momentFormatter,
@@ -129,41 +129,7 @@ export default function EstimateFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames(CLASSES.FILL, 'form-group--customer')}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
intent={inputIntent({ error, touched })}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<EstimateFormCustomerSelect />
{/* ----------- Exchange Rate ----------- */}
<EstimateExchangeRateInputField
@@ -264,6 +230,45 @@ export default function EstimateFormHeader() {
);
}
/**
* Customer select field of estimate form.
* @returns {React.ReactNode}
*/
function EstimateFormCustomerSelect() {
const { setFieldValue, values } = useFormikContext();
const { customers } = useEstimateFormContext();
return (
<FFormGroup
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
fastField={true}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
fastField={true}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -10,7 +10,7 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FastField, ErrorMessage, useFormikContext, useFormik } from 'formik';
import * as R from 'ramda';
import {
@@ -19,12 +19,12 @@ import {
Col,
Row,
CustomerDrawerLink,
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
FeatureCan,
FInputGroup,
CustomersSelect,
} from '@/components';
import {
momentFormatter,
@@ -134,49 +134,13 @@ InvoiceFormInvoiceNumberField.displayName = 'InvoiceFormInvoiceNumberField';
*/
export default function InvoiceFormHeaderFields() {
// Invoice form context.
const { customers, projects } = useInvoiceFormContext();
const { projects } = useInvoiceFormContext();
const { values } = useFormikContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customerNameFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
inline={true}
className={classNames(
'form-group--customer-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
)}
</FastField>
<InvoiceFormCustomerSelect />
{/* ----------- Exchange rate ----------- */}
<InvoiceExchangeRateInputField
@@ -284,6 +248,44 @@ export default function InvoiceFormHeaderFields() {
);
}
/**
* Customer select field of the invoice form.
* @returns {React.ReactNode}
*/
function InvoiceFormCustomerSelect() {
const { customers } = useInvoiceFormContext();
const { values, setFieldValue } = useFormikContext();
return (
<FFormGroup
name={'customer_id'}
customers={customers}
shouldUpdate={customerNameFieldShouldUpdate}
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
fastField={true}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
allowCreate={true}
fastField={true}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;

View File

@@ -15,8 +15,12 @@ import { toSafeInteger } from 'lodash';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import * as R from 'ramda';
import { FInputGroup, FeatureCan, FormattedMessage as T } from '@/components';
import { useAutofocus } from '@/hooks';
import {
CustomersSelect,
FInputGroup,
FeatureCan,
FormattedMessage as T,
} from '@/components';
import { CLASSES } from '@/constants/classes';
import {
safeSumBy,
@@ -28,7 +32,6 @@ import {
import {
FFormGroup,
AccountsSelect,
CustomerSelectField,
FieldRequiredHint,
Icon,
InputPrependButton,
@@ -143,8 +146,7 @@ const PaymentReceivePaymentNoField = R.compose(
*/
export default function PaymentReceiveHeaderFields() {
// Payment receive form context.
const { customers, accounts, projects, isNewMode } =
usePaymentReceiveFormContext();
const { accounts, projects } = usePaymentReceiveFormContext();
// Formik form context.
const {
@@ -152,8 +154,6 @@ export default function PaymentReceiveHeaderFields() {
setFieldValue,
} = useFormikContext();
const customerFieldRef = useAutofocus();
// Calculates the full-amount received.
const totalDueAmount = useMemo(
() => safeSumBy(entries, 'due_amount'),
@@ -176,45 +176,7 @@ export default function PaymentReceiveHeaderFields() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------- Customer name ------------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames('form-group--select-list', CLASSES.FILL)}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('full_amount', '');
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
disabled={!isNewMode}
buttonProps={{
elementRef: (ref) => (customerFieldRef.current = ref),
}}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<PaymentReceiveCustomerSelect />
{/* ----------- Exchange rate ----------- */}
<PaymentReceiveExchangeRateInputField
@@ -361,3 +323,47 @@ const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;
`;
/**
* Customer select field of payment receive form.
* @returns {React.ReactNode}
*/
function PaymentReceiveCustomerSelect() {
// Payment receive form context.
const { customers, isNewMode } = usePaymentReceiveFormContext();
// Formik form context.
const { values, setFieldValue } = useFormikContext();
return (
<FFormGroup
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
name={'customer_id'}
customers={customers}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('full_amount', '');
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
disabled={!isNewMode}
allowCreate={true}
fastField={true}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}

View File

@@ -10,7 +10,7 @@ import {
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FastField, ErrorMessage, useFormikContext, useFormik } from 'formik';
import * as R from 'ramda';
import { CLASSES } from '@/constants/classes';
@@ -27,6 +27,7 @@ import {
FormattedMessage as T,
FeatureCan,
FInputGroup,
CustomersSelect,
} from '@/components';
import { ProjectsSelect } from '@/containers/Projects/components';
import {
@@ -130,44 +131,12 @@ const ReceiptFormReceiptNumberField = R.compose(
* Receipt form header fields.
*/
export default function ReceiptFormHeader() {
const { accounts, customers, projects } = useReceiptFormContext();
const { accounts, projects } = useReceiptFormContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
<FastField
name={'customer_id'}
customers={customers}
shouldUpdate={customersFieldShouldUpdate}
>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'customer_name'} />}
inline={true}
className={classNames(CLASSES.FILL, 'form-group--customer')}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />}
>
<CustomerSelectField
contacts={customers}
selectedContactId={value}
defaultSelectText={<T id={'select_customer_account'} />}
onContactSelected={(customer) => {
form.setFieldValue('customer_id', customer.id);
form.setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{value && (
<CustomerButtonLink customerId={value}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FormGroup>
)}
</FastField>
<ReceiptFormCustomerSelect />
{/* ----------- Exchange rate ----------- */}
<ReceiptExchangeRateInputField
@@ -264,6 +233,44 @@ export default function ReceiptFormHeader() {
);
}
/**
* Customer select field of receipt form.
* @returns {React.ReactNode}
*/
function ReceiptFormCustomerSelect() {
const { setFieldValue, values } = useFormikContext();
const { customers } = useReceiptFormContext();
return (
<FFormGroup
name={'customer_id'}
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
customers={customers}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
onItemChange={(customer) => {
setFieldValue('customer_id', customer.id);
setFieldValue('currency_code', customer?.currency_code);
}}
popoverFill={true}
allowCreate={true}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px;
margin-top: 6px;