Compare commits

...

2 Commits

Author SHA1 Message Date
elforjani13
43c3f5e3de feat: add easysms integrate api. 2021-12-01 19:24:02 +02:00
elforjani13
a99fc78fe1 feat: EasySMS integrate. 2021-11-29 20:28:28 +02:00
18 changed files with 530 additions and 21 deletions

View File

@@ -26,6 +26,7 @@ import NotifyEstimateViaSMSDialog from '../containers/Dialogs/NotifyEstimateViaS
import NotifyPaymentReceiveViaSMSDialog from '../containers/Dialogs/NotifyPaymentReceiveViaSMSDialog';
import SMSMessageDialog from '../containers/Dialogs/SMSMessageDialog';
import TransactionsLockingDialog from '../containers/Dialogs/TransactionsLockingDialog';
import EasySMSIntegrationDialog from '../containers/Dialogs/EasySMSIntegrationDialog';
/**
* Dialogs container.
@@ -60,6 +61,7 @@ export default function DialogsContainer() {
<BadDebtDialog dialogName={'write-off-bad-debt'} />
<SMSMessageDialog dialogName={'sms-message-form'} />
<TransactionsLockingDialog dialogName={'transactions-locking'} />
<EasySMSIntegrationDialog dialogName={'easysms-integrate'} />
</div>
);
}

View File

@@ -0,0 +1,68 @@
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 { useSettingEasySMSDisconnect } from 'hooks/query';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Easy SMS disconnect alert.
*/
function EasySMSDisconnectAlert({
name,
// #withAlertStoreConnect
isOpen,
payload: {},
// #withAlertActions
closeAlert,
}) {
const { mutateAsync: disconnectEasySMS, isLoading } =
useSettingEasySMSDisconnect();
// Handle cancel Disconnect alert.
const handleCancelDisconnect = () => {
closeAlert(name);
};
// Handle confirm Disconnect alert.
const handleConfirmDisconnect = () => {
disconnectEasySMS()
.then(() => {
AppToaster.show({
message: intl.get('easysms.disconnect.alert.success_message'),
intent: Intent.SUCCESS,
});
})
.catch(() => {})
.finally(() => {
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'easysms.label.disconnect'} />}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleCancelDisconnect}
onConfirm={handleConfirmDisconnect}
loading={isLoading}
>
<p>Ea aliqua elit reprehenderit pariatur consequat voluptate quis.</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(EasySMSDisconnectAlert);

View File

@@ -17,6 +17,7 @@ import AccountTransactionsAlerts from '../CashFlow/AccountTransactions/AccountTr
import UsersAlerts from '../Preferences/Users/UsersAlerts';
import CurrenciesAlerts from '../Preferences/Currencies/CurrenciesAlerts';
import RolesAlerts from '../Preferences/Users/Roles/RolesAlerts';
import EasySMSIntegrationAlerts from '../Preferences/EasySMSIntegration/EasySMSIntegrationAlerts';
export default [
...AccountsAlerts,
@@ -38,4 +39,5 @@ export default [
...UsersAlerts,
...CurrenciesAlerts,
...RolesAlerts,
...EasySMSIntegrationAlerts,
];

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { EasySMSIntegrationProvider } from './EasySMSIntegrationProvider';
import EasySMSIntegrationForm from './EasySMSIntegrationForm';
/**
* EasySMS integration dialog content.
*/
export default function EasySMSIntegrationDialogContent({
// #ownProps
dialogName,
}) {
return (
<EasySMSIntegrationProvider dialogName={dialogName}>
<EasySMSIntegrationForm />
</EasySMSIntegrationProvider>
);
}

View File

@@ -0,0 +1,66 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Formik } from 'formik';
import { Intent } from '@blueprintjs/core';
import '../../../style/pages/EasySMSIntegration/EasySMSIntegration.scss';
import { AppToaster } from 'components';
import EasySMSIntegrationFormContent from './EasySMSIntegrationFormContent';
import { CreateEasySMSIntegrationSchema } from './EasySMSIntegrationForm.schema';
import { useEasySMSIntegration } from './EasySMSIntegrationProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
const defaultInitialValues = {
token: '',
};
/**
* EasySMS Integration form.
*/
function EasySMSIntegrationForm({
// #withDialogActions
closeDialog,
}) {
const { dialogName, easySMSIntegrateMutate } = useEasySMSIntegration();
// Initial form values.
const initialValues = {
...defaultInitialValues,
};
// Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
// Handle request response success.
const onSuccess = (response) => {
AppToaster.show({
message: intl.get('easysms.integrate.dialog.success_message'),
intent: Intent.SUCCESS,
});
closeDialog(dialogName);
};
// Handle request response errors.
const onError = ({
response: {
data: { errors },
},
}) => {
setSubmitting(false);
};
easySMSIntegrateMutate(values).then(onSuccess).catch(onError);
};
return (
<Formik
validationSchema={CreateEasySMSIntegrationSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
component={EasySMSIntegrationFormContent}
/>
);
}
export default compose(withDialogActions)(EasySMSIntegrationForm);

View File

@@ -0,0 +1,7 @@
import * as Yup from 'yup';
const Schema = Yup.object().shape({
token: Yup.string().required(),
});
export const CreateEasySMSIntegrationSchema = Schema;

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { Form, FastField, ErrorMessage, useFormikContext } from 'formik';
import { Classes, FormGroup, InputGroup, Button } from '@blueprintjs/core';
import { FieldRequiredHint, FormattedMessage as T } from 'components';
import { useEasySMSIntegration } from './EasySMSIntegrationProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { inputIntent, compose } from 'utils';
/**
* EasySMS Integration form content.
*/
function EasySMSIntegrationFormContent({
// #withDialogActions
closeDialog,
}) {
// Formik context.
const { isSubmitting } = useFormikContext();
// EasySMS Integration dialog context.
const { dialogName } = useEasySMSIntegration();
// Handle close button click.
const handleCloseBtnClick = () => {
closeDialog(dialogName);
};
return (
<Form>
<div className={Classes.DIALOG_BODY}>
{/*------------ Token -----------*/}
<FastField name={'token'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'easysms.integrate.dialog.label.token'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="token" />}
className={'form-group--token'}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
disabled={isSubmitting}
onClick={handleCloseBtnClick}
style={{ minWidth: '75px' }}
>
<T id={'close'} />
</Button>
<Button
loading={isSubmitting}
style={{ minWidth: '75px' }}
type="submit"
>
{<T id={'save'} />}
</Button>
</div>
</div>
</Form>
);
}
export default compose(withDialogActions)(EasySMSIntegrationFormContent);

View File

@@ -0,0 +1,29 @@
import React from 'react';
import { DialogContent } from 'components';
import { useSettingEasySMSIntegrate } from 'hooks/query';
const EasySMSIntegrationDialogContext = React.createContext();
/**
* Easy SMS integration dialog provider.
*/
function EasySMSIntegrationProvider({ dialogName, ...props }) {
// easysms integrate mutations.
const { mutateAsync: easySMSIntegrateMutate } = useSettingEasySMSIntegrate();
// State provider.
const provider = {
dialogName,
easySMSIntegrateMutate,
};
return (
<DialogContent>
<EasySMSIntegrationDialogContext.Provider value={provider} {...props} />
</DialogContent>
);
}
const useEasySMSIntegration = () =>
React.useContext(EasySMSIntegrationDialogContext);
export { EasySMSIntegrationProvider, useEasySMSIntegration };

View File

@@ -0,0 +1,29 @@
import React from 'react';
import { Dialog, DialogSuspense, FormattedMessage as T } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'redux';
const EasySMSIntegrationDialogContent = React.lazy(() =>
import('./EasySMSIntegrationDialogContent'),
);
/**
* EasySms integration dialog.
*/
function EasySMSIntegrationDialog({ dialogName, payload = {}, isOpen }) {
return (
<Dialog
name={dialogName}
title={'EasySMS Inegration'}
isOpen={isOpen}
canEscapeJeyClose={true}
autoFocus={true}
className={'dialog--easysms-integrate'}
>
<DialogSuspense>
<EasySMSIntegrationDialogContent dialogName={dialogName} />
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(EasySMSIntegrationDialog);

View File

@@ -0,0 +1,12 @@
import React from 'react';
const EasySMSDisconnectAlert = React.lazy(() =>
import('../../Alerts/EasySMSIntegration/EasySMSDisconnectAlert'),
);
export default [
{
name: 'easysms-disconnect',
component: EasySMSDisconnectAlert,
},
];

View File

@@ -0,0 +1,29 @@
import React from 'react';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import {
useSettingEasySMS,
useSettingEasySMSDisconnect,
useSettingEasySMSIntegrate,
} from 'hooks/query';
const EasySMSIntegrationContext = React.createContext();
/**
* Easysms integration data provider.
*/
function EasySMSIntegrationProvider({ ...props }) {
// Provider
const provider = {};
return (
<div className={classNames(CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT)}>
<EasySMSIntegrationContext.Provider value={provider} {...props} />
</div>
);
}
const useEasySMSIntegrationContext = () =>
React.useContext(EasySMSIntegrationContext);
export { EasySMSIntegrationProvider, useEasySMSIntegrationContext };

View File

@@ -0,0 +1,99 @@
import React from 'react';
import styled from 'styled-components';
import { Button, Intent } from '@blueprintjs/core';
import clsx from 'classnames';
import { FormattedMessage as T } from 'components';
import { EasySMSIntegrationProvider } from './EasySMSIntegrationProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* EasySMS integration.
*/
function EasySMSIntegrationList({
// #withDialogActions
openDialog,
// #withAlertsActions
openAlert,
}) {
// Handle EasySMS integrate.
const handleEasySMSIntegrate = () => {
openDialog('easysms-integrate', {});
};
// Handle EsaySMS disconnect
const handleEasySMSDiconnect = ({}) => {
openAlert('easysms-disconnect', {});
};
return (
<EasySMSIntegrationProvider>
<EasySMSIntegration>
<EasySMSIntegrationInner>
<EasySMSIntegrationTitle>Easysms Integration</EasySMSIntegrationTitle>
<EasySMSIntegrationParagraph>
<b>Lorem ipsum</b> dolor sit amet, consectetur adipisicing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem
ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
</EasySMSIntegrationParagraph>
<Button
text={<T id={'esaysms.label.connect'} />}
onClick={handleEasySMSIntegrate}
/>
</EasySMSIntegrationInner>
<EasySMSIntegrationFooter>
<Button
onClick={handleEasySMSIntegrate}
text={<T id={'easysms.label.edit_integration_settings'} />}
/>
<Button
text={<T id={'easysms.label.disconnect'} />}
onClick={handleEasySMSDiconnect}
/>
</EasySMSIntegrationFooter>
</EasySMSIntegration>
</EasySMSIntegrationProvider>
);
}
export default compose(
withAlertsActions,
withDialogActions,
)(EasySMSIntegrationList);
const EasySMSIntegration = styled.div`
display: flex;
flex-direction: column;
padding: 14px;
`;
const EasySMSIntegrationInner = styled.div`
max-width: 45rem; // 100%
margin-bottom: 48px; // 48
`;
const EasySMSIntegrationTitle = styled.h2`
font-weight: 50px;
margin-bottom: 12px;
`;
const EasySMSIntegrationParagraph = styled.p`
font-size: 14px;
line-height: 1.75rem;
margin-bottom: 15px;
`;
const EasySMSIntegrationFooter = styled.div`
width: 100%;
/* max-width: 30rem; */
display: flex;
.bp3-button {
margin-right: 10px;
}
`;

View File

@@ -5,6 +5,7 @@ import { Tabs, Tab } from '@blueprintjs/core';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import SMSMessagesDataTable from './SMSMessagesDataTable';
import EasySMSIntegration from '../EasySMSIntegration';
import '../../../style/pages/Preferences/SMSIntegration.scss';
@@ -27,6 +28,7 @@ function SMSIntegrationTabs({
<Tab
id="overview"
title={intl.get('sms_integration.label.overview')}
panel={<EasySMSIntegration />}
/>
<Tab
id="sms_messages"

View File

@@ -180,3 +180,57 @@ export function useSettingEditSMSNotification(props) {
},
);
}
/**
* Retrieve EasySMS Integration settings.
*/
export function useSettingEasySMSIntegrate(props) {
const queryClient = useQueryClient();
const apiRequest = useApiRequest();
return useMutation(
(values) => apiRequest.post(`settings/easysms/integrate`, values),
{
onSuccess: () => {
queryClient.invalidateQueries([t.SETTING_EASY_SMS]);
queryClient.invalidateQueries([t.SETTING_SMS_NOTIFICATIONS]);
},
...props,
},
);
}
/**
* Retrieve EasySMS Disconnect settings.
*/
export function useSettingEasySMSDisconnect(props) {
const queryClient = useQueryClient();
const apiRequest = useApiRequest();
return useMutation(
(values) => apiRequest.post(`settings/easysms/disconnect`, values),
{
onSuccess: () => {
queryClient.invalidateQueries([t.SETTING_EASY_SMS]);
queryClient.invalidateQueries([t.SETTING_SMS_NOTIFICATIONS]);
},
...props,
},
);
}
/**
* Retrieve EasySMS settings.
*/
export function useSettingEasySMS(props) {
return useRequestQuery(
[t.SETTING_EASY_SMS],
{ method: 'get', url: `settings/easysms` },
{
select: (res) => res.data,
defaultData: [],
...props,
},
);
}

View File

@@ -120,6 +120,7 @@ const SETTING = {
SETTING_SMS_NOTIFICATION: 'SETTING_SMS_NOTIFICATION',
SETTING_SMS_NOTIFICATIONS: 'SETTING_SMS_NOTIFICATIONS',
SETTING_EDIT_SMS_NOTIFICATION: 'SETTING_EDIT_SMS_NOTIFICATION',
SETTING_EASY_SMS: 'SETTING_EASY_SMS',
};
const ORGANIZATIONS = {

View File

@@ -1467,25 +1467,31 @@
"sms_notification.payment_details.type": "Payment receive thank you.",
"sms_notification.receipt_details.type": "Sale receipt details",
"personal": "Personal",
"list.create":"Create {value}",
"roles.label":"Roles",
"roles.column.name":"Name",
"roles.column.description":"Description",
"roles.edit_roles":"Edit Roles",
"roles.delete_roles":"Delete Roles",
"roles.label.role_name":"Role Name",
"roles.label.role_name_":"Role name",
"roles.error.role_is_predefined":"Role is predefined, you cannot delete predefined roles.",
"roles.error.you_cannot_change_your_own_role":"You cannot change your own role.",
"sidebar.transactions_locaking":"Transactions Locaking",
"transactions_locking.dialog.label":"Transactions locking",
"roles.permission_schema.success_message":"The role has been created successfully.",
"roles.permission_schema.upload_message":"The given role has been updated successfully.",
"roles.permission_schema.delete.alert_message":"The given role has been deleted successfully.",
"roles.permission_schema.once_delete_this_role_you_will_able_to_restore_it":"Once you delete this role, you won't be able to restore it later. Are you sure you want to delete this role?",
"users.column.role_name":"Role Name",
"roles.error.you_cannot_edit_predefined_roles":"You cannot edit predefined roles.",
"roles.error.you_cannot_delete_predefined_roles":"You cannot delete predefined roles.",
"roles.error.the_submit_role_has_invalid_permissions":"The submit role has invalid permissions.",
"roles.error.you_cannot_delete_role_that_associated_to_users":"You cannot delete role that associated to users"
"list.create": "Create {value}",
"roles.label": "Roles",
"roles.column.name": "Name",
"roles.column.description": "Description",
"roles.edit_roles": "Edit Roles",
"roles.delete_roles": "Delete Roles",
"roles.label.role_name": "Role Name",
"roles.label.role_name_": "Role name",
"roles.error.role_is_predefined": "Role is predefined, you cannot delete predefined roles.",
"roles.error.you_cannot_change_your_own_role": "You cannot change your own role.",
"sidebar.transactions_locaking": "Transactions Locaking",
"transactions_locking.dialog.label": "Transactions locking",
"roles.permission_schema.success_message": "The role has been created successfully.",
"roles.permission_schema.upload_message": "The given role has been updated successfully.",
"roles.permission_schema.delete.alert_message": "The given role has been deleted successfully.",
"roles.permission_schema.once_delete_this_role_you_will_able_to_restore_it": "Once you delete this role, you won't be able to restore it later. Are you sure you want to delete this role?",
"users.column.role_name": "Role Name",
"roles.error.you_cannot_edit_predefined_roles": "You cannot edit predefined roles.",
"roles.error.you_cannot_delete_predefined_roles": "You cannot delete predefined roles.",
"roles.error.the_submit_role_has_invalid_permissions": "The submit role has invalid permissions.",
"roles.error.you_cannot_delete_role_that_associated_to_users": "You cannot delete role that associated to users",
"esaysms.label.connect": "Connect",
"easysms.label.disconnect": "Disconnect",
"easysms.label.edit_integration_settings": "Edit integration settings",
"easysms.disconnect.alert.success_message": "The sms gateway integration has been disconnected successfully.",
"easysms.integrate.dialog.label.token": "Token",
"easysms.integrate.dialog.success_message": "The system has been integrated with Easysms sms gateway successfully."
}

View File

@@ -0,0 +1,17 @@
.dialog--easysms-integrate {
max-width: 400px;
.bp3-dialog-body {
.bp3-form-group {
margin-bottom: 15px;
margin-top: 15px;
label.bp3-label {
margin-bottom: 3px;
font-size: 13px;
}
}
}
.bp3-dialog-footer {
padding-top: 10px;
}
}