re-structure to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 01:02:31 +02:00
parent 8242ec64ba
commit 7a0a13f9d5
10400 changed files with 46966 additions and 17223 deletions

View File

@@ -0,0 +1,21 @@
// @ts-nocheck
import React from 'react';
import { Dragzone, FormattedMessage as T } from '@/components';
/**
* Vendor Attahment Tab.
*/
function VendorAttahmentTab() {
return (
<div>
<Dragzone
initialFiles={[]}
onDrop={null}
onDeleteFile={[]}
hint={<T id={'attachments_maximum'} />}
/>
</div>
);
}
export default VendorAttahmentTab;

View File

@@ -0,0 +1,136 @@
// @ts-nocheck
import React from 'react';
import moment from 'moment';
import classNames from 'classnames';
import { FormGroup, ControlGroup, Position, Classes } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik';
import { Features } from '@/constants';
import {
FFormGroup,
MoneyInputGroup,
InputPrependText,
CurrencySelectList,
BranchSelect,
BranchSelectButton,
FeatureCan,
Row,
Col,
FormattedMessage as T,
} from '@/components';
import { useSetPrimaryBranchToForm } from './utils';
import { momentFormatter, tansformDateValue, inputIntent } from '@/utils';
import { useVendorFormContext } from './VendorFormProvider';
/**
* Vendor Finaniceal Panel Tab.
*/
export default function VendorFinanicalPanelTab() {
const { vendorId, currencies, branches } = useVendorFormContext();
// Sets the primary branch to form.
useSetPrimaryBranchToForm();
return (
<div className={'tab-panel--financial'}>
<Row>
<Col xs={6}>
{/*------------ Opening balance at -----------*/}
<FastField name={'opening_balance_at'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'opening_balance_at'} />}
className={classNames('form-group--select-list', Classes.FILL)}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name="opening_balance_at" />}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
onChange={(date) => {
form.setFieldValue(
'opening_balance_at',
moment(date).format('YYYY-MM-DD'),
);
}}
value={tansformDateValue(value)}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
disabled={vendorId}
/>
</FormGroup>
)}
</FastField>
{/*------------ Opening balance -----------*/}
<FastField name={'opening_balance'}>
{({ form, field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'opening_balance'} />}
className={classNames(
'form-group--opening-balance',
Classes.FILL,
)}
intent={inputIntent({ error, touched })}
inline={true}
>
<ControlGroup>
<InputPrependText text={form.values.currency_code} />
<MoneyInputGroup
value={value}
inputGroupProps={{
fill: true,
// ...field,
}}
onChange={(balance) => {
form.setFieldValue('opening_balance', balance);
}}
disabled={vendorId}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
{/*------------ Opening branch -----------*/}
<FeatureCan feature={Features.Branches}>
<FFormGroup
label={<T id={'vendor.label.opening_branch'} />}
name={'opening_balance_branch_id'}
inline={true}
className={classNames('form-group--select-list', Classes.FILL)}
>
<BranchSelect
name={'opening_balance_branch_id'}
branches={branches}
input={BranchSelectButton}
popoverProps={{ minimal: true }}
/>
</FFormGroup>
</FeatureCan>
{/*------------ Currency -----------*/}
<FastField name={'currency_code'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'currency'} />}
className={classNames(
'form-group--select-list',
'form-group--balance-currency',
Classes.FILL,
)}
inline={true}
>
<CurrencySelectList
currenciesList={currencies}
selectedCurrencyCode={value}
onCurrencySelected={(currency) => {
form.setFieldValue('currency_code', currency.currency_code);
}}
/>
</FormGroup>
)}
</FastField>
</Col>
</Row>
</div>
);
}

View File

@@ -0,0 +1,106 @@
// @ts-nocheck
import React from 'react';
import {
Intent,
Button,
ButtonGroup,
Popover,
PopoverInteractionKind,
Position,
Menu,
MenuItem,
} from '@blueprintjs/core';
import styled from 'styled-components';
import classNames from 'classnames';
import { useFormikContext } from 'formik';
import { Icon, FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes';
import { useVendorFormContext } from './VendorFormProvider';
import { safeInvoke } from '@/utils';
/**
* Vendor floating actions bar.
*/
export default function VendorFloatingActions({ onCancel }) {
// Formik context.
const { resetForm, isSubmitting, submitForm } = useFormikContext();
// Vendor form context.
const { isNewMode, setSubmitPayload } = useVendorFormContext();
// Handle the submit button.
const handleSubmitBtnClick = (event) => {
setSubmitPayload({ noRedirect: false });
submitForm();
};
// Handle the submit & new button click.
const handleSubmitAndNewClick = (event) => {
submitForm();
setSubmitPayload({ noRedirect: true });
};
// Handle cancel button click.
const handleCancelBtnClick = (event) => {
safeInvoke(onCancel, event);
};
// Handle clear button click.
const handleClearBtnClick = (event) => {
resetForm();
};
return (
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
<ButtonGroup>
{/* ----------- Save and New ----------- */}
<SaveButton
disabled={isSubmitting}
loading={isSubmitting}
intent={Intent.PRIMARY}
type="submit"
onClick={handleSubmitBtnClick}
text={!isNewMode ? <T id={'edit'} /> : <T id={'save'} />}
/>
<Popover
content={
<Menu>
<MenuItem
text={<T id={'save_and_new'} />}
onClick={handleSubmitAndNewClick}
/>
</Menu>
}
minimal={true}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
disabled={isSubmitting}
intent={Intent.PRIMARY}
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
/>
</Popover>
</ButtonGroup>
{/* ----------- Clear & Reset----------- */}
<Button
className={'ml1'}
disabled={isSubmitting}
onClick={handleClearBtnClick}
text={!isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
/>
{/* ----------- Cancel ----------- */}
<Button
className={'ml1'}
disabled={isSubmitting}
onClick={handleCancelBtnClick}
text={<T id={'cancel'} />}
/>
</div>
);
}
const SaveButton = styled(Button)`
min-width: 100px;
`;

View File

@@ -0,0 +1,43 @@
// @ts-nocheck
import * as Yup from 'yup';
import intl from 'react-intl-universal';
const Schema = Yup.object().shape({
salutation: Yup.string().trim(),
first_name: Yup.string().trim(),
last_name: Yup.string().trim(),
company_name: Yup.string().trim(),
display_name: Yup.string().trim().required().label(intl.get('display_name_')),
email: Yup.string().email().nullable(),
work_phone: Yup.number(),
personal_phone: Yup.number(),
website: Yup.string().url().nullable(),
active: Yup.boolean(),
note: Yup.string().trim(),
billing_address_country: Yup.string().trim(),
billing_address_1: Yup.string().trim(),
billing_address_2: Yup.string().trim(),
billing_address_city: Yup.string().trim(),
billing_address_state: Yup.string().trim(),
billing_address_postcode: Yup.number().nullable(),
billing_address_phone: Yup.number(),
shipping_address_country: Yup.string().trim(),
shipping_address_1: Yup.string().trim(),
shipping_address_2: Yup.string().trim(),
shipping_address_city: Yup.string().trim(),
shipping_address_state: Yup.string().trim(),
shipping_address_postcode: Yup.number().nullable(),
shipping_address_phone: Yup.number(),
opening_balance: Yup.number().nullable(),
currency_code: Yup.string(),
opening_balance_at: Yup.date(),
opening_balance_branch_id: Yup.string(),
});
export const CreateVendorFormSchema = Schema;
export const EditVendorFormSchema = Schema;

View File

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

View File

@@ -0,0 +1,137 @@
// @ts-nocheck
import React, { useMemo } from 'react';
import intl from 'react-intl-universal';
import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import { AppToaster } from '@/components';
import {
CreateVendorFormSchema,
EditVendorFormSchema,
} from './VendorForm.schema';
import VendorTabs from './VendorsTabs';
import VendorFormPrimarySection from './VendorFormPrimarySection';
import VendorFormAfterPrimarySection from './VendorFormAfterPrimarySection';
import VendorFloatingActions from './VendorFloatingActions';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import { useVendorFormContext } from './VendorFormProvider';
import { compose, transformToForm, safeInvoke } from '@/utils';
import { defaultInitialValues } from './utils';
import '@/style/pages/Vendors/Form.scss';
/**
* Vendor form.
*/
function VendorFormFormik({
// #withCurrentOrganization
organization: { base_currency },
// #ownProps
onSubmitSuccess,
onSubmitError,
onCancel,
className,
}) {
// Vendor form context.
const {
vendorId,
vendor,
contactDuplicate,
createVendorMutate,
editVendorMutate,
setSubmitPayload,
submitPayload,
isNewMode,
} = useVendorFormContext();
/**
* Initial values in create and edit mode.
*/
const initialValues = useMemo(
() => ({
...defaultInitialValues,
currency_code: base_currency,
...transformToForm(vendor, defaultInitialValues),
...transformToForm(contactDuplicate, defaultInitialValues),
}),
[vendor, contactDuplicate, base_currency],
);
// Handles the form submit.
const handleFormSubmit = (values, form) => {
const { setSubmitting, resetForm } = form;
const requestForm = { ...values };
setSubmitting(true);
const onSuccess = (response) => {
AppToaster.show({
message: intl.get(
isNewMode
? 'the_vendor_has_been_created_successfully'
: 'the_item_vendor_has_been_edited_successfully',
),
intent: Intent.SUCCESS,
});
setSubmitPayload(false);
setSubmitting(false);
resetForm();
safeInvoke(onSubmitSuccess, values, form, submitPayload, response);
};
const onError = () => {
setSubmitPayload(false);
setSubmitting(false);
safeInvoke(onSubmitError, values, form, submitPayload);
};
if (isNewMode) {
createVendorMutate(requestForm).then(onSuccess).catch(onError);
} else {
editVendorMutate([vendor.id, requestForm]).then(onSuccess).catch(onError);
}
};
return (
<div
className={classNames(
CLASSES.PAGE_FORM,
CLASSES.PAGE_FORM_VENDOR,
className,
)}
>
<Formik
validationSchema={
isNewMode ? CreateVendorFormSchema : EditVendorFormSchema
}
initialValues={initialValues}
onSubmit={handleFormSubmit}
>
<Form>
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
<VendorFormPrimarySection />
</div>
<div className={'page-form__after-priamry-section'}>
<VendorFormAfterPrimarySection />
</div>
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
<VendorTabs vendor={vendorId} />
</div>
<VendorFloatingActions onCancel={onCancel} />
</Form>
</Formik>
</div>
);
}
export default compose(withCurrentOrganization())(VendorFormFormik);

View File

@@ -0,0 +1,69 @@
// @ts-nocheck
import React from 'react';
import styled from 'styled-components';
import { useParams, useHistory } from 'react-router-dom';
import '@/style/pages/Vendors/PageForm.scss';
import { DashboardCard, DashboardInsider } from '@/components';
import { VendorFormProvider, useVendorFormContext } from './VendorFormProvider';
import VendorFormFormik from './VendorFormFormik';
/**
* Vendor form page loading wrapper.
* @returns {JSX}
*/
function VendorFormPageLoading({ children }) {
const { isFormLoading } = useVendorFormContext();
return (
<VendorDashboardInsider loading={isFormLoading}>
{children}
</VendorDashboardInsider>
);
}
/**
* Vendor form page.
*/
export default function VendorFormPage() {
const history = useHistory();
const { id } = useParams();
// Handle the form submit success.
const handleSubmitSuccess = (values, formArgs, submitPayload) => {
if (!submitPayload.noRedirect) {
history.push('/vendors');
}
};
// Handle the form cancel button click.
const handleFormCancel = () => {
history.goBack();
};
return (
<VendorFormProvider vendorId={id}>
<VendorFormPageLoading>
<DashboardCard page>
<VendorFormPageFormik
onSubmitSuccess={handleSubmitSuccess}
onCancel={handleFormCancel}
/>
</DashboardCard>
</VendorFormPageLoading>
</VendorFormProvider>
);
}
const VendorFormPageFormik = styled(VendorFormFormik)`
.page-form {
&__floating-actions {
margin-left: -40px;
margin-right: -40px;
}
}
`;
const VendorDashboardInsider = styled(DashboardInsider)`
padding-bottom: 64px;
`;

View File

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

View File

@@ -0,0 +1,88 @@
// @ts-nocheck
import React, { useState, createContext } from 'react';
import { omit } from 'lodash';
import { useLocation } from 'react-router-dom';
import {
useVendor,
useContact,
useCurrencies,
useCreateVendor,
useEditVendor,
useBranches,
} from '@/hooks/query';
import { Features } from '@/constants';
import { useFeatureCan } from '@/hooks/state';
const VendorFormContext = createContext();
/**
* Vendor form provider.
*/
function VendorFormProvider({ query, vendorId, ...props }) {
const { state } = useLocation();
const contactId = state?.action;
// Features guard.
const { featureCan } = useFeatureCan();
const isBranchFeatureCan = featureCan(Features.Branches);
// Handle fetch Currencies data table
const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
// Handle fetch vendor details.
const { data: vendor, isLoading: isVendorLoading } = useVendor(vendorId, {
enabled: !!vendorId,
});
// Handle fetch contact duplicate details.
const { data: contactDuplicate, isLoading: isContactLoading } = useContact(
contactId,
{ enabled: !!contactId },
);
// Fetches the branches list.
const {
data: branches,
isLoading: isBranchesLoading,
isSuccess: isBranchesSuccess,
} = useBranches(query, { enabled: isBranchFeatureCan });
// Create and edit vendor mutations.
const { mutateAsync: createVendorMutate } = useCreateVendor();
const { mutateAsync: editVendorMutate } = useEditVendor();
// Form submit payload.
const [submitPayload, setSubmitPayload] = useState({});
// determines whether the form new or duplicate mode.
const isNewMode = contactId || !vendorId;
const isFormLoading =
isVendorLoading ||
isContactLoading ||
isCurrenciesLoading ||
isBranchesLoading;
const provider = {
vendorId,
currencies,
vendor,
branches,
contactDuplicate: { ...omit(contactDuplicate, ['opening_balance_at']) },
submitPayload,
isNewMode,
isFormLoading,
isBranchesSuccess,
createVendorMutate,
editVendorMutate,
setSubmitPayload,
};
return <VendorFormContext.Provider value={provider} {...props} />;
}
const useVendorFormContext = () => React.useContext(VendorFormContext);
export { VendorFormProvider, useVendorFormContext };

View File

@@ -0,0 +1,50 @@
// @ts-nocheck
import React from 'react';
import intl from 'react-intl-universal';
import classNames from 'classnames';
import { Tabs, Tab } from '@blueprintjs/core';
import { CLASSES } from '@/constants/classes';
import VendorFinanicalPanelTab from './VendorFinanicalPanelTab';
import VendorAttahmentTab from './VendorAttahmentTab';
import CustomerAddressTabs from '@/containers/Customers/CustomerForm/CustomerAddressTabs';
import CustomerNotePanel from '@/containers/Customers/CustomerForm/CustomerNotePanel';
/**
* Vendor form tabs.
*/
export default function VendorTabs() {
return (
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
<Tabs
animate={true}
id={'vendor-tabs'}
large={true}
defaultSelectedTabId="financial"
>
<Tab
id={'financial'}
title={intl.get('financial_details')}
panel={<VendorFinanicalPanelTab />}
/>
<Tab
id={'address'}
title={intl.get('address')}
panel={<CustomerAddressTabs />}
/>
<Tab
id="notes"
title={intl.get('notes')}
panel={<CustomerNotePanel />}
/>
<Tab
id={'attachement'}
title={intl.get('attachement')}
panel={<VendorAttahmentTab />}
/>
</Tabs>
</div>
);
}

View File

@@ -0,0 +1,58 @@
// @ts-nocheck
import React from 'react';
import moment from 'moment';
import { useFormikContext } from 'formik';
import { first } from 'lodash';
import { useVendorFormContext } from './VendorFormProvider';
export const defaultInitialValues = {
salutation: '',
first_name: '',
last_name: '',
company_name: '',
display_name: '',
email: '',
work_phone: '',
personal_phone: '',
website: '',
note: '',
active: true,
billing_address_country: '',
billing_address_1: '',
billing_address_2: '',
billing_address_city: '',
billing_address_state: '',
billing_address_postcode: '',
billing_address_phone: '',
shipping_address_country: '',
shipping_address_1: '',
shipping_address_2: '',
shipping_address_city: '',
shipping_address_state: '',
shipping_address_postcode: '',
shipping_address_phone: '',
opening_balance: '',
currency_code: '',
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
opening_balance_branch_id: '',
};
export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useVendorFormContext();
React.useEffect(() => {
if (isBranchesSuccess) {
const primaryBranch = branches.find((b) => b.primary) || first(branches);
if (primaryBranch) {
setFieldValue('opening_balance_branch_id', primaryBranch.id);
}
}
}, [isBranchesSuccess, setFieldValue, branches]);
};