diff --git a/backend/Actions/WCAffiliate/RecordApiHelper.php b/backend/Actions/WCAffiliate/RecordApiHelper.php new file mode 100644 index 00000000..8f595abe --- /dev/null +++ b/backend/Actions/WCAffiliate/RecordApiHelper.php @@ -0,0 +1,192 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + /** + * Execute the integration. + * + * @param array $fieldValues Field values from trigger. + * @param array $fieldMap Field mapping. + * @param array $utilities Utilities. + * + * @return array + */ + public function execute($fieldValues, $fieldMap, $utilities) + { + if ( + !class_exists('\WC_Affiliate\Models\Affiliate') + || !class_exists('\WC_Affiliate\Models\Referral') + || !class_exists('\WC_Affiliate\Models\Transaction') + ) { + return [ + 'success' => false, + 'message' => __('WC Affiliate is not installed or activated', 'bit-integrations') + ]; + } + + $fieldData = $this->generateReqDataFromFieldMap($fieldMap, $fieldValues); + $fieldData = $this->mergeFixedFieldValues($fieldData); + $mainAction = $this->_integrationDetails->mainAction ?? 'create_affiliate'; + + $defaultResponse = [ + 'success' => false, + // translators: %s: Plugin name. + 'message' => wp_sprintf( + __('%s plugin is not installed or activated', 'bit-integrations'), + 'Bit Integrations Pro' + ) + ]; + + switch ($mainAction) { + case 'create_affiliate': + $response = Hooks::apply( + Config::withPrefix('wcaffiliate_create_affiliate'), + $defaultResponse, + $fieldData + ); + $type = 'affiliate'; + $actionType = 'create_affiliate'; + + break; + + case 'update_affiliate_status': + $response = Hooks::apply( + Config::withPrefix('wcaffiliate_update_affiliate_status'), + $defaultResponse, + $fieldData + ); + $type = 'affiliate'; + $actionType = 'update_affiliate_status'; + + break; + + case 'create_referral': + $response = Hooks::apply( + Config::withPrefix('wcaffiliate_create_referral'), + $defaultResponse, + $fieldData + ); + $type = 'referral'; + $actionType = 'create_referral'; + + break; + + case 'update_referral_status': + $response = Hooks::apply( + Config::withPrefix('wcaffiliate_update_referral_status'), + $defaultResponse, + $fieldData + ); + $type = 'referral'; + $actionType = 'update_referral_status'; + + break; + + case 'create_transaction': + $response = Hooks::apply( + Config::withPrefix('wcaffiliate_create_transaction'), + $defaultResponse, + $fieldData + ); + $type = 'transaction'; + $actionType = 'create_transaction'; + + break; + + case 'update_transaction_status': + $response = Hooks::apply( + Config::withPrefix('wcaffiliate_update_transaction_status'), + $defaultResponse, + $fieldData + ); + $type = 'transaction'; + $actionType = 'update_transaction_status'; + + break; + + default: + $response = [ + 'success' => false, + 'message' => __('Invalid action', 'bit-integrations') + ]; + $type = 'WCAffiliate'; + $actionType = 'unknown'; + + break; + } + + $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; + + LogHandler::save( + $this->_integrationID, + ['type' => $type, 'type_name' => $actionType], + $responseType, + $response + ); + + return $response; + } + + private function generateReqDataFromFieldMap($fieldMap, $fieldValues) + { + $dataFinal = []; + + foreach ($fieldMap as $item) { + $triggerValue = $item->formField; + $actionValue = $item->wcAffiliateField; + + $dataFinal[$actionValue] = $triggerValue === 'custom' && isset($item->customValue) + ? Common::replaceFieldWithValue($item->customValue, $fieldValues) + : ($fieldValues[$triggerValue] ?? ''); + } + + return $dataFinal; + } + + private function mergeFixedFieldValues($fieldData) + { + $fixedFieldValues = isset($this->_integrationDetails->fixedFieldValues) + ? (array) $this->_integrationDetails->fixedFieldValues + : []; + + if (empty($fixedFieldValues)) { + return $fieldData; + } + + foreach ($fixedFieldValues as $key => $value) { + if ( + !empty($key) + && $value !== '' + && (!isset($fieldData[$key]) || $fieldData[$key] === '') + ) { + $fieldData[$key] = $value; + } + } + + return $fieldData; + } +} diff --git a/backend/Actions/WCAffiliate/Routes.php b/backend/Actions/WCAffiliate/Routes.php new file mode 100644 index 00000000..6e413ab3 --- /dev/null +++ b/backend/Actions/WCAffiliate/Routes.php @@ -0,0 +1,10 @@ +flow_details; + $integId = $integrationData->id; + $fieldMap = $integrationDetails->field_map; + $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; + + if (empty($fieldMap)) { + return new WP_Error('field_map_empty', __('Field map is empty', 'bit-integrations')); + } + + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $wcAffiliateResponse = $recordApiHelper->execute($fieldValues, $fieldMap, $utilities); + + if (is_wp_error($wcAffiliateResponse)) { + return $wcAffiliateResponse; + } + + return $wcAffiliateResponse; + } + + private static function isPluginReady() + { + return class_exists('\WC_Affiliate\Models\Affiliate') + && class_exists('\WC_Affiliate\Models\Referral') + && class_exists('\WC_Affiliate\Models\Transaction'); + } +} diff --git a/backend/Core/Util/AllTriggersName.php b/backend/Core/Util/AllTriggersName.php index 1b2623a2..fe4df96d 100644 --- a/backend/Core/Util/AllTriggersName.php +++ b/backend/Core/Util/AllTriggersName.php @@ -47,6 +47,7 @@ public static function allTriggersName() 'EVF' => ['name' => 'Everest Forms', 'isPro' => true, 'is_active' => false], 'FormGent' => ['name' => 'FormGent', 'isPro' => true, 'is_active' => false], 'FluentAffiliate' => ['name' => 'FluentAffiliate', 'isPro' => true, 'is_active' => false], + 'WCAffiliate' => ['name' => 'WC Affiliate', 'isPro' => true, 'is_active' => false], 'FF' => ['name' => 'Fluent Forms', 'isPro' => true, 'is_active' => false], 'FluentBoards' => ['name' => 'Fluent Boards', 'isPro' => true, 'is_active' => false], 'FluentBooking' => ['name' => 'Fluent Booking', 'isPro' => true, 'is_active' => false], diff --git a/frontend/src/Utils/StaticData/webhookIntegrations.js b/frontend/src/Utils/StaticData/webhookIntegrations.js index a846b22f..7879c4d5 100644 --- a/frontend/src/Utils/StaticData/webhookIntegrations.js +++ b/frontend/src/Utils/StaticData/webhookIntegrations.js @@ -71,6 +71,7 @@ export const customFormIntegrations = [ 'WPTravelEngine', 'WPUserFrontend', 'FluentAffiliate', + 'WCAffiliate', 'EasyCommerce', 'FormGent', 'GeoDirectory', diff --git a/frontend/src/components/AllIntegrations/EditInteg.jsx b/frontend/src/components/AllIntegrations/EditInteg.jsx index c7e138e0..86ad6922 100644 --- a/frontend/src/components/AllIntegrations/EditInteg.jsx +++ b/frontend/src/components/AllIntegrations/EditInteg.jsx @@ -170,6 +170,7 @@ const EditLine = lazy(() => import('./Line/EditLine')) const EditACPT = lazy(() => import('./ACPT/EditACPT')) const EditWishlistMember = lazy(() => import('./WishlistMember/EditWishlistMember')) const EditFluentCart = lazy(() => import('./FluentCart/EditFluentCart')) +const EditWCAffiliate = lazy(() => import('./WCAffiliate/EditWCAffiliate')) const EditWPCafe = lazy(() => import('./WPCafe/EditWPCafe')) const EditNotificationX = lazy(() => import('./NotificationX/EditNotificationX')) const EditTeamsForWooCommerceMemberships = lazy(() => @@ -583,6 +584,8 @@ const IntegType = memo(({ allIntegURL, flow }) => { return case 'FluentCart': return + case 'WC Affiliate': + return case 'WPCafe': return case 'NotificationX': diff --git a/frontend/src/components/AllIntegrations/IntegInfo.jsx b/frontend/src/components/AllIntegrations/IntegInfo.jsx index f41dba67..579649cb 100644 --- a/frontend/src/components/AllIntegrations/IntegInfo.jsx +++ b/frontend/src/components/AllIntegrations/IntegInfo.jsx @@ -171,6 +171,7 @@ const LineAuthorization = lazy(() => import('./Line/LineAuthorization')) const ACPTAuthorization = lazy(() => import('./ACPT/ACPTAuthorization')) const WishlistMemberAuthorization = lazy(() => import('./WishlistMember/WishlistMemberAuthorization')) const FluentCartAuthorization = lazy(() => import('./FluentCart/FluentCartAuthorization')) +const WCAffiliateAuthorization = lazy(() => import('./WCAffiliate/WCAffiliateAuthorization')) const WPCafeAuthorization = lazy(() => import('./WPCafe/WPCafeAuthorization')) const TeamsForWooCommerceMembershipsAuthorization = lazy( () => import('./TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsAuthorization') @@ -618,6 +619,8 @@ export default function IntegInfo() { return case 'FluentCart': return + case 'WC Affiliate': + return case 'WPCafe': return case 'Teams For WooCommerce Memberships': diff --git a/frontend/src/components/AllIntegrations/NewInteg.jsx b/frontend/src/components/AllIntegrations/NewInteg.jsx index 5fe23a7c..a93cfb3b 100644 --- a/frontend/src/components/AllIntegrations/NewInteg.jsx +++ b/frontend/src/components/AllIntegrations/NewInteg.jsx @@ -169,6 +169,7 @@ const Line = lazy(() => import('./Line/Line')) const ACPT = lazy(() => import('./ACPT/ACPT')) const WishlistMember = lazy(() => import('./WishlistMember/WishlistMember')) const FluentCart = lazy(() => import('./FluentCart/FluentCart')) +const WCAffiliate = lazy(() => import('./WCAffiliate/WCAffiliate')) const WPCafe = lazy(() => import('./WPCafe/WPCafe')) const NotificationX = lazy(() => import('./NotificationX/NotificationX')) const TeamsForWooCommerceMemberships = lazy(() => @@ -1644,6 +1645,15 @@ export default function NewInteg({ allIntegURL }) { setFlow={setFlow} /> ) + case 'WC Affiliate': + return ( + + ) case 'WPCafe': return ( + + +
+ {__('Integration Name:', 'bit-integrations')} + handleInput(e, wcAffiliateConf, setWCAffiliateConf)} + name="name" + value={wcAffiliateConf.name} + type="text" + placeholder={__('Integration Name...', 'bit-integrations')} + /> +
+
+ + + + + + { + if (!validation.isValid) { + setSnackbar({ + show: true, + msg: validation.message + }) + return + } + + saveActionConf({ + flow, + setFlow, + allIntegURL, + conf: wcAffiliateConf, + navigate, + id, + edit: 1, + setIsLoading, + setSnackbar + }) + }} + disabled={!validation.isValid} + isLoading={isLoading} + dataConf={wcAffiliateConf} + setDataConf={setWCAffiliateConf} + formFields={formFields} + /> +
+ + ) +} diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliate.jsx b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliate.jsx new file mode 100644 index 00000000..8dbc3e63 --- /dev/null +++ b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliate.jsx @@ -0,0 +1,114 @@ +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 WCAffiliateAuthorization from './WCAffiliateAuthorization' +import { validateRequiredFields } from './WCAffiliateCommonFunc' +import WCAffiliateIntegLayout from './WCAffiliateIntegLayout' + +export default function WCAffiliate({ 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 [wcAffiliateConf, setWCAffiliateConf] = useState({ + name: 'WC Affiliate', + type: 'WC Affiliate', + field_map: [{ formField: '', wcAffiliateField: '' }], + actions: {}, + fixedFieldValues: {}, + mainAction: '' + }) + + const nextPage = val => { + setTimeout(() => { + document.getElementById('btcd-settings-wrp').scrollTop = 0 + }, 300) + + if (val === 3) { + const validation = validateRequiredFields(wcAffiliateConf) + if (!validation.isValid) { + setSnackbar({ + show: true, + msg: validation.message + }) + return + } + + if (wcAffiliateConf.name !== '' && wcAffiliateConf.field_map.length > 0) { + setStep(val) + } + } else { + setStep(val) + } + } + + return ( +
+ +
+ + + +
+ +
+
+
+ +
+ + { + const validation = validateRequiredFields(wcAffiliateConf) + if (!validation.isValid) { + setSnackbar({ + show: true, + msg: validation.message + }) + return + } + + saveIntegConfig(flow, setFlow, allIntegURL, wcAffiliateConf, navigate, '', '', setIsLoading) + }} + isLoading={isLoading} + /> +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx new file mode 100644 index 00000000..3e979453 --- /dev/null +++ b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx @@ -0,0 +1,120 @@ +import { useState } from 'react' +import BackIcn from '../../../Icons/BackIcn' +import bitsFetch from '../../../Utils/bitsFetch' +import { __ } from '../../../Utils/i18nwrap' +import LoaderSm from '../../Loaders/LoaderSm' + +export default function WCAffiliateAuthorization({ + wcAffiliateConf, + setWCAffiliateConf, + step, + nextPage, + isLoading, + setIsLoading, + setSnackbar, + isInfo +}) { + const [isAuthorized, setIsAuthorized] = useState(false) + const [showAuthMsg, setShowAuthMsg] = useState(false) + + const authorizeHandler = () => { + if (isInfo) { + return + } + + setIsLoading('auth') + bitsFetch({}, 'wc_affiliate_authorize').then(result => { + if (result?.success) { + setIsAuthorized(true) + setSnackbar({ + show: true, + msg: __('Connected with WC Affiliate successfully', 'bit-integrations') + }) + } + setIsLoading(false) + setShowAuthMsg(true) + }) + } + + const handleInput = e => { + if (!setWCAffiliateConf) { + return + } + + const newConf = { ...wcAffiliateConf } + newConf[e.target.name] = e.target.value + setWCAffiliateConf(newConf) + } + + return ( +
+
+ {__('Integration Name:', 'bit-integrations')} +
+ + + {isLoading === 'auth' && ( +
+ + {__('Checking if WC Affiliate is authorized...', 'bit-integrations')} +
+ )} + + {showAuthMsg && !isAuthorized && !isLoading && ( +
+
+
+ +
+
+ {__('WC Affiliate is not activated or not installed', 'bit-integrations')} +
+
+
+ )} + + {showAuthMsg && isAuthorized && !isLoading && ( +
+
+ +
+
{__('WC Affiliate is activated', 'bit-integrations')}
+
+ )} + + +
+ {!isInfo && ( + + )} +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateCommonFunc.js b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateCommonFunc.js new file mode 100644 index 00000000..040c6f7a --- /dev/null +++ b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateCommonFunc.js @@ -0,0 +1,88 @@ +import { create } from 'mutative' +import { __, sprintf } from '../../../Utils/i18nwrap' + +export const handleInput = (e, wcAffiliateConf, setWCAffiliateConf) => { + const { name, value } = e.target + + setWCAffiliateConf(prevConf => + create(prevConf, draftConf => { + draftConf[name] = value + }) + ) +} + +export const checkMappedFields = wcAffiliateConf => { + const mappedFields = wcAffiliateConf?.field_map + ? wcAffiliateConf.field_map.filter( + mappedField => + !mappedField.formField || + !mappedField.wcAffiliateField || + (mappedField.formField === 'custom' && !mappedField.customValue) + ) + : [] + + return mappedFields.length === 0 +} + +const fixedRequiredFieldsByAction = { + create_affiliate: ['status', 'commission_type'], + update_affiliate_status: ['status'], + create_referral: ['type', 'payment_status'], + update_referral_status: ['status'], + create_transaction: ['payment_method', 'status'], + update_transaction_status: ['status'] +} + +const fixedFieldLabelMap = { + status: __('Status', 'bit-integrations'), + commission_type: __('Commission Type', 'bit-integrations'), + type: __('Referral Type', 'bit-integrations'), + payment_status: __('Payment Status', 'bit-integrations'), + payment_method: __('Payment Method', 'bit-integrations') +} + +export const validateRequiredFields = wcAffiliateConf => { + if (!wcAffiliateConf?.mainAction) { + return { + isValid: false, + message: __('Please select an action to continue.', 'bit-integrations') + } + } + + const requiredFixedFields = fixedRequiredFieldsByAction[wcAffiliateConf.mainAction] || [] + const fixedFieldValues = wcAffiliateConf?.fixedFieldValues || {} + + const missingFixedField = requiredFixedFields.find(fieldKey => !fixedFieldValues[fieldKey]) + if (missingFixedField) { + return { + isValid: false, + message: sprintf( + __('Please select %s to continue.', 'bit-integrations'), + fixedFieldLabelMap[missingFixedField] || missingFixedField + ) + } + } + + if (!checkMappedFields(wcAffiliateConf)) { + return { + isValid: false, + message: __('Please map all required fields to continue.', 'bit-integrations') + } + } + + return { + isValid: true, + message: '' + } +} + +export const generateMappedField = fields => { + const requiredFlds = fields.filter(fld => fld.required === true) + + return requiredFlds.length > 0 + ? requiredFlds.map(field => ({ + formField: '', + wcAffiliateField: field.key + })) + : [{ formField: '', wcAffiliateField: '' }] +} diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateFieldMap.jsx b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateFieldMap.jsx new file mode 100644 index 00000000..28eb364c --- /dev/null +++ b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateFieldMap.jsx @@ -0,0 +1,113 @@ +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 WCAffiliateFieldMap({ + i, + formFields, + field, + wcAffiliateConf, + setWCAffiliateConf +}) { + const btcbi = useRecoilValue($appConfigState) + const { isPro } = btcbi + + const requiredFlds = wcAffiliateConf?.wcAffiliateFields?.filter(fld => fld.required === true) || [] + const nonRequiredFlds = wcAffiliateConf?.wcAffiliateFields?.filter(fld => fld.required === false) || [] + + const requiredField = requiredFlds[i] + + return ( +
+
+
+ + + {field.formField === 'custom' && ( + handleCustomValue(e, i, wcAffiliateConf, setWCAffiliateConf)} + label={__('Custom Value', 'bit-integrations')} + className="mr-2" + type="text" + value={field.customValue} + placeholder={__('Custom Value', 'bit-integrations')} + formFields={formFields} + /> + )} + + +
+ + {!requiredField && ( + <> + + + + )} +
+
+ ) +} diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateIntegLayout.jsx b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateIntegLayout.jsx new file mode 100644 index 00000000..cda3118a --- /dev/null +++ b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateIntegLayout.jsx @@ -0,0 +1,244 @@ +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 { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' +import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' +import WCAffiliateFieldMap from './WCAffiliateFieldMap' +import { generateMappedField } from './WCAffiliateCommonFunc' +import { + affiliateStatusOptions, + commissionTypeOptions, + createAffiliateFields, + referralStatusOptions, + referralTypeOptions, + createReferralFields, + transactionPaymentMethodOptions, + transactionStatusOptions, + createTransactionFields, + modules, + updateAffiliateStatusFields, + updateReferralStatusFields, + updateTransactionStatusFields +} from './staticData' +import Note from '../../Utilities/Note' + +const getFieldsByAction = action => { + switch (action) { + case 'create_affiliate': + return createAffiliateFields + case 'update_affiliate_status': + return updateAffiliateStatusFields + case 'create_referral': + return createReferralFields + case 'update_referral_status': + return updateReferralStatusFields + case 'create_transaction': + return createTransactionFields + case 'update_transaction_status': + return updateTransactionStatusFields + default: + return [] + } +} + +const getDefaultFixedFieldValues = action => { + switch (action) { + case 'create_affiliate': + return { + status: 'pending', + commission_type: 'default' + } + case 'update_affiliate_status': + return { status: 'pending' } + case 'create_referral': + return { + type: 'sale', + payment_status: 'pending' + } + case 'update_referral_status': + return { status: 'pending' } + case 'create_transaction': + return { + payment_method: 'manual', + status: 'pending' + } + case 'update_transaction_status': + return { status: 'pending' } + default: + return {} + } +} + +const fixedFieldOptionsByAction = { + create_affiliate: [ + { + key: 'status', + label: __('Affiliate Status', 'bit-integrations'), + options: affiliateStatusOptions + }, + { + key: 'commission_type', + label: __('Commission Type', 'bit-integrations'), + options: commissionTypeOptions + } + ], + update_affiliate_status: [ + { + key: 'status', + label: __('New Status', 'bit-integrations'), + options: affiliateStatusOptions + } + ], + create_referral: [ + { + key: 'type', + label: __('Referral Type', 'bit-integrations'), + options: referralTypeOptions + }, + { + key: 'payment_status', + label: __('Payment Status', 'bit-integrations'), + options: referralStatusOptions + } + ], + update_referral_status: [ + { + key: 'status', + label: __('New Status', 'bit-integrations'), + options: referralStatusOptions + } + ], + create_transaction: [ + { + key: 'payment_method', + label: __('Payment Method', 'bit-integrations'), + options: transactionPaymentMethodOptions + }, + { + key: 'status', + label: __('Transaction Status', 'bit-integrations'), + options: transactionStatusOptions + } + ], + update_transaction_status: [ + { + key: 'status', + label: __('New Status', 'bit-integrations'), + options: transactionStatusOptions + } + ] +} + +export default function WCAffiliateIntegLayout({ formFields, wcAffiliateConf, setWCAffiliateConf }) { + const btcbi = useRecoilValue($appConfigState) + const { isPro } = btcbi + + const handleMainAction = value => { + const wcAffiliateFields = getFieldsByAction(value) + const fixedFieldValues = getDefaultFixedFieldValues(value) + + setWCAffiliateConf(prevConf => + create(prevConf, draftConf => { + draftConf.mainAction = value + draftConf.wcAffiliateFields = wcAffiliateFields + draftConf.fixedFieldValues = fixedFieldValues + draftConf.field_map = generateMappedField(wcAffiliateFields) + }) + ) + } + + const handleFixedFieldValue = (fieldKey, value) => { + setWCAffiliateConf(prevConf => + create(prevConf, draftConf => { + if (!draftConf.fixedFieldValues) { + draftConf.fixedFieldValues = {} + } + + draftConf.fixedFieldValues[fieldKey] = value + }) + ) + } + + const fixedFieldOptions = fixedFieldOptionsByAction[wcAffiliateConf?.mainAction] || [] + + 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 + /> +
+ + {fixedFieldOptions.length > 0 && + fixedFieldOptions.map(({ key, label, options }) => ( +
+ {`${label}:`} + handleFixedFieldValue(key, value)} + options={options} + singleSelect + closeOnSelect + /> +
+ ))} + + {wcAffiliateConf?.mainAction && wcAffiliateConf?.wcAffiliateFields?.length > 0 && ( +
+ {__('Map Fields', 'bit-integrations')} +
+
+
+ {__('Form Fields', 'bit-integrations')} +
+
+ {__('WC Affiliate Fields', 'bit-integrations')} +
+
+ + {wcAffiliateConf?.field_map?.map((itm, i) => ( + + ))} + +
+ +
+
+ )} + + ) +} diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/staticData.js b/frontend/src/components/AllIntegrations/WCAffiliate/staticData.js new file mode 100644 index 00000000..f05b10ab --- /dev/null +++ b/frontend/src/components/AllIntegrations/WCAffiliate/staticData.js @@ -0,0 +1,94 @@ +import { __ } from '../../../Utils/i18nwrap' + +export const modules = [ + { name: 'create_affiliate', label: __('Create Affiliate', 'bit-integrations'), is_pro: true }, + { + name: 'update_affiliate_status', + label: __('Update Affiliate Status', 'bit-integrations'), + is_pro: true + }, + { name: 'create_referral', label: __('Create Referral', 'bit-integrations'), is_pro: true }, + { + name: 'update_referral_status', + label: __('Update Referral Status', 'bit-integrations'), + is_pro: true + }, + { name: 'create_transaction', label: __('Create Transaction', 'bit-integrations'), is_pro: true }, + { + name: 'update_transaction_status', + label: __('Update Transaction Status', 'bit-integrations'), + is_pro: true + } +] + +export const affiliateStatusOptions = [ + { label: __('Pending', 'bit-integrations'), value: 'pending' }, + { label: __('Active', 'bit-integrations'), value: 'active' }, + { label: __('Rejected', 'bit-integrations'), value: 'rejected' }, + { label: __('Suspended', 'bit-integrations'), value: 'suspended' } +] + +export const commissionTypeOptions = [ + { label: __('Default', 'bit-integrations'), value: 'default' }, + { label: __('Fixed', 'bit-integrations'), value: 'fixed' }, + { label: __('Percent', 'bit-integrations'), value: 'percent' } +] + +export const referralTypeOptions = [{ label: __('Sale', 'bit-integrations'), value: 'sale' }] + +export const referralStatusOptions = [ + { label: __('Pending', 'bit-integrations'), value: 'pending' }, + { label: __('Approved', 'bit-integrations'), value: 'approved' }, + { label: __('Paid', 'bit-integrations'), value: 'paid' }, + { label: __('Rejected', 'bit-integrations'), value: 'rejected' }, + { label: __('Cancelled', 'bit-integrations'), value: 'cancelled' } +] + +export const transactionStatusOptions = [ + { label: __('Pending', 'bit-integrations'), value: 'pending' }, + { label: __('Processing', 'bit-integrations'), value: 'processing' }, + { label: __('Completed', 'bit-integrations'), value: 'completed' }, + { label: __('Failed', 'bit-integrations'), value: 'failed' }, + { label: __('Cancelled', 'bit-integrations'), value: 'cancelled' } +] + +export const transactionPaymentMethodOptions = [ + { label: __('Manual', 'bit-integrations'), value: 'manual' } +] + +export const createAffiliateFields = [ + { key: 'user_id', label: __('User ID', 'bit-integrations'), required: true }, + { key: 'commission_amount', label: __('Commission Amount', 'bit-integrations'), required: false } +] + +export const updateAffiliateStatusFields = [ + { key: 'affiliate_id', label: __('Affiliate ID', 'bit-integrations'), required: true }, + { key: 'admin_message', label: __('Admin Message', 'bit-integrations'), required: false } +] + +export const createReferralFields = [ + { key: 'affiliate_id', label: __('Affiliate ID', 'bit-integrations'), required: true }, + { key: 'commission', label: __('Commission', 'bit-integrations'), required: true }, + { key: 'order_total', label: __('Order Total', 'bit-integrations'), required: true }, + { key: 'order_id', label: __('Order ID', 'bit-integrations'), required: false }, + { key: 'visit', label: __('Visit', 'bit-integrations'), required: false }, + { key: 'transaction_id', label: __('Transaction ID', 'bit-integrations'), required: false }, + { key: 'time', label: __('Time', 'bit-integrations'), required: false }, + { key: 'products', label: __('Products', 'bit-integrations'), required: false } +] + +export const updateReferralStatusFields = [ + { key: 'referral_id', label: __('Referral ID', 'bit-integrations'), required: true } +] + +export const createTransactionFields = [ + { key: 'affiliate_id', label: __('Affiliate ID', 'bit-integrations'), required: true }, + { key: 'amount', label: __('Amount', 'bit-integrations'), required: true }, + { key: 'txn_id', label: __('External Transaction ID', 'bit-integrations'), required: false }, + { key: 'notes', label: __('Notes', 'bit-integrations'), required: false } +] + +export const updateTransactionStatusFields = [ + { key: 'transaction_id', label: __('Transaction ID', 'bit-integrations'), required: true }, + { key: 'txn_id', label: __('External Transaction ID', 'bit-integrations'), required: false } +] diff --git a/frontend/src/components/Flow/New/SelectAction.jsx b/frontend/src/components/Flow/New/SelectAction.jsx index 95f8ae45..6a7bd891 100644 --- a/frontend/src/components/Flow/New/SelectAction.jsx +++ b/frontend/src/components/Flow/New/SelectAction.jsx @@ -174,6 +174,7 @@ export default function SelectAction() { { type: 'Wishlist Member' }, { type: 'MailerPress' }, { type: 'FluentCart' }, + { type: 'WC Affiliate' }, { type: 'WPCafe' }, { type: 'Teams For WooCommerce Memberships' }, { type: 'SeoPress' }, diff --git a/frontend/src/resource/img/integ/wCAffiliate.webp b/frontend/src/resource/img/integ/wCAffiliate.webp new file mode 100644 index 00000000..37b367d5 Binary files /dev/null and b/frontend/src/resource/img/integ/wCAffiliate.webp differ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a701caa..1e2d38e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1542,8 +1542,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001784: - resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==} + caniuse-lite@1.0.30001785: + resolution: {integrity: sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ==} chalk@0.4.0: resolution: {integrity: sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ==} @@ -4640,7 +4640,7 @@ snapshots: browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.14 - caniuse-lite: 1.0.30001784 + caniuse-lite: 1.0.30001785 electron-to-chromium: 1.5.331 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -4666,7 +4666,7 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001784: {} + caniuse-lite@1.0.30001785: {} chalk@0.4.0: dependencies: