mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
feat: quick create action on select/suggest items fields.
This commit is contained in:
@@ -9,23 +9,24 @@ import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useFormikContext } from 'formik';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Icon } from 'components';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
|
||||
import { safeInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Customer floating actions bar.
|
||||
*/
|
||||
export default function CustomerFloatingActions() {
|
||||
const history = useHistory();
|
||||
|
||||
export default function CustomerFloatingActions({ onCancel }) {
|
||||
// Customer form context.
|
||||
const { isNewMode,setSubmitPayload } = useCustomerFormContext();
|
||||
const { isNewMode, setSubmitPayload } = useCustomerFormContext();
|
||||
|
||||
// Formik context.
|
||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||
@@ -37,7 +38,7 @@ export default function CustomerFloatingActions() {
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
history.goBack();
|
||||
safeInvoke(onCancel, event);
|
||||
};
|
||||
|
||||
// handle clear button clicl.
|
||||
@@ -55,7 +56,7 @@ export default function CustomerFloatingActions() {
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
<ButtonGroup>
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<Button
|
||||
<SaveButton
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
@@ -83,6 +84,7 @@ export default function CustomerFloatingActions() {
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
|
||||
{/* ----------- Clear & Reset----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
@@ -99,3 +101,7 @@ export default function CustomerFloatingActions() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const SaveButton = styled(Button)`
|
||||
min-width: 100px;
|
||||
`;
|
||||
|
||||
@@ -1,152 +1,14 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import AppToaster from 'components/AppToaster';
|
||||
|
||||
import CustomerFormPrimarySection from './CustomerFormPrimarySection';
|
||||
import CustomerFormAfterPrimarySection from './CustomerFormAfterPrimarySection';
|
||||
import CustomersTabs from './CustomersTabs';
|
||||
import CustomerFloatingActions from './CustomerFloatingActions';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
import { compose, transformToForm } from 'utils';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
import { CreateCustomerForm, EditCustomerForm } from './CustomerForm.schema';
|
||||
|
||||
const defaultInitialValues = {
|
||||
customer_type: 'business',
|
||||
salutation: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
company_name: '',
|
||||
display_name: '',
|
||||
|
||||
email: '',
|
||||
work_phone: '',
|
||||
personal_phone: '',
|
||||
website: '',
|
||||
note: '',
|
||||
active: true,
|
||||
|
||||
billing_address_country: '',
|
||||
billing_address_1: '',
|
||||
billing_address_2: '',
|
||||
billing_address_city: '',
|
||||
billing_address_state: '',
|
||||
billing_address_postcode: '',
|
||||
billing_address_phone: '',
|
||||
|
||||
shipping_address_country: '',
|
||||
shipping_address_1: '',
|
||||
shipping_address_2: '',
|
||||
shipping_address_city: '',
|
||||
shipping_address_state: '',
|
||||
shipping_address_postcode: '',
|
||||
shipping_address_phone: '',
|
||||
|
||||
opening_balance: '',
|
||||
currency_code: '',
|
||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||
};
|
||||
import React from 'react';
|
||||
import { CustomerFormProvider } from './CustomerFormProvider';
|
||||
import CustomerFormFormik from './CustomerFormFormik';
|
||||
|
||||
/**
|
||||
* Customer form.
|
||||
* Abstructed customer form.
|
||||
*/
|
||||
function CustomerForm({ organization: { base_currency } }) {
|
||||
const {
|
||||
customer,
|
||||
customerId,
|
||||
submitPayload,
|
||||
contactDuplicate,
|
||||
editCustomerMutate,
|
||||
createCustomerMutate,
|
||||
isNewMode,
|
||||
} = useCustomerFormContext();
|
||||
|
||||
// const isNewMode = !customerId;
|
||||
const history = useHistory();
|
||||
|
||||
/**
|
||||
* Initial values in create and edit mode.
|
||||
*/
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...defaultInitialValues,
|
||||
currency_code: base_currency,
|
||||
...transformToForm(contactDuplicate || customer, defaultInitialValues),
|
||||
}),
|
||||
[customer, contactDuplicate, base_currency],
|
||||
);
|
||||
|
||||
//Handles the form submit.
|
||||
const handleFormSubmit = (
|
||||
values,
|
||||
{ setSubmitting, resetForm, setErrors },
|
||||
) => {
|
||||
const formValues = { ...values };
|
||||
|
||||
const onSuccess = () => {
|
||||
AppToaster.show({
|
||||
message: intl.get(
|
||||
isNewMode
|
||||
? 'the_customer_has_been_created_successfully'
|
||||
: 'the_item_customer_has_been_edited_successfully',
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
resetForm();
|
||||
|
||||
if (!submitPayload.noRedirect) {
|
||||
history.push('/customers');
|
||||
}
|
||||
};
|
||||
|
||||
const onError = () => {
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
if (isNewMode) {
|
||||
createCustomerMutate(formValues).then(onSuccess).catch(onError);
|
||||
} else {
|
||||
editCustomerMutate([customer.id, formValues])
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
export default function CustomerForm({ customerId }) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM, CLASSES.PAGE_FORM_CUSTOMER)}>
|
||||
<Formik
|
||||
validationSchema={isNewMode ? CreateCustomerForm : EditCustomerForm}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
<Form>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<CustomerFormPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={'page-form__after-priamry-section'}>
|
||||
<CustomerFormAfterPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||
<CustomersTabs />
|
||||
</div>
|
||||
|
||||
<CustomerFloatingActions />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
<CustomerFormProvider customerId={customerId}>
|
||||
<CustomerFormFormik />
|
||||
</CustomerFormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withCurrentOrganization())(CustomerForm);
|
||||
|
||||
127
src/containers/Customers/CustomerForm/CustomerFormFormik.js
Normal file
127
src/containers/Customers/CustomerForm/CustomerFormFormik.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import AppToaster from 'components/AppToaster';
|
||||
|
||||
import { CreateCustomerForm, EditCustomerForm } from './CustomerForm.schema';
|
||||
|
||||
import CustomerFormPrimarySection from './CustomerFormPrimarySection';
|
||||
import CustomerFormAfterPrimarySection from './CustomerFormAfterPrimarySection';
|
||||
import CustomersTabs from './CustomersTabs';
|
||||
import CustomerFloatingActions from './CustomerFloatingActions';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
import { compose, transformToForm, saveInvoke } from 'utils';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
import { defaultInitialValues } from './utils';
|
||||
|
||||
import 'style/pages/Customers/Form.scss';
|
||||
|
||||
/**
|
||||
* Customer form.
|
||||
*/
|
||||
function CustomerFormFormik({
|
||||
organization: { base_currency },
|
||||
|
||||
// #ownProps
|
||||
initialValues: initialCustomerValues,
|
||||
onSubmitSuccess,
|
||||
onSubmitError,
|
||||
onCancel,
|
||||
className,
|
||||
}) {
|
||||
const {
|
||||
customer,
|
||||
submitPayload,
|
||||
contactDuplicate,
|
||||
editCustomerMutate,
|
||||
createCustomerMutate,
|
||||
isNewMode,
|
||||
} = useCustomerFormContext();
|
||||
|
||||
/**
|
||||
* Initial values in create and edit mode.
|
||||
*/
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...defaultInitialValues,
|
||||
currency_code: base_currency,
|
||||
...transformToForm(contactDuplicate || customer, defaultInitialValues),
|
||||
...initialCustomerValues,
|
||||
}),
|
||||
[customer, contactDuplicate, base_currency, initialCustomerValues],
|
||||
);
|
||||
|
||||
// Handles the form submit.
|
||||
const handleFormSubmit = (values, formArgs) => {
|
||||
const { setSubmitting, resetForm } = formArgs;
|
||||
const formValues = { ...values };
|
||||
|
||||
const onSuccess = () => {
|
||||
AppToaster.show({
|
||||
message: intl.get(
|
||||
isNewMode
|
||||
? 'the_customer_has_been_created_successfully'
|
||||
: 'the_item_customer_has_been_edited_successfully',
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
resetForm();
|
||||
|
||||
saveInvoke(onSubmitSuccess, values, formArgs, submitPayload);
|
||||
};
|
||||
|
||||
const onError = () => {
|
||||
setSubmitting(false);
|
||||
saveInvoke(onSubmitError, values, formArgs, submitPayload);
|
||||
};
|
||||
|
||||
if (isNewMode) {
|
||||
createCustomerMutate(formValues).then(onSuccess).catch(onError);
|
||||
} else {
|
||||
editCustomerMutate([customer.id, formValues])
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.PAGE_FORM,
|
||||
CLASSES.PAGE_FORM_CUSTOMER,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Formik
|
||||
validationSchema={isNewMode ? CreateCustomerForm : EditCustomerForm}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
<Form>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<CustomerFormPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={'page-form__after-priamry-section'}>
|
||||
<CustomerFormAfterPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||
<CustomersTabs />
|
||||
</div>
|
||||
|
||||
<CustomerFloatingActions onCancel={onCancel} />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withCurrentOrganization())(CustomerFormFormik);
|
||||
@@ -1,20 +1,74 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DashboardCard } from 'components';
|
||||
import CustomerForm from './CustomerForm';
|
||||
import { CustomerFormProvider } from './CustomerFormProvider';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import 'style/pages/Customers/PageForm.scss';
|
||||
import CustomerFormFormik from './CustomerFormFormik';
|
||||
import {
|
||||
CustomerFormProvider,
|
||||
useCustomerFormContext,
|
||||
} from './CustomerFormProvider';
|
||||
|
||||
export default function CustomerFormPage() {
|
||||
const { id } = useParams();
|
||||
/**
|
||||
* Customer form page loading.
|
||||
* @returns {JSX}
|
||||
*/
|
||||
function CustomerFormPageLoading({ children }) {
|
||||
const { isFormLoading } = useCustomerFormContext();
|
||||
|
||||
return (
|
||||
<CustomerFormProvider customerId={id}>
|
||||
<DashboardCard page>
|
||||
<CustomerForm />
|
||||
</DashboardCard>
|
||||
<CustomerDashboardInsider loading={isFormLoading}>
|
||||
{children}
|
||||
</CustomerDashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer form page.
|
||||
* @returns {JSX}
|
||||
*/
|
||||
export default function CustomerFormPage() {
|
||||
const history = useHistory();
|
||||
const { id } = useParams();
|
||||
|
||||
const customerId = parseInt(id, 10);
|
||||
|
||||
// Handle the form submit success.
|
||||
const handleSubmitSuccess = (values, formArgs, submitPayload) => {
|
||||
if (!submitPayload.noRedirect) {
|
||||
history.push('/customers');
|
||||
}
|
||||
};
|
||||
// Handle the form cancel button click.
|
||||
const handleFormCancel = () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<CustomerFormProvider customerId={customerId}>
|
||||
<CustomerFormPageLoading>
|
||||
<DashboardCard page>
|
||||
<CustomerFormPageFormik
|
||||
onSubmitSuccess={handleSubmitSuccess}
|
||||
onCancel={handleFormCancel}
|
||||
/>
|
||||
</DashboardCard>
|
||||
</CustomerFormPageLoading>
|
||||
</CustomerFormProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CustomerFormPageFormik = styled(CustomerFormFormik)`
|
||||
.page-form {
|
||||
&__floating-actions {
|
||||
margin-left: -40px;
|
||||
margin-right: -40px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CustomerDashboardInsider = styled(DashboardInsider)`
|
||||
padding-bottom: 64px;
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState, createContext } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useCustomer,
|
||||
useCurrencies,
|
||||
@@ -24,7 +23,7 @@ function CustomerFormProvider({ customerId, ...props }) {
|
||||
// Handle fetch contact duplicate details.
|
||||
const { data: contactDuplicate, isLoading: isContactLoading } = useContact(
|
||||
contactId,
|
||||
{ enabled: !!contactId, },
|
||||
{ enabled: !!contactId },
|
||||
);
|
||||
// Handle fetch Currencies data table
|
||||
const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
|
||||
@@ -38,6 +37,9 @@ function CustomerFormProvider({ customerId, ...props }) {
|
||||
// determines whether the form new or duplicate mode.
|
||||
const isNewMode = contactId || !customerId;
|
||||
|
||||
const isFormLoading =
|
||||
isCustomerLoading || isCurrenciesLoading || isContactLoading;
|
||||
|
||||
const provider = {
|
||||
customerId,
|
||||
customer,
|
||||
@@ -48,24 +50,14 @@ function CustomerFormProvider({ customerId, ...props }) {
|
||||
|
||||
isCustomerLoading,
|
||||
isCurrenciesLoading,
|
||||
isFormLoading,
|
||||
|
||||
setSubmitPayload,
|
||||
editCustomerMutate,
|
||||
createCustomerMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
isCustomerLoading ||
|
||||
isCurrenciesLoading ||
|
||||
isContactLoading
|
||||
}
|
||||
name={'customer-form'}
|
||||
>
|
||||
<CustomerFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
return <CustomerFormContext.Provider value={provider} {...props} />;
|
||||
}
|
||||
|
||||
const useCustomerFormContext = () => React.useContext(CustomerFormContext);
|
||||
|
||||
38
src/containers/Customers/CustomerForm/utils.js
Normal file
38
src/containers/Customers/CustomerForm/utils.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import moment from 'moment';
|
||||
|
||||
|
||||
export const defaultInitialValues = {
|
||||
customer_type: 'business',
|
||||
salutation: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
company_name: '',
|
||||
display_name: '',
|
||||
|
||||
email: '',
|
||||
work_phone: '',
|
||||
personal_phone: '',
|
||||
website: '',
|
||||
note: '',
|
||||
active: true,
|
||||
|
||||
billing_address_country: '',
|
||||
billing_address_1: '',
|
||||
billing_address_2: '',
|
||||
billing_address_city: '',
|
||||
billing_address_state: '',
|
||||
billing_address_postcode: '',
|
||||
billing_address_phone: '',
|
||||
|
||||
shipping_address_country: '',
|
||||
shipping_address_1: '',
|
||||
shipping_address_2: '',
|
||||
shipping_address_city: '',
|
||||
shipping_address_state: '',
|
||||
shipping_address_postcode: '',
|
||||
shipping_address_phone: '',
|
||||
|
||||
opening_balance: '',
|
||||
currency_code: '',
|
||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||
};
|
||||
Reference in New Issue
Block a user