From 4f5dcb3609938193a4e45cca9c40cb7d447bdbf3 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] 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 dfd8f839d..86ecefdbc 100644 --- a/src/containers/AlertsContainer/registered.js +++ b/src/containers/AlertsContainer/registered.js @@ -21,7 +21,8 @@ import CreditNotesAlerts from '../Sales/CreditNotes/CreditNotesAlerts'; import VendorCreditNotesAlerts from '../Purchases/CreditNotes/VendorCreditNotesAlerts'; import TransactionsLockingAlerts from '../TransactionsLocking/TransactionsLockingAlerts'; import WarehousesAlerts from '../Preferences/Warehouses/WarehousesAlerts'; -import WarehousesTransfersAlerts from '../WarehouseTransfers/WarehousesTransfersAlerts' +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 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 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 1bd1584b3..dd3f7d3e4 100644 --- a/src/hooks/query/types.js +++ b/src/hooks/query/types.js @@ -202,6 +202,11 @@ const WAREHOUSE_TRANSFERS = { WAREHOUSE_TRANSFERS: 'WAREHOUSE_TRANSFERS', }; +const BRANCHES = { + BRANCHES: 'BRANCHES', + BRANCH: 'BRANCH', +}; + export default { ...ACCOUNTS, ...BILLS, @@ -231,4 +236,5 @@ export default { ...TARNSACTIONS_LOCKING, ...WAREHOUSES, ...WAREHOUSE_TRANSFERS, + ...BRANCHES, }; diff --git a/src/lang/en/index.json b/src/lang/en/index.json index 904427d0a..dc1d99646 100644 --- a/src/lang/en/index.json +++ b/src/lang/en/index.json @@ -1826,20 +1826,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