mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
Fix : Customers
This commit is contained in:
@@ -2,22 +2,25 @@ import React, { useCallback } from 'react';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import CustomerForm from 'containers/Customers/CustomerForm';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withCustomersActions from './withCustomersActions';
|
||||
import withCurrenciesActions from 'containers/Currencies/withCurrenciesActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function Customer({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
// // #withDashboardActions
|
||||
// changePageTitle,
|
||||
|
||||
formik,
|
||||
// formik,
|
||||
//#withCustomersActions
|
||||
requestFetchCustomers,
|
||||
requestFetchCustomer,
|
||||
|
||||
// #wihtCurrenciesActions
|
||||
requestFetchCurrencies,
|
||||
}) {
|
||||
const { id } = useParams();
|
||||
const history = useHistory();
|
||||
@@ -32,7 +35,11 @@ function Customer({
|
||||
(key, customerId) => requestFetchCustomer(customerId),
|
||||
{ enabled: id && id },
|
||||
);
|
||||
|
||||
// Handle fetch Currencies data table
|
||||
const fetchCurrencies = useQuery('currencies', () =>
|
||||
requestFetchCurrencies(),
|
||||
);
|
||||
|
||||
const handleFormSubmit = useCallback(
|
||||
(payload) => {
|
||||
payload.redirect && history.push('/customers');
|
||||
@@ -46,7 +53,11 @@ function Customer({
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={fetchCustomer.isFetching || fetchCustomers.isFetching}
|
||||
loading={
|
||||
fetchCustomer.isFetching ||
|
||||
fetchCustomers.isFetching ||
|
||||
fetchCurrencies.isFetching
|
||||
}
|
||||
name={'customer-form'}
|
||||
>
|
||||
<CustomerForm
|
||||
@@ -58,4 +69,4 @@ function Customer({
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDashboardActions, withCustomersActions)(Customer);
|
||||
export default compose(withCustomersActions, withCurrenciesActions)(Customer);
|
||||
|
||||
@@ -18,7 +18,7 @@ const CustomerBillingAddress = ({
|
||||
<h4>
|
||||
<T id={'billing_address'} />
|
||||
</h4>
|
||||
|
||||
{/*------------ Billing Address country -----------*/}
|
||||
<FormGroup
|
||||
className={'form-group--journal-number'}
|
||||
intent={
|
||||
@@ -44,7 +44,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_country')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Billing Address 1 -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'address_line_1'} />}
|
||||
className={'form-group--address_line_1'}
|
||||
@@ -67,7 +67,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_1')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Billing Address 2 -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'address_line_2'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -90,7 +90,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_2')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Billing Address city -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'city_town'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -116,7 +116,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_city')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Billing Address state -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'state'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -142,47 +142,47 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_state')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Billing Address postcode -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'zip_code'} />}
|
||||
intent={
|
||||
errors.billing_address_zipcode &&
|
||||
touched.billing_address_zipcode &&
|
||||
errors.billing_address_postcode &&
|
||||
touched.billing_address_postcode &&
|
||||
Intent.DANGER
|
||||
}
|
||||
inline={true}
|
||||
helperText={
|
||||
<ErrorMessage
|
||||
name="billing_address_zipcode"
|
||||
name="billing_address_postcode"
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.billing_address_zipcode &&
|
||||
touched.billing_address_zipcode &&
|
||||
errors.billing_address_postcode &&
|
||||
touched.billing_address_postcode &&
|
||||
Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('billing_address_zipcode')}
|
||||
{...getFieldProps('billing_address_postcode')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Billing Address phone -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'phone'} />}
|
||||
intent={
|
||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
||||
errors.billing_address_phone && touched.billing_address_phone && Intent.DANGER
|
||||
}
|
||||
inline={true}
|
||||
helperText={
|
||||
<ErrorMessage name="shipping_phone" {...{ errors, touched }} />
|
||||
<ErrorMessage name="billing_address_phone" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
||||
errors.billing_address_phone && touched.billing_address_phone && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('shipping_phone')}
|
||||
{...getFieldProps('billing_address_phone')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
@@ -191,7 +191,7 @@ const CustomerBillingAddress = ({
|
||||
<h4>
|
||||
<T id={'shipping_address'} />
|
||||
</h4>
|
||||
|
||||
{/*------------ Shipping Address country -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'country'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -217,7 +217,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('shipping_address_country')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Shipping Address 1 -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'address_line_1'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -240,7 +240,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_1')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Shipping Address 2 -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'address_line_2'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -263,7 +263,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('billing_address_2')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Shipping Address city -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'city_town'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -289,7 +289,7 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('shipping_address_city')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Shipping Address state -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'state'} />}
|
||||
className={'form-group--journal-number'}
|
||||
@@ -315,47 +315,47 @@ const CustomerBillingAddress = ({
|
||||
{...getFieldProps('shipping_address_state')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Shipping Address postcode -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'zip_code'} />}
|
||||
intent={
|
||||
errors.shipping_address_zipcode &&
|
||||
touched.shipping_address_zipcode &&
|
||||
errors.shipping_address_postcode &&
|
||||
touched.shipping_address_postcode &&
|
||||
Intent.DANGER
|
||||
}
|
||||
inline={true}
|
||||
helperText={
|
||||
<ErrorMessage
|
||||
name="shipping_address_zipcode"
|
||||
name="shipping_address_postcode"
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.shipping_address_zipcode &&
|
||||
touched.shipping_address_zipcode &&
|
||||
errors.shipping_address_postcode &&
|
||||
touched.shipping_address_postcode &&
|
||||
Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('shipping_address_zipcode')}
|
||||
{...getFieldProps('shipping_address_postcode')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Shipping Address phone -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'phone'} />}
|
||||
intent={
|
||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
||||
errors.shipping_address_phone && touched.shipping_address_phone && Intent.DANGER
|
||||
}
|
||||
inline={true}
|
||||
helperText={
|
||||
<ErrorMessage name="shipping_phone" {...{ errors, touched }} />
|
||||
<ErrorMessage name="shipping_address_phone" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
||||
errors.shipping_address_phone && touched.shipping_address_phone && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('shipping_phone')}
|
||||
{...getFieldProps('shipping_address_phone')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import moment from 'moment';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, Intent, Position, Classes } from '@blueprintjs/core';
|
||||
@@ -6,52 +6,86 @@ import { DateInput } from '@blueprintjs/datetime';
|
||||
import {
|
||||
ErrorMessage,
|
||||
MoneyInputGroup,
|
||||
CurrenciesSelectList,
|
||||
CurrencySelectList,
|
||||
Row,
|
||||
Col,
|
||||
} from 'components';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { momentFormatter, tansformDateValue } from 'utils';
|
||||
|
||||
export default function CustomerFinancialPanel({
|
||||
import withCurrencies from 'containers/Currencies/withCurrencies';
|
||||
|
||||
import { compose, momentFormatter, tansformDateValue } from 'utils';
|
||||
|
||||
function CustomerFinancialPanel({
|
||||
setFieldValue,
|
||||
errors,
|
||||
touched,
|
||||
values,
|
||||
|
||||
// #withCurrencies
|
||||
currenciesList,
|
||||
|
||||
customerId,
|
||||
}) {
|
||||
const [selectedItems, setSelectedItems] = useState();
|
||||
|
||||
const handleDateChange = useCallback(
|
||||
(date) => {
|
||||
const formatted = moment(date).format('YYYY-MM-DD');
|
||||
setFieldValue('payment_date', formatted);
|
||||
setFieldValue('opening_balance_at', formatted);
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
const handleMoneyInputChange = useCallback(
|
||||
(e, value) => {
|
||||
setFieldValue('opening_balance', value);
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
const onItemsSelect = useCallback(
|
||||
(filedName) => {
|
||||
return (filed) => {
|
||||
setSelectedItems({
|
||||
...selectedItems,
|
||||
[filedName]: filed,
|
||||
});
|
||||
setFieldValue(filedName, filed.currency_code);
|
||||
};
|
||||
},
|
||||
[setFieldValue, selectedItems],
|
||||
);
|
||||
return (
|
||||
<div className={'tab-panel--financial'}>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
{/*------------ Opening balance at -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'opening_balance_at'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
intent={
|
||||
errors.opening_balance_date &&
|
||||
touched.opening_balance_date &&
|
||||
errors.opening_balance_at &&
|
||||
touched.opening_balance_at &&
|
||||
Intent.DANGER
|
||||
}
|
||||
inline={true}
|
||||
helperText={
|
||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||
<ErrorMessage
|
||||
name="opening_balance_at"
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(values.payment_date)}
|
||||
value={tansformDateValue(values.opening_balance_at)}
|
||||
onChange={handleDateChange}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
disabled={customerId}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Opening balance -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'opening_balance'} />}
|
||||
className={classNames('form-group--opening-balance', Classes.FILL)}
|
||||
@@ -63,12 +97,14 @@ export default function CustomerFinancialPanel({
|
||||
<MoneyInputGroup
|
||||
value={values.opening_balance}
|
||||
prefix={'$'}
|
||||
onChange={handleMoneyInputChange}
|
||||
inputGroupProps={{
|
||||
fill: true,
|
||||
}}
|
||||
disabled={customerId}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Currency -----------*/}
|
||||
<FormGroup
|
||||
label={<T id={'currency'} />}
|
||||
className={classNames(
|
||||
@@ -78,10 +114,20 @@ export default function CustomerFinancialPanel({
|
||||
)}
|
||||
inline={true}
|
||||
>
|
||||
<CurrenciesSelectList />
|
||||
{/* <CurrenciesSelectList /> */}
|
||||
<CurrencySelectList
|
||||
currenciesList={currenciesList}
|
||||
selectedCurrencyCode={values.currency_code}
|
||||
onCurrencySelected={onItemsSelect('currency_code')}
|
||||
disabled={customerId}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withCurrencies(({ currenciesList }) => ({ currenciesList })),
|
||||
)(CustomerFinancialPanel);
|
||||
|
||||
@@ -15,8 +15,8 @@ export default function CustomerFloatingActions({
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
onSubmitClick({ publish: true, redirect: true });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
@@ -24,6 +25,9 @@ import useMedia from 'hooks/useMedia';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Customer form.
|
||||
*/
|
||||
function CustomerForm({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
@@ -36,7 +40,6 @@ function CustomerForm({
|
||||
|
||||
// #withCustomersActions
|
||||
requestSubmitCustomer,
|
||||
requestFetchCustomers,
|
||||
requestEditCustomer,
|
||||
|
||||
// #withMediaActions
|
||||
@@ -67,6 +70,7 @@ function CustomerForm({
|
||||
.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(),
|
||||
@@ -74,48 +78,71 @@ function CustomerForm({
|
||||
.trim()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'display_name_' })),
|
||||
|
||||
email: Yup.string().email(),
|
||||
|
||||
work_phone: Yup.number(),
|
||||
personal_phone: Yup.number(),
|
||||
website: Yup.string().url(),
|
||||
|
||||
active: Yup.boolean(),
|
||||
note: Yup.string().trim(),
|
||||
|
||||
billing_address_city: Yup.string().trim(),
|
||||
billing_address_country: Yup.string().trim(),
|
||||
billing_address_email: Yup.string().email(),
|
||||
billing_address_zipcode: Yup.number().nullable(),
|
||||
billing_address_phone: Yup.number(),
|
||||
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_city: Yup.string().trim(),
|
||||
shipping_address_country: Yup.string().trim(),
|
||||
shipping_address_email: Yup.string().email(),
|
||||
shipping_address_zipcode: Yup.number().nullable(),
|
||||
shipping_address_phone: Yup.number(),
|
||||
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(),
|
||||
currency_code: Yup.string(),
|
||||
opening_balance_at: Yup.date(),
|
||||
});
|
||||
|
||||
const defaultInitialValues = useMemo(
|
||||
() => ({
|
||||
customer_type: 'business',
|
||||
salutation: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
company_name: '',
|
||||
display_name: '',
|
||||
|
||||
email: '',
|
||||
work_phone: '',
|
||||
personal_phone: '',
|
||||
website: '',
|
||||
note: '',
|
||||
active: true,
|
||||
|
||||
billing_address_city: '',
|
||||
billing_address_country: '',
|
||||
billing_address_zipcode: null,
|
||||
billing_address_phone: '',
|
||||
billing_address_1: '',
|
||||
billing_address_2: '',
|
||||
billing_address_city: '',
|
||||
billing_address_state: '',
|
||||
billing_address_postcode: null,
|
||||
billing_address_phone: '',
|
||||
|
||||
shipping_address_city: '',
|
||||
shipping_address_country: '',
|
||||
shipping_address_zipcode: null,
|
||||
shipping_address_phone: '',
|
||||
shipping_address_1: '',
|
||||
shipping_address_2: '',
|
||||
shipping_address_city: '',
|
||||
shipping_address_state: '',
|
||||
shipping_address_postcode: null,
|
||||
shipping_address_phone: '',
|
||||
|
||||
opening_balance: '',
|
||||
currency_code: '',
|
||||
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@@ -147,7 +174,10 @@ function CustomerForm({
|
||||
: changePageTitle(formatMessage({ id: 'new_customer' }));
|
||||
}, [changePageTitle, customer, formatMessage]);
|
||||
|
||||
const handleFormSubmit = (values, { setSubmitting, resetForm, setErrors }) => {
|
||||
const handleFormSubmit = (
|
||||
values,
|
||||
{ setSubmitting, resetForm, setErrors },
|
||||
) => {
|
||||
const formValues = { ...values, status: payload.publish };
|
||||
if (customer && customer.id) {
|
||||
requestEditCustomer(customer.id, formValues)
|
||||
@@ -189,7 +219,8 @@ function CustomerForm({
|
||||
errors,
|
||||
values,
|
||||
touched,
|
||||
handleSubmit
|
||||
isSubmitting,
|
||||
handleSubmit,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: validationSchema,
|
||||
@@ -199,7 +230,6 @@ function CustomerForm({
|
||||
onSubmit: handleFormSubmit,
|
||||
});
|
||||
|
||||
|
||||
const initialAttachmentFiles = useMemo(() => {
|
||||
return customer && customer.media
|
||||
? customer.media.map((attach) => ({
|
||||
@@ -209,6 +239,7 @@ function CustomerForm({
|
||||
}))
|
||||
: [];
|
||||
}, []);
|
||||
|
||||
const handleDropFiles = useCallback((_files) => {
|
||||
setFiles(_files.filter((file) => file.uploaded === false));
|
||||
}, []);
|
||||
@@ -269,16 +300,19 @@ function CustomerForm({
|
||||
getFieldProps={getFieldProps}
|
||||
errors={errors}
|
||||
values={values}
|
||||
touched={touched} />
|
||||
touched={touched}
|
||||
customerId={customer}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CustomerFloatingActions
|
||||
onSubmitClick={handleSubmitClick}
|
||||
customer={customer}
|
||||
onCancelClick={handleCancelClick}
|
||||
customerId={null}
|
||||
/>
|
||||
</form>
|
||||
|
||||
<CustomerFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
// customer={customer}
|
||||
onCancelClick={handleCancelClick}
|
||||
customerId={customer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,12 +31,8 @@ export default function CustomerFormAfterPrimarySection({
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/*------------ Customer email -----------*/}
|
||||
{/*------------ Phone number -----------*/}
|
||||
<FormGroup
|
||||
intent={errors.work_phone && touched.work_phone && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'work_phone'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--phone-number'}
|
||||
label={<T id={'phone_number'} />}
|
||||
inline={true}
|
||||
@@ -49,8 +45,8 @@ export default function CustomerFormAfterPrimarySection({
|
||||
/>
|
||||
|
||||
<InputGroup
|
||||
intent={errors.work_phone && touched.work_phone && Intent.DANGER}
|
||||
{...getFieldProps('work_phone')}
|
||||
intent={errors.personal_phone && touched.personal_phone && Intent.DANGER}
|
||||
{...getFieldProps('personal_phone')}
|
||||
placeholder={'Mobile'}
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
@@ -32,15 +32,20 @@ export default function CustomerFormPrimarySection({
|
||||
);
|
||||
|
||||
// Handle salutation field select.
|
||||
const handleSalutationSelect = useCallback((salutation) => {
|
||||
setFieldValue('salutation', salutation.label);
|
||||
}, [setFieldValue]);
|
||||
const handleSalutationSelect = useCallback(
|
||||
(salutation) => {
|
||||
setFieldValue('salutation', salutation.label);
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
// Handle display name field select.
|
||||
const handleDisplayNameSelect = useCallback((displayName) => {
|
||||
setFieldValue('display_name', displayName.label);
|
||||
}, [setFieldValue]);
|
||||
|
||||
const handleDisplayNameSelect = useCallback(
|
||||
(displayName) => {
|
||||
setFieldValue('display_name', displayName.label);
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
return (
|
||||
<div className={'customer-form__primary-section-content'}>
|
||||
{/**-----------Customer type. -----------*/}
|
||||
@@ -58,6 +63,7 @@ export default function CustomerFormPrimarySection({
|
||||
<ControlGroup>
|
||||
<SalutationList
|
||||
onItemSelect={handleSalutationSelect}
|
||||
selectedItem={values.salutation}
|
||||
popoverProps={{ minimal: true }}
|
||||
className={classNames(
|
||||
CLASSES.FORM_GROUP_LIST_SELECT,
|
||||
@@ -119,6 +125,7 @@ export default function CustomerFormPrimarySection({
|
||||
company={values.company_name}
|
||||
salutation={values.salutation}
|
||||
onItemSelect={handleDisplayNameSelect}
|
||||
// selectedItem={values.display_name}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@@ -10,14 +10,12 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import DataTable from 'components/DataTable';
|
||||
import Icon from 'components/Icon';
|
||||
import { Money } from 'components';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
import withCustomers from './withCustomers';
|
||||
import { DataTable, Icon, Money } from 'components';
|
||||
|
||||
import withCustomers from './withCustomers';
|
||||
import { compose, firstLettersArgs, saveInvoke } from 'utils';
|
||||
|
||||
const AvatarCell = (row) => {
|
||||
@@ -25,13 +23,13 @@ const AvatarCell = (row) => {
|
||||
};
|
||||
|
||||
const CustomerTable = ({
|
||||
loading,
|
||||
|
||||
//#withCustomers
|
||||
customers,
|
||||
customersLoading,
|
||||
customerPagination,
|
||||
|
||||
//#props
|
||||
//#OwnProps
|
||||
loading,
|
||||
onEditCustomer,
|
||||
onDeleteCustomer,
|
||||
onFetchData,
|
||||
@@ -80,18 +78,21 @@ const CustomerTable = ({
|
||||
);
|
||||
|
||||
// Renders actions table cell.
|
||||
const renderActionsCell = useMemo(() => ({ cell }) => (
|
||||
<Popover
|
||||
content={renderContextMenu({
|
||||
customer: cell.row.original,
|
||||
onEditCustomer,
|
||||
onDeleteCustomer,
|
||||
})}
|
||||
position={Position.RIGHT_BOTTOM}
|
||||
>
|
||||
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||
</Popover>
|
||||
), [onDeleteCustomer, onEditCustomer, renderContextMenu]);
|
||||
const renderActionsCell = useMemo(
|
||||
() => ({ cell }) => (
|
||||
<Popover
|
||||
content={renderContextMenu({
|
||||
customer: cell.row.original,
|
||||
onEditCustomer,
|
||||
onDeleteCustomer,
|
||||
})}
|
||||
position={Position.RIGHT_BOTTOM}
|
||||
>
|
||||
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||
</Popover>
|
||||
),
|
||||
[onDeleteCustomer, onEditCustomer, renderContextMenu],
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
@@ -186,14 +187,19 @@ const CustomerTable = ({
|
||||
loading={customersLoading && !initialMount}
|
||||
spinnerProps={{ size: 30 }}
|
||||
rowContextMenu={rowContextMenu}
|
||||
pagination={true}
|
||||
pagesCount={customerPagination.pagesCount}
|
||||
initialPageSize={customerPagination.pageSize}
|
||||
initialPageIndex={customerPagination.page - 1}
|
||||
/>
|
||||
</LoadingIndicator>
|
||||
);
|
||||
};
|
||||
|
||||
export default compose(
|
||||
withCustomers(({ customers, customersLoading }) => ({
|
||||
withCustomers(({ customers, customersLoading, customerPagination }) => ({
|
||||
customers,
|
||||
customersLoading,
|
||||
customerPagination,
|
||||
})),
|
||||
)(CustomerTable);
|
||||
|
||||
@@ -16,8 +16,10 @@ import CustomersTable from 'containers/Customers/CustomerTable';
|
||||
import CustomerActionsBar from 'containers/Customers/CustomerActionsBar';
|
||||
import CustomersViewsTabs from 'containers/Customers/CustomersViewsTabs';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||
import withViewsActions from 'containers/Views/withViewsActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
@@ -30,7 +32,10 @@ function CustomersList({
|
||||
requestFetchResourceViews,
|
||||
requestFetchResourceFields,
|
||||
|
||||
//#withCustomersActions
|
||||
// #withCustomers
|
||||
customersTableQuery,
|
||||
|
||||
// #withCustomersActions
|
||||
requestFetchCustomers,
|
||||
requestDeleteCustomer,
|
||||
requestDeleteBulkCustomers,
|
||||
@@ -57,9 +62,10 @@ function CustomersList({
|
||||
// ]);
|
||||
// });
|
||||
|
||||
const fetchCustomers = useQuery('customers-table', () => {
|
||||
requestFetchCustomers({});
|
||||
});
|
||||
const fetchCustomers = useQuery(
|
||||
['customers-table', customersTableQuery],
|
||||
() => requestFetchCustomers(),
|
||||
);
|
||||
|
||||
const handleEditCustomer = useCallback(
|
||||
(cusomter) => {
|
||||
@@ -67,6 +73,7 @@ function CustomersList({
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
// Handle click delete customer.
|
||||
const handleDeleteCustomer = useCallback(
|
||||
(customer) => {
|
||||
@@ -169,26 +176,29 @@ function CustomersList({
|
||||
}, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={fetchCustomers.isFetching}
|
||||
name={'customers-list'}
|
||||
>
|
||||
<DashboardInsider name={'customers-list'}>
|
||||
<CustomerActionsBar
|
||||
selectedRows={selectedRows}
|
||||
onFilterChanged={handleFilterChanged}
|
||||
onBulkDelete={handleBulkDelete}
|
||||
/>
|
||||
|
||||
<CustomersViewsTabs />
|
||||
|
||||
<DashboardPageContent>
|
||||
<CustomersTable
|
||||
loading={tableLoading}
|
||||
onDeleteCustomer={handleDeleteCustomer}
|
||||
onEditCustomer={handleEditCustomer}
|
||||
onfetchData={handleFetchData}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/customers/:custom_view_id/custom_view', '/customers']}
|
||||
>
|
||||
<CustomersViewsTabs />
|
||||
<CustomersTable
|
||||
loading={fetchCustomers.isFetching}
|
||||
onDeleteCustomer={handleDeleteCustomer}
|
||||
onEditCustomer={handleEditCustomer}
|
||||
onfetchData={handleFetchData}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
@@ -230,6 +240,7 @@ function CustomersList({
|
||||
|
||||
export default compose(
|
||||
withResourceActions,
|
||||
withDashboardActions,
|
||||
withCustomersActions,
|
||||
withDashboardActions,
|
||||
withCustomers(({ customersTableQuery }) => ({ customersTableQuery })),
|
||||
)(CustomersList);
|
||||
|
||||
@@ -12,9 +12,11 @@ export default function CustomersTabs({
|
||||
errors,
|
||||
values,
|
||||
touched,
|
||||
customerId,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const [customer] = useState(customerId);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs
|
||||
@@ -32,6 +34,7 @@ export default function CustomersTabs({
|
||||
errors={errors}
|
||||
setFieldValue={setFieldValue}
|
||||
touched={touched}
|
||||
customerId={customer}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getCustomersItems, getCustomersListFactory } from 'store/customers/customers.selectors';
|
||||
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||
import {
|
||||
getCustomerCurrentPageFactory,
|
||||
getCustomerPaginationMetaFactory,
|
||||
getCustomerTableQueryFactory,
|
||||
} from 'store/customers/customers.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getCustomersList = getCustomersListFactory();
|
||||
const getCustomersList = getCustomerCurrentPageFactory();
|
||||
const getCustomerPaginationMeta = getCustomerPaginationMetaFactory();
|
||||
const getCustomerTableQuery = getCustomerTableQueryFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const query = getCustomerTableQuery(state, props);
|
||||
|
||||
const mapped = {
|
||||
customers: getCustomersList(state, props, query),
|
||||
customersViews: getResourceViews(state, props, 'customers'),
|
||||
customersItems: getCustomersList(state, props),
|
||||
customers: getCustomersItems(state, state.customers.currentViewId),
|
||||
customersTableQuery: query,
|
||||
customerPagination: getCustomerPaginationMeta(state, props, query),
|
||||
customersLoading: state.customers.loading,
|
||||
customerErrors: state.customers.errors,
|
||||
customersItems: state.customers.items,
|
||||
// customerErrors: state.customers.errors,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { resolve } from 'p-progress';
|
||||
import ApiService from 'services/ApiService';
|
||||
import t from 'store/types';
|
||||
|
||||
export const submitCustomer = ({ form }) => {
|
||||
return (dispatch) =>
|
||||
new Promise((resolve, reject) => {
|
||||
dispatch({
|
||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||
});
|
||||
|
||||
ApiService.post('customers', form)
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
@@ -25,10 +20,6 @@ export const submitCustomer = ({ form }) => {
|
||||
export const editCustomer = ({ form, id }) => {
|
||||
return (dispatch) =>
|
||||
new Promise((resolve, reject) => {
|
||||
dispatch({
|
||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||
});
|
||||
|
||||
ApiService.post(`customers/${id}`, form)
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
@@ -45,7 +36,8 @@ export const editCustomer = ({ form, id }) => {
|
||||
export const fetchCustomers = ({ query }) => {
|
||||
return (dispatch, getState) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const pageQuery = getState().items.tableQuery;
|
||||
const pageQuery = getState().customers.tableQuery;
|
||||
|
||||
dispatch({
|
||||
type: t.CUSTOMERS_TABLE_LOADING,
|
||||
payload: { loading: true },
|
||||
@@ -53,14 +45,25 @@ export const fetchCustomers = ({ query }) => {
|
||||
ApiService.get(`customers`, { params: { ...pageQuery, ...query } })
|
||||
.then((response) => {
|
||||
dispatch({
|
||||
type: t.CUSTOMERS_ITEMS_SET,
|
||||
customers: response.data.customers,
|
||||
type: t.CUSTOMERS_PAGE_SET,
|
||||
payload: {
|
||||
customers: response.data.customers,
|
||||
customViewId: response.data.customViewId || -1,
|
||||
paginationMeta: response.data.pagination,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: t.CUSTOMERS_PAGE_SET,
|
||||
customers: response.data.customers,
|
||||
customViewId: response.data.customers?.viewMeta?.customViewId || -1,
|
||||
paginationMeta: response.data.pagination,
|
||||
type: t.CUSTOMERS_ITEMS_SET,
|
||||
payload: {
|
||||
customers: response.data.customers,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: t.CUSTOMERS_PAGINATION_SET,
|
||||
payload: {
|
||||
pagination: response.data.pagination,
|
||||
customViewId: response.data.customViewId || -1,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: t.CUSTOMERS_TABLE_LOADING,
|
||||
@@ -83,7 +86,7 @@ export const fetchCustomer = ({ id }) => {
|
||||
type: t.CUSTOMER_SET,
|
||||
payload: {
|
||||
id,
|
||||
customer: response.data.contact,
|
||||
customer: response.data.customer,
|
||||
},
|
||||
});
|
||||
resolve(response);
|
||||
@@ -99,7 +102,10 @@ export const deleteCustomer = ({ id }) => {
|
||||
new Promise((resolve, reject) => {
|
||||
ApiService.delete(`customers/${id}`)
|
||||
.then((response) => {
|
||||
dispatch({ type: t.CUSTOMER_DELETE, id });
|
||||
dispatch({
|
||||
type: t.CUSTOMER_DELETE,
|
||||
payload: { id },
|
||||
});
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import t from 'store/types';
|
||||
import { snakeCase } from 'lodash';
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { createTableQueryReducers } from 'store/queryReducers';
|
||||
|
||||
@@ -7,14 +8,26 @@ const initialState = {
|
||||
views: {},
|
||||
loading: false,
|
||||
currentViewId: -1,
|
||||
tableQuery: {
|
||||
page_size: 5,
|
||||
page: 1,
|
||||
},
|
||||
errors: [],
|
||||
};
|
||||
|
||||
const customersReducer = createReducer(initialState, {
|
||||
[t.CUSTOMER_SET]: (state, action) => {
|
||||
const { id, customer } = action.payload;
|
||||
const _customers = state.items[id] || {};
|
||||
state.items[id] = { ..._customers, ...customer };
|
||||
},
|
||||
|
||||
[t.CUSTOMERS_ITEMS_SET]: (state, action) => {
|
||||
const { customers } = action.payload;
|
||||
|
||||
const _customers = {};
|
||||
|
||||
action.customers.forEach((customer) => {
|
||||
customers.forEach((customer) => {
|
||||
_customers[customer.id] = customer;
|
||||
});
|
||||
state.items = {
|
||||
@@ -22,24 +35,59 @@ const customersReducer = createReducer(initialState, {
|
||||
..._customers,
|
||||
};
|
||||
},
|
||||
|
||||
[t.CUSTOMERS_PAGE_SET]: (state, action) => {
|
||||
const viewId = action.customViewId || -1;
|
||||
const { customViewId, customers, paginationMeta } = action.payload;
|
||||
|
||||
const viewId = customViewId || -1;
|
||||
const view = state.views[viewId] || {};
|
||||
|
||||
state.views[viewId] = {
|
||||
...view,
|
||||
ids: action.customers.map((i) => i.id),
|
||||
pages: {
|
||||
...(state.views?.[viewId]?.pages || {}),
|
||||
[paginationMeta.page]: {
|
||||
ids: customers.map((i) => i.id),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
[t.CUSTOMER_DELETE]: (state, action) => {
|
||||
if (typeof state.items[action.id] !== 'undefined') {
|
||||
const { id } = action.payload;
|
||||
|
||||
if (typeof state.items[id] !== 'undefined') {
|
||||
delete state.items[action.id];
|
||||
}
|
||||
},
|
||||
|
||||
[t.CUSTOMERS_TABLE_LOADING]: (state, action) => {
|
||||
const { loading } = action.payload;
|
||||
state.loading = !!loading;
|
||||
state.loading = loading;
|
||||
},
|
||||
|
||||
[t.CUSTOMERS_PAGINATION_SET]: (state, action) => {
|
||||
const { pagination, customViewId } = action.payload;
|
||||
|
||||
const mapped = {
|
||||
pageSize: parseInt(pagination.page_size, 10),
|
||||
page: parseInt(pagination.page, 10),
|
||||
total: parseInt(pagination.total, 10),
|
||||
};
|
||||
const paginationMeta = {
|
||||
...mapped,
|
||||
pagesCount: Math.ceil(mapped.total / mapped.pageSize),
|
||||
pageIndex: Math.max(mapped.page - 1, 0),
|
||||
};
|
||||
state.views = {
|
||||
...state.views,
|
||||
[customViewId]: {
|
||||
...(state.views?.[customViewId] || {}),
|
||||
paginationMeta,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
[t.CUSTOMERS_BULK_DELETE]: (state, action) => {
|
||||
const { ids } = action.payload;
|
||||
const items = { ...state.items };
|
||||
@@ -51,10 +99,6 @@ const customersReducer = createReducer(initialState, {
|
||||
});
|
||||
state.items = items;
|
||||
},
|
||||
[t.CUSTOMER_SET]: (state, action) => {
|
||||
const { id, customer } = action.payload;
|
||||
state.items[id] = { ...customer };
|
||||
},
|
||||
});
|
||||
|
||||
export default createTableQueryReducers('customers', customersReducer);
|
||||
|
||||
@@ -1,26 +1,54 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { pickItemsFromIds } from 'store/selectors';
|
||||
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||
|
||||
const customersViewsSelector = state => state.customers.views;
|
||||
const customersItemsSelector = state => state.customers.items;
|
||||
const customersCurrentViewSelector = state => state.customers.currentViewId;
|
||||
const customerTableQuery = (state) => state.customers.tableQuery;
|
||||
|
||||
export const getCustomersItems = createSelector(
|
||||
customersViewsSelector,
|
||||
customersItemsSelector,
|
||||
customersCurrentViewSelector,
|
||||
(customersViews, customersItems, currentViewId) => {
|
||||
const customersView = customersViews[currentViewId || -1];
|
||||
const customersByIdSelector = (state, props) => {
|
||||
return state.customers.items[props.customerId];
|
||||
};
|
||||
|
||||
return (typeof customersView === 'object')
|
||||
? pickItemsFromIds(customersItems, customersView.ids) || []
|
||||
: [];
|
||||
},
|
||||
);
|
||||
const customersPaginationSelector = (state, props) => {
|
||||
const viewId = state.customers.currentViewId;
|
||||
return state.customers.views?.[viewId];
|
||||
};
|
||||
|
||||
const customerPageSelector = (state, props, query) => {
|
||||
const viewId = state.customers.currentViewId;
|
||||
return state.customers.views?.[viewId]?.pages?.[query.page];
|
||||
};
|
||||
|
||||
const customersItemsSelector = (state) => state.customers.items;
|
||||
|
||||
export const getCustomerTableQueryFactory = () =>
|
||||
createSelector(
|
||||
paginationLocationQuery,
|
||||
customerTableQuery,
|
||||
(locationQuery, tableQuery) => {
|
||||
return {
|
||||
...locationQuery,
|
||||
...tableQuery,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const getCustomerCurrentPageFactory = () =>
|
||||
createSelector(
|
||||
customerPageSelector,
|
||||
customersItemsSelector,
|
||||
(customerPage, customersItems) => {
|
||||
return typeof customerPage === 'object'
|
||||
? pickItemsFromIds(customersItems, customerPage.ids) || []
|
||||
: [];
|
||||
},
|
||||
);
|
||||
|
||||
export const getCustomersByIdFactory = () =>
|
||||
createSelector(customersByIdSelector, (customer) => {
|
||||
return customer;
|
||||
});
|
||||
|
||||
export const getCustomerPaginationMetaFactory = () =>
|
||||
createSelector(customersPaginationSelector, (customerPage) => {
|
||||
return customerPage?.paginationMeta || {};
|
||||
});
|
||||
|
||||
export const getCustomersListFactory = () => createSelector(
|
||||
customersItemsSelector,
|
||||
(customersItems) => {
|
||||
return Object.values(customersItems);
|
||||
}
|
||||
);
|
||||
@@ -4,6 +4,7 @@ export default {
|
||||
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
|
||||
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
|
||||
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
|
||||
CUSTOMER_DELETE:'CUSTOMER_DELETE',
|
||||
CUSTOMERS_BULK_DELETE:'CUSTOMERS_BULK_DELETE'
|
||||
CUSTOMER_DELETE: 'CUSTOMER_DELETE',
|
||||
CUSTOMERS_BULK_DELETE: 'CUSTOMERS_BULK_DELETE',
|
||||
CUSTOMERS_PAGINATION_SET: 'CUSTOMERS_PAGINATION_SET',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user