Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ const ListMenuContentWithData = withCommerceSdkReact(
}
)

const onClient = typeof window !== 'undefined'

const App = (props) => {
const {children} = props
const {data: categoriesTree} = useCategory({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import {useEffect, useState} from 'react'
import React, {useEffect, useState} from 'react'
import useScript from '@salesforce/retail-react-app/app/hooks/use-script'
import {useUsid} from '@salesforce/commerce-sdk-react'
import PropTypes from 'prop-types'

const onClient = typeof window !== 'undefined'

Expand Down Expand Up @@ -64,45 +65,43 @@ function useMiaw(
}

function isEnabled(enabled) {
return enabled === 'true'
return enabled === 'true' && onClient
}

/**
* ShopperAgent component that initializes and manages the embedded messaging service
* @param {Object} props - Component props
* @param {string} props.commerceAgent - JSON stringified commerce agent settings
* @param {string} props.domainUrl - The domain URL for the embedded messaging script
* @param {string} props.basketId - The basket ID for the embedded messaging script
* @param {string} props.locale - The locale for the embedded messaging script
* @param {function} props.onAgentConversationOpened - The callback function for when the conversation is opened
* @param {function} props.onAgentConversationClosed - The callback function for when the conversation is closed
* @returns {JSX.Element} The ShopperAgent component
*/
const ShopperAgent = ({
commerceAgent,
domainUrl,
basketId,
locale,
onAgentConversationOpened,
onAgentConversationClosed
}) => {
const { enabled, embeddedServiceName, embeddedServiceEndpoint, scriptSourceUrl, scrt2Url, salesforceOrgId, siteId } = JSON.parse(commerceAgent)
if (!onClient || !isEnabled(enabled)) {
return null
function FeatureToggle({...props}) {
if (props.isEnabled) {
return props.children
}

const {usid} = useUsid();
return null
}

FeatureToggle.propTypes = {
isEnabled: PropTypes.bool,
children: PropTypes.node
}

function ShopperAgentWindow({commerceAgent, locale, domainUrl, basketId}) {
const {
embeddedServiceName,
embeddedServiceEndpoint,
scriptSourceUrl,
scrt2Url,
salesforceOrgId,
siteId
} = JSON.parse(commerceAgent)

const {usid} = useUsid()

useEffect(() => {
window.addEventListener('onEmbeddedMessagingReady', (e) => {
window.addEventListener('onEmbeddedMessagingReady', () => {
window.embeddedservice_bootstrap.prechatAPI.setHiddenPrechatFields({
DomainURL: domainUrl,
SiteId: siteId,
BasketId: basketId,
Locale: locale,
OrganizationId: salesforceOrgId,
UsId: usid

})
})

Expand All @@ -117,8 +116,6 @@ const ShopperAgent = ({
window.addEventListener('onEmbeddedMessagingConversationEnded', (e) => {
console.log('Conversation ended', e)
})


}, [commerceAgent])

// Load the embedded messaging script
Expand All @@ -133,9 +130,46 @@ const ShopperAgent = ({
scrt2Url
)

// The component doesn't render anything visible
// It's just a wrapper for the embedded messaging service
return null
}

ShopperAgentWindow.propTypes = {
commerceAgent: PropTypes.string,
domainUrl: PropTypes.string,
basketId: PropTypes.string,
locale: PropTypes.string
}

/**
* ShopperAgent component that initializes and manages the embedded messaging service
* @param {Object} props - Component props
* @param {string} props.commerceAgent - JSON stringified commerce agent settings
* @param {string} props.domainUrl - The domain URL for the embedded messaging script
* @param {string} props.basketId - The basket ID for the embedded messaging script
* @param {string} props.locale - The locale for the embedded messaging script
* @returns {JSX.Element} The ShopperAgent component
*/
function ShopperAgent({commerceAgent, domainUrl, basketId, locale}) {
const {enabled} = JSON.parse(commerceAgent)
const isShopperAgentEnabled = isEnabled(enabled)

return (
<FeatureToggle isEnabled={isShopperAgentEnabled}>
<ShopperAgentWindow
commerceAgent={commerceAgent}
locale={locale}
domainUrl={domainUrl}
basketId={basketId}
/>
</FeatureToggle>
)
}

ShopperAgent.propTypes = {
commerceAgent: PropTypes.string,
domainUrl: PropTypes.string,
basketId: PropTypes.string,
locale: PropTypes.string
}

export default ShopperAgent
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import React from 'react'
import {render, screen, act} from '@testing-library/react'
import {render, act} from '@testing-library/react'
import ShopperAgent from '@salesforce/retail-react-app/app/components/shopper-agent/index'
import useScript from '@salesforce/retail-react-app/app/hooks/use-script'
// Mock the embeddedservice_bootstrap object
Expand All @@ -23,21 +23,21 @@ jest.mock('@salesforce/commerce-sdk-react', () => {
const originalModule = jest.requireActual('@salesforce/commerce-sdk-react')
return {
...originalModule,
useUsid: () => ({ usid: 'test-usid' })
useUsid: () => ({usid: 'test-usid'})
}
})

const commerceAgentSettings = {
enabled: "true",
embeddedServiceName: "MIAW_Guided_Shopper_production",
embeddedServiceEndpoint: "https://myorg.salesforce.com/ESWMIAWGuidedShopper",
scriptSourceUrl: "https://myorg.salesforce.com/ESWMIAWGuidedShopper/assets/js/bootstrap.min.js",
scrt2Url: "https://myorg.salesforce.com-scrt.com",
salesforceOrgId: "00DSB00000MJ7YH",
siteId: "RefArchGlobal",
enabled: 'true',
embeddedServiceName: 'MIAW_Guided_Shopper_production',
embeddedServiceEndpoint: 'https://myorg.salesforce.com/ESWMIAWGuidedShopper',
scriptSourceUrl: 'https://myorg.salesforce.com/ESWMIAWGuidedShopper/assets/js/bootstrap.min.js',
scrt2Url: 'https://myorg.salesforce.com-scrt.com',
salesforceOrgId: '00DSB00000MJ7YH',
siteId: 'RefArchGlobal'
}

const commerceAgentSettingsString = JSON.stringify(commerceAgentSettings);
const commerceAgentSettingsString = JSON.stringify(commerceAgentSettings)

describe('ShopperAgent Component', () => {
const defaultProps = {
Expand Down Expand Up @@ -147,27 +147,25 @@ describe('ShopperAgent Component', () => {
render(<ShopperAgent {...defaultProps} />)

await act(async () => {
window.dispatchEvent(new Event('onEmbeddedMessagingReady'));
});
window.dispatchEvent(new Event('onEmbeddedMessagingReady'))
})

// Verify embedded service initialization
expect(mockEmbeddedService.prechatAPI.setHiddenPrechatFields).toHaveBeenCalledWith(
{
BasketId: undefined,
DomainURL: defaultProps.domainUrl,
Locale: defaultProps.locale,
OrganizationId: commerceAgentSettings.salesforceOrgId,
SiteId: commerceAgentSettings.siteId,
UsId: 'test-usid',
}
)
expect(mockEmbeddedService.prechatAPI.setHiddenPrechatFields).toHaveBeenCalledWith({
BasketId: undefined,
DomainURL: defaultProps.domainUrl,
Locale: defaultProps.locale,
OrganizationId: commerceAgentSettings.salesforceOrgId,
SiteId: commerceAgentSettings.siteId,
UsId: 'test-usid'
})
})

test('should not load the script when the commerceAgent is disabled', () => {
const commerceAgentSettings = {...defaultProps.commerceAgent, enabled: 'false'};
const props = {...defaultProps, commerceAgent: JSON.stringify(commerceAgentSettings)};
const commerceAgentSettings = {...defaultProps.commerceAgent, enabled: 'false'}
const props = {...defaultProps, commerceAgent: JSON.stringify(commerceAgentSettings)}

const {container} = render(<ShopperAgent {...props} />)
render(<ShopperAgent {...props} />)

// Component should not render anything when there's an error
expect(useScript).not.toHaveBeenCalled()
Expand Down
5 changes: 5 additions & 0 deletions packages/template-retail-react-app/app/hooks/use-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import {useEffect, useState} from 'react'
import PropTypes from 'prop-types'

/**
* Custom hook to handle script loading
Expand Down Expand Up @@ -46,4 +47,8 @@ const useScript = (src) => {
return scriptLoadStatus
}

useScript.propTypes = {
src: PropTypes.string
}

export default useScript
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
import React from 'react'
import {render, act} from '@testing-library/react'
import useScript from '@salesforce/retail-react-app/app/hooks/use-script'

import PropTypes from 'prop-types'
// Test component that uses the hook
const TestComponent = ({src}) => {
const scriptLoadStatus = useScript(src)

return <div data-testid="script-status">{JSON.stringify(scriptLoadStatus)}</div>
}

TestComponent.propTypes = {
src: PropTypes.string
}

describe('useScript hook', () => {
beforeEach(() => {
// Clear any existing scripts
Expand Down
21 changes: 12 additions & 9 deletions packages/template-retail-react-app/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
const sites = require('./sites.js')

const defaultCommerceAgentSettings = {
enabled: "false",
embeddedServiceName: "MIAW_Guided_Shopper_production_functional38",
embeddedServiceEndpoint: "https://orgfarm-7455a909de.test1.my.pc-rnd.site.com/ESWMIAWGuidedShopperpr1743525851212",
scriptSourceUrl: "https://orgfarm-7455a909de.test1.my.pc-rnd.site.com/ESWMIAWGuidedShopperpr1743525851212/assets/js/bootstrap.min.js",
scrt2Url: "https://orgfarm-7455a909de.test1.my.pc-rnd.salesforce-scrt.com",
salesforceOrgId: "00DSB00000MJ7YH",
siteId: "RefArchGlobal",
};
enabled: 'true',
embeddedServiceName: 'MIAW_Guided_Shopper_production_functional38',
embeddedServiceEndpoint:
'https://orgfarm-7455a909de.test1.my.pc-rnd.site.com/ESWMIAWGuidedShopperpr1743525851212',
scriptSourceUrl:
'https://orgfarm-7455a909de.test1.my.pc-rnd.site.com/ESWMIAWGuidedShopperpr1743525851212/assets/js/bootstrap.min.js',
scrt2Url: 'https://orgfarm-7455a909de.test1.my.pc-rnd.salesforce-scrt.com',
salesforceOrgId: '00DSB00000MJ7YH',
siteId: 'RefArchGlobal'
}

module.exports = {
app: {
commerceAgent: process.env.COMMERCE_AGENT_SETTINGS || JSON.stringify(defaultCommerceAgentSettings),
commerceAgent:
process.env.COMMERCE_AGENT_SETTINGS || JSON.stringify(defaultCommerceAgentSettings),
url: {
site: 'path',
locale: 'path',
Expand Down
13 changes: 13 additions & 0 deletions packages/template-retail-react-app/config/mocks/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,21 @@
* To ensure that feature work correctly, we test our code with multi-site config in mind, so we created this mock config.
* A single-site, single-locale config is a special case of multi-site case.
*/
const commerceAgentSettings = {
enabled: 'true',
embeddedServiceName: 'MIAW_Guided_Shopper_production',
embeddedServiceEndpoint: 'https://myorg.salesforce.com/ESWMIAWGuidedShopper',
scriptSourceUrl: 'https://myorg.salesforce.com/ESWMIAWGuidedShopper/assets/js/bootstrap.min.js',
scrt2Url: 'https://myorg.salesforce.com-scrt.com',
salesforceOrgId: '00DSB00000MJ7YH',
siteId: 'RefArchGlobal'
}

const commerceAgentSettingsString = JSON.stringify(commerceAgentSettings)

module.exports = {
app: {
commerceAgent: commerceAgentSettingsString,
url: {
locale: 'path',
site: 'path',
Expand Down
Loading