diff --git a/src/common/classes.js b/src/common/classes.js index 1bbcf9873..c00f489f3 100644 --- a/src/common/classes.js +++ b/src/common/classes.js @@ -71,6 +71,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 f52002200..ed46913da 100644 --- a/src/components/DialogsContainer.js +++ b/src/components/DialogsContainer.js @@ -35,6 +35,7 @@ import UnlockingPartialTransactionsDialog from '../containers/Dialogs/UnlockingP import CreditNotePdfPreviewDialog from '../containers/Dialogs/CreditNotePdfPreviewDialog'; import PaymentReceivePdfPreviewDialog from '../containers/Dialogs/PaymentReceivePdfPreviewDialog'; import WarehouseFormDialog from '../containers/Dialogs/WarehouseFormDialog'; +import BranchFormDialog from '../containers/Dialogs/BranchFormDialog'; /** * Dialogs container. @@ -80,6 +81,7 @@ export default function DialogsContainer() { + ); } diff --git a/src/components/Preferences/PreferencesTopbar.js b/src/components/Preferences/PreferencesTopbar.js index 7b27a83e8..7212b2116 100644 --- a/src/components/Preferences/PreferencesTopbar.js +++ b/src/components/Preferences/PreferencesTopbar.js @@ -7,6 +7,7 @@ import DashboardTopbarUser from 'components/Dashboard/TopbarUser'; import UsersActions from 'containers/Preferences/Users/UsersActions'; import CurrenciesActions from 'containers/Preferences/Currencies/CurrenciesActions'; import WarehousesActions from '../../containers/Preferences/Warehouses/WarehousesActions' +import BranchesActions from '../../containers/Preferences/Branches/BranchesActions'; import withDashboard from 'containers/Dashboard/withDashboard'; import { compose } from 'utils'; @@ -41,6 +42,11 @@ function PreferencesTopbar({ preferencesPageTitle }) { path={'/preferences/warehouses'} component={WarehousesActions} /> + diff --git a/src/config/preferencesMenu.js b/src/config/preferencesMenu.js index f01ca6e12..555b46fe8 100644 --- a/src/config/preferencesMenu.js +++ b/src/config/preferencesMenu.js @@ -13,13 +13,16 @@ export default [ }, { text: , - href: '/preferences/currencies', }, { text: , href: '/preferences/warehouses', }, + { + text: , + href: '/preferences/branches', + }, { text: , disabled: false, 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 dfd8f839d..e84ffcbb8 100644 --- a/src/containers/AlertsContainer/registered.js +++ b/src/containers/AlertsContainer/registered.js @@ -22,6 +22,7 @@ import VendorCreditNotesAlerts from '../Purchases/CreditNotes/VendorCreditNotesA import TransactionsLockingAlerts from '../TransactionsLocking/TransactionsLockingAlerts'; import WarehousesAlerts from '../Preferences/Warehouses/WarehousesAlerts'; import WarehousesTransfersAlerts from '../WarehouseTransfers/WarehousesTransfersAlerts' +import BranchesAlerts from '../Preferences/Branches/BranchesAlerts'; export default [ ...AccountsAlerts, @@ -47,5 +48,6 @@ export default [ ...VendorCreditNotesAlerts, ...TransactionsLockingAlerts, ...WarehousesAlerts, - ...WarehousesTransfersAlerts + ...WarehousesTransfersAlerts, + ...BranchesAlerts, ]; diff --git a/src/containers/Dialogs/BranchFormDialog/BranchForm.js b/src/containers/Dialogs/BranchFormDialog/BranchForm.js new file mode 100644 index 000000000..17f2c8504 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchForm.js @@ -0,0 +1,85 @@ +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'; + +import BranchFormContent from './BranchFormContent'; +import { useBranchFormContext } from './BranchFormProvider'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose, transformToForm } from 'utils'; + +const defaultInitialValues = { + name: '', + code: '', + address: '', + phone_number: '', + email: '', + website: '', + 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 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 ( + + ); +} +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..4995f2413 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchForm.schema.js @@ -0,0 +1,16 @@ +import * as Yup from 'yup'; +import intl from 'react-intl-universal'; +import { DATATYPES_LENGTH } from 'common/dataTypes'; + +const Schema = Yup.object().shape({ + name: Yup.string().required().label(intl.get('branch_name')), + code: Yup.string().trim().min(0).max(DATATYPES_LENGTH.STRING), + address: Yup.string().trim(), + city: Yup.string().trim(), + 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..336a37b22 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormDialogContent.js @@ -0,0 +1,21 @@ +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, + branchId, +}) { + return ( + + + + ); +} diff --git a/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js new file mode 100644 index 000000000..6d6a7203e --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormFields.js @@ -0,0 +1,153 @@ +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 Code -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + intent={inputIntent({ error, touched })} + inline={true} + helperText={} + className={'form-group--branch_name'} + > + + + )} + + + {/*------------ Branch Address -----------*/} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--branch_address'} + > + + + )} + + + {/*------------ 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..7d9ee8a3d --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/BranchFormProvider.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { useCreateBranch, useEditBranch, useBranch } from 'hooks/query'; + +const BranchFormContext = React.createContext(); + +/** + * Branch form dialog provider. + */ +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 ( + + + + ); +} +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..f5b611148 --- /dev/null +++ b/src/containers/Dialogs/BranchFormDialog/index.js @@ -0,0 +1,41 @@ +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: { branchId, action }, + 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..a59309d69 --- /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/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 new file mode 100644 index 000000000..2f238fafa --- /dev/null +++ b/src/containers/Preferences/Branches/BranchesDataTable.js @@ -0,0 +1,58 @@ +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 { useBranchesContext } from './BranchesProvider'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import withAlertActions from 'containers/Alert/withAlertActions'; + +import { compose } from 'utils'; + +/** + * Branches data table. + */ +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, withAlertActions)(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..eb6f47f3a --- /dev/null +++ b/src/containers/Preferences/Branches/BranchesProvider.js @@ -0,0 +1,48 @@ +import React from 'react'; +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(); + +/** + * Branches data provider. + */ +function BranchesProvider({ ...props }) { + // Fetches the branches list. + const { + isLoading: isBranchesLoading, + isFetching: isBranchesFetching, + data: branches, + } = useBranches(); + + // Provider state. + const provider = { + branches, + isBranchesLoading, + isBranchesFetching, + }; + + 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..771681fba --- /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, Menu, MenuDivider, MenuItem } 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: 'name', + Header: intl.get('branches.column.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', + 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/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 b27ebd2b5..1d4fa2f8d 100644 --- a/src/hooks/query/index.js +++ b/src/hooks/query/index.js @@ -34,3 +34,4 @@ export * from './creditNote'; export * from './vendorCredit'; export * from './transactionsLocking'; export * from './warehouses' +export * from './branches'; diff --git a/src/hooks/query/types.js b/src/hooks/query/types.js index 58b34a13e..663ea5679 100644 --- a/src/hooks/query/types.js +++ b/src/hooks/query/types.js @@ -199,6 +199,10 @@ const WAREHOUSES = { const WAREHOUSE_TRANSFERS = { WAREHOUSE_TRANSFER: 'WAREHOUSE_TRANSFER', WAREHOUSE_TRANSFERS: 'WAREHOUSE_TRANSFERS', +} +const BRANCHES = { + BRANCHES: 'BRANCHES', + BRANCH: 'BRANCH', }; export default { @@ -230,4 +234,5 @@ export default { ...TARNSACTIONS_LOCKING, ...WAREHOUSES, ...WAREHOUSE_TRANSFERS, + ...BRANCHES, }; diff --git a/src/lang/en/index.json b/src/lang/en/index.json index 72c1d35d2..b5d16acc1 100644 --- a/src/lang/en/index.json +++ b/src/lang/en/index.json @@ -1802,5 +1802,27 @@ "select_warehouse_transfer":"Select Warehouse Transfer", "warehouse_transfer.alert.delete_message":"The warehouse transfer transaction has been deleted successfully", "warehouse_transfer.once_delete_this_warehouse_transfer":"Once you delete this warehouse transfer, you won't be able to restore it later. Are you sure you want to delete this warehouse transfer?", - "warehouse_transfer.error.could_not_transfer_item_from_source_to_destination":"Could not transfer item from source to destination on the same warehouse" + "warehouse_transfer.error.could_not_transfer_item_from_source_to_destination":"Could not transfer item from source to destination on the same warehouse", + "branches.label": "Branches", + "branches.label.new_branch": "New Branch", + "branches.action.edit_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", + "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", + "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.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 diff --git a/src/routes/preferences.js b/src/routes/preferences.js index b4200758e..bf8949a19 100644 --- a/src/routes/preferences.js +++ b/src/routes/preferences.js @@ -8,6 +8,7 @@ import Item from 'containers/Preferences/Item'; import SMSIntegration from '../containers/Preferences/SMSIntegration'; import DefaultRoute from '../containers/Preferences/DefaultRoute'; import Warehouses from '../containers/Preferences/Warehouses'; +import Branches from '../containers/Preferences/Branches'; const BASE_URL = '/preferences'; @@ -42,6 +43,11 @@ export default [ component: Warehouses, 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..9dd06954f --- /dev/null +++ b/src/style/pages/Branches/BranchFormDialog.scss @@ -0,0 +1,40 @@ +.dialog--branch-form { + width: 650px; + + .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: 278px; + } + .bp3-control-group > * { + flex-shrink: unset; + padding-right: 5px; + padding-left: 5px; + + &: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; + } +}