feat(webapp): customer/vendor form ux improvement
This commit is contained in:
@@ -1,153 +1,17 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Row, Col } from '@/components';
|
||||
import {
|
||||
FormattedMessage as T,
|
||||
FFormGroup,
|
||||
FInputGroup,
|
||||
FTextArea,
|
||||
} from '@/components';
|
||||
import { Row } from '@/components';
|
||||
|
||||
const CustomerBillingAddress = ({}) => {
|
||||
import CustomerBillingAddress from './CustomerBillingAddress';
|
||||
import CustomerShippingAddress from './CustomerShippingAddress';
|
||||
|
||||
export default function CustomerAddressTabs() {
|
||||
return (
|
||||
<div className={'tab-panel--address'}>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<h4>
|
||||
<T id={'billing_address'} />
|
||||
</h4>
|
||||
{/*------------ Billing Address country -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address_country'}
|
||||
inline={true}
|
||||
label={<T id={'country'} />}
|
||||
>
|
||||
<FInputGroup name={'billing_address_country'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Billing Address 1 -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address1'}
|
||||
label={<T id={'address_line_1'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FTextArea name={'billing_address1'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Billing Address 2 -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address2'}
|
||||
label={<T id={'address_line_2'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FTextArea name={'billing_address2'} />
|
||||
</FFormGroup>
|
||||
{/*------------ Billing Address city -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address_city'}
|
||||
label={<T id={'city_town'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'billing_address_city'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Billing Address state -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address_state'}
|
||||
label={<T id={'state'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'billing_address_state'} />
|
||||
</FFormGroup>
|
||||
{/*------------ Billing Address postcode -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address_postcode'}
|
||||
label={<T id={'zip_code'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'billing_address_postcode'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Billing Address phone -----------*/}
|
||||
<FFormGroup
|
||||
name={'billing_address_phone'}
|
||||
label={<T id={'phone'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'billing_address_phone'} />
|
||||
</FFormGroup>
|
||||
</Col>
|
||||
|
||||
<Col xs={6}>
|
||||
<h4>
|
||||
<T id={'shipping_address'} />
|
||||
</h4>
|
||||
{/*------------ Shipping Address country -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address_country'}
|
||||
label={<T id={'country'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'shipping_address_country'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Shipping Address 1 -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address1'}
|
||||
label={<T id={'address_line_1'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FTextArea name={'shipping_address1'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Shipping Address 2 -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address2'}
|
||||
label={<T id={'address_line_2'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FTextArea name={'shipping_address2'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Shipping Address city -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address_city'}
|
||||
label={<T id={'city_town'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'shipping_address_city'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Shipping Address state -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address_state'}
|
||||
label={<T id={'state'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'shipping_address_state'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Shipping Address postcode -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address_postcode'}
|
||||
label={<T id={'zip_code'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'shipping_address_postcode'} />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Shipping Address phone -----------*/}
|
||||
<FFormGroup
|
||||
name={'shipping_address_phone'}
|
||||
label={<T id={'phone'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup name={'shipping_address_phone'} />
|
||||
</FFormGroup>
|
||||
</Col>
|
||||
<CustomerBillingAddress />
|
||||
<CustomerShippingAddress />
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomerBillingAddress;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Box } from '@/components';
|
||||
import {
|
||||
FormattedMessage as T,
|
||||
FFormGroup,
|
||||
FInputGroup,
|
||||
FTextArea,
|
||||
} from '@/components';
|
||||
import CustomerFormSectionTitle from './CustomerFormSectionTitle';
|
||||
|
||||
export default function CustomerBillingAddress() {
|
||||
return (
|
||||
<Box>
|
||||
<CustomerFormSectionTitle>
|
||||
<T id={'billing_address'} />
|
||||
</CustomerFormSectionTitle>
|
||||
<FFormGroup
|
||||
name={'billing_address_country'}
|
||||
label={<T id={'country'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'billing_address_country'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'billing_address1'}
|
||||
label={<T id={'address_line_1'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FTextArea name={'billing_address1'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'billing_address2'}
|
||||
label={<T id={'address_line_2'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FTextArea name={'billing_address2'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'billing_address_city'}
|
||||
label={<T id={'city_town'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'billing_address_city'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'billing_address_state'}
|
||||
label={<T id={'state'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'billing_address_state'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'billing_address_postcode'}
|
||||
label={<T id={'zip_code'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'billing_address_postcode'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'billing_address_phone'}
|
||||
label={<T id={'phone'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'billing_address_phone'} fill />
|
||||
</FFormGroup>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
|
||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||
import { FormGroup, Position, ControlGroup } from '@blueprintjs/core';
|
||||
import { ErrorMessage, useFormikContext } from 'formik';
|
||||
import { Features } from '@/constants';
|
||||
import {
|
||||
FFormGroup,
|
||||
@@ -16,6 +15,7 @@ import {
|
||||
FMoneyInputGroup,
|
||||
ExchangeRateInputGroup,
|
||||
FDateInput,
|
||||
Box,
|
||||
} from '@/components';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
import {
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
useSetPrimaryBranchToForm,
|
||||
} from './utils';
|
||||
import { useCurrentOrganization } from '@/hooks/state';
|
||||
import CustomerFormSectionTitle from './CustomerFormSectionTitle';
|
||||
|
||||
/**
|
||||
* Customer financial panel.
|
||||
@@ -35,21 +36,23 @@ export default function CustomerFinancialPanel() {
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
return (
|
||||
<div className={'tab-panel--financial'}>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<Box>
|
||||
<CustomerFormSectionTitle>
|
||||
<T id={'financial'} />
|
||||
</CustomerFormSectionTitle>
|
||||
{/*------------ Currency -----------*/}
|
||||
<FFormGroup
|
||||
name={'currency_code'}
|
||||
label={<T id={'currency'} />}
|
||||
fastField
|
||||
inline
|
||||
>
|
||||
fill
|
||||
>
|
||||
<CurrencySelectList
|
||||
name="currency_code"
|
||||
items={currencies}
|
||||
disabled={customerId}
|
||||
/>
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Opening balance -----------*/}
|
||||
@@ -66,7 +69,8 @@ export default function CustomerFinancialPanel() {
|
||||
<FFormGroup
|
||||
label={<T id={'customer.label.opening_branch'} />}
|
||||
name={'opening_balance_branch_id'}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<BranchSelect
|
||||
name={'opening_balance_branch_id'}
|
||||
@@ -75,9 +79,7 @@ export default function CustomerFinancialPanel() {
|
||||
/>
|
||||
</FFormGroup>
|
||||
</FeatureCan>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,7 +97,8 @@ function CustomerOpeningBalanceAtField() {
|
||||
<FormGroup
|
||||
name={'opening_balance_at'}
|
||||
label={<T id={'opening_balance_at'} />}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
helperText={<ErrorMessage name="opening_balance_at" />}
|
||||
>
|
||||
<FDateInput
|
||||
@@ -125,12 +128,13 @@ function CustomerOpeningBalanceField() {
|
||||
<FFormGroup
|
||||
label={<T id={'opening_balance'} />}
|
||||
name={'opening_balance'}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
shouldUpdate={openingBalanceFieldShouldUpdate}
|
||||
shouldUpdateDeps={{ currencyCode: values.currency_code }}
|
||||
fastField={true}
|
||||
>
|
||||
<ControlGroup>
|
||||
<ControlGroup fill>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<FMoneyInputGroup
|
||||
name={'opening_balance'}
|
||||
@@ -161,7 +165,8 @@ function CustomerOpeningBalanceExchangeRateField() {
|
||||
<FFormGroup
|
||||
label={' '}
|
||||
name={'opening_balance_exchange_rate'}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<ExchangeRateInputGroup
|
||||
fromCurrency={values.currency_code}
|
||||
|
||||
@@ -21,7 +21,7 @@ import { safeInvoke } from '@/utils';
|
||||
/**
|
||||
* Customer floating actions bar.
|
||||
*/
|
||||
export default function CustomerFloatingActions({ onCancel }) {
|
||||
export function CustomerFloatingActions({ onCancel }) {
|
||||
// Customer form context.
|
||||
const { isNewMode, setSubmitPayload } = useCustomerFormContext();
|
||||
|
||||
@@ -50,10 +50,7 @@ export default function CustomerFloatingActions({ onCancel }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Group
|
||||
spacing={10}
|
||||
className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}
|
||||
>
|
||||
<FloatingActionsGroup spacing={10}>
|
||||
<ButtonGroup>
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<SaveButton
|
||||
@@ -92,16 +89,19 @@ export default function CustomerFloatingActions({ onCancel }) {
|
||||
onClick={handleClearBtnClick}
|
||||
text={!isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
/>
|
||||
{/* ----------- Cancel ----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
onClick={handleCancelBtnClick}
|
||||
text={<T id={'cancel'} />}
|
||||
/>
|
||||
</Group>
|
||||
</FloatingActionsGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const SaveButton = styled(Button)`
|
||||
min-width: 100px;
|
||||
const FloatingActionsGroup = styled(Group)`
|
||||
padding: 12px 0;
|
||||
padding-left: 165px;
|
||||
border-top: 1px solid #50555a;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: var(--color-card-background);
|
||||
`;
|
||||
|
||||
const SaveButton = styled(Button)`
|
||||
min-width: 80px;
|
||||
`;
|
||||
|
||||
+20
-8
@@ -6,34 +6,46 @@ import { FormattedMessage as T, FFormGroup, FInputGroup } from '@/components';
|
||||
|
||||
export default function CustomerFormAfterPrimarySection({}) {
|
||||
return (
|
||||
<div className={'customer-form__after-primary-section-content'}>
|
||||
<div>
|
||||
{/*------------ Customer email -----------*/}
|
||||
<FFormGroup
|
||||
name={'email'}
|
||||
label={<T id={'customer_email'} />}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'email'} />
|
||||
<FInputGroup name={'email'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Phone number -----------*/}
|
||||
<FFormGroup
|
||||
name={'personal_phone'}
|
||||
label={<T id={'phone_number'} />}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<ControlGroup>
|
||||
<ControlGroup fill>
|
||||
<FInputGroup
|
||||
name={'personal_phone'}
|
||||
placeholder={intl.get('personal')}
|
||||
fill
|
||||
/>
|
||||
<FInputGroup
|
||||
name={'work_phone'}
|
||||
placeholder={intl.get('work')}
|
||||
fill
|
||||
/>
|
||||
<FInputGroup name={'work_phone'} placeholder={intl.get('work')} />
|
||||
</ControlGroup>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Customer website -----------*/}
|
||||
<FFormGroup name={'website'} label={<T id={'website'} />} inline={true}>
|
||||
<FInputGroup name={'website'} placeholder={'http://'} />
|
||||
<FFormGroup
|
||||
name={'website'}
|
||||
label={<T id={'website'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'website'} placeholder={'http://'} fill />
|
||||
</FFormGroup>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { Divider, Intent, Tab, Tabs } from '@blueprintjs/core';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
@@ -11,20 +11,19 @@ import { CreateCustomerForm, EditCustomerForm } from './CustomerForm.schema';
|
||||
import { compose, transformToForm, saveInvoke, parseBoolean } from '@/utils';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
import { defaultInitialValues } from './utils';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { AppToaster } from '@/components';
|
||||
import { AppToaster, Box, Card, Group } from '@/components';
|
||||
import CustomerFormPrimarySection from './CustomerFormPrimarySection';
|
||||
import CustomerFormAfterPrimarySection from './CustomerFormAfterPrimarySection';
|
||||
import CustomersTabs from './CustomersTabs';
|
||||
import CustomerFloatingActions from './CustomerFloatingActions';
|
||||
import { CustomerFloatingActions } from './CustomerFloatingActions';
|
||||
|
||||
import { withCurrentOrganization } from '@/containers/Organization/withCurrentOrganization';
|
||||
import CustomerFinancialPanel from './CustomerFinancialPanel';
|
||||
import CustomerShippingAddress from './CustomerShippingAddress';
|
||||
import CustomerBillingAddress from './CustomerBillingAddress';
|
||||
|
||||
import '@/style/pages/Customers/Form.scss';
|
||||
|
||||
/**
|
||||
* Customer form.
|
||||
*/
|
||||
function CustomerFormFormik({
|
||||
organization: { base_currency },
|
||||
|
||||
@@ -95,11 +94,7 @@ function CustomerFormFormik({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.PAGE_FORM,
|
||||
CLASSES.PAGE_FORM_CUSTOMER,
|
||||
className,
|
||||
)}
|
||||
className={classNames(CLASSES.PAGE_FORM, className)}
|
||||
>
|
||||
<Formik
|
||||
validationSchema={isNewMode ? CreateCustomerForm : EditCustomerForm}
|
||||
@@ -107,25 +102,28 @@ function CustomerFormFormik({
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
<Form>
|
||||
<CustomerFormHeaderPrimary>
|
||||
<CustomerFormPrimarySection />
|
||||
</CustomerFormHeaderPrimary>
|
||||
|
||||
<div className={'page-form__after-priamry-section'}>
|
||||
<CustomerFormAfterPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||
<CustomersTabs />
|
||||
</div>
|
||||
|
||||
<CustomerFloatingActions onCancel={onCancel} />
|
||||
<CustomerFormFields>
|
||||
<Box px={'20px'} py={'10px'} mx={'auto'} maxWidth={'800px'}>
|
||||
<CustomerFormContent />
|
||||
</Box>
|
||||
</CustomerFormFields>
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const CustomerFormFields = styled.div`
|
||||
.bp4-form-content,
|
||||
.bp6-form-content {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.bp4-form-group.bp4-inline label.bp4-label {
|
||||
min-width: 140px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const CustomerFormHeaderPrimary = styled.div`
|
||||
--x-border: #e4e4e4;
|
||||
|
||||
@@ -140,3 +138,42 @@ export const CustomerFormHeaderPrimary = styled.div`
|
||||
`;
|
||||
|
||||
export default compose(withCurrentOrganization())(CustomerFormFormik);
|
||||
|
||||
function CustomerFormContent() {
|
||||
return (
|
||||
<Card>
|
||||
<Group verticalAlign={'top'} alignItems={'flex-start'} flexWrap={'nowrap'}>
|
||||
<Tabs vertical large defaultSelectedTabId={'primary'} className={css`position: sticky; top: 20px;`}>
|
||||
<Tab id={'primary'} title={'Basic'} />
|
||||
<Tab id={'financial'} title={'Financial'} />
|
||||
<Tab id={'billing_address'} title={'Billing address'} />
|
||||
<Tab id={'shipping_address'} title={'Ship address'} />
|
||||
</Tabs>
|
||||
|
||||
<CustomerFormBasicSection />
|
||||
</Group>
|
||||
<CustomerFloatingActions />
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const customerFormSectionDividerClass = css`
|
||||
margin: 20px 0;
|
||||
`;
|
||||
|
||||
function CustomerFormBasicSection() {
|
||||
return (
|
||||
<Box>
|
||||
<CustomerFormPrimarySection />
|
||||
<Divider className={customerFormSectionDividerClass} />
|
||||
<CustomerFinancialPanel />
|
||||
<Divider className={customerFormSectionDividerClass} />
|
||||
<CustomerBillingAddress />
|
||||
<Divider className={customerFormSectionDividerClass} />
|
||||
<CustomerShippingAddress />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -49,26 +49,17 @@ export default function CustomerFormPage() {
|
||||
return (
|
||||
<CustomerFormProvider customerId={customerId}>
|
||||
<CustomerFormPageLoading>
|
||||
<DashboardCard page>
|
||||
<CustomerFormPageFormik
|
||||
onSubmitSuccess={handleSubmitSuccess}
|
||||
onCancel={handleFormCancel}
|
||||
/>
|
||||
</DashboardCard>
|
||||
</CustomerFormPageLoading>
|
||||
</CustomerFormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const CustomerFormPageFormik = styled(CustomerFormFormik)`
|
||||
.page-form {
|
||||
&__floating-actions {
|
||||
margin-left: -40px;
|
||||
margin-right: -40px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CustomerDashboardInsider = styled(DashboardInsider)`
|
||||
padding-bottom: 64px;
|
||||
`;
|
||||
|
||||
+76
-17
@@ -1,9 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, InputGroup, ControlGroup } from '@blueprintjs/core';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { ControlGroup, Divider, Icon as BlueprintIcon } from '@blueprintjs/core';
|
||||
import {
|
||||
Hint,
|
||||
FieldRequiredHint,
|
||||
@@ -12,10 +10,12 @@ import {
|
||||
FormattedMessage as T,
|
||||
FInputGroup,
|
||||
FFormGroup,
|
||||
Box,
|
||||
Icon,
|
||||
Stack,
|
||||
} from '@/components';
|
||||
import CustomerTypeRadioField from './CustomerTypeRadioField';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { inputIntent } from '@/utils';
|
||||
import CustomerFormSectionTitle from './CustomerFormSectionTitle';
|
||||
import { useAutofocus } from '@/hooks';
|
||||
|
||||
/**
|
||||
@@ -25,7 +25,9 @@ export default function CustomerFormPrimarySection({}) {
|
||||
const firstNameFieldRef = useAutofocus();
|
||||
|
||||
return (
|
||||
<div className={'customer-form__primary-section-content'}>
|
||||
<Box>
|
||||
<CustomerFormSectionTitle>Customer details</CustomerFormSectionTitle>
|
||||
|
||||
{/**-----------Customer type. -----------*/}
|
||||
<CustomerTypeRadioField />
|
||||
|
||||
@@ -33,9 +35,10 @@ export default function CustomerFormPrimarySection({}) {
|
||||
<FFormGroup
|
||||
name={'salutation'}
|
||||
label={<T id={'contact_name'} />}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<ControlGroup>
|
||||
<ControlGroup fill>
|
||||
<SalutationList
|
||||
name={'salutation'}
|
||||
popoverProps={{ minimal: true }}
|
||||
@@ -44,37 +47,93 @@ export default function CustomerFormPrimarySection({}) {
|
||||
name={'first_name'}
|
||||
placeholder={intl.get('first_name')}
|
||||
inputRef={(ref) => (firstNameFieldRef.current = ref)}
|
||||
fill
|
||||
/>
|
||||
<FInputGroup
|
||||
name={'last_name'}
|
||||
placeholder={intl.get('last_name')}
|
||||
fill
|
||||
/>
|
||||
<FInputGroup name={'last_name'} placeholder={intl.get('last_name')} />
|
||||
</ControlGroup>
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'customer_code'}
|
||||
label={'Customer Code'}
|
||||
helperText="Add a unique account number to identify, reference and search for the contact."
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup
|
||||
name={'customer_code'}
|
||||
fill />
|
||||
</FFormGroup>
|
||||
|
||||
{/*----------- Company Name -----------*/}
|
||||
<FFormGroup
|
||||
name={'company_name'}
|
||||
label={<T id={'company_name'} />}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'company_name'} />
|
||||
<FInputGroup name={'company_name'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
{/*----------- Display Name -----------*/}
|
||||
<FFormGroup
|
||||
name={'display_name'}
|
||||
label={
|
||||
<>
|
||||
<T id={'display_name'} />
|
||||
<FieldRequiredHint />
|
||||
<Hint />
|
||||
</>
|
||||
}
|
||||
inline={true}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<DisplayNameList
|
||||
name={'display_name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
buttonProps={{ fill: true }}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
|
||||
|
||||
<Divider style={{ margin: '20px 0' }} />
|
||||
|
||||
{/*------------ Vendor email -----------*/}
|
||||
<FFormGroup
|
||||
name={'email'}
|
||||
label={<T id={'vendor_email'} />}
|
||||
inline={true}
|
||||
>
|
||||
<FInputGroup
|
||||
name={'email'}
|
||||
leftIcon={<Icon icon="envelope" />}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Phone number -----------*/}
|
||||
<FFormGroup
|
||||
name={'work_phone'}
|
||||
className={'form-group--phone-number'}
|
||||
label={<T id={'phone_number'} />}
|
||||
inline={true}
|
||||
>
|
||||
<Stack spacing={10}>
|
||||
<FInputGroup name={'work_phone'} placeholder={intl.get('work')} />
|
||||
<FInputGroup
|
||||
name={'personal_phone'}
|
||||
placeholder={intl.get('mobile')}
|
||||
/>
|
||||
</Stack>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Vendor website -----------*/}
|
||||
<FFormGroup name={'website'} label={<T id={'website'} />} inline={true}>
|
||||
<FInputGroup
|
||||
name={'website'}
|
||||
placeholder={'http://'}
|
||||
leftIcon={<BlueprintIcon icon="globe-network" />}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
const customerFormSectionTitleClass = css`
|
||||
font-size: 14px;
|
||||
color: #8f99a8;
|
||||
margin-bottom: 18px;
|
||||
margin-top: 0;
|
||||
`;
|
||||
|
||||
export default function CustomerFormSectionTitle({ children }) {
|
||||
return <h4 className={customerFormSectionTitleClass}>{children}</h4>;
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Classes } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, FFormGroup, FTextArea } from '@/components';
|
||||
|
||||
export default function CustomerNotePanel({ errors, touched, getFieldProps }) {
|
||||
return (
|
||||
<div className={'tab-panel--note'}>
|
||||
<FFormGroup name={'note'} label={<T id={'note'} />} inline={false}>
|
||||
<FTextArea name={'note'} />
|
||||
</FFormGroup>
|
||||
</div>
|
||||
<FFormGroup name={'note'} label={<T id={'note'} />} inline={false} fill>
|
||||
<FTextArea name={'note'} fill />
|
||||
</FFormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Box } from '@/components';
|
||||
import {
|
||||
FormattedMessage as T,
|
||||
FFormGroup,
|
||||
FInputGroup,
|
||||
FTextArea,
|
||||
} from '@/components';
|
||||
import CustomerFormSectionTitle from './CustomerFormSectionTitle';
|
||||
|
||||
export default function CustomerShippingAddress() {
|
||||
return (
|
||||
<Box>
|
||||
<CustomerFormSectionTitle>
|
||||
<T id={'shipping_address'} />
|
||||
</CustomerFormSectionTitle>
|
||||
<FFormGroup
|
||||
name={'shipping_address_country'}
|
||||
label={<T id={'country'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'shipping_address_country'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'shipping_address1'}
|
||||
label={<T id={'address_line_1'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FTextArea name={'shipping_address1'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'shipping_address2'}
|
||||
label={<T id={'address_line_2'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FTextArea name={'shipping_address2'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'shipping_address_city'}
|
||||
label={<T id={'city_town'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'shipping_address_city'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'shipping_address_state'}
|
||||
label={<T id={'state'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'shipping_address_state'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'shipping_address_postcode'}
|
||||
label={<T id={'zip_code'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'shipping_address_postcode'} fill />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'shipping_address_phone'}
|
||||
label={<T id={'phone'} />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
<FInputGroup name={'shipping_address_phone'} fill />
|
||||
</FFormGroup>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +1,52 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
import { Radio } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, FFormGroup, FRadioGroup } from '@/components';
|
||||
|
||||
import { handleStringChange, saveInvoke } from '@/utils';
|
||||
import { Button, ButtonGroup } from '@blueprintjs/core';
|
||||
import { FastField } from 'formik';
|
||||
import { FormattedMessage as T, FFormGroup } from '@/components';
|
||||
|
||||
/**
|
||||
* Customer type radio field.
|
||||
* Customer type selector (button group).
|
||||
*/
|
||||
export default function RadioCustomer() {
|
||||
export default function CustomerTypeRadioField() {
|
||||
return (
|
||||
<FFormGroup
|
||||
name={'customer_type'}
|
||||
label={<T id={'customer_type'} />}
|
||||
inline
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
<FRadioGroup name={'customer_type'} inline>
|
||||
<Radio label={intl.get('business')} value="business" />
|
||||
<Radio label={intl.get('individual')} value="individual" />
|
||||
</FRadioGroup>
|
||||
<FastField name="customer_type">
|
||||
{({ field, form }) => (
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
small
|
||||
active={field.value === 'business'}
|
||||
onClick={() => {
|
||||
form.setFieldValue('customer_type', 'business');
|
||||
form.setFieldTouched('customer_type', true);
|
||||
}}
|
||||
>
|
||||
{intl.get('business')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
small
|
||||
active={field.value === 'individual'}
|
||||
onClick={() => {
|
||||
form.setFieldValue('customer_type', 'individual');
|
||||
form.setFieldTouched('customer_type', true);
|
||||
}}
|
||||
>
|
||||
{intl.get('individual')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</FFormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1942,8 +1942,8 @@
|
||||
"vendor_opening_balance.label": "Edit Vendor Opening Balance",
|
||||
"vendor_opening_balance.label.opening_balance": "Opening balance",
|
||||
"vendor_opening_balance.label.opening_balance_at": "Opening balance at",
|
||||
"customer.label.opening_branch": "Opening Balance Branch",
|
||||
"vendor.label.opening_branch": "Opening Balance Branch",
|
||||
"customer.label.opening_branch": "Balance Branch",
|
||||
"vendor.label.opening_branch": "Balance Branch",
|
||||
"warehouse.error.warehouse_code_not_unique": "Warehouse code not unique",
|
||||
"warehouse.error.warehouse_has_associated_transactions": "You could not delete the warehouse that has associated transactions.",
|
||||
"branche.error.warehouse_code_not_unique": "Branch code not unique",
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
@import '../../_base.scss';
|
||||
|
||||
.page-form--customer {
|
||||
$self: '.page-form';
|
||||
padding: 20px;
|
||||
|
||||
--x-color-tabs-border: #f0f0f0;
|
||||
|
||||
.bp4-dark & {
|
||||
--x-color-tabs-border: var(--color-dark-gray3);
|
||||
}
|
||||
|
||||
#{$self}__header {
|
||||
padding: 0;
|
||||
}
|
||||
#{$self}__primary-section {
|
||||
padding: 10px 0 0;
|
||||
margin: 0 0 20px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #e4e4e4;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.bp4-form-group {
|
||||
max-width: 500px;
|
||||
|
||||
.bp4-control {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&.bp4-inline {
|
||||
.bp4-label {
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
.bp4-form-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-group--contact_name {
|
||||
max-width: 600px;
|
||||
|
||||
.bp4-control-group > * {
|
||||
flex-shrink: unset;
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
&.input-group--salutation-list {
|
||||
width: 25%;
|
||||
}
|
||||
&.input-group--first-name,
|
||||
&.input-group--last-name {
|
||||
width: 37%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bp4-form-group {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.bp4-tab-panel {
|
||||
margin-top: 26px;
|
||||
}
|
||||
|
||||
.form-group--phone-number {
|
||||
.bp4-control-group > * {
|
||||
flex-shrink: unset;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#{$self}__tabs {
|
||||
margin-top: 20px;
|
||||
max-width: 1000px;
|
||||
|
||||
h4 {
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
margin-bottom: 1.2rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
// Tab panels.
|
||||
.tab-panel {
|
||||
&--address {
|
||||
.bp4-form-group {
|
||||
max-width: 440px;
|
||||
|
||||
&.bp4-inline {
|
||||
.bp4-label {
|
||||
min-width: 145px;
|
||||
}
|
||||
}
|
||||
|
||||
.bp4-form-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea.bp4-input {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
min-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&--note {
|
||||
.form-group--note {
|
||||
.bp4-form-group {
|
||||
max-width: 600px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone-container {
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
.bp4-tabs {
|
||||
.bp4-tab-list {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--x-color-tabs-border);
|
||||
}
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 25px;
|
||||
}
|
||||
&.bp4-large > .bp4-tab {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user