From 45d9e2cc15fcdb1e484a878b14692a04c7f9dfc8 Mon Sep 17 00:00:00 2001 From: elforjani13 <39470382+elforjani13@users.noreply.github.com> Date: Sun, 23 Jan 2022 23:23:40 +0200 Subject: [PATCH 1/3] feat(branches): add branches. --- src/common/classes.js | 1 + src/components/DialogsContainer.js | 2 + .../Preferences/PreferencesTopbar.js | 6 + src/config/preferencesMenu.js | 5 +- .../Dialogs/BranchFormDialog/BranchForm.js | 45 +++++ .../BranchFormDialog/BranchForm.schema.js | 15 ++ .../BranchFormDialog/BranchFormContent.js | 17 ++ .../BranchFormDialogContent.js | 20 +++ .../BranchFormDialog/BranchFormFields.js | 157 ++++++++++++++++++ .../BranchFormFloatingActions.js | 46 +++++ .../BranchFormDialog/BranchFormProvider.js | 25 +++ .../Dialogs/BranchFormDialog/index.js | 31 ++++ .../Preferences/Branches/Branches.js | 21 +++ .../Preferences/Branches/BranchesActions.js | 29 ++++ .../Preferences/Branches/BranchesDataTable.js | 36 ++++ .../Preferences/Branches/BranchesProvider.js | 35 ++++ .../Preferences/Branches/components.js | 76 +++++++++ src/containers/Preferences/Branches/index.js | 15 ++ src/lang/en/index.json | 31 +++- src/routes/preferences.js | 6 + .../pages/Branches/BranchFormDialog.scss | 39 +++++ 21 files changed, 648 insertions(+), 10 deletions(-) create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchForm.js create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchFormContent.js create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchFormDialogContent.js create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchFormFields.js create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchFormFloatingActions.js create mode 100644 src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js create mode 100644 src/containers/Dialogs/BranchFormDialog/index.js create mode 100644 src/containers/Preferences/Branches/Branches.js create mode 100644 src/containers/Preferences/Branches/BranchesActions.js create mode 100644 src/containers/Preferences/Branches/BranchesDataTable.js create mode 100644 src/containers/Preferences/Branches/BranchesProvider.js create mode 100644 src/containers/Preferences/Branches/components.js create mode 100644 src/containers/Preferences/Branches/index.js create mode 100644 src/style/pages/Branches/BranchFormDialog.scss diff --git a/src/common/classes.js b/src/common/classes.js index 6b0872396..d58ecc6f5 100644 --- a/src/common/classes.js +++ b/src/common/classes.js @@ -70,6 +70,7 @@ const CLASSES = { PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT: 'preferences-page__inside-content--accountant', PREFERENCES_PAGE_INSIDE_CONTENT_SMS_INTEGRATION: 'preferences-page__inside-content--sms-integration', PREFERENCES_PAGE_INSIDE_CONTENT_ROLES_FORM: 'preferences-page__inside-content--roles-form', + PREFERENCES_PAGE_INSIDE_CONTENT_BRANCHES: 'preferences-page__inside-content--branches', FINANCIAL_REPORT_INSIDER: 'dashboard__insider--financial-report', diff --git a/src/components/DialogsContainer.js b/src/components/DialogsContainer.js index cd8eb745e..204b40264 100644 --- a/src/components/DialogsContainer.js +++ b/src/components/DialogsContainer.js @@ -34,6 +34,7 @@ import UnlockingTransactionsDialog from '../containers/Dialogs/UnlockingTransact import UnlockingPartialTransactionsDialog from '../containers/Dialogs/UnlockingPartialTransactionsDialog'; import CreditNotePdfPreviewDialog from '../containers/Dialogs/CreditNotePdfPreviewDialog'; import PaymentReceivePdfPreviewDialog from '../containers/Dialogs/PaymentReceivePdfPreviewDialog'; +import BranchFormDialog from '../containers/Dialogs/BranchFormDialog'; /** * Dialogs container. @@ -78,6 +79,7 @@ export default function DialogsContainer() { /> + ); } diff --git a/src/components/Preferences/PreferencesTopbar.js b/src/components/Preferences/PreferencesTopbar.js index 1633ecc8f..41ed14894 100644 --- a/src/components/Preferences/PreferencesTopbar.js +++ b/src/components/Preferences/PreferencesTopbar.js @@ -6,6 +6,7 @@ import { CLASSES } from 'common/classes'; import DashboardTopbarUser from 'components/Dashboard/TopbarUser'; import UsersActions from 'containers/Preferences/Users/UsersActions'; import CurrenciesActions from 'containers/Preferences/Currencies/CurrenciesActions'; +import BranchesActions from '../../containers/Preferences/Branches/BranchesActions'; import withDashboard from 'containers/Dashboard/withDashboard'; import { compose } from 'utils'; @@ -35,6 +36,11 @@ function PreferencesTopbar({ preferencesPageTitle }) { path={'/preferences/currencies'} component={CurrenciesActions} /> + diff --git a/src/config/preferencesMenu.js b/src/config/preferencesMenu.js index 031dfe0e1..b42ce876a 100644 --- a/src/config/preferencesMenu.js +++ b/src/config/preferencesMenu.js @@ -13,9 +13,12 @@ export default [ }, { text: , - href: '/preferences/currencies', }, + { + text: , + href: '/preferences/branches', + }, { text: , disabled: false, diff --git a/src/containers/Dialogs/BranchFormDialog/BranchForm.js b/src/containers/Dialogs/BranchFormDialog/BranchForm.js new file mode 100644 index 000000000..99f41f76e --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchForm.js @@ -0,0 +1,45 @@ +import React from 'react'; +import { Formik } from 'formik'; + +import { AppToaster } from 'components'; +import { CreateBranchFormSchema } from './BranchForm.schema'; + +import BranchFormContent from './BranchFormContent'; +import { useBranchFormContext } from './BranchFormProvider'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +const defaultInitialValues = { + branch_name: '', + branch_address_1: '', + branch_address_2: '', + phone_number: '', + email: '', + website: '', + branch_address_city: '', + branch_address_country: '', +}; + +function BranchForm({ + // #withDialogActions + closeDialog, +}) { + // Initial form values. + const initialValues = { + ...defaultInitialValues, + }; + + // Handles the form submit. + const handleFormSubmit = (values, { setSubmitting, setErrors }) => {}; + + return ( + + ); +} +export default compose(withDialogActions)(BranchForm); diff --git a/src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js b/src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js new file mode 100644 index 000000000..2fe48f301 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js @@ -0,0 +1,15 @@ +import * as Yup from 'yup'; +import intl from 'react-intl-universal'; + +const Schema = Yup.object().shape({ + branch_name: Yup.string().required().label(intl.get('branch_name')), + branch_address_1: Yup.string().trim(), + branch_address_2: Yup.string().trim(), + branch_address_city: Yup.string().trim(), + branch_address_country: Yup.string().trim(), + website: Yup.string().url().nullable(), + phone_number: Yup.number(), + email: Yup.string().email().nullable(), +}); + +export const CreateBranchFormSchema = Schema; diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormContent.js b/src/containers/Dialogs/BranchFormDialog/BranchFormContent.js new file mode 100644 index 000000000..e47e5706e --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormContent.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { Form } from 'formik'; + +import BranchFormFields from './BranchFormFields'; +import BranchFormFloatingActions from './BranchFormFloatingActions'; + +/** + * Branch form content. + */ +export default function BranchFormContent() { + return ( +
+ + + + ); +} diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormDialogContent.js b/src/containers/Dialogs/BranchFormDialog/BranchFormDialogContent.js new file mode 100644 index 000000000..b073f6f6e --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormDialogContent.js @@ -0,0 +1,20 @@ +import React from 'react'; + +import '../../../style/pages/Branches/BranchFormDialog.scss'; + +import { BranchFormProvider } from './BranchFormProvider'; +import BranchForm from './BranchForm'; + +/** + * Branch form dialog content. + */ +export default function BranchFormDialogContent({ + // #ownProps + dialogName, +}) { + return ( + + + + ); +} diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js new file mode 100644 index 000000000..90eae49f9 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js @@ -0,0 +1,157 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { FastField, ErrorMessage, Field } from 'formik'; +import styled from 'styled-components'; +import { + Classes, + FormGroup, + InputGroup, + ControlGroup, + Position, +} from '@blueprintjs/core'; +import { inputIntent } from 'utils'; +import { FieldRequiredHint, Col, Row, FormattedMessage as T } from 'components'; +import { useBranchFormContext } from './BranchFormProvider'; + +/** + * Branch form dialog fields. + */ +function BranchFormFields() { + return ( +
+ {/*------------ Branch Name -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + labelInfo={} + intent={inputIntent({ error, touched })} + inline={true} + helperText={} + className={'form-group--branch_name'} + > + + + )} + + + {/*------------ Branch Address 1 -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--branch_address_1'} + > + + + )} + + + {/*------------ Branch Address 2 -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--branch_address_2'} + > + + + )} + + + {/*------------ Branch Address City & Country-----------*/} + } + > + + + {({ field, meta: { error, touched } }) => ( + + )} + + + + {({ field, meta: { error, touched } }) => ( + + )} + + + + + + {/*------------ Phone Number -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--phone_number'} + > + + + )} + + + {/*------------ Email -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--email'} + > + + + )} + + + {/*------------ Website -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--website'} + > + + + )} + +
+ ); +} + +export default BranchFormFields; + +const BranchAddressWrap = styled.div` + margin-left: 160px; +`; diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormFloatingActions.js b/src/containers/Dialogs/BranchFormDialog/BranchFormFloatingActions.js new file mode 100644 index 000000000..8b2c558bc --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormFloatingActions.js @@ -0,0 +1,46 @@ +import React from 'react'; + +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { useFormikContext } from 'formik'; +import { FormattedMessage as T } from 'components'; + +import { useBranchFormContext } from './BranchFormProvider'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +/** + * Branch form floating actions. + */ +function BranchFormFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // Formik context. + const { isSubmitting } = useFormikContext(); + + const { dialogName } = useBranchFormContext(); + + // Handle close button click. + const handleCancelBtnClick = () => { + closeDialog(dialogName); + }; + + return ( +
+
+ + +
+
+ ); +} +export default compose(withDialogActions)(BranchFormFloatingActions); diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js b/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js new file mode 100644 index 000000000..18e97bc92 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { DialogContent } from 'components'; +// import {} from 'hooks/query'; + +const BranchFormContext = React.createContext(); + +/** + * Branch form dialog provider. + */ +function BranchFormProvider({ dialogName, ...props }) { + // State provider. + const provider = { + dialogName, + }; + return ( + + + + ); +} +const useBranchFormContext = () => React.useContext(BranchFormContext); + +export { BranchFormProvider, useBranchFormContext }; diff --git a/src/containers/Dialogs/BranchFormDialog/index.js b/src/containers/Dialogs/BranchFormDialog/index.js new file mode 100644 index 000000000..fca572330 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/index.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { FormattedMessage as T } from 'components'; +import { Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; + +import { compose } from 'utils'; + +const BranchFormDialogContent = React.lazy(() => + import('./BranchFormDialogContent'), +); + +/** + * Branch form form dialog. + */ +function BranchFormDialog({ dialogName, payload = {}, isOpen }) { + return ( + } + isOpen={isOpen} + canEscapeJeyClose={true} + autoFocus={true} + className={'dialog--branch-form'} + > + + + + + ); +} +export default compose(withDialogRedux())(BranchFormDialog); diff --git a/src/containers/Preferences/Branches/Branches.js b/src/containers/Preferences/Branches/Branches.js new file mode 100644 index 000000000..af93b014c --- /dev/null +++ b/src/containers/Preferences/Branches/Branches.js @@ -0,0 +1,21 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; + +import BranchesDataTable from './BranchesDataTable'; +import withDashboardActions from 'containers/Dashboard/withDashboardActions'; + +import { compose } from 'utils'; + +function Branches({ + // #withDashboardActions + changePreferencesPageTitle, +}) { + React.useEffect(() => { + changePreferencesPageTitle(intl.get('branches.label')); + }, [changePreferencesPageTitle]); + + return ; +} +export default compose(withDashboardActions)(Branches); diff --git a/src/containers/Preferences/Branches/BranchesActions.js b/src/containers/Preferences/Branches/BranchesActions.js new file mode 100644 index 000000000..a73c4bb93 --- /dev/null +++ b/src/containers/Preferences/Branches/BranchesActions.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { Button, Intent } from '@blueprintjs/core'; + +import { FormattedMessage as T, Icon } from 'components'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +function BranchesActions({ + //#ownProps + openDialog, +}) { + const handleClickNewBranche = () => { + openDialog('branch-form'); + }; + + return ( + + + + ); +} + +export default compose(withDialogActions)(BranchesActions); diff --git a/src/containers/Preferences/Branches/BranchesDataTable.js b/src/containers/Preferences/Branches/BranchesDataTable.js new file mode 100644 index 000000000..91e992a10 --- /dev/null +++ b/src/containers/Preferences/Branches/BranchesDataTable.js @@ -0,0 +1,36 @@ +import React from 'react'; +import styled from 'styled-components'; + +import { DataTable } from 'components'; +import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; +import { useBranchesTableColumns, ActionsMenu } from './components'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +/** + * Branches data table. + */ +function BranchesDataTable({ + // #withDialogAction + openDialog, +}) { + // Table columns. + const columns = useBranchesTableColumns(); + + return ( + + ); +} + +export default compose(withDialogActions)(BranchesDataTable); + +const BranchesTable = styled(DataTable)``; diff --git a/src/containers/Preferences/Branches/BranchesProvider.js b/src/containers/Preferences/Branches/BranchesProvider.js new file mode 100644 index 000000000..a8fdea708 --- /dev/null +++ b/src/containers/Preferences/Branches/BranchesProvider.js @@ -0,0 +1,35 @@ +import React from 'react'; +import styled from 'styled-components'; +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; +import { Card } from 'components'; + +const BranchesContext = React.createContext(); + +/** + * Branches data provider. + */ +function BranchesProvider({ ...props }) { + // Provider state. + const provider = {}; + + return ( +
+ + + +
+ ); +} + +const useBranchesContext = () => React.useContext(BranchesContext); +export { BranchesProvider, useBranchesContext }; + +const BrachesPreferencesCard = styled(Card)` + padding: 0; +`; diff --git a/src/containers/Preferences/Branches/components.js b/src/containers/Preferences/Branches/components.js new file mode 100644 index 000000000..00239bf23 --- /dev/null +++ b/src/containers/Preferences/Branches/components.js @@ -0,0 +1,76 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { + Intent, + Button, + Popover, + Menu, + MenuDivider, + Tag, + MenuItem, + Position, +} from '@blueprintjs/core'; + +import { safeCallback } from 'utils'; +import { Icon } from 'components'; + +/** + * Context menu of Branches. + */ +export function ActionsMenu({ + payload: { onEdit, onDelete }, + row: { original }, +}) { + return ( + + } + text={intl.get('branches.action.edit_branch')} + onClick={safeCallback(onEdit, original)} + /> + + } + text={intl.get('branches.action.delete_branch')} + onClick={safeCallback(onDelete, original)} + intent={Intent.DANGER} + /> + + ); +} + +/** + * Retrieve branches table columns + * @returns + */ +export function useBranchesTableColumns() { + return React.useMemo( + () => [ + { + id: 'branch_name', + Header: intl.get('branches.column.branch_name'), + accessor: 'branch_name', + className: 'branch_name', + width: '120', + disableSortBy: true, + textOverview: true, + }, + { + Header: intl.get('branches.column.address'), + accessor: 'address', + className: 'address', + width: '180', + disableSortBy: true, + textOverview: true, + }, + { + Header: intl.get('branches.column.phone_number'), + accessor: 'phone_number', + className: 'phone_number', + width: '120', + disableSortBy: true, + }, + ], + [], + ); +} diff --git a/src/containers/Preferences/Branches/index.js b/src/containers/Preferences/Branches/index.js new file mode 100644 index 000000000..9bd083d91 --- /dev/null +++ b/src/containers/Preferences/Branches/index.js @@ -0,0 +1,15 @@ +import React from 'react'; + +import { BranchesProvider } from './BranchesProvider'; +import Branches from './Branches'; + +/** + * Branches . + */ +export default function BranchesPreferences() { + return ( + + + + ); +} diff --git a/src/lang/en/index.json b/src/lang/en/index.json index 4147975a2..eb7b674b3 100644 --- a/src/lang/en/index.json +++ b/src/lang/en/index.json @@ -1738,19 +1738,32 @@ "global_error.you_dont_have_permissions": "You do not have permissions to access this page.", "global_error.transactions_locked": "Transactions before {lockedToDate} has been locked. Hence action cannot be performed.", "global_error.authorized_user_inactive": "The authorized user is inactive.", - "the_vendor_has_been_inactivated_successfully": "The contact has been inactivated successfully.", - "vendor.alert.activated_message": "The vendor has been activated successfully.", - "vendor.alert.are_you_sure_want_to_inactivate_this_vendor":"Are you sure want to inactivate this vendor? You will to able to activate it later.", + "vendor.alert.are_you_sure_want_to_inactivate_this_vendor": "Are you sure want to inactivate this vendor? You will to able to activate it later.", "vendor.alert.inactivated_message": "The vendor has been inactivated successfully.", "vendor.alert.are_you_sure_want_to_activate_this_vendor": "Are you sure want to activate this vendor? You will to able to inactivate it later.", - - "customer.alert.activated_message":"The customer has been activated successfully.", + "customer.alert.activated_message": "The customer has been activated successfully.", "customer.alert.are_you_sure_want_to_activate_this_customer": "Are you sure want to activate this customer? You will to able to inactivate it later.", "customer.alert.inactivated_message": "The customer has been inactivated successfully.", - "customer.alert.are_you_sure_want_to_inactivate_this_customer":"Are you sure want to inactivate this customer? You will to able to activate it later.", - - "credit_note_preview.dialog.title":"Credit Note PDF Preview", - "payment_receive_preview.dialog.title":"Payment Receive PDF Preview" + "customer.alert.are_you_sure_want_to_inactivate_this_customer": "Are you sure want to inactivate this customer? You will to able to activate it later.", + "credit_note_preview.dialog.title": "Credit Note PDF Preview", + "payment_receive_preview.dialog.title": "Payment Receive PDF Preview", + "branches.label": "Branches", + "branches.label.new_branche": "New Branch", + "branches.action.edit_branch": "Edit Branch", + "branches.action.delete_branch": "Edit Branch", + "branches.column.branch_name": "Branch name", + "branches.column.address": "Address", + "branches.column.phone_number": "Phone number", + "branch.dialog.label": "New Branch", + "branch.dialog.label.branch_name": "Branch Name", + "branch.dialog.label.branch_address": "Branch Address", + "branch.dialog.label.address_1": "Address 1", + "branch.dialog.label.address_2": "Address 2", + "branch.dialog.label.city": "City", + "branch.dialog.label.country": "Country", + "branch.dialog.label.phone_number": "Phone Number", + "branch.dialog.label.email": "Email", + "branch.dialog.label.website": "Website" } \ No newline at end of file diff --git a/src/routes/preferences.js b/src/routes/preferences.js index 9fc454ddd..c8962e8b6 100644 --- a/src/routes/preferences.js +++ b/src/routes/preferences.js @@ -7,6 +7,7 @@ import Currencies from 'containers/Preferences/Currencies/Currencies'; import Item from 'containers/Preferences/Item'; import SMSIntegration from '../containers/Preferences/SMSIntegration'; import DefaultRoute from '../containers/Preferences/DefaultRoute'; +import Branches from '../containers/Preferences/Branches' const BASE_URL = '/preferences'; @@ -36,6 +37,11 @@ export default [ component: Currencies, exact: true, }, + { + path: `${BASE_URL}/branches`, + component: Branches, + exact: true, + }, { path: `${BASE_URL}/accountant`, component: Accountant, diff --git a/src/style/pages/Branches/BranchFormDialog.scss b/src/style/pages/Branches/BranchFormDialog.scss new file mode 100644 index 000000000..b10bea737 --- /dev/null +++ b/src/style/pages/Branches/BranchFormDialog.scss @@ -0,0 +1,39 @@ +.dialog--branch-form { + width: 600px; + + .bp3-dialog-body { + .bp3-form-group { + margin-bottom: 15px; + } + .bp3-form-group.bp3-inline { + .bp3-label { + font-size: 13px; + margin-bottom: 3px; + min-width: 150px; + } + .bp3-form-content { + width: 320px; + } + } + .form-group { + &--branch_address_city { + .bp3-control-group > * { + flex-shrink: unset; + padding-right: 5px; + padding-left: 5px; + + &:first-child { + padding-left: 0; + } + &:last-child { + padding-right: 0; + } + } + } + } + } + + .bp3-dialog-footer { + padding-top: 10px; + } +} From dcac1053bea00429daef6ffe4d6e307b030672e8 Mon Sep 17 00:00:00 2001 From: elforjani13 <39470382+elforjani13@users.noreply.github.com> Date: Mon, 24 Jan 2022 01:44:46 +0200 Subject: [PATCH 2/3] feat(warehouse): add warehouse fields. --- .../BranchFormDialog/BranchFormFields.js | 6 ++-- .../pages/Branches/BranchFormDialog.scss | 33 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js index 90eae49f9..fcebeb72e 100644 --- a/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js @@ -43,7 +43,7 @@ function BranchFormFields() { intent={inputIntent({ error, touched })} inline={true} helperText={} - className={'form-group--branch_address_1'} + className={'form-group--branch_address'} > } - className={'form-group--branch_address_2'} + className={'form-group--branch_address'} > } > diff --git a/src/style/pages/Branches/BranchFormDialog.scss b/src/style/pages/Branches/BranchFormDialog.scss index b10bea737..9dd06954f 100644 --- a/src/style/pages/Branches/BranchFormDialog.scss +++ b/src/style/pages/Branches/BranchFormDialog.scss @@ -1,5 +1,5 @@ .dialog--branch-form { - width: 600px; + width: 650px; .bp3-dialog-body { .bp3-form-group { @@ -12,27 +12,28 @@ min-width: 150px; } .bp3-form-content { - width: 320px; + width: 278px; } - } - .form-group { - &--branch_address_city { - .bp3-control-group > * { - flex-shrink: unset; - padding-right: 5px; - padding-left: 5px; + .bp3-control-group > * { + flex-shrink: unset; + padding-right: 5px; + padding-left: 5px; - &:first-child { - padding-left: 0; - } - &:last-child { - padding-right: 0; - } + &:first-child { + padding-left: 0; + } + &:last-child { + padding-right: 0; + } + } + + &.form-group--branch_address { + .bp3-form-content { + width: 388px; } } } } - .bp3-dialog-footer { padding-top: 10px; } From dea4fe0e790503f66cb00995252bbcbab7a2403c Mon Sep 17 00:00:00 2001 From: elforjani13 <39470382+elforjani13@users.noreply.github.com> Date: Tue, 1 Feb 2022 23:18:32 +0200 Subject: [PATCH 3/3] feat(branche): add crud branches. --- .../Alerts/Branches/BranchDeleteAlert.js | 77 +++++++++++++++ src/containers/AlertsContainer/registered.js | 6 +- .../Dialogs/BranchFormDialog/BranchForm.js | 54 ++++++++-- .../BranchFormDialog/BranchForm.schema.js | 11 ++- .../BranchFormDialogContent.js | 3 +- .../BranchFormDialog/BranchFormFields.js | 46 ++++----- .../BranchFormDialog/BranchFormProvider.js | 22 ++++- .../Dialogs/BranchFormDialog/index.js | 16 ++- .../Preferences/Branches/BranchesActions.js | 2 +- .../Preferences/Branches/BranchesAlerts.js | 7 ++ .../Preferences/Branches/BranchesDataTable.js | 32 +++++- .../Preferences/Branches/BranchesProvider.js | 15 ++- .../Preferences/Branches/components.js | 26 ++--- src/hooks/query/branches.js | 99 +++++++++++++++++++ src/hooks/query/index.js | 1 + src/hooks/query/types.js | 6 ++ src/lang/en/index.json | 17 ++-- 17 files changed, 366 insertions(+), 74 deletions(-) create mode 100644 src/containers/Alerts/Branches/BranchDeleteAlert.js create mode 100644 src/containers/Preferences/Branches/BranchesAlerts.js create mode 100644 src/hooks/query/branches.js diff --git a/src/containers/Alerts/Branches/BranchDeleteAlert.js b/src/containers/Alerts/Branches/BranchDeleteAlert.js new file mode 100644 index 000000000..a58bb63ea --- /dev/null +++ b/src/containers/Alerts/Branches/BranchDeleteAlert.js @@ -0,0 +1,77 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { FormattedMessage as T, FormattedHTMLMessage } from 'components'; +import { Intent, Alert } from '@blueprintjs/core'; +import { AppToaster } from 'components'; + +import { useDeleteBranch } from 'hooks/query'; + +import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; +import withAlertActions from 'containers/Alert/withAlertActions'; + +import { compose } from 'utils'; + +/** + * Branch delete alert. + */ +function BranchDeleteAlert({ + name, + + // #withAlertStoreConnect + isOpen, + payload: { branchId }, + + // #withAlertActions + closeAlert, +}) { + + const { mutateAsync: deleteBranch, isLoading } = useDeleteBranch(); + + // Handle cancel delete alert. + const handleCancelDelete = () => { + closeAlert(name); + }; + + // Handle confirm delete branch. + const handleConfirmDeleteBranch = () => { + deleteBranch(branchId) + .then(() => { + AppToaster.show({ + message: intl.get('branch.alert.delete_message'), + intent: Intent.SUCCESS, + }); + }) + .catch( + ({ + response: { + data: { errors }, + }, + }) => {}, + ) + .finally(() => { + closeAlert(name); + }); + }; + + return ( + } + confirmButtonText={} + icon="trash" + intent={Intent.DANGER} + isOpen={isOpen} + onCancel={handleCancelDelete} + onConfirm={handleConfirmDeleteBranch} + loading={isLoading} + > +

+ +

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(BranchDeleteAlert); diff --git a/src/containers/AlertsContainer/registered.js b/src/containers/AlertsContainer/registered.js index 57d66288a..774000282 100644 --- a/src/containers/AlertsContainer/registered.js +++ b/src/containers/AlertsContainer/registered.js @@ -19,7 +19,8 @@ import CurrenciesAlerts from '../Preferences/Currencies/CurrenciesAlerts'; import RolesAlerts from '../Preferences/Users/Roles/RolesAlerts'; import CreditNotesAlerts from '../Sales/CreditNotes/CreditNotesAlerts'; import VendorCreditNotesAlerts from '../Purchases/CreditNotes/VendorCreditNotesAlerts'; -import TransactionsLockingAlerts from '../TransactionsLocking/TransactionsLockingAlerts' +import TransactionsLockingAlerts from '../TransactionsLocking/TransactionsLockingAlerts'; +import BranchesAlerts from '../Preferences/Branches/BranchesAlerts'; export default [ ...AccountsAlerts, @@ -43,5 +44,6 @@ export default [ ...RolesAlerts, ...CreditNotesAlerts, ...VendorCreditNotesAlerts, - ...TransactionsLockingAlerts + ...TransactionsLockingAlerts, + ...BranchesAlerts, ]; diff --git a/src/containers/Dialogs/BranchFormDialog/BranchForm.js b/src/containers/Dialogs/BranchFormDialog/BranchForm.js index 99f41f76e..17f2c8504 100644 --- a/src/containers/Dialogs/BranchFormDialog/BranchForm.js +++ b/src/containers/Dialogs/BranchFormDialog/BranchForm.js @@ -1,5 +1,8 @@ import React from 'react'; +import intl from 'react-intl-universal'; + import { Formik } from 'formik'; +import { Intent } from '@blueprintjs/core'; import { AppToaster } from 'components'; import { CreateBranchFormSchema } from './BranchForm.schema'; @@ -8,30 +11,67 @@ import BranchFormContent from './BranchFormContent'; import { useBranchFormContext } from './BranchFormProvider'; import withDialogActions from 'containers/Dialog/withDialogActions'; -import { compose } from 'utils'; +import { compose, transformToForm } from 'utils'; const defaultInitialValues = { - branch_name: '', - branch_address_1: '', - branch_address_2: '', + name: '', + code: '', + address: '', phone_number: '', email: '', website: '', - branch_address_city: '', - branch_address_country: '', + city: '', + country: '', }; function BranchForm({ // #withDialogActions closeDialog, }) { + const { + dialogName, + branch, + branchId, + createBranchMutate, + editBranchMutate, + } = useBranchFormContext(); + // Initial form values. const initialValues = { ...defaultInitialValues, + ...transformToForm(branch, defaultInitialValues), }; // Handles the form submit. - const handleFormSubmit = (values, { setSubmitting, setErrors }) => {}; + const handleFormSubmit = (values, { setSubmitting, setErrors }) => { + const form = { ...values }; + + // Handle request response success. + const onSuccess = (response) => { + AppToaster.show({ + message: intl.get('branch.dialog.success_message'), + intent: Intent.SUCCESS, + }); + closeDialog(dialogName); + }; + + // Handle request response errors. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + if (errors) { + } + setSubmitting(false); + }; + + if (branchId) { + editBranchMutate([branchId, form]).then(onSuccess).catch(onError); + } else { + createBranchMutate(form).then(onSuccess).catch(onError); + } + }; return ( + ); diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js index fcebeb72e..6d6a7203e 100644 --- a/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js @@ -20,7 +20,7 @@ function BranchFormFields() { return (
{/*------------ Branch Name -----------*/} - + {({ form, field, meta: { error, touched } }) => ( } @@ -34,15 +34,29 @@ function BranchFormFields() { )} + {/*------------ Branch Code -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + intent={inputIntent({ error, touched })} + inline={true} + helperText={} + className={'form-group--branch_name'} + > + + + )} + - {/*------------ Branch Address 1 -----------*/} - + {/*------------ Branch Address -----------*/} + {({ form, field, meta: { error, touched } }) => ( } + helperText={} className={'form-group--branch_address'} > - {/*------------ Branch Address 2 -----------*/} - - {({ form, field, meta: { error, touched } }) => ( - } - className={'form-group--branch_address'} - > - - - )} - - {/*------------ Branch Address City & Country-----------*/} } > - + {({ field, meta: { error, touched } }) => ( - + {({ field, meta: { error, touched } }) => ( } className={'form-group--phone_number'} > - + )} diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js b/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js index 18e97bc92..7d9ee8a3d 100644 --- a/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js @@ -1,21 +1,33 @@ import React from 'react'; import { DialogContent } from 'components'; -// import {} from 'hooks/query'; +import { useCreateBranch, useEditBranch, useBranch } from 'hooks/query'; const BranchFormContext = React.createContext(); /** * Branch form dialog provider. */ -function BranchFormProvider({ dialogName, ...props }) { +function BranchFormProvider({ dialogName, branchId, ...props }) { + // Create and edit warehouse mutations. + const { mutateAsync: createBranchMutate } = useCreateBranch(); + const { mutateAsync: editBranchMutate } = useEditBranch(); + + // Handle fetch branch detail. + const { data: branch, isLoading: isBranchLoading } = useBranch(branchId, { + enabled: !!branchId, + }); + // State provider. const provider = { dialogName, + branch, + branchId, + createBranchMutate, + editBranchMutate, }; + return ( - + ); diff --git a/src/containers/Dialogs/BranchFormDialog/index.js b/src/containers/Dialogs/BranchFormDialog/index.js index fca572330..f5b611148 100644 --- a/src/containers/Dialogs/BranchFormDialog/index.js +++ b/src/containers/Dialogs/BranchFormDialog/index.js @@ -12,18 +12,28 @@ const BranchFormDialogContent = React.lazy(() => /** * Branch form form dialog. */ -function BranchFormDialog({ dialogName, payload = {}, isOpen }) { +function BranchFormDialog({ + dialogName, + payload: { branchId, action }, + isOpen, +}) { return ( } + title={ + action === 'edit' ? ( + + ) : ( + + ) + } isOpen={isOpen} canEscapeJeyClose={true} autoFocus={true} className={'dialog--branch-form'} > - + ); diff --git a/src/containers/Preferences/Branches/BranchesActions.js b/src/containers/Preferences/Branches/BranchesActions.js index a73c4bb93..a59309d69 100644 --- a/src/containers/Preferences/Branches/BranchesActions.js +++ b/src/containers/Preferences/Branches/BranchesActions.js @@ -20,7 +20,7 @@ function BranchesActions({ onClick={handleClickNewBranche} intent={Intent.PRIMARY} > - + ); diff --git a/src/containers/Preferences/Branches/BranchesAlerts.js b/src/containers/Preferences/Branches/BranchesAlerts.js new file mode 100644 index 000000000..c444030db --- /dev/null +++ b/src/containers/Preferences/Branches/BranchesAlerts.js @@ -0,0 +1,7 @@ +import React from 'react'; + +const BranchDeleteAlert = React.lazy(() => + import('../../Alerts/Branches/BranchDeleteAlert'), +); + +export default [{ name: 'branch-delete', component: BranchDeleteAlert }]; diff --git a/src/containers/Preferences/Branches/BranchesDataTable.js b/src/containers/Preferences/Branches/BranchesDataTable.js index 91e992a10..2f238fafa 100644 --- a/src/containers/Preferences/Branches/BranchesDataTable.js +++ b/src/containers/Preferences/Branches/BranchesDataTable.js @@ -4,8 +4,11 @@ import styled from 'styled-components'; import { DataTable } from 'components'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import { useBranchesTableColumns, ActionsMenu } from './components'; +import { useBranchesContext } from './BranchesProvider'; import withDialogActions from 'containers/Dialog/withDialogActions'; +import withAlertActions from 'containers/Alert/withAlertActions'; + import { compose } from 'utils'; /** @@ -14,23 +17,42 @@ import { compose } from 'utils'; function BranchesDataTable({ // #withDialogAction openDialog, + + // #withAlertActions + openAlert, }) { // Table columns. const columns = useBranchesTableColumns(); + const { branches, isBranchesLoading, isBranchesFetching } = + useBranchesContext(); + + const handleEditBranch = ({ id }) => { + openDialog('branch-form', { branchId: id, action: 'edit' }); + }; + + const handleDeleteBranch = ({ id }) => { + openAlert('branch-delete', { branchId: id }); + }; + return ( ); } -export default compose(withDialogActions)(BranchesDataTable); +export default compose(withDialogActions, withAlertActions)(BranchesDataTable); const BranchesTable = styled(DataTable)``; diff --git a/src/containers/Preferences/Branches/BranchesProvider.js b/src/containers/Preferences/Branches/BranchesProvider.js index a8fdea708..eb6f47f3a 100644 --- a/src/containers/Preferences/Branches/BranchesProvider.js +++ b/src/containers/Preferences/Branches/BranchesProvider.js @@ -3,6 +3,8 @@ import styled from 'styled-components'; import classNames from 'classnames'; import { CLASSES } from 'common/classes'; import { Card } from 'components'; +import { useBranches } from 'hooks/query'; +import PreferencesPageLoader from '../PreferencesPageLoader'; const BranchesContext = React.createContext(); @@ -10,8 +12,19 @@ const BranchesContext = React.createContext(); * Branches data provider. */ function BranchesProvider({ ...props }) { + // Fetches the branches list. + const { + isLoading: isBranchesLoading, + isFetching: isBranchesFetching, + data: branches, + } = useBranches(); + // Provider state. - const provider = {}; + const provider = { + branches, + isBranchesLoading, + isBranchesFetching, + }; return (
[ { - id: 'branch_name', + id: 'name', Header: intl.get('branches.column.branch_name'), - accessor: 'branch_name', - className: 'branch_name', + accessor: 'name', + className: 'name', width: '120', disableSortBy: true, textOverview: true, }, + { + id: 'code', + Header: intl.get('branches.column.code'), + accessor: 'code', + className: 'code', + width: '100', + disableSortBy: true, + textOverview: true, + }, { Header: intl.get('branches.column.address'), accessor: 'address', diff --git a/src/hooks/query/branches.js b/src/hooks/query/branches.js new file mode 100644 index 000000000..b4cbb7daf --- /dev/null +++ b/src/hooks/query/branches.js @@ -0,0 +1,99 @@ +import { useQueryClient, useMutation } from 'react-query'; +import { useRequestQuery } from '../useQueryRequest'; +import useApiRequest from '../useRequest'; +import t from './types'; + +// Common invalidate queries. +const commonInvalidateQueries = (queryClient) => { + // Invalidate warehouses. + queryClient.invalidateQueries(t.BRANCHES); + queryClient.invalidateQueries(t.BRANCH); +}; + +/** + * Create a new branch. + */ +export function useCreateBranch(props) { + const queryClient = useQueryClient(); + const apiRequest = useApiRequest(); + + return useMutation((values) => apiRequest.post('branches', values), { + onSuccess: (res, values) => { + // Common invalidate queries. + commonInvalidateQueries(queryClient); + }, + ...props, + }); +} + +/** + * Edits the given branch. + */ +export function useEditBranch(props) { + const queryClient = useQueryClient(); + const apiRequest = useApiRequest(); + + return useMutation( + ([id, values]) => apiRequest.post(`branches/${id}`, values), + { + onSuccess: (res, [id, values]) => { + // Invalidate specific branch. + queryClient.invalidateQueries([t.BRANCH, id]); + + // Common invalidate queries. + commonInvalidateQueries(queryClient); + }, + ...props, + }, + ); +} + +/** + * Deletes the given branch. + */ +export function useDeleteBranch(props) { + const queryClient = useQueryClient(); + const apiRequest = useApiRequest(); + + return useMutation((id) => apiRequest.delete(`branches/${id}`), { + onSuccess: (res, id) => { + // Invalidate specific branch. + queryClient.invalidateQueries([t.BRANCH, id]); + + // Common invalidate queries. + commonInvalidateQueries(queryClient); + }, + ...props, + }); +} + +/** + * Retrieve Branches list. + */ +export function useBranches(query, props) { + return useRequestQuery( + [t.BRANCHES, query], + { method: 'get', url: 'branches', params: query }, + { + select: (res) => res.data.branches, + defaultData: [], + ...props, + }, + ); +} + +/** + * Retrieve the branch details. + * @param {number} + */ +export function useBranch(id, props, requestProps) { + return useRequestQuery( + [t.BRANCH, id], + { method: 'get', url: `branches/${id}`, ...requestProps }, + { + select: (res) => res.data.branch, + defaultData: {}, + ...props, + }, + ); +} diff --git a/src/hooks/query/index.js b/src/hooks/query/index.js index 629f4f1b4..433717958 100644 --- a/src/hooks/query/index.js +++ b/src/hooks/query/index.js @@ -33,3 +33,4 @@ export * from './roles'; export * from './creditNote'; export * from './vendorCredit'; export * from './transactionsLocking'; +export * from './branches'; diff --git a/src/hooks/query/types.js b/src/hooks/query/types.js index d98f99db8..afa6a21ef 100644 --- a/src/hooks/query/types.js +++ b/src/hooks/query/types.js @@ -191,6 +191,11 @@ const TARNSACTIONS_LOCKING = { TRANSACTIONS_LOCKING: 'TRANSACTIONS_LOCKING', }; +const BRANCHES = { + BRANCHES: 'BRANCHES', + BRANCH: 'BRANCH', +}; + export default { ...ACCOUNTS, ...BILLS, @@ -218,4 +223,5 @@ export default { ...CREDIT_NOTES, ...VENDOR_CREDIT_NOTES, ...TARNSACTIONS_LOCKING, + ...BRANCHES, }; diff --git a/src/lang/en/index.json b/src/lang/en/index.json index eb7b674b3..c61dcb604 100644 --- a/src/lang/en/index.json +++ b/src/lang/en/index.json @@ -1750,20 +1750,25 @@ "credit_note_preview.dialog.title": "Credit Note PDF Preview", "payment_receive_preview.dialog.title": "Payment Receive PDF Preview", "branches.label": "Branches", - "branches.label.new_branche": "New Branch", + "branches.label.new_branch": "New Branch", "branches.action.edit_branch": "Edit Branch", - "branches.action.delete_branch": "Edit Branch", + "branches.action.delete_branch": "Delete Branch", "branches.column.branch_name": "Branch name", "branches.column.address": "Address", "branches.column.phone_number": "Phone number", - "branch.dialog.label": "New Branch", + "branches.column.code": "Code", + "branch.dialog.label_new_branch": "New Branch", + "branch.dialog.label_edit_branch": "New Branch", "branch.dialog.label.branch_name": "Branch Name", + "branch.dialog.label.branch_code": "Code", "branch.dialog.label.branch_address": "Branch Address", - "branch.dialog.label.address_1": "Address 1", - "branch.dialog.label.address_2": "Address 2", + "branch.dialog.label.address_1": "Address", "branch.dialog.label.city": "City", "branch.dialog.label.country": "Country", "branch.dialog.label.phone_number": "Phone Number", "branch.dialog.label.email": "Email", - "branch.dialog.label.website": "Website" + "branch.dialog.label.website": "Website", + "branch.dialog.success_message": "The branch has been created successfully.", + "branch.alert.delete_message":"The branch has been deleted successfully", + "branch.once_delete_this_branch":"Once you delete this branch, you won't be able to restore it later. Are you sure you want to delete this branch?" } \ No newline at end of file