mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
WIP/customers
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import { FormattedMessage as T} from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
@@ -8,9 +8,9 @@ export default [
|
|||||||
{
|
{
|
||||||
icon: 'homepage',
|
icon: 'homepage',
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
text: <T id={'homepage'}/>,
|
text: <T id={'homepage'} />,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
href: '/homepage',
|
href: '/homepage',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
divider: true,
|
divider: true,
|
||||||
@@ -18,18 +18,18 @@ export default [
|
|||||||
{
|
{
|
||||||
icon: 'homepage',
|
icon: 'homepage',
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
text: <T id={'items'}/>,
|
text: <T id={'items'} />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
text: <T id={'items_list'}/>,
|
text: <T id={'items_list'} />,
|
||||||
href: '/items',
|
href: '/items',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'new_item'}/>,
|
text: <T id={'new_item'} />,
|
||||||
href: '/items/new',
|
href: '/items/new',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'category_list'}/>,
|
text: <T id={'category_list'} />,
|
||||||
href: '/items/categories',
|
href: '/items/categories',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -40,22 +40,22 @@ export default [
|
|||||||
{
|
{
|
||||||
icon: 'balance-scale',
|
icon: 'balance-scale',
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
text: <T id={'financial'}/>,
|
text: <T id={'financial'} />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
text: <T id={'accounts_chart'}/>,
|
text: <T id={'accounts_chart'} />,
|
||||||
href: '/accounts',
|
href: '/accounts',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'manual_journal'}/>,
|
text: <T id={'manual_journal'} />,
|
||||||
href: '/manual-journals',
|
href: '/manual-journals',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'make_journal'}/>,
|
text: <T id={'make_journal'} />,
|
||||||
href: '/make-journal-entry',
|
href: '/make-journal-entry',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'exchange_rate'}/>,
|
text: <T id={'exchange_rate'} />,
|
||||||
href: '/ExchangeRates',
|
href: '/ExchangeRates',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -63,19 +63,19 @@ export default [
|
|||||||
{
|
{
|
||||||
icon: 'university',
|
icon: 'university',
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
text: <T id={'banking'}/>,
|
text: <T id={'banking'} />,
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'shopping-cart',
|
icon: 'shopping-cart',
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
text: <T id={'sales'}/>,
|
text: <T id={'sales'} />,
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'balance-scale',
|
icon: 'balance-scale',
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
text: <T id={'purchases'}/>,
|
text: <T id={'purchases'} />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
icon: 'cut',
|
icon: 'cut',
|
||||||
@@ -88,54 +88,69 @@ export default [
|
|||||||
{
|
{
|
||||||
icon: 'analytics',
|
icon: 'analytics',
|
||||||
iconSize: 18,
|
iconSize: 18,
|
||||||
text: <T id={'financial_reports'}/>,
|
text: <T id={'financial_reports'} />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
text: <T id={'balance_sheet'}/>,
|
text: <T id={'balance_sheet'} />,
|
||||||
href: '/balance-sheet',
|
href: '/balance-sheet',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'trial_balance_sheet'}/>,
|
text: <T id={'trial_balance_sheet'} />,
|
||||||
href: '/trial-balance-sheet',
|
href: '/trial-balance-sheet',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'journal'}/>,
|
text: <T id={'journal'} />,
|
||||||
href: '/journal-sheet',
|
href: '/journal-sheet',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'general_ledger'}/>,
|
text: <T id={'general_ledger'} />,
|
||||||
href: '/general-ledger',
|
href: '/general-ledger',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'profit_loss_sheet'}/>,
|
text: <T id={'profit_loss_sheet'} />,
|
||||||
href: '/profit-loss-sheet',
|
href: '/profit-loss-sheet',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'expenses'}/>,
|
text: <T id={'expenses'} />,
|
||||||
icon: 'receipt',
|
icon: 'receipt',
|
||||||
iconSize: 18,
|
iconSize: 18,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
text: <T id={'expenses'}/>,
|
text: <T id={'expenses'} />,
|
||||||
href: '/expenses',
|
href: '/expenses',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'new_expenses'}/>,
|
text: <T id={'new_expenses'} />,
|
||||||
href: '/expenses/new',
|
href: '/expenses/new',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: <T id={'customers'} />,
|
||||||
|
icon: 'receipt',
|
||||||
|
iconSize: 18,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: <T id={'customers'} />,
|
||||||
|
// href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: <T id={'new_customers'} />,
|
||||||
|
href: '/customers/new',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
divider: true,
|
divider: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'preferences'}/>,
|
text: <T id={'preferences'} />,
|
||||||
href: '/preferences',
|
href: '/preferences',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'auditing_system'}/>,
|
text: <T id={'auditing_system'} />,
|
||||||
href: '/auditing/list',
|
href: '/auditing/list',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
20
client/src/containers/Customers/Customer.js
Normal file
20
client/src/containers/Customers/Customer.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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 withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function Customer({}) {
|
||||||
|
return (
|
||||||
|
<DashboardInsider name={'customer-form'}>
|
||||||
|
<CustomerForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Customer;
|
||||||
175
client/src/containers/Customers/CustomerForm.js
Normal file
175
client/src/containers/Customers/CustomerForm.js
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
MenuItem,
|
||||||
|
Intent,
|
||||||
|
InputGroup,
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Checkbox,
|
||||||
|
RadioGroup,
|
||||||
|
Radio,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { Row, Col } from 'react-grid-system';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import { queryCache, useQuery } from 'react-query';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { pick } from 'lodash';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import AppToaster from 'components/AppToaster';
|
||||||
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import MoneyInputGroup from 'components/MoneyInputGroup';
|
||||||
|
import Dragzone from 'components/Dragzone';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withCustomerDetail from './withCustomerDetail';
|
||||||
|
import withCustomersActions from './withCustomersActions';
|
||||||
|
import RadioCustomer from './RadioCustomer';
|
||||||
|
|
||||||
|
import { compose, handleStringChange } from 'utils';
|
||||||
|
import withCustomers from './withCustomers';
|
||||||
|
|
||||||
|
function CustomerForm({
|
||||||
|
// #withDashboardActions
|
||||||
|
changePageTitle,
|
||||||
|
|
||||||
|
customers,
|
||||||
|
//#withCustomersActions
|
||||||
|
requestSubmitCustomer,
|
||||||
|
requestFetchCustomers,
|
||||||
|
}) {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
customer_type: Yup.string()
|
||||||
|
.required()
|
||||||
|
.trim()
|
||||||
|
.label(formatMessage({ id: 'customer_type_' })),
|
||||||
|
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(),
|
||||||
|
work_phone: Yup.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changePageTitle(formatMessage({ id: 'new_customer' }));
|
||||||
|
}, [changePageTitle, formatMessage]);
|
||||||
|
//business
|
||||||
|
const initialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
customer_type: 'business',
|
||||||
|
first_name: '',
|
||||||
|
last_name: '',
|
||||||
|
company_name: '',
|
||||||
|
display_name: '',
|
||||||
|
// email: '',
|
||||||
|
work_phone: '',
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
getFieldProps,
|
||||||
|
setFieldValue,
|
||||||
|
values,
|
||||||
|
touched,
|
||||||
|
errors,
|
||||||
|
handleSubmit,
|
||||||
|
isSubmitting,
|
||||||
|
} = useFormik({
|
||||||
|
enableReinitialize: true,
|
||||||
|
validationSchema: validationSchema,
|
||||||
|
initialValues: {
|
||||||
|
...initialValues,
|
||||||
|
},
|
||||||
|
|
||||||
|
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
||||||
|
requestSubmitCustomer(values)
|
||||||
|
.then((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_customer_has_been_successfully_created',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const requiredSpan = useMemo(() => <span class="required">*</span>, []);
|
||||||
|
const handleCustomerTypeCahange = useCallback(
|
||||||
|
(value) => {
|
||||||
|
setFieldValue('customer_type', value);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(customers, 'ER');
|
||||||
|
|
||||||
|
const fetch = useQuery('customers-table', (key) => requestFetchCustomers());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'customer-form'}>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className={'customer-form__primary-section'}>
|
||||||
|
<RadioCustomer
|
||||||
|
selectedValue={values.customer_type}
|
||||||
|
onChange={handleCustomerTypeCahange}
|
||||||
|
/>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'display_name'} />}
|
||||||
|
className={'form-group--name'}
|
||||||
|
intent={
|
||||||
|
errors.display_name && touched.display_name && Intent.DANGER
|
||||||
|
}
|
||||||
|
inline={true}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage {...{ errors, touched }} name={'display_name'} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={
|
||||||
|
errors.display_name && touched.display_name && Intent.DANGER
|
||||||
|
}
|
||||||
|
{...getFieldProps('display_name')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form__floating-footer">
|
||||||
|
<Button intent={Intent.PRIMARY} disabled={isSubmitting} type="submit">
|
||||||
|
<T id={'save'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className={'ml1'} disabled={isSubmitting}>
|
||||||
|
<T id={'save_as_draft'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className={'ml1'}>
|
||||||
|
<T id={'close'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withCustomerDetail,
|
||||||
|
withCustomers(({ customers }) => ({
|
||||||
|
customers,
|
||||||
|
})),
|
||||||
|
withDashboardActions,
|
||||||
|
withCustomersActions,
|
||||||
|
)(CustomerForm);
|
||||||
22
client/src/containers/Customers/RadioCustomer.js
Normal file
22
client/src/containers/Customers/RadioCustomer.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { handleStringChange } from 'utils';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import { RadioGroup, Radio } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
export default function RadioCustomer(props) {
|
||||||
|
const { onChange, ...rest } = props;
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
return (
|
||||||
|
<RadioGroup
|
||||||
|
inline={true}
|
||||||
|
label={formatMessage({ id: 'customer_type' })}
|
||||||
|
onChange={handleStringChange((value) => {
|
||||||
|
onChange && onChange(value);
|
||||||
|
})}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<Radio label={formatMessage({ id: 'business' })} value="business" />
|
||||||
|
<Radio label={formatMessage({ id: 'individual' })} value="individual" />
|
||||||
|
</RadioGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
client/src/containers/Customers/withCustomerDetail.js
Normal file
8
client/src/containers/Customers/withCustomerDetail.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getCustomerById } from 'store/customers/customers.reducer';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
customerDetail: getCustomerById(state, props.customerId),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps);
|
||||||
18
client/src/containers/Customers/withCustomers.js
Normal file
18
client/src/containers/Customers/withCustomers.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getCustomersItems } from 'store/customers/customers.selectors';
|
||||||
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
|
|
||||||
|
export default (mapState) => {
|
||||||
|
const mapStateToProps = (state, props) => {
|
||||||
|
|
||||||
|
const mapped = {
|
||||||
|
customersViews: getResourceViews(state, 'customers'),
|
||||||
|
customers: getCustomersItems(state, state.customers.currentViewId),
|
||||||
|
customersLoading: state.customers.loading,
|
||||||
|
customerErrors: state.customers.errors,
|
||||||
|
};
|
||||||
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
return connect(mapStateToProps);
|
||||||
|
};
|
||||||
23
client/src/containers/Customers/withCustomersActions.js
Normal file
23
client/src/containers/Customers/withCustomersActions.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
fetchCustomers,
|
||||||
|
submitCustomer,
|
||||||
|
editCustomer,
|
||||||
|
} from 'store/customers/customers.actions';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
|
requestFetchCustomers: (query) => dispatch(fetchCustomers({ query })),
|
||||||
|
// requestDeleteCustomer: (id) => dispatch(deleteCustomer({ id })),
|
||||||
|
// requestDeleteBulkCustomers:(ids)=>dispatch(deleteBulkCustomers({ids})),
|
||||||
|
requestSubmitCustomer: (form) => dispatch(submitCustomer({ form })),
|
||||||
|
// requestEditCustomer: (id, form) => dispatch(editCustomer({ id, form })),
|
||||||
|
|
||||||
|
addCustomersTableQueries: (queries) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.CUSTOMERS_TABLE_QUERIES_ADD,
|
||||||
|
queries,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps);
|
||||||
@@ -417,4 +417,17 @@ export default {
|
|||||||
quick_new: 'Quick new',
|
quick_new: 'Quick new',
|
||||||
help: 'Help',
|
help: 'Help',
|
||||||
organization_id: 'Orgnization ID',
|
organization_id: 'Orgnization ID',
|
||||||
|
|
||||||
|
customers: 'Customers',
|
||||||
|
new_customers:'New Customers',
|
||||||
|
customer_type_: 'Customer type',
|
||||||
|
display_name_: 'Display name',
|
||||||
|
new_customer: 'New Customer',
|
||||||
|
customer_type: 'Customer Type',
|
||||||
|
business: 'business',
|
||||||
|
individual: 'Individual',
|
||||||
|
display_name:'Display Name',
|
||||||
|
|
||||||
|
the_customer_has_been_successfully_created:
|
||||||
|
'The customer has been successfully created.',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,4 +141,30 @@ export default [
|
|||||||
}),
|
}),
|
||||||
breadcrumb: 'Exchange Rates',
|
breadcrumb: 'Exchange Rates',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: `/customers/:id/edit`,
|
||||||
|
component: LazyLoader({
|
||||||
|
// loader: () => import(),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Edit Customer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/customers/new`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Customers/Customer'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'New Customer',
|
||||||
|
},
|
||||||
|
|
||||||
|
// Customers
|
||||||
|
{
|
||||||
|
path: `/customers`,
|
||||||
|
component: LazyLoader({
|
||||||
|
// loader: () => import(''),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Customers',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
93
client/src/store/customers/customers.actions.js
Normal file
93
client/src/store/customers/customers.actions.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
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) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchCustomers = ({ query }) => {
|
||||||
|
return (dispatch, getState) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const pageQuery = getState().items.tableQuery;
|
||||||
|
dispatch({
|
||||||
|
type: t.ITEMS_TABLE_LOADING,
|
||||||
|
payload: { loading: true },
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
|
});
|
||||||
|
ApiService.get(`customers`, { params: { ...pageQuery, ...query } })
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.CUSTOMER_SET,
|
||||||
|
customers: response.data.customers.results,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.CUSTOMERS_PAGE_SET,
|
||||||
|
customers: response.data.customers.results,
|
||||||
|
customViewId: response.data.customers.customViewId,
|
||||||
|
paginationMeta: response.data.customers.pagination,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.CUSTOMERS_TABLE_LOADING,
|
||||||
|
payload: { loading: false },
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
45
client/src/store/customers/customers.reducer.js
Normal file
45
client/src/store/customers/customers.reducer.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import t from 'store/types';
|
||||||
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
|
import { createTableQueryReducers } from 'store/queryReducers';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
items: {},
|
||||||
|
views: {},
|
||||||
|
loading: false,
|
||||||
|
currentViewId: -1,
|
||||||
|
errors: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const customersReducer = createReducer(initialState, {
|
||||||
|
[t.CUSTOMER_SET]: (state, action) => {
|
||||||
|
const _customers = {};
|
||||||
|
|
||||||
|
action.customers.forEach((customer) => {
|
||||||
|
_customers[customer.id] = customer;
|
||||||
|
});
|
||||||
|
state.items = {
|
||||||
|
...state.items,
|
||||||
|
..._customers,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[t.CUSTOMERS_PAGE_SET]: (state, action) => {
|
||||||
|
const viewId = action.customViewId || -1;
|
||||||
|
const view = state.views[viewId] || {};
|
||||||
|
|
||||||
|
state.views[viewId] = {
|
||||||
|
...view,
|
||||||
|
ids: action.customers.map((i) => i.id),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[t.CUSTOMER_DELETE]: (state, action) => {
|
||||||
|
if (typeof state.items[action.id] !== 'undefined') {
|
||||||
|
delete state.items[action.id];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createTableQueryReducers('customers', customersReducer);
|
||||||
|
|
||||||
|
export const getCustomerById = (state, id) => {
|
||||||
|
return state.customers[id];
|
||||||
|
};
|
||||||
10
client/src/store/customers/customers.selectors.js
Normal file
10
client/src/store/customers/customers.selectors.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { pickItemsFromIds } from 'store/selectors';
|
||||||
|
|
||||||
|
export const getCustomersItems = (state, viewId) => {
|
||||||
|
const customersView = state.customers.views[viewId || -1];
|
||||||
|
const customersItems = state.customers.items;
|
||||||
|
|
||||||
|
return typeof customersView === 'object'
|
||||||
|
? pickItemsFromIds(customersItems, customersView.ids) || []
|
||||||
|
: [];
|
||||||
|
};
|
||||||
8
client/src/store/customers/customers.type.js
Normal file
8
client/src/store/customers/customers.type.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default {
|
||||||
|
CUSTOMERS_ITEMS_SET: 'CUSTOMERS_ITEMS_SET',
|
||||||
|
CUSTOMER_SET: 'CUSTOMER_SET',
|
||||||
|
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
|
||||||
|
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
|
||||||
|
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
|
||||||
|
CUSTOMER_DELETE:'CUSTOMER_DELETE'
|
||||||
|
};
|
||||||
@@ -15,9 +15,9 @@ import itemCategories from './itemCategories/itemsCategory.reducer';
|
|||||||
import settings from './settings/settings.reducer';
|
import settings from './settings/settings.reducer';
|
||||||
import manualJournals from './manualJournals/manualJournals.reducers';
|
import manualJournals from './manualJournals/manualJournals.reducers';
|
||||||
import globalSearch from './search/search.reducer';
|
import globalSearch from './search/search.reducer';
|
||||||
import exchangeRates from './ExchangeRate/exchange.reducer'
|
import exchangeRates from './ExchangeRate/exchange.reducer';
|
||||||
import globalErrors from './globalErrors/globalErrors.reducer';
|
import globalErrors from './globalErrors/globalErrors.reducer';
|
||||||
|
import customers from './customers/customers.reducer';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
authentication,
|
authentication,
|
||||||
@@ -37,4 +37,5 @@ export default combineReducers({
|
|||||||
globalSearch,
|
globalSearch,
|
||||||
exchangeRates,
|
exchangeRates,
|
||||||
globalErrors,
|
globalErrors,
|
||||||
|
customers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import settings from './settings/settings.type';
|
|||||||
import search from './search/search.type';
|
import search from './search/search.type';
|
||||||
import register from './registers/register.type';
|
import register from './registers/register.type';
|
||||||
import exchangeRate from './ExchangeRate/exchange.type';
|
import exchangeRate from './ExchangeRate/exchange.type';
|
||||||
|
import customer from './customers/customers.type';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...authentication,
|
...authentication,
|
||||||
@@ -36,4 +37,5 @@ export default {
|
|||||||
...search,
|
...search,
|
||||||
...register,
|
...register,
|
||||||
...exchangeRate,
|
...exchangeRate,
|
||||||
|
...customer,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ $menu-item-color-active: $light-gray3;
|
|||||||
$breadcrumbs-collapsed-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#6B8193' enable-background='new 0 0 16 16' xml:space='preserve'><g><circle cx='2' cy='8.03' r='2'/><circle cx='14' cy='8.03' r='2'/><circle cx='8' cy='8.03' r='2'/></g></svg>");
|
$breadcrumbs-collapsed-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#6B8193' enable-background='new 0 0 16 16' xml:space='preserve'><g><circle cx='2' cy='8.03' r='2'/><circle cx='14' cy='8.03' r='2'/><circle cx='8' cy='8.03' r='2'/></g></svg>");
|
||||||
|
|
||||||
$sidebar-zindex: 15;
|
$sidebar-zindex: 15;
|
||||||
$pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue, Icons16, sans-serif;
|
$pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||||
|
Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue, Icons16, sans-serif;
|
||||||
|
|
||||||
// Blueprint framework.
|
// Blueprint framework.
|
||||||
@import '@blueprintjs/core/src/blueprint.scss';
|
@import '@blueprintjs/core/src/blueprint.scss';
|
||||||
@@ -48,16 +48,16 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|||||||
@import 'pages/items';
|
@import 'pages/items';
|
||||||
@import 'pages/items-categories';
|
@import 'pages/items-categories';
|
||||||
@import 'pages/invite-form.scss';
|
@import 'pages/invite-form.scss';
|
||||||
@import "pages/currency";
|
@import 'pages/currency';
|
||||||
@import "pages/invite-user.scss";
|
@import 'pages/invite-user.scss';
|
||||||
@import 'pages/exchange-rate.scss';
|
@import 'pages/exchange-rate.scss';
|
||||||
|
@import 'pages/customer.scss';
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
@import 'views/filter-dropdown';
|
@import 'views/filter-dropdown';
|
||||||
@import 'views/sidebar';
|
@import 'views/sidebar';
|
||||||
|
|
||||||
|
.App {
|
||||||
.App{
|
|
||||||
min-width: 960px;
|
min-width: 960px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +75,11 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bigcapital--alt{
|
.bigcapital--alt {
|
||||||
|
svg {
|
||||||
svg{
|
|
||||||
path,
|
path,
|
||||||
.path-13,
|
.path-13,
|
||||||
.path-1{
|
.path-1 {
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,6 +89,6 @@ body.authentication {
|
|||||||
background-color: #fcfdff;
|
background-color: #fcfdff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-toast{
|
.bp3-toast {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
24
client/src/style/pages/customer.scss
Normal file
24
client/src/style/pages/customer.scss
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.customer-form {
|
||||||
|
padding: 22px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
// padding-bottom: 90px;
|
||||||
|
|
||||||
|
.bp3-form-group {
|
||||||
|
// margin: 25px 20px 20px;
|
||||||
|
|
||||||
|
.bp3-label {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
.bp3-form-content {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__primary-section {
|
||||||
|
background-color: #fafafa;
|
||||||
|
padding: 40px 22px 22px;
|
||||||
|
margin: -22px -22px 22px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user