+ saveActionConf({
+ flow,
+ setFlow,
+ allIntegURL,
+ conf: wsmsConf,
+ navigate,
+ id,
+ edit: 1,
+ setIsLoading,
+ setSnackbar
+ })
+ }
+ disabled={!checkMappedFields(wsmsConf)}
+ isLoading={isLoading}
+ dataConf={wsmsConf}
+ setDataConf={setWsmsConf}
+ formFields={formFields}
+ />
+
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/Wsms/Wsms.jsx b/frontend/src/components/AllIntegrations/Wsms/Wsms.jsx
new file mode 100644
index 00000000..a3c66d11
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/Wsms/Wsms.jsx
@@ -0,0 +1,106 @@
+import { useState } from 'react'
+import 'react-multiple-select-dropdown-lite/dist/index.css'
+import { useNavigate, useParams } from 'react-router'
+import BackIcn from '../../../Icons/BackIcn'
+import { __ } from '../../../Utils/i18nwrap'
+import SnackMsg from '../../Utilities/SnackMsg'
+import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers'
+import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree'
+import WsmsAuthorization from './WsmsAuthorization'
+import { checkMappedFields } from './WsmsCommonFunc'
+import WsmsIntegLayout from './WsmsIntegLayout'
+
+export default function Wsms({ formFields, setFlow, flow, allIntegURL }) {
+ const navigate = useNavigate()
+ const { formID } = useParams()
+ const [isLoading, setIsLoading] = useState(false)
+ const [step, setStep] = useState(1)
+ const [snack, setSnackbar] = useState({ show: false })
+ const [wsmsConf, setWsmsConf] = useState({
+ name: 'WSMS (WP SMS)',
+ type: 'Wsms',
+ field_map: [{ formField: '', wsmsField: '' }],
+ actions: {},
+ mainAction: ''
+ })
+
+ const nextPage = val => {
+ setTimeout(() => {
+ document.getElementById('btcd-settings-wrp').scrollTop = 0
+ }, 300)
+
+ if (val === 3) {
+ if (!checkMappedFields(wsmsConf)) {
+ setSnackbar({
+ show: true,
+ msg: __('Please map all required fields to continue.', 'bit-integrations')
+ })
+ return
+ }
+
+ if (wsmsConf.name !== '' && wsmsConf.field_map.length > 0) {
+ setStep(val)
+ }
+ } else {
+ setStep(val)
+ }
+ }
+
+ return (
+
+
+
{/* */}
+
+ {/* STEP 1 */}
+
+
+ {/* STEP 2 */}
+
+
+
+
+
+
+
+
+ {/* STEP 3 */}
+
+ saveIntegConfig(flow, setFlow, allIntegURL, wsmsConf, navigate, '', '', setIsLoading)
+ }
+ isLoading={isLoading}
+ />
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/Wsms/WsmsAuthorization.jsx b/frontend/src/components/AllIntegrations/Wsms/WsmsAuthorization.jsx
new file mode 100644
index 00000000..53ad0243
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/Wsms/WsmsAuthorization.jsx
@@ -0,0 +1,117 @@
+import { useState } from 'react'
+import BackIcn from '../../../Icons/BackIcn'
+import bitsFetch from '../../../Utils/bitsFetch'
+import { __ } from '../../../Utils/i18nwrap'
+import LoaderSm from '../../Loaders/LoaderSm'
+import TutorialLink from '../../Utilities/TutorialLink'
+
+export default function WsmsAuthorization({
+ formID,
+ wsmsConf,
+ setWsmsConf,
+ step,
+ nextPage,
+ isLoading,
+ setIsLoading,
+ setSnackbar
+}) {
+ const [isAuthorized, setIsAuthorized] = useState(false)
+ const [showAuthMsg, setShowAuthMsg] = useState(false)
+ const authorizeHandler = () => {
+ setIsLoading('auth')
+ bitsFetch({}, 'wsms_authorize')
+ .then(result => {
+ if (result?.success) {
+ setIsAuthorized(true)
+ setSnackbar({
+ show: true,
+ msg: __('Connected with WSMS (WP SMS) Successfully', 'bit-integrations')
+ })
+ }
+ setIsLoading(false)
+ setShowAuthMsg(true)
+ })
+ .catch(() => {
+ setIsLoading(false)
+ setShowAuthMsg(true)
+ })
+ }
+
+ const handleInput = e => {
+ const newConf = { ...wsmsConf }
+ newConf[e.target.name] = e.target.value
+ setWsmsConf(newConf)
+ }
+
+ return (
+
+
+
+
+ {__('Integration Name:', 'bit-integrations')}
+
+
+
+ {isLoading === 'auth' && (
+
+
+ {__('Checking if WSMS (WP SMS) is authorized!!!', 'bit-integrations')}
+
+ )}
+
+ {showAuthMsg && !isAuthorized && !isLoading && (
+
+
+
+ ✕
+
+
+ {__('WSMS (WP SMS) is not activated or not installed', 'bit-integrations')}
+
+
+
+ )}
+
+ {showAuthMsg && isAuthorized && !isLoading && (
+
+
+ ✓
+
+
{__('WSMS (WP SMS) is activated', 'bit-integrations')}
+
+ )}
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/Wsms/WsmsCommonFunc.js b/frontend/src/components/AllIntegrations/Wsms/WsmsCommonFunc.js
new file mode 100644
index 00000000..2be8af4f
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/Wsms/WsmsCommonFunc.js
@@ -0,0 +1,60 @@
+import { create } from 'mutative'
+import toast from 'react-hot-toast'
+import bitsFetch from '../../../Utils/bitsFetch'
+import { __ } from '../../../Utils/i18nwrap'
+
+export const handleInput = (e, wsmsConf, setWsmsConf) => {
+ const { name, value } = e.target
+
+ setWsmsConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf[name] = value
+ })
+ )
+}
+
+export const refreshGroups = (setWsmsConf, setIsLoading) => {
+ setIsLoading(true)
+ bitsFetch(null, 'refresh_wsms_groups')
+ .then(result => {
+ if (result && result?.success && result?.data?.groups) {
+ setWsmsConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.allGroups = result.data.groups
+ })
+ )
+
+ setIsLoading(false)
+ toast.success(__('All groups fetched successfully', 'bit-integrations'))
+ return
+ }
+ setIsLoading(false)
+ toast.error(__('WSMS groups fetch failed. Please try again', 'bit-integrations'))
+ })
+ .catch(() => setIsLoading(false))
+}
+
+export const checkMappedFields = wsmsConf => {
+ const mappedFields = wsmsConf?.field_map
+ ? wsmsConf.field_map.filter(
+ mappedField =>
+ !mappedField.formField ||
+ !mappedField.wsmsField ||
+ (mappedField.formField === 'custom' && !mappedField.customValue)
+ )
+ : []
+ if (mappedFields.length > 0) {
+ return false
+ }
+ return true
+}
+
+export const generateMappedField = fields => {
+ const requiredFlds = fields.filter(fld => fld.required === true)
+ return requiredFlds.length > 0
+ ? requiredFlds.map(field => ({
+ formField: '',
+ wsmsField: field.key
+ }))
+ : [{ formField: '', wsmsField: '' }]
+}
diff --git a/frontend/src/components/AllIntegrations/Wsms/WsmsFieldMap.jsx b/frontend/src/components/AllIntegrations/Wsms/WsmsFieldMap.jsx
new file mode 100644
index 00000000..777c2fb3
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/Wsms/WsmsFieldMap.jsx
@@ -0,0 +1,104 @@
+import { useRecoilValue } from 'recoil'
+import { $appConfigState } from '../../../GlobalStates'
+import { __, sprintf } from '../../../Utils/i18nwrap'
+import { SmartTagField } from '../../../Utils/StaticData/SmartTagField'
+import TagifyInput from '../../Utilities/TagifyInput'
+import {
+ addFieldMap,
+ delFieldMap,
+ handleCustomValue,
+ handleFieldMapping
+} from '../GlobalIntegrationHelper'
+
+export default function WsmsFieldMap({ i, formFields, field, wsmsConf, setWsmsConf }) {
+ const btcbi = useRecoilValue($appConfigState)
+ const { isPro } = btcbi
+
+ const requiredFlds = wsmsConf?.wsmsFields?.filter(fld => fld.required === true) || []
+ const nonRequiredFlds = wsmsConf?.wsmsFields?.filter(fld => fld.required === false) || []
+
+ return (
+
+
+
+
+
+ {field.formField === 'custom' && (
+ handleCustomValue(e, i, wsmsConf, setWsmsConf)}
+ label={__('Custom Value', 'bit-integrations')}
+ className="mr-2"
+ type="text"
+ value={field.customValue}
+ placeholder={__('Custom Value', 'bit-integrations')}
+ formFields={formFields}
+ />
+ )}
+
+
+
+ {i >= requiredFlds.length && (
+ <>
+
+
+ >
+ )}
+
+
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/Wsms/WsmsIntegLayout.jsx b/frontend/src/components/AllIntegrations/Wsms/WsmsIntegLayout.jsx
new file mode 100644
index 00000000..36b903c7
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/Wsms/WsmsIntegLayout.jsx
@@ -0,0 +1,176 @@
+import { create } from 'mutative'
+import MultiSelect from 'react-multiple-select-dropdown-lite'
+import { useRecoilValue } from 'recoil'
+import { $appConfigState } from '../../../GlobalStates'
+import { __ } from '../../../Utils/i18nwrap'
+import Loader from '../../Loaders/Loader'
+import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers'
+import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers'
+import { generateMappedField, refreshGroups } from './WsmsCommonFunc'
+import WsmsFieldMap from './WsmsFieldMap'
+import { modules, wsmsStatuses, WsmsStaticData } from './staticData'
+
+const GROUP_ACTIONS = ['add_subscriber', 'update_subscriber', 'delete_subscriber']
+const STATUS_ACTIONS = ['add_subscriber', 'update_subscriber']
+
+export default function WsmsIntegLayout({
+ formID,
+ formFields,
+ wsmsConf,
+ setWsmsConf,
+ isLoading,
+ setIsLoading,
+ setSnackbar
+}) {
+ const btcbi = useRecoilValue($appConfigState)
+ const { isPro } = btcbi
+
+ const handleMainAction = value => {
+ setWsmsConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.mainAction = value
+ draftConf.wsmsFields = WsmsStaticData[value] || []
+ draftConf.field_map = generateMappedField(draftConf.wsmsFields)
+ // Clear identifiers from the previous action so they do not leak into the new one.
+ delete draftConf.groupId
+ delete draftConf.status
+ })
+ )
+
+ if (GROUP_ACTIONS.includes(value)) {
+ refreshGroups(setWsmsConf, setIsLoading)
+ }
+ }
+
+ return (
+ <>
+
+
+ {__('Action:', 'bit-integrations')}
+ handleMainAction(value)}
+ options={modules?.map(action => ({
+ label: checkIsPro(isPro, action.is_pro) ? action.label : getProLabel(action.label),
+ value: action.name,
+ disabled: checkIsPro(isPro, action.is_pro) ? false : true
+ }))}
+ singleSelect
+ closeOnSelect
+ />
+
+
+ {GROUP_ACTIONS.includes(wsmsConf?.mainAction) && (
+ <>
+
+
+ {__('Group:', 'bit-integrations')}
+ ({
+ label: group.label,
+ value: group.value?.toString()
+ }))
+ : []
+ }
+ onChange={val =>
+ setWsmsConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.groupId = val
+ })
+ )
+ }
+ singleSelect
+ closeOnSelect
+ />
+
+
+ >
+ )}
+
+ {STATUS_ACTIONS.includes(wsmsConf?.mainAction) && (
+ <>
+
+
+ {__('Status:', 'bit-integrations')}
+
+ setWsmsConf(prevConf =>
+ create(prevConf, draftConf => {
+ draftConf.status = val
+ })
+ )
+ }
+ singleSelect
+ closeOnSelect
+ />
+
+ >
+ )}
+
+ {isLoading && (
+
+ )}
+
+ {wsmsConf?.mainAction && wsmsConf.wsmsFields && wsmsConf.wsmsFields.length > 0 && (
+
+
{__('Map Fields', 'bit-integrations')}
+
+
+
+ {__('Form Fields', 'bit-integrations')}
+
+
+ {__('WSMS Fields', 'bit-integrations')}
+
+
+
+ {wsmsConf?.field_map?.map((itm, i) => (
+
+ ))}
+
+
+
+
+
+ )}
+ >
+ )
+}
diff --git a/frontend/src/components/AllIntegrations/Wsms/staticData.js b/frontend/src/components/AllIntegrations/Wsms/staticData.js
new file mode 100644
index 00000000..b76d511e
--- /dev/null
+++ b/frontend/src/components/AllIntegrations/Wsms/staticData.js
@@ -0,0 +1,59 @@
+import { __ } from '../../../Utils/i18nwrap'
+
+export const modules = [
+ { name: 'send_sms', label: __('Send SMS', 'bit-integrations'), is_pro: true },
+ { name: 'add_subscriber', label: __('Add Subscriber', 'bit-integrations'), is_pro: true },
+ { name: 'update_subscriber', label: __('Update Subscriber', 'bit-integrations'), is_pro: true },
+ { name: 'delete_subscriber', label: __('Delete Subscriber', 'bit-integrations'), is_pro: true },
+ { name: 'add_group', label: __('Add Group', 'bit-integrations'), is_pro: true },
+ { name: 'update_group', label: __('Update Group', 'bit-integrations'), is_pro: true },
+ { name: 'delete_group', label: __('Delete Group', 'bit-integrations'), is_pro: true }
+]
+
+const SendSmsFields = [
+ { key: 'to', label: __('To (Recipient Numbers)', 'bit-integrations'), required: true },
+ { key: 'message', label: __('Message', 'bit-integrations'), required: true },
+ { key: 'sender_id', label: __('Sender ID', 'bit-integrations'), required: false },
+ { key: 'is_flash', label: __('Is Flash', 'bit-integrations'), required: false },
+ { key: 'media_urls', label: __('Media URLs', 'bit-integrations'), required: false }
+]
+
+const AddSubscriberFields = [
+ { key: 'name', label: __('Name', 'bit-integrations'), required: true },
+ { key: 'mobile', label: __('Mobile', 'bit-integrations'), required: true }
+]
+
+const UpdateSubscriberFields = [
+ { key: 'name', label: __('Name', 'bit-integrations'), required: true },
+ { key: 'mobile', label: __('Mobile', 'bit-integrations'), required: true }
+]
+
+const DeleteSubscriberFields = [
+ { key: 'mobile', label: __('Mobile', 'bit-integrations'), required: true }
+]
+
+const AddGroupFields = [{ key: 'name', label: __('Group Name', 'bit-integrations'), required: true }]
+
+const UpdateGroupFields = [
+ { key: 'group_id', label: __('Group ID', 'bit-integrations'), required: true },
+ { key: 'name', label: __('Group Name', 'bit-integrations'), required: true }
+]
+
+const DeleteGroupFields = [
+ { key: 'group_id', label: __('Group ID', 'bit-integrations'), required: true }
+]
+
+export const WsmsStaticData = {
+ send_sms: SendSmsFields,
+ add_subscriber: AddSubscriberFields,
+ update_subscriber: UpdateSubscriberFields,
+ delete_subscriber: DeleteSubscriberFields,
+ add_group: AddGroupFields,
+ update_group: UpdateGroupFields,
+ delete_group: DeleteGroupFields
+}
+
+export const wsmsStatuses = [
+ { value: '1', label: __('Active', 'bit-integrations') },
+ { value: '0', label: __('Inactive', 'bit-integrations') }
+]
diff --git a/frontend/src/components/Flow/New/SelectAction.jsx b/frontend/src/components/Flow/New/SelectAction.jsx
index 9259273f..809d381a 100644
--- a/frontend/src/components/Flow/New/SelectAction.jsx
+++ b/frontend/src/components/Flow/New/SelectAction.jsx
@@ -180,6 +180,7 @@ export default function SelectAction() {
{ type: 'CreatorLms' },
{ type: 'Bookly' },
{ type: 'FluentCart' },
+ { type: 'Wsms', name: 'WSMS (WP SMS)' },
{ type: 'MoreConvert Wishlist', logo: 'moreConvertWishlist' },
{ type: 'Heffl CRM' },
{ type: 'Secure Custom Fields' },
diff --git a/frontend/src/resource/img/integ/wsms.svg b/frontend/src/resource/img/integ/wsms.svg
new file mode 100644
index 00000000..93933e9a
--- /dev/null
+++ b/frontend/src/resource/img/integ/wsms.svg
@@ -0,0 +1,5 @@
+
diff --git a/frontend/src/resource/img/integ/wsms.webp b/frontend/src/resource/img/integ/wsms.webp
new file mode 100644
index 00000000..30995d99
Binary files /dev/null and b/frontend/src/resource/img/integ/wsms.webp differ