mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: customer form styling.
feat: customers list.
This commit is contained in:
@@ -12,11 +12,7 @@ const CustomerBillingAddress = ({
|
||||
getFieldProps,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'customer-form__tabs-section customer-form__tabs-section--address'
|
||||
}
|
||||
>
|
||||
<div className={'tab-panel--address'}>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<h4>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function CustomerFinancialPanel({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'customer-form__tabs-section customer-form__tabs-section--financial'}>
|
||||
<div className={'tab-panel--financial'}>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<FormGroup
|
||||
|
||||
@@ -143,10 +143,46 @@ function CustomerForm({
|
||||
|
||||
useEffect(() => {
|
||||
customer && customer.id
|
||||
? changePageTitle(formatMessage({ id: 'edit_customer_details' }))
|
||||
? changePageTitle(formatMessage({ id: 'edit_customer' }))
|
||||
: changePageTitle(formatMessage({ id: 'new_customer' }));
|
||||
}, [changePageTitle, customer, formatMessage]);
|
||||
|
||||
const handleFormSubmit = (values, { setSubmitting, resetForm, setErrors }) => {
|
||||
const formValues = { ...values, status: payload.publish };
|
||||
if (customer && customer.id) {
|
||||
requestEditCustomer(customer.id, formValues)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'the_item_customer_has_been_successfully_edited',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
resetForm();
|
||||
saveInvokeSubmit({ action: 'update', ...payload });
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
} else {
|
||||
requestSubmitCustomer(formValues)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'the_customer_has_been_successfully_created',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
saveInvokeSubmit({ action: 'new', ...payload });
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
setFieldValue,
|
||||
getFieldProps,
|
||||
@@ -160,42 +196,7 @@ function CustomerForm({
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
||||
const formValues = { ...values, status: payload.publish };
|
||||
if (customer && customer.id) {
|
||||
requestEditCustomer(customer.id, formValues)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'the_item_customer_has_been_successfully_edited',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
resetForm();
|
||||
saveInvokeSubmit({ action: 'update', ...payload });
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
} else {
|
||||
requestSubmitCustomer(formValues)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'the_customer_has_been_successfully_created',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
// history.push('/customers');
|
||||
setSubmitting(false);
|
||||
saveInvokeSubmit({ action: 'new', ...payload });
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
}
|
||||
},
|
||||
onSubmit: handleFormSubmit,
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -32,13 +32,14 @@ export default function CustomerFormPrimarySection({
|
||||
);
|
||||
|
||||
// Handle salutation field select.
|
||||
const handleSalutationSelect = (salutation) => {
|
||||
const handleSalutationSelect = useCallback((salutation) => {
|
||||
setFieldValue('salutation', salutation.label);
|
||||
};
|
||||
}, [setFieldValue]);
|
||||
|
||||
// Handle display name field select.
|
||||
const handleDisplayNameSelect = (displayName) => {
|
||||
const handleDisplayNameSelect = useCallback((displayName) => {
|
||||
setFieldValue('display_name', displayName.label);
|
||||
};
|
||||
}, [setFieldValue]);
|
||||
|
||||
return (
|
||||
<div className={'customer-form__primary-section-content'}>
|
||||
@@ -116,6 +117,7 @@ export default function CustomerFormPrimarySection({
|
||||
firstName={values.first_name}
|
||||
lastName={values.last_name}
|
||||
company={values.company_name}
|
||||
salutation={values.salutation}
|
||||
onItemSelect={handleDisplayNameSelect}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
|
||||
@@ -7,28 +7,21 @@ import ErrorMessage from 'components/ErrorMessage';
|
||||
|
||||
export default function CustomerNotePanel({ errors, touched, getFieldProps }) {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'customer-form__tabs-section customer-form__tabs-section--note'
|
||||
}
|
||||
>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<FormGroup
|
||||
label={<T id={'note'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
intent={errors.note && touched.note && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<TextArea
|
||||
intent={errors.note && touched.note && Intent.DANGER}
|
||||
{...getFieldProps('note')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className={'tab-panel--note'}>
|
||||
<FormGroup
|
||||
label={<T id={'note'} />}
|
||||
className={classNames('form-group--note', Classes.FILL)}
|
||||
intent={errors.note && touched.note && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<TextArea
|
||||
intent={errors.note && touched.note && Intent.DANGER}
|
||||
{...getFieldProps('note')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
@@ -6,17 +6,23 @@ import {
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Position,
|
||||
Intent,
|
||||
} 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 { compose } from 'utils';
|
||||
import { compose, firstLettersArgs, saveInvoke } from 'utils';
|
||||
|
||||
const AvatarCell = (row) => {
|
||||
return <span className="avatar">{firstLettersArgs(row.display_name)}</span>;
|
||||
};
|
||||
|
||||
const CustomerTable = ({
|
||||
loading,
|
||||
@@ -32,7 +38,6 @@ const CustomerTable = ({
|
||||
onSelectedRowsChange,
|
||||
}) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const [initialMount, setInitialMount] = useState(false);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
@@ -41,36 +46,64 @@ const CustomerTable = ({
|
||||
}
|
||||
}, [customersLoading, setInitialMount]);
|
||||
|
||||
const handleEditCustomer = useCallback(
|
||||
(customer) => () => {
|
||||
onEditCustomer && onEditCustomer(customer);
|
||||
// Customers actions list.
|
||||
const renderContextMenu = useMemo(
|
||||
() => ({ customer, onEditCustomer, onDeleteCustomer }) => {
|
||||
const handleEditCustomer = () => {
|
||||
saveInvoke(onEditCustomer, customer);
|
||||
};
|
||||
const handleDeleteCustomer = () => {
|
||||
saveInvoke(onDeleteCustomer, customer);
|
||||
};
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="reader-18" />}
|
||||
text={formatMessage({ id: 'view_details' })}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={formatMessage({ id: 'edit_customer' })}
|
||||
onClick={handleEditCustomer}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={formatMessage({ id: 'delete_customer' })}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleDeleteCustomer}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
},
|
||||
[onEditCustomer],
|
||||
[formatMessage],
|
||||
);
|
||||
|
||||
const handleDeleteCustomer = useCallback(
|
||||
(customer) => () => {
|
||||
onDeleteCustomer && onDeleteCustomer(customer);
|
||||
},
|
||||
[onDeleteCustomer],
|
||||
);
|
||||
const actionMenuList = useCallback((customer) => (
|
||||
<Menu>
|
||||
<MenuItem text={<T id={'view_details'} />} />
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={<T id={'edit_customer'} />}
|
||||
onClick={handleEditCustomer(customer)}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'delete_customer'} />}
|
||||
onClick={handleDeleteCustomer(customer)}
|
||||
/>
|
||||
</Menu>
|
||||
));
|
||||
// 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 columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'avatar',
|
||||
Header: '',
|
||||
accessor: AvatarCell,
|
||||
className: 'avatar',
|
||||
width: 50,
|
||||
disableResizing: true,
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
id: 'display_name',
|
||||
Header: formatMessage({ id: 'display_name' }),
|
||||
@@ -95,26 +128,20 @@ const CustomerTable = ({
|
||||
{
|
||||
id: 'receivable_balance',
|
||||
Header: formatMessage({ id: 'receivable_balance' }),
|
||||
// accessor: '',
|
||||
accessor: (r) => <Money amount={r.closing_balance} currency={'USD'} />,
|
||||
className: 'receivable_balance',
|
||||
width: 100,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'actions',
|
||||
Cell: ({ cell }) => (
|
||||
<Popover
|
||||
content={actionMenuList(cell.row.original)}
|
||||
position={Position.RIGHT_BOTTOM}
|
||||
>
|
||||
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||
</Popover>
|
||||
),
|
||||
Cell: renderActionsCell,
|
||||
className: 'actions',
|
||||
width: 50,
|
||||
width: 70,
|
||||
disableResizing: true,
|
||||
disableSortBy: true,
|
||||
},
|
||||
],
|
||||
[actionMenuList, formatMessage],
|
||||
[formatMessage, renderActionsCell],
|
||||
);
|
||||
|
||||
const selectionColumn = useMemo(
|
||||
@@ -138,6 +165,13 @@ const CustomerTable = ({
|
||||
[onSelectedRowsChange],
|
||||
);
|
||||
|
||||
const rowContextMenu = (cell) =>
|
||||
renderContextMenu({
|
||||
customer: cell.row.original,
|
||||
onEditCustomer,
|
||||
onDeleteCustomer,
|
||||
});
|
||||
|
||||
return (
|
||||
<LoadingIndicator loading={loading} mount={false}>
|
||||
<DataTable
|
||||
@@ -146,11 +180,12 @@ const CustomerTable = ({
|
||||
data={customers}
|
||||
selectionColumn={selectionColumn}
|
||||
onFetchData={handleFetchDate}
|
||||
expandable={true}
|
||||
treeGraph={true}
|
||||
expandable={false}
|
||||
treeGraph={false}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
loading={customersLoading && !initialMount}
|
||||
spinnerProps={{ size: 30 }}
|
||||
rowContextMenu={rowContextMenu}
|
||||
/>
|
||||
</LoadingIndicator>
|
||||
);
|
||||
|
||||
@@ -135,11 +135,10 @@ function CustomersList({
|
||||
filter_roles: filterConditions || '',
|
||||
});
|
||||
},
|
||||
[fetchCustomers],
|
||||
[addCustomersTableQueries],
|
||||
);
|
||||
|
||||
// Handle Customers bulk delete button click.,
|
||||
|
||||
const handleBulkDelete = useCallback(
|
||||
(customersIds) => {
|
||||
setBulkDelete(customersIds);
|
||||
@@ -184,7 +183,7 @@ function CustomersList({
|
||||
|
||||
<DashboardPageContent>
|
||||
<CustomersTable
|
||||
loadong={tableLoading}
|
||||
loading={tableLoading}
|
||||
onDeleteCustomer={handleDeleteCustomer}
|
||||
onEditCustomer={handleEditCustomer}
|
||||
onfetchData={handleFetchData}
|
||||
|
||||
Reference in New Issue
Block a user