Merge branch 'develop' into e2e-onboarding

This commit is contained in:
Ahmed Bouhuolia
2023-06-23 16:12:03 +02:00
27 changed files with 539 additions and 623 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 // @ts-nocheck
export * from './CustomerSelectField';
export * from './CustomerDrawerLink'; 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 // @ts-nocheck
export * from './VendorDrawerLink' 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 { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage } from 'formik';
import { FormattedMessage as T } from '@/components'; import { CustomersSelect, FormattedMessage as T } from '@/components';
import classNames from 'classnames'; import classNames from 'classnames';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
@@ -121,33 +121,39 @@ export default function ExpenseFormHeader() {
)} )}
</FastField> </FastField>
<FastField {/* ----------- Customer ----------- */}
name={'customer_id'} <ExpenseFormCustomerSelect />
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>
</div> </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'}
fastField={true}
shouldUpdateDeps={{ items: customers }}
shouldUpdate={customersFieldShouldUpdate}
>
<CustomersSelect
name={'customer_id'}
items={customers}
placeholder={<T id={'select_customer_account'} />}
allowCreate={true}
popoverFill={true}
fastField={true}
shouldUpdateDeps={{ items: customers }}
shouldUpdate={customersFieldShouldUpdate}
/>
</FormGroup>
);
}

View File

@@ -104,7 +104,7 @@ export const transformToEditForm = (
*/ */
export const customersFieldShouldUpdate = (newProps, oldProps) => { export const customersFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.customers !== oldProps.customers || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -15,11 +15,10 @@ import {
FMoneyInputGroup, FMoneyInputGroup,
InputPrependText, InputPrependText,
FormattedMessage as T, FormattedMessage as T,
FieldRequiredHint,
CustomerSelectField,
Stack, Stack,
CustomersSelect,
} from '@/components'; } from '@/components';
import { inputIntent, momentFormatter } from '@/utils'; import { momentFormatter } from '@/utils';
import { useProjectFormContext } from './ProjectFormProvider'; import { useProjectFormContext } from './ProjectFormProvider';
/** /**
@@ -27,9 +26,6 @@ import { useProjectFormContext } from './ProjectFormProvider';
* @returns * @returns
*/ */
function ProjectFormFields() { function ProjectFormFields() {
// project form dialog context.
const { customers } = useProjectFormContext();
// Formik context. // Formik context.
const { values } = useFormikContext(); const { values } = useFormikContext();
@@ -37,26 +33,7 @@ function ProjectFormFields() {
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
<Stack spacing={25}> <Stack spacing={25}>
{/*------------ Contact -----------*/} {/*------------ Contact -----------*/}
<FastField name={'contact_id'}> <ProjectFormCustomerSelect />
{({ 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>
{/*------------ Project Name -----------*/} {/*------------ Project Name -----------*/}
<FFormGroup <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; export default ProjectFormFields;

View File

@@ -2,18 +2,18 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FormGroup, InputGroup, Classes, Position } from '@blueprintjs/core'; import { FormGroup, InputGroup, Classes, Position } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FeatureCan, FormattedMessage as T } from '@/components'; import { FeatureCan, FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
FFormGroup, FFormGroup,
VendorSelectField,
FieldRequiredHint, FieldRequiredHint,
Icon, Icon,
VendorDrawerLink, VendorDrawerLink,
VendorsSelect,
} from '@/components'; } from '@/components';
import { useBillFormContext } from './BillFormProvider'; import { useBillFormContext } from './BillFormProvider';
@@ -43,43 +43,7 @@ function BillFormHeader() {
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------- Vendor name ------ */} {/* ------- Vendor name ------ */}
<FastField <BillFormVendorField />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<BillExchangeRateInputField <BillExchangeRateInputField
@@ -190,6 +154,46 @@ 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 />}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
>
<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}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(withDialogActions)(BillFormHeader); export default compose(withDialogActions)(BillFormHeader);
const VendorButtonLink = styled(VendorDrawerLink)` const VendorButtonLink = styled(VendorDrawerLink)`

View File

@@ -19,7 +19,10 @@ import {
ensureEntriesHaveEmptyLine, ensureEntriesHaveEmptyLine,
} from '@/containers/Entries/utils'; } from '@/containers/Entries/utils';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { isLandedCostDisabled, getEntriesTotal } from '@/containers/Entries/utils'; import {
isLandedCostDisabled,
getEntriesTotal,
} from '@/containers/Entries/utils';
import { useBillFormContext } from './BillFormProvider'; import { useBillFormContext } from './BillFormProvider';
export const MIN_LINES_NUMBER = 1; export const MIN_LINES_NUMBER = 1;
@@ -153,7 +156,7 @@ export const handleDeleteErrors = (errors) => {
*/ */
export const vendorsFieldShouldUpdate = (newProps, oldProps) => { export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.vendors !== oldProps.vendors || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -9,17 +9,17 @@ import {
ControlGroup, ControlGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
FFormGroup, FFormGroup,
VendorSelectField,
FieldRequiredHint, FieldRequiredHint,
InputPrependButton, InputPrependButton,
Icon, Icon,
FormattedMessage as T, FormattedMessage as T,
VendorDrawerLink, VendorDrawerLink,
VendorsSelect,
} from '@/components'; } from '@/components';
import { import {
vendorsFieldShouldUpdate, vendorsFieldShouldUpdate,
@@ -51,9 +51,6 @@ function VendorCreditNoteFormHeaderFields({
vendorcreditNumberPrefix, vendorcreditNumberPrefix,
vendorcreditNextNumber, vendorcreditNextNumber,
}) { }) {
// Vendor Credit form context.
const { vendors } = useVendorCreditNoteFormContext();
// Handle vendor credit number changing. // Handle vendor credit number changing.
const handleVendorCreditNumberChange = () => { const handleVendorCreditNumberChange = () => {
openDialog('vendor-credit-form'); openDialog('vendor-credit-form');
@@ -81,39 +78,7 @@ function VendorCreditNoteFormHeaderFields({
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Vendor name ----------- */} {/* ----------- Vendor name ----------- */}
<FastField <VendorCreditFormVendorSelect />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<VendorCreditNoteExchangeRateInputField <VendorCreditNoteExchangeRateInputField
@@ -205,6 +170,49 @@ 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}
labelInfo={<FieldRequiredHint />}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
>
<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}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose( export default compose(
withDialogActions, withDialogActions,
withSettings(({ vendorsCreditNoteSetting }) => ({ withSettings(({ vendorsCreditNoteSetting }) => ({

View File

@@ -113,7 +113,7 @@ export const transformFormValuesToRequest = (values) => {
*/ */
export const vendorsFieldShouldUpdate = (newProps, oldProps) => { export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.vendors !== oldProps.vendors || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -12,14 +12,13 @@ import {
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik'; 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 { toSafeInteger } from 'lodash';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
FFormGroup, FFormGroup,
AccountsSelect, AccountsSelect,
VendorSelectField,
FieldRequiredHint, FieldRequiredHint,
InputPrependText, InputPrependText,
Money, Money,
@@ -55,8 +54,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
} = useFormikContext(); } = useFormikContext();
// Payment made form context. // Payment made form context.
const { vendors, accounts, isNewMode, setPaymentVendorId } = const { accounts } = usePaymentMadeFormContext();
usePaymentMadeFormContext();
// Sumation of payable full-amount. // Sumation of payable full-amount.
const payableFullAmount = useMemo( const payableFullAmount = useMemo(
@@ -82,41 +80,7 @@ function PaymentMadeFormHeaderFields({ organization: { base_currency } }) {
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------ Vendor name ------------ */} {/* ------------ Vendor name ------------ */}
<FastField <PaymentFormVendorSelect />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<PaymentMadeExchangeRateInputField <PaymentMadeExchangeRateInputField
@@ -258,6 +222,52 @@ 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'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
>
<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}
fastField={true}
shouldUpdate={vendorsFieldShouldUpdate}
shouldUpdateDeps={{ items: vendors }}
/>
{values.vendor_id && (
<VendorButtonLink vendorId={values.vendor_id}>
<T id={'view_vendor_details'} />
</VendorButtonLink>
)}
</FFormGroup>
);
}
export default compose(withCurrentOrganization())(PaymentMadeFormHeaderFields); export default compose(withCurrentOrganization())(PaymentMadeFormHeaderFields);
const VendorButtonLink = styled(VendorDrawerLink)` const VendorButtonLink = styled(VendorDrawerLink)`

View File

@@ -74,7 +74,7 @@ export const transformToNewPageEntries = (entries) => {
*/ */
export const vendorsFieldShouldUpdate = (newProps, oldProps) => { export const vendorsFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.vendors !== oldProps.vendors || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -4,15 +4,16 @@ import classNames from 'classnames';
import styled from 'styled-components'; import styled from 'styled-components';
import { FormGroup, InputGroup, Position } from '@blueprintjs/core'; import { FormGroup, InputGroup, Position } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
CustomerSelectField,
FieldRequiredHint, FieldRequiredHint,
Icon, Icon,
FormattedMessage as T, FormattedMessage as T,
CustomerDrawerLink, CustomerDrawerLink,
FFormGroup,
CustomersSelect,
} from '@/components'; } from '@/components';
import { customerNameFieldShouldUpdate } from './utils'; import { customerNameFieldShouldUpdate } from './utils';
@@ -30,49 +31,10 @@ import {
* Credit note form header fields. * Credit note form header fields.
*/ */
export default function CreditNoteFormHeaderFields({}) { export default function CreditNoteFormHeaderFields({}) {
// Credit note form context.
const { customers } = useCreditNoteFormContext();
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
<FastField <CreditNoteCustomersSelect />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<CreditNoteExchangeRateInputField <CreditNoteExchangeRateInputField
@@ -125,6 +87,48 @@ 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'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<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}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)` const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;
margin-top: 6px; margin-top: 6px;

View File

@@ -116,7 +116,7 @@ export const transformFormValuesToRequest = (values) => {
*/ */
export const customerNameFieldShouldUpdate = (newProps, oldProps) => { export const customerNameFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.customers !== oldProps.customers || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -4,16 +4,16 @@ import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core'; import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { import {
FeatureCan, FeatureCan,
FFormGroup, FFormGroup,
FormattedMessage as T, FormattedMessage as T,
CustomerSelectField,
FieldRequiredHint, FieldRequiredHint,
Icon, Icon,
CustomerDrawerLink, CustomerDrawerLink,
CustomersSelect,
} from '@/components'; } from '@/components';
import { import {
momentFormatter, momentFormatter,
@@ -42,41 +42,7 @@ export default function EstimateFormHeader() {
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
<FastField <EstimateFormCustomerSelect />
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>
{/* ----------- Exchange Rate ----------- */} {/* ----------- Exchange Rate ----------- */}
<EstimateExchangeRateInputField <EstimateExchangeRateInputField
@@ -177,6 +143,47 @@ 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'}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<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}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)` const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;
margin-top: 6px; margin-top: 6px;

View File

@@ -80,7 +80,7 @@ export const transformToEditForm = (estimate) => {
*/ */
export const customersFieldShouldUpdate = (newProps, oldProps) => { export const customersFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.customers !== oldProps.customers || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -12,9 +12,9 @@ import {
Col, Col,
Row, Row,
CustomerDrawerLink, CustomerDrawerLink,
CustomerSelectField,
FieldRequiredHint, FieldRequiredHint,
FeatureCan, FeatureCan,
CustomersSelect,
} from '@/components'; } from '@/components';
import { import {
momentFormatter, momentFormatter,
@@ -42,49 +42,13 @@ import { Features } from '@/constants';
*/ */
export default function InvoiceFormHeaderFields() { export default function InvoiceFormHeaderFields() {
// Invoice form context. // Invoice form context.
const { customers, projects } = useInvoiceFormContext(); const { projects } = useInvoiceFormContext();
const { values } = useFormikContext(); const { values } = useFormikContext();
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
<FastField <InvoiceFormCustomerSelect />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<InvoiceExchangeRateInputField <InvoiceExchangeRateInputField
@@ -192,6 +156,46 @@ 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'}
label={<T id={'customer_name'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
fastField={true}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<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}
shouldUpdate={customerNameFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)` const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;
margin-top: 6px; margin-top: 6px;

View File

@@ -114,7 +114,7 @@ export const transformErrors = (errors, { setErrors }) => {
*/ */
export const customerNameFieldShouldUpdate = (newProps, oldProps) => { export const customerNameFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.customers !== oldProps.customers || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -14,8 +14,11 @@ import { DateInput } from '@blueprintjs/datetime';
import { toSafeInteger } from 'lodash'; import { toSafeInteger } from 'lodash';
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik'; import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import { FeatureCan, FormattedMessage as T } from '@/components'; import {
import { useAutofocus } from '@/hooks'; FeatureCan,
CustomersSelect,
FormattedMessage as T,
} from '@/components';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { import {
safeSumBy, safeSumBy,
@@ -27,7 +30,6 @@ import {
import { import {
FFormGroup, FFormGroup,
AccountsSelect, AccountsSelect,
CustomerSelectField,
FieldRequiredHint, FieldRequiredHint,
Icon, Icon,
MoneyInputGroup, MoneyInputGroup,
@@ -58,8 +60,7 @@ import { PaymentReceivePaymentNoField } from './PaymentReceivePaymentNoField';
*/ */
export default function PaymentReceiveHeaderFields() { export default function PaymentReceiveHeaderFields() {
// Payment receive form context. // Payment receive form context.
const { customers, accounts, projects, isNewMode } = const { accounts, projects } = usePaymentReceiveFormContext();
usePaymentReceiveFormContext();
// Formik form context. // Formik form context.
const { const {
@@ -67,8 +68,6 @@ export default function PaymentReceiveHeaderFields() {
setFieldValue, setFieldValue,
} = useFormikContext(); } = useFormikContext();
const customerFieldRef = useAutofocus();
// Calculates the full-amount received. // Calculates the full-amount received.
const totalDueAmount = useMemo( const totalDueAmount = useMemo(
() => safeSumBy(entries, 'due_amount'), () => safeSumBy(entries, 'due_amount'),
@@ -91,45 +90,7 @@ export default function PaymentReceiveHeaderFields() {
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------- Customer name ------------- */} {/* ------------- Customer name ------------- */}
<FastField <PaymentReceiveCustomerSelect />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<PaymentReceiveExchangeRateInputField <PaymentReceiveExchangeRateInputField
@@ -276,3 +237,49 @@ const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;
margin-top: 6px; 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'}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<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}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}

View File

@@ -129,7 +129,7 @@ export const fullAmountPaymentEntries = (entries) => {
*/ */
export const customersFieldShouldUpdate = (newProps, oldProps) => { export const customersFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.customers !== oldProps.customers || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -4,7 +4,7 @@ import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core'; import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { ACCOUNT_TYPE } from '@/constants/accountTypes'; import { ACCOUNT_TYPE } from '@/constants/accountTypes';
@@ -12,7 +12,7 @@ import { Features } from '@/constants';
import { import {
FFormGroup, FFormGroup,
AccountsSelect, AccountsSelect,
CustomerSelectField, CustomersSelect,
FieldRequiredHint, FieldRequiredHint,
Icon, Icon,
CustomerDrawerLink, CustomerDrawerLink,
@@ -38,44 +38,12 @@ import { ReceiptFormReceiptNumberField } from './ReceiptFormReceiptNumberField';
* Receipt form header fields. * Receipt form header fields.
*/ */
export default function ReceiptFormHeader() { export default function ReceiptFormHeader() {
const { accounts, customers, projects } = useReceiptFormContext(); const { accounts, projects } = useReceiptFormContext();
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
<FastField <ReceiptFormCustomerSelect />
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>
{/* ----------- Exchange rate ----------- */} {/* ----------- Exchange rate ----------- */}
<ReceiptExchangeRateInputField <ReceiptExchangeRateInputField
@@ -172,6 +140,47 @@ 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'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
fastField={true}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
>
<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}
shouldUpdate={customersFieldShouldUpdate}
shouldUpdateDeps={{ items: customers }}
/>
{values.customer_id && (
<CustomerButtonLink customerId={values.customer_id}>
<T id={'view_customer_details'} />
</CustomerButtonLink>
)}
</FFormGroup>
);
}
const CustomerButtonLink = styled(CustomerDrawerLink)` const CustomerButtonLink = styled(CustomerDrawerLink)`
font-size: 11px; font-size: 11px;
margin-top: 6px; margin-top: 6px;

View File

@@ -104,7 +104,7 @@ export const accountsFieldShouldUpdate = (newProps, oldProps) => {
*/ */
export const customersFieldShouldUpdate = (newProps, oldProps) => { export const customersFieldShouldUpdate = (newProps, oldProps) => {
return ( return (
newProps.customers !== oldProps.customers || newProps.shouldUpdateDeps.items !== oldProps.shouldUpdateDeps.items ||
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };

View File

@@ -379,7 +379,7 @@
"the_expenses_have_been_deleted_successfully": "The expenses have been deleted successfully", "the_expenses_have_been_deleted_successfully": "The expenses have been deleted successfully",
"once_delete_these_expenses_you_will_not_able_restore_them": "Once you delete these expenses, you won't be able to retrieve them later. Are you sure you want to delete them?", "once_delete_these_expenses_you_will_not_able_restore_them": "Once you delete these expenses, you won't be able to retrieve them later. Are you sure you want to delete them?",
"the_expense_has_been_published": "The expense has been published", "the_expense_has_been_published": "The expense has been published",
"select_customer": "Select customer", "select_customer": "Select Customer...",
"total_amount_equals_zero": "Total amount equals zero", "total_amount_equals_zero": "Total amount equals zero",
"value": "Value", "value": "Value",
"you_reached_conditions_limit": "You have reached to conditions limit.", "you_reached_conditions_limit": "You have reached to conditions limit.",
@@ -480,7 +480,7 @@
"estimate_date": "Estimate Date", "estimate_date": "Estimate Date",
"expiration_date": "Expiration Date", "expiration_date": "Expiration Date",
"customer_note": "Customer Note", "customer_note": "Customer Note",
"select_customer_account": "Select Customer Account", "select_customer_account": "Select Customer Account...",
"select_product": "Select Product", "select_product": "Select Product",
"reference": "Reference #", "reference": "Reference #",
"clear": "Clear", "clear": "Clear",