diff --git a/client/src/common/contactsOptions.js b/client/src/common/contactsOptions.js new file mode 100644 index 000000000..0cbe1a5ed --- /dev/null +++ b/client/src/common/contactsOptions.js @@ -0,0 +1,4 @@ +export default [ + { name: 'Customer', path: 'customers' }, + { name: 'Vendor', path: 'vendors' }, +]; diff --git a/client/src/components/DialogsContainer.js b/client/src/components/DialogsContainer.js index 7a3bdbea4..12e26077c 100644 --- a/client/src/components/DialogsContainer.js +++ b/client/src/components/DialogsContainer.js @@ -9,7 +9,7 @@ import ExchangeRateFormDialog from 'containers/Dialogs/ExchangeRateFormDialog'; import InventoryAdjustmentDialog from 'containers/Dialogs/InventoryAdjustmentFormDialog'; import PaymentViaVoucherDialog from 'containers/Dialogs/PaymentViaVoucherDialog'; import KeyboardShortcutsDialog from 'containers/Dialogs/keyboardShortcutsDialog'; - +import ContactDuplicateDialog from 'containers/Dialogs/ContactDuplicateDialog'; /** * Dialogs container. */ @@ -24,6 +24,7 @@ export default function DialogsContainer() { + ); } diff --git a/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateDialogContent.js b/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateDialogContent.js new file mode 100644 index 000000000..f6b890089 --- /dev/null +++ b/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateDialogContent.js @@ -0,0 +1,18 @@ +import React from 'react'; + +import ContactDuplicateForm from './ContactDuplicateForm'; +import { ContactDuplicateProvider } from './ContactDuplicateProvider'; + +import 'style/pages/ContactDuplicate/ContactDuplicateDialog.scss'; + +export default function ContactDuplicateDialogContent({ + // #ownProp + contact, + dialogName, +}) { + return ( + + + + ); +} diff --git a/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateForm.js b/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateForm.js new file mode 100644 index 000000000..42e534bf0 --- /dev/null +++ b/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateForm.js @@ -0,0 +1,109 @@ +import React from 'react'; +import * as Yup from 'yup'; +import { formatMessage } from 'services/intl'; +import { Formik, Form, Field, ErrorMessage } from 'formik'; +import { inputIntent } from 'utils'; +import { ListSelect, FieldRequiredHint } from 'components'; +import { Button, FormGroup, Intent, Classes } from '@blueprintjs/core'; +import { FormattedMessage as T } from 'react-intl'; +import { useHistory } from 'react-router-dom'; +import { useContactDuplicateFromContext } from './ContactDuplicateProvider'; + +import Contacts from 'common/contactsOptions'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +function ContactDuplicateForm({ + // #withDialogActions + closeDialog, +}) { + const history = useHistory(); + + const { dialogName, contactId } = useContactDuplicateFromContext(); + + const validationSchema = Yup.object().shape({ + contact_type: Yup.string() + .required() + .label(formatMessage({ id: 'contact_type_' })), + }); + + const initialValues = { + contact_type: '', + }; + + // Handle cancel button click. + const handleCancelClick = () => { + closeDialog(dialogName); + }; + + // Handle form submit. + const handleFormSubmit = (values) => { + closeDialog(dialogName); + history.push(`${values.contact_type}/new?duplicate=${contactId}`, { + action: contactId, + }); + }; + + return ( + + {({ isSubmitting }) => ( +
+
+

+ +

+ + {/*------------ Contact Type -----------*/} + + {({ form, meta: { error, touched } }) => ( + } + labelInfo={} + intent={inputIntent({ error, touched })} + className={'form-group--select-list'} + inline={true} + helperText={} + > + + form.setFieldValue('contact_type', path) + } + defaultText={} + textProp={'name'} + selectedItemProp={'name'} + filterable={false} + popoverProps={{ minimal: true }} + /> + + )} + +
+ +
+
+ + + +
+
+
+ )} +
+ ); +} + +export default compose(withDialogActions)(ContactDuplicateForm); diff --git a/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateProvider.js b/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateProvider.js new file mode 100644 index 000000000..a6a66c1ac --- /dev/null +++ b/client/src/containers/Dialogs/ContactDuplicateDialog/ContactDuplicateProvider.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { DialogContent } from 'components'; + +const ContactDuplicateContext = React.createContext(); + +/** + * contact duplicate provider. + */ +function ContactDuplicateProvider({ contactId, dialogName, ...props }) { + // Provider state. + const provider = { + dialogName, + contactId, + }; + + return ( + + + + ); +} + +const useContactDuplicateFromContext = () => + React.useContext(ContactDuplicateContext); + +export { ContactDuplicateProvider, useContactDuplicateFromContext }; diff --git a/client/src/containers/Dialogs/ContactDuplicateDialog/index.js b/client/src/containers/Dialogs/ContactDuplicateDialog/index.js new file mode 100644 index 000000000..af68c318e --- /dev/null +++ b/client/src/containers/Dialogs/ContactDuplicateDialog/index.js @@ -0,0 +1,33 @@ +import React, { lazy } from 'react'; +import { FormattedMessage as T } from 'react-intl'; +import { Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const ContactDialogContent = lazy(() => + import('./ContactDuplicateDialogContent'), +); +/** + * Contact duplicate dialog. + */ +function ContactDuplicateDialog({ dialogName, payload, isOpen }) { + return ( + } + autoFocus={true} + canEscapeKeyClose={true} + className={'dialog--contact-duplicate'} + isOpen={isOpen} + > + + + + + ); +} + +export default compose(withDialogRedux())(ContactDuplicateDialog); diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js index 88e260f38..5a6964a2d 100644 --- a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js +++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js @@ -3,9 +3,6 @@ import React from 'react'; import ExchangeRateForm from './ExchangeRateForm'; import { ExchangeRateFormProvider } from './ExchangeRateFormProvider'; -import withExchangeRateDetail from 'containers/ExchangeRates/withExchangeRateDetail'; -import { compose } from 'utils'; - import 'style/pages/ExchangeRate/ExchangeRateDialog.scss'; /** diff --git a/client/src/hooks/query/contacts.js b/client/src/hooks/query/contacts.js new file mode 100644 index 000000000..16b0d44c1 --- /dev/null +++ b/client/src/hooks/query/contacts.js @@ -0,0 +1,14 @@ +import { useQuery } from 'react-query'; +import useApiRequest from '../useRequest'; + +/** + * Retrieve the contact duplicate. + */ +export function useContact(id, props) { + const apiRequest = useApiRequest(); + + return useQuery(['CONTACT', id], () => apiRequest.get(`contacts/${id}`), { + select: (res) => res.data.customer, + ...props, + }); +} diff --git a/client/src/hooks/query/index.js b/client/src/hooks/query/index.js index 02dd868a5..268856ea1 100644 --- a/client/src/hooks/query/index.js +++ b/client/src/hooks/query/index.js @@ -20,3 +20,4 @@ export * from './settings'; export * from './users'; export * from './invite'; export * from './exchangeRates'; +export * from './contacts'; diff --git a/client/src/static/json/icons.js b/client/src/static/json/icons.js index 86a0d3ed4..7ca4d037a 100644 --- a/client/src/static/json/icons.js +++ b/client/src/static/json/icons.js @@ -406,4 +406,10 @@ export default { ], viewBox: '0 0 24 24', }, + 'duplicate-18': { + path: [ + 'M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z', + ], + viewBox: '0 0 24 24', + }, }; diff --git a/client/src/style/pages/ContactDuplicate/ContactDuplicateDialog.scss b/client/src/style/pages/ContactDuplicate/ContactDuplicateDialog.scss new file mode 100644 index 000000000..e3f297398 --- /dev/null +++ b/client/src/style/pages/ContactDuplicate/ContactDuplicateDialog.scss @@ -0,0 +1,20 @@ +.dialog--contact-duplicate { + .bp3-dialog-body { + .bp3-form-group.bp3-inline { + margin: 18px 0px; + .bp3-label { + min-width: 100px; + } + } + .bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) { + width: 260px; + } + } + + .bp3-dialog-footer { + .bp3-dialog-footer-actions .bp3-button { + margin-left: 8px; + min-width: 65px; + } + } +}