Skip to content

feat(cart-abandonment): add recovery integration#190

Open
RishadAlam wants to merge 4 commits into
mainfrom
feat/cart-abendonment
Open

feat(cart-abandonment): add recovery integration#190
RishadAlam wants to merge 4 commits into
mainfrom
feat/cart-abendonment

Conversation

@RishadAlam

Copy link
Copy Markdown
Member

Description

Adds the Cart Abandonment Recovery integration to the Free plugin, including the action configuration UI, session ID dropdown/mapping flow, backend route/controller, action bridge, and integration registrations.

Motivation & Context

This ports the Woo Cart Abandonment Recovery action setup into Bit Integrations so users can configure cart-session based automation and hand execution off to the Pro plugin handlers.

Type of Change

  • New feature

Verification

  • PHP lint passed for touched backend files
  • Self-review completed
  • Frontend build/tests not run

Checklist

  • Code follows project style guidelines
  • Documentation updated if needed

Copilot AI review requested due to automatic review settings July 6, 2026 12:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new integration for Cart Abandonment Recovery, adding backend controllers, API helpers, and routes, alongside frontend components for authorization, field mapping, and editing. The feedback highlights a few critical improvements: adding a .catch block to the authorization API request to prevent infinite loading states, implementing a useEffect hook (and its required imports) in the edit component to correctly fetch and populate abandoned carts on mount, and correcting a minor grammatical typo in the backend default response message.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +23 to +33
bitsFetch({}, 'cart_abandonment_recovery_authorize').then(result => {
if (result?.success) {
setIsAuthorized(true)
setSnackbar?.({
show: true,
msg: __('Connected with Cart Abandonment Recovery Successfully', 'bit-integrations')
})
}
setIsLoading?.(false)
setShowAuthMsg(true)
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error. Currently, if the authorization request fails, the loading state will remain active indefinitely.

    bitsFetch({}, 'cart_abandonment_recovery_authorize')
      .then(result => {
        if (result?.success) {
          setIsAuthorized(true)
          setSnackbar?.({
            show: true,
            msg: __('Connected with Cart Abandonment Recovery Successfully', 'bit-integrations')
          })
        }
        setIsLoading?.(false)
        setShowAuthMsg(true)
      })
      .catch(() => setIsLoading?.(false))
References
  1. When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented. Authorization requests now reset loading and show the failure state if the request rejects.

}).catch(() => {
  setIsLoading?.(false)
  setShowAuthMsg(true)
})

Comment on lines +1 to +11
import { useState } from 'react'
import { useNavigate, useParams } from 'react-router'
import { useRecoilState, useRecoilValue } from 'recoil'
import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates'
import { __ } from '../../../Utils/i18nwrap'
import SnackMsg from '../../Utilities/SnackMsg'
import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers'
import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree'
import SetEditIntegComponents from '../IntegrationHelpers/SetEditIntegComponents'
import { checkMappedFields, handleInput } from './CartAbandonmentRecoveryCommonFunc'
import CartAbandonmentRecoveryIntegLayout from './CartAbandonmentRecoveryIntegLayout'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When editing an integration, ensure that non-persisted configuration fields (like static fields or fetched options) are rebuilt on mount from the saved action without modifying the saved field mappings, to prevent empty options and component crashes. We need to import useEffect and refreshCartAbandonmentRecoveryCarts to fetch the abandoned carts on mount.

Suggested change
import { useState } from 'react'
import { useNavigate, useParams } from 'react-router'
import { useRecoilState, useRecoilValue } from 'recoil'
import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates'
import { __ } from '../../../Utils/i18nwrap'
import SnackMsg from '../../Utilities/SnackMsg'
import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers'
import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree'
import SetEditIntegComponents from '../IntegrationHelpers/SetEditIntegComponents'
import { checkMappedFields, handleInput } from './CartAbandonmentRecoveryCommonFunc'
import CartAbandonmentRecoveryIntegLayout from './CartAbandonmentRecoveryIntegLayout'
import { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router'
import { useRecoilState, useRecoilValue } from 'recoil'
import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates'
import { __ } from '../../../Utils/i18nwrap'
import SnackMsg from '../../Utilities/SnackMsg'
import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers'
import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree'
import SetEditIntegComponents from '../IntegrationHelpers/SetEditIntegComponents'
import { checkMappedFields, handleInput, refreshCartAbandonmentRecoveryCarts } from './CartAbandonmentRecoveryCommonFunc'
import CartAbandonmentRecoveryIntegLayout from './CartAbandonmentRecoveryIntegLayout'
References
  1. When editing an integration, ensure that non-persisted configuration fields (like static fields) are rebuilt on mount from the saved action without modifying the saved field mappings, to prevent empty options and component crashes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented. Added useEffect and refreshCartAbandonmentRecoveryCarts so the edit screen can rebuild fetched cart options on mount.

import { useEffect, useState } from 'react'
import {
  checkMappedFields,
  handleInput,
  refreshCartAbandonmentRecoveryCarts
} from './CartAbandonmentRecoveryCommonFunc'

Comment on lines +13 to +20
export default function EditCartAbandonmentRecovery({ allIntegURL }) {
const navigate = useNavigate()
const { id } = useParams()
const [cartAbandonmentRecoveryConf, setCartAbandonmentRecoveryConf] = useRecoilState($actionConf)
const [flow, setFlow] = useRecoilState($newFlow)
const formFields = useRecoilValue($formFields)
const [isLoading, setIsLoading] = useState(false)
const [snack, setSnackbar] = useState({ show: false })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Add a useEffect hook to fetch the abandoned carts on mount when editing an existing integration so that the dropdown is populated correctly.

export default function EditCartAbandonmentRecovery({ allIntegURL }) {
  const navigate = useNavigate()
  const { id } = useParams()
  const [cartAbandonmentRecoveryConf, setCartAbandonmentRecoveryConf] = useRecoilState($actionConf)
  const [flow, setFlow] = useRecoilState($newFlow)
  const formFields = useRecoilValue($formFields)
  const [isLoading, setIsLoading] = useState(false)
  const [snack, setSnackbar] = useState({ show: false })

  useEffect(() => {
    if (cartAbandonmentRecoveryConf?.mainAction) {
      refreshCartAbandonmentRecoveryCarts(setCartAbandonmentRecoveryConf, setIsLoading)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])
References
  1. When editing an integration, ensure that non-persisted configuration fields (like static fields) are rebuilt on mount from the saved action without modifying the saved field mappings, to prevent empty options and component crashes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented. The edit screen now refreshes abandoned carts on mount when the saved config has a main action.

useEffect(() => {
  if (cartAbandonmentRecoveryConf?.mainAction) {
    refreshCartAbandonmentRecoveryCarts(setCartAbandonmentRecoveryConf, setIsLoading)
  }
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

Comment on lines +33 to +36
$defaultResponse = [
'success' => false,
'message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is a small grammatical typo in the error message: 'activate' should be 'activated'.

        $defaultResponse = [
            'success' => false,
            'message' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro')
        ];

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented. Fixed the message typo from activate to activated.

'message' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro')

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🔍 WordPress Plugin Check Report

⚠️ Status: Passed with warnings

📊 Report

🎯 Total Issues ❌ Errors ⚠️ Warnings
1 0 1

⚠️ Warnings (1)

📁 readme.txt (1 warning)
📍 Line 🔖 Check 💬 Message
0 mismatched_plugin_name Plugin name "Bit integrations - Form Integration, Webhook, Spreadsheets, CRM, LMS & Email Automation" is different from the name declared in plugin header "Bit Integrations".

🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

Copilot AI review requested due to automatic review settings July 7, 2026 05:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants