refactoring: expenses landing list.

refactoring: customers landing list.
refactoring: vendors landing list.
refactoring: manual journals landing list.
This commit is contained in:
a.bouhuolia
2021-02-10 18:35:19 +02:00
parent 6e10ed0721
commit c68b4ca9ba
170 changed files with 2835 additions and 4430 deletions

View File

@@ -0,0 +1,230 @@
import React from 'react';
import { FormGroup, InputGroup, TextArea } from '@blueprintjs/core';
import { Row, Col } from 'components';
import { FormattedMessage as T } from 'react-intl';
import { FastField, ErrorMessage } from 'formik';
import { inputIntent } from 'utils';
const CustomerBillingAddress = ({}) => {
return (
<div className={'tab-panel--address'}>
<Row>
<Col xs={6}>
<h4>
<T id={'billing_address'} />
</h4>
{/*------------ Billing Address country -----------*/}
<FastField name={'billing_address_country'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_country" />}
label={<T id={'country'} />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Billing Address 1 -----------*/}
<FastField name={'billing_address_1'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'address_line_1'} />}
className={'form-group--address_line_1'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_1" />}
>
<TextArea {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Billing Address 2 -----------*/}
<FastField name={'billing_address_2'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'address_line_2'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_2" />}
>
<TextArea {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Billing Address city -----------*/}
<FastField name={'billing_address_city'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'city_town'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_city" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Billing Address state -----------*/}
<FastField name={'billing_address_state'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'state'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_state" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Billing Address postcode -----------*/}
<FastField name={'billing_address_postcode'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'zip_code'} />}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_postcode" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Billing Address phone -----------*/}
<FastField name={'billing_address_phone'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'phone'} />}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="billing_address_phone" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
</Col>
<Col xs={6}>
<h4>
<T id={'shipping_address'} />
</h4>
{/*------------ Shipping Address country -----------*/}
<FastField name={'shipping_address_country'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'country'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_country" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Shipping Address 1 -----------*/}
<FastField name={'shipping_address_1'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'address_line_1'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_1" />}
>
<TextArea {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Shipping Address 2 -----------*/}
<FastField name={'shipping_address_2'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'address_line_2'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_2" />}
>
<TextArea {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Shipping Address city -----------*/}
<FastField name={'shipping_address_city'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'city_town'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_city" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Shipping Address state -----------*/}
<FastField name={'shipping_address_state'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'state'} />}
className={'form-group--journal-number'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_state" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Shipping Address postcode -----------*/}
<FastField name={'shipping_address_postcode'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'zip_code'} />}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_postcode" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Shipping Address phone -----------*/}
<FastField name={'shipping_address_phone'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'phone'} />}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="shipping_address_phone" />}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
</Col>
</Row>
</div>
);
};
export default CustomerBillingAddress;

View File

@@ -0,0 +1,23 @@
import React, {
useMemo,
useState,
useEffect,
useRef,
useCallback,
} from 'react';
import Dragzone from 'components/Dragzone';
function CustomerAttachmentTabs() {
return (
<div>
<Dragzone
initialFiles={[]}
onDrop={null}
onDeleteFile={[]}
hint={'Attachments: Maxiumum size: 20MB'}
/>
</div>
);
}
export default CustomerAttachmentTabs;

View File

@@ -0,0 +1,122 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import moment from 'moment';
import {
MoneyInputGroup,
InputPrependText,
CurrencySelectList,
Row,
Col,
} from 'components';
import { FormattedMessage as T } from 'react-intl';
import { useCustomerFormContext } from './CustomerFormProvider';
import {
momentFormatter,
tansformDateValue,
inputIntent,
} from 'utils';
/**
* Customer financial panel.
*/
export default function CustomerFinancialPanel() {
const {
currencies,
customerId
} = useCustomerFormContext();
return (
<div className={'tab-panel--financial'}>
<Row>
<Col xs={6}>
{/*------------ Opening balance at -----------*/}
<FastField name={'opening_balance_at'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'opening_balance_at'} />}
className={classNames('form-group--select-list', Classes.FILL)}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="opening_balance_at" />}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
onChange={(date) => {
form.setFieldValue(
'opening_balance_at',
moment(date).format('YYYY-MM-DD'),
);
}}
value={tansformDateValue(value)}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
disabled={customerId}
/>
</FormGroup>
)}
</FastField>
{/*------------ Opening balance -----------*/}
<FastField name={'opening_balance'}>
{({
form,
field,
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'opening_balance'} />}
className={classNames(
'form-group--opening-balance',
Classes.FILL,
)}
intent={inputIntent({ error, touched })}
inline={true}
>
<ControlGroup>
<InputPrependText text={form.values.currency_code} />
<MoneyInputGroup
value={value}
inputGroupProps={{ fill: true }}
disabled={customerId}
onChange={(balance) => {
form.setFieldValue('opening_balance', balance);
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
{/*------------ Currency -----------*/}
<FastField name={'currency_code'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'currency'} />}
className={classNames(
'form-group--select-list',
'form-group--balance-currency',
Classes.FILL,
)}
inline={true}
>
<CurrencySelectList
currenciesList={currencies}
selectedCurrencyCode={value}
onCurrencySelected={(currency) => {
form.setFieldValue('currency_code', currency.currency_code);
}}
disabled={customerId}
/>
</FormGroup>
)}
</FastField>
</Col>
</Row>
</div>
);
}

View File

@@ -0,0 +1,101 @@
import React from 'react';
import {
Intent,
Button,
ButtonGroup,
Popover,
PopoverInteractionKind,
Position,
Menu,
MenuItem,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
import { useFormikContext } from 'formik';
import { CLASSES } from 'common/classes';
import { Icon } from 'components';
import { useCustomerFormContext } from './CustomerFormProvider';
/**
* Customer floating actions bar.
*/
export default function CustomerFloatingActions() {
const history = useHistory();
// Customer form context.
const { customerId, setSubmitPayload } = useCustomerFormContext();
// Formik context.
const { resetForm, submitForm, isSubmitting } = useFormikContext();
// Handle submit button click.
const handleSubmitBtnClick = (event) => {
setSubmitPayload({ noRedirect: false });
};
// Handle cancel button click.
const handleCancelBtnClick = (event) => {
history.goBack();
};
// handle clear button clicl.
const handleClearBtnClick = (event) => {
resetForm();
};
// Handle submit & new button click.
const handleSubmitAndNewClick = (event) => {
submitForm();
setSubmitPayload({ noRedirect: true });
};
return (
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
<ButtonGroup>
{/* ----------- Save and New ----------- */}
<Button
disabled={isSubmitting}
loading={isSubmitting}
intent={Intent.PRIMARY}
type="submit"
onClick={handleSubmitBtnClick}
text={customerId ? <T id={'edit'} /> : <T id={'save'} />}
/>
<Popover
content={
<Menu>
<MenuItem
text={<T id={'save_and_new'} />}
onClick={handleSubmitAndNewClick}
/>
</Menu>
}
minimal={true}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
disabled={isSubmitting}
intent={Intent.PRIMARY}
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
/>
</Popover>
</ButtonGroup>
{/* ----------- Clear & Reset----------- */}
<Button
className={'ml1'}
disabled={isSubmitting}
onClick={handleClearBtnClick}
text={customerId ? <T id={'reset'} /> : <T id={'clear'} />}
/>
{/* ----------- Cancel ----------- */}
<Button
className={'ml1'}
onClick={handleCancelBtnClick}
text={<T id={'cancel'} />}
/>
</div>
);
}

View File

@@ -0,0 +1,169 @@
import React, { useMemo, useEffect } from 'react';
import { Formik, Form } from 'formik';
import moment from 'moment';
import { Intent } from '@blueprintjs/core';
import { useIntl } from 'react-intl';
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 withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSettings from 'containers/Settings/withSettings';
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'),
};
/**
* Customer form.
*/
function CustomerForm({
// #withDashboardActions
changePageTitle,
// #withSettings
baseCurrency,
}) {
const {
customer,
customerId,
submitPayload,
editCustomerMutate,
createCustomerMutate,
} = useCustomerFormContext();
const isNewMode = !customerId;
const history = useHistory();
const { formatMessage } = useIntl();
/**
* Initial values in create and edit mode.
*/
const initialValues = useMemo(
() => ({
...defaultInitialValues,
currency_code: baseCurrency,
...transformToForm(customer, defaultInitialValues),
}),
[customer, baseCurrency],
);
useEffect(() => {
!isNewMode
? changePageTitle(formatMessage({ id: 'edit_customer' }))
: changePageTitle(formatMessage({ id: 'new_customer' }));
}, [changePageTitle, isNewMode, formatMessage]);
//Handles the form submit.
const handleFormSubmit = (
values,
{ setSubmitting, resetForm, setErrors },
) => {
const formValues = { ...values };
const onSuccess = () => {
AppToaster.show({
message: formatMessage({
id: customer
? 'the_item_customer_has_been_edited_successfully'
: 'the_customer_has_been_created_successfully',
}),
intent: Intent.SUCCESS,
});
setSubmitting(false);
resetForm();
if (!submitPayload.noRedirect) {
history.push('/customers');
}
};
const onError = () => {
setSubmitting(false);
};
if (customer && customer.id) {
editCustomerMutate(customer.id, formValues)
.then(onSuccess)
.catch(onError);
} else {
createCustomerMutate(formValues).then(onSuccess).catch(onError);
}
};
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>
);
}
export default compose(
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
withDashboardActions,
)(CustomerForm);

View File

@@ -0,0 +1,49 @@
import * as Yup from 'yup';
import { formatMessage } from 'services/intl';
const Schema = Yup.object().shape({
customer_type: Yup.string()
.required()
.trim()
.label(formatMessage({ id: 'customer_type_' })),
salutation: Yup.string().trim(),
first_name: Yup.string().trim(),
last_name: Yup.string().trim(),
company_name: Yup.string().trim(),
display_name: Yup.string()
.trim()
.required()
.label(formatMessage({ id: 'display_name_' })),
email: Yup.string().email().nullable(),
work_phone: Yup.number(),
personal_phone: Yup.number(),
website: Yup.string().url().nullable(),
active: Yup.boolean(),
note: Yup.string().trim(),
billing_address_country: Yup.string().trim(),
billing_address_1: Yup.string().trim(),
billing_address_2: Yup.string().trim(),
billing_address_city: Yup.string().trim(),
billing_address_state: Yup.string().trim(),
billing_address_postcode: Yup.number().nullable(),
billing_address_phone: Yup.number(),
shipping_address_country: Yup.string().trim(),
shipping_address_1: Yup.string().trim(),
shipping_address_2: Yup.string().trim(),
shipping_address_city: Yup.string().trim(),
shipping_address_state: Yup.string().trim(),
shipping_address_postcode: Yup.number().nullable(),
shipping_address_phone: Yup.number(),
opening_balance: Yup.number().nullable(),
currency_code: Yup.string(),
opening_balance_at: Yup.date(),
});
export const CreateCustomerForm = Schema;
export const EditCustomerForm = Schema;

View File

@@ -0,0 +1,70 @@
import React from 'react';
import { FormGroup, InputGroup, ControlGroup } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'react-intl';
import { inputIntent } from 'utils';
export default function CustomerFormAfterPrimarySection({}) {
return (
<div class="customer-form__after-primary-section-content">
{/*------------ Customer email -----------*/}
<FastField name={'email'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'email'} />}
className={'form-group--email'}
label={<T id={'customer_email'} />}
inline={true}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Phone number -----------*/}
<FormGroup
className={'form-group--phone-number'}
label={<T id={'phone_number'} />}
inline={true}
>
<ControlGroup>
<FastField name={'work_phone'}>
{({ field, meta: { error, touched } }) => (
<InputGroup
intent={inputIntent({ error, touched })}
placeholder={'Work'}
{...field}
/>
)}
</FastField>
<FastField name={'personal_phone'}>
{({ field, meta: { error, touched } }) => (
<InputGroup
intent={inputIntent({ error, touched })}
placeholder={'Mobile'}
{...field}
/>
)}
</FastField>
</ControlGroup>
</FormGroup>
{/*------------ Customer website -----------*/}
<FastField name={'website'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'website'} />}
className={'form-group--website'}
label={<T id={'website'} />}
inline={true}
>
<InputGroup placeholder={'http://'} {...field} />
</FormGroup>
)}
</FastField>
</div>
);
}

View File

@@ -0,0 +1,20 @@
import React from 'react';
import { useParams } from 'react-router-dom';
import { DashboardCard } from 'components';
import CustomerForm from './CustomerForm';
import { CustomerFormProvider } from './CustomerFormProvider';
import 'style/pages/Customers/PageForm.scss';
export default function CustomerFormPage() {
const { id } = useParams();
return (
<CustomerFormProvider customerId={id}>
<DashboardCard page>
<CustomerForm />
</DashboardCard>
</CustomerFormProvider>
);
}

View File

@@ -0,0 +1,125 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup, InputGroup, ControlGroup } from '@blueprintjs/core';
import { FastField, Field, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'react-intl';
import {
Hint,
FieldRequiredHint,
SalutationList,
DisplayNameList,
} from 'components';
import CustomerTypeRadioField from './CustomerTypeRadioField';
import { CLASSES } from 'common/classes';
import { inputIntent } from 'utils';
import { useAutofocus } from 'hooks';
/**
* Customer form primary section.
*/
export default function CustomerFormPrimarySection({}) {
const firstNameFieldRef = useAutofocus();
return (
<div className={'customer-form__primary-section-content'}>
{/**-----------Customer type. -----------*/}
<CustomerTypeRadioField />
{/**----------- Contact name -----------*/}
<FormGroup
className={classNames('form-group--contact_name')}
label={<T id={'contact_name'} />}
inline={true}
>
<ControlGroup>
<FastField name={'salutation'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<SalutationList
onItemSelect={(salutation) => {
form.setFieldValue('salutation', salutation.label);
}}
selectedItem={value}
popoverProps={{ minimal: true }}
className={classNames(
CLASSES.FORM_GROUP_LIST_SELECT,
CLASSES.FILL,
'input-group--salutation-list',
'select-list--fill-button',
)}
/>
)}
</FastField>
<FastField name={'first_name'}>
{({ field, meta: { error, touched } }) => (
<InputGroup
placeholder={'First Name'}
intent={inputIntent({ error, touched })}
className={classNames('input-group--first-name')}
inputRef={(ref) => (firstNameFieldRef.current = ref)}
{...field}
/>
)}
</FastField>
<FastField name={'last_name'}>
{({ field, meta: { error, touched } }) => (
<InputGroup
placeholder={'Last Name'}
intent={inputIntent({ error, touched })}
className={classNames('input-group--last-name')}
{...field}
/>
)}
</FastField>
</ControlGroup>
</FormGroup>
{/*----------- Company Name -----------*/}
<FastField name={'company_name'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
className={classNames('form-group--company_name')}
label={<T id={'company_name'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'company_name'} />}
inline={true}
>
<InputGroup {...field} />
</FormGroup>
)}
</FastField>
{/*----------- Display Name -----------*/}
<Field name={'display_name'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
helperText={<ErrorMessage name={'display_name'} />}
intent={inputIntent({ error, touched })}
label={
<>
<T id={'display_name'} />
<FieldRequiredHint />
<Hint />
</>
}
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, CLASSES.FILL)}
inline={true}
>
<DisplayNameList
firstName={form.values.first_name}
lastName={form.values.last_name}
company={form.values.company_name}
salutation={form.values.salutation}
onItemSelect={(displayName) => {
form.setFieldValue('display_name', displayName.label);
}}
selectedItem={value}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</Field>
</div>
);
}

View File

@@ -0,0 +1,65 @@
import React, { useState, createContext } from 'react';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import {
useCustomers,
useCustomer,
useCurrencies,
useCreateCustomer,
useEditCustomer,
} from 'hooks/query';
const CustomerFormContext = createContext();
function CustomerFormProvider({ customerId, ...props }) {
// Handle fetch customer details.
const { data: customer, isFetching: isCustomerLoading } = useCustomer(
customerId,
{
enabled: !!customerId,
}
);
// Handle fetch customers data table
const {
data: { customers },
isFetching: isCustomersLoading,
} = useCustomers();
// Handle fetch Currencies data table
const { data: currencies, isFetching: isCurrenciesLoading } = useCurrencies();
// Form submit payload.
const [submitPayload, setSubmitPayload] = useState({});
const { mutateAsync: editCustomerMutate } = useEditCustomer();
const { mutateAsync: createCustomerMutate } = useCreateCustomer();
const provider = {
customerId,
customer,
customers,
currencies,
submitPayload,
isCustomerLoading,
isCustomersLoading,
isCurrenciesLoading,
setSubmitPayload,
editCustomerMutate,
createCustomerMutate
};
return (
<DashboardInsider
loading={isCustomerLoading || isCustomerLoading || isCurrenciesLoading}
name={'customer-form'}
>
<CustomerFormContext.Provider value={provider} {...props} />
</DashboardInsider>
);
}
const useCustomerFormContext = () => React.useContext(CustomerFormContext);
export { CustomerFormProvider, useCustomerFormContext };

View File

@@ -0,0 +1,25 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup, TextArea, Classes } from '@blueprintjs/core';
import { FastField, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'react-intl';
import { inputIntent } from 'utils';
export default function CustomerNotePanel({ errors, touched, getFieldProps }) {
return (
<div className={'tab-panel--note'}>
<FastField name={'note'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'note'} />}
className={classNames('form-group--note', Classes.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="payment_date" />}
>
<TextArea {...field} />
</FormGroup>
)}
</FastField>
</div>
);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import classNames from 'classnames';
import { RadioGroup, Radio, FormGroup } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { FastField } from 'formik';
import { handleStringChange, saveInvoke } from 'utils';
/**
* Customer type radio field.
*/
export default function RadioCustomer(props) {
const { onChange, ...rest } = props;
const { formatMessage } = useIntl();
return (
<FastField name={'customer_type'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
inline={true}
label={<T id={'customer_type'} />}
className={classNames('form-group--customer_type')}
>
<RadioGroup
inline={true}
onChange={handleStringChange((value) => {
saveInvoke(onChange, value);
form.setFieldValue('customer_type', value);
})}
selectedValue={value}
>
<Radio label={formatMessage({ id: 'business' })} value="business" />
<Radio
label={formatMessage({ id: 'individual' })}
value="individual"
/>
</RadioGroup>
</FormGroup>
)}
</FastField>
);
}

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { Tabs, Tab } from '@blueprintjs/core';
import { useIntl } from 'react-intl';
import CustomerAddressTabs from './CustomerAddressTabs';
import CustomerAttachmentTabs from './CustomerAttachmentTabs';
import CustomerFinancialPanel from './CustomerFinancialPanel';
import CustomerNotePanel from './CustomerNotePanel';
export default function CustomersTabs() {
const { formatMessage } = useIntl();
return (
<div>
<Tabs
animate={true}
id={'customer-tabs'}
large={true}
defaultSelectedTabId="financial"
>
<Tab
id={'financial'}
title={formatMessage({ id: 'financial_details' })}
panel={<CustomerFinancialPanel />}
/>
<Tab
id={'address'}
title={formatMessage({ id: 'address' })}
panel={<CustomerAddressTabs />}
/>
<Tab
id="notes"
title={formatMessage({ id: 'notes' })}
panel={<CustomerNotePanel />}
/>
<Tab
id={'attachement'}
title={formatMessage({ id: 'attachement' })}
panel={<CustomerAttachmentTabs />}
/>
</Tabs>
</div>
);
}