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; + } +}