Review Invite

Note on shared code sample:
To prevent the code examples from getting long, we will replace code from the previous sections with 3 dots thus "..." and show only code from the current section.
You will however be able to view the Complete code here

Complete code (baselayer.js)

Click to view
const EVENTS = window.eventsLib.EVENTS
const dispatchAction = window.eventsLib.dispatchAction
const registerEvents = window.eventsLib.registerEvents

const sendingNotification = (event, message, status) => {
    dispatchAction({
        action: EVENTS.NOTIFICATION,
        payload: {
            event,
            message,
            status,
            type: 'save'
        },
    })
}

let credentials = { clientId: '', clientSecret: '' }

const storeDetails = [
    {
        id: 'shop-7e52920a-2722-4881-9908-ecec98c716e4',
        name: 'Shop 1',
        url: 'shop1.example.com',
        locale: 'de-DE'
    },
    {
        id: 'shop-1e570f63-10f8-4d5a-ae18-21d3d933eb93',
        name: 'Trustsurance',
        url: 'shop2.example.com',
        locale: 'fr-FR'
    },
]

const widgetLocations = [
    {
        id: 'wdg-loc-pn',
        name: 'Product Title',
    },
    {
        id: 'wdg-loc-ft',
        name: 'Footer',
    },

    {
        id: 'wdg-loc-hp',
        name: 'Home Page',
    },
]

const productIdentifiers = [
    { id: 'data-sku', name: 'SKU' },
    { id: 'data-gtin', name: 'GTIN' },
    { id: 'data-mpn', name: 'MPN' },
]

const availableOrderStatuses = [ // touchpoints
    { name: 'Awaiting Payment', ID: '1' },
    { name: 'Payment accepted', ID: '2' },
    { name: 'Processing in progress', ID: '3' },
    { name: 'Shipped', ID: '4' },
    { name: 'Delivered', ID: '5' },
    { name: 'Canceled', ID: '6' },
    { name: 'Refunded', ID: '7' },
]

const baseLayer = () => {
    registerEvents({
        [EVENTS.GET_INFORMATION_OF_SYSTEM]: () => {
            console.log('GET_INFORMATION_OF_SYSTEM')
            dispatchAction({
                action: EVENTS.SET_INFORMATION_OF_SYSTEM,
                payload: {
                    nameOfSystem: 'eCommercPlatformName',
                    versionNumberOfSystem: 'v-0.0.1',
                    versionNumberOfPlugin: 'v-0.0.1',
                    allowsEstimatedDeliveryDate: true,
                    allowsEventsByOrderStatus: true,
                },
            })
        },
        [EVENTS.GET_LOCALE]: () => {
            dispatchAction({
                action: EVENTS.SET_LOCALE,
                payload: 'en-GB', // de-DE , en-EN, es-ES, fr-FR , it-IT , nl-NL , pl-PL , pt-PT
            })
        },
        [EVENTS.ERROR]: () => {
            console.log('eventError', error)
        },

        [EVENTS.SAVE_CREDENTIALS]: (event) => {
            try {
                console.log('SAVE_CREDENTIALS')
                sessionStorage.setItem('credentials', JSON.stringify(event.payload))
                setTimeout(() => {
                    sendingNotification(EVENTS.SAVE_CREDENTIALS, 'CREDENTIALS SAVED', 'success')
                }, 400)
            } catch (error) {
                setTimeout(() => {
                    sendingNotification(EVENTS.SAVE_CREDENTIALS, 'CREDENTIALS NOT SAVED', 'error')
                }, 400)
            }
        },
        [EVENTS.GET_CREDENTIALS_PROVIDED]: () => {
            console.log('GET_CREDENTIALS_PROVIDED')
            const savedCredentials = sessionStorage.getItem('credentials')
            console.log(savedCredentials, "credentials");
            setTimeout(() => {
                dispatchAction({
                    action: EVENTS.SET_CREDENTIALS_PROVIDED,
                    payload: savedCredentials ? JSON.parse(savedCredentials) : credentials,
                })
            }, 400)
        },

        [EVENTS.GET_SALES_CHANNELS_PROVIDED]: () => {
            console.log('EVENTS.SET_SALES_CHANNELS_PROVIDED');
            setTimeout(() => {
                dispatchAction({
                    action: EVENTS.SET_SALES_CHANNELS_PROVIDED,
                    payload: storeDetails,
                })
            }, 1000)

        },

        [EVENTS.GET_MAPPED_CHANNELS]: () => {
            console.log('GET_MAPPED_CHANNELS')
            const savedMappedChannels = sessionStorage.getItem('mappedChannelsData')

            setTimeout(() => {
                dispatchAction({
                    action: EVENTS.SET_MAPPED_CHANNELS,
                    payload: savedMappedChannels ? JSON.parse(savedMappedChannels) : []
                })
            }, 400)
        },

        [EVENTS.SAVE_MAPPED_CHANNEL]: (event) => {
            console.log('SAVE_MAPPED_CHANNEL', event.payload)
            sessionStorage.setItem('mappedChannelsData', JSON.stringify(event.payload))
            try {
                setTimeout(() => {
                    dispatchAction({
                        action: EVENTS.SET_MAPPED_CHANNELS,
                        payload: event.payload,
                    })
                    sendingNotification(EVENTS.SET_MAPPED_CHANNELS, 'MAPPED CHANNELS SAVED', 'success')
                }, 2000)
            } catch (error) {
                setTimeout(() => {
                    sendingNotification(EVENTS.SET_MAPPED_CHANNELS, 'MAPPED CHANNELS NOT SAVED', 'error')
                }, 400)
            }
        },
        [EVENTS.GET_TRUSTBADGE_CONFIGURATION_PROVIDED]: (event) => {
            console.log('GET_TRUSTBADGE_CONFIGURATION_PROVIDED', event.payload)
            const shopId = event.payload.salesChannelRef
            const TBData = sessionStorage.getItem('trustBadge-' + shopId)
            setTimeout(() => {
                dispatchAction({
                    action: EVENTS.SET_TRUSTBADGE_CONFIGURATION_PROVIDED,
                    payload: (TBData) ? JSON.parse(TBData) : [],
                })
            }, 400)
        },
        [EVENTS.SAVE_TRUSTBADGE_CONFIGURATION]: (event) => {
            console.log('SAVE_TRUSTBADGE_CONFIGURATION', event.payload)
            const shopId = event.payload.salesChannelRef
            sessionStorage.setItem('trustBadge-' + shopId, JSON.stringify(event.payload))
            try {
                setTimeout(() => {
                    dispatchAction({
                        action: EVENTS.SET_TRUSTBADGE_CONFIGURATION_PROVIDED,
                        payload: event.payload,
                    })
                    sendingNotification(
                        EVENTS.SAVE_TRUSTBADGE_CONFIGURATION,
                        'TRUSTBADGE CONFIGURATION SAVED',
                        'success'
                    )
                }, 3000)
            } catch (error) {
                setTimeout(() => {
                    sendingNotification(
                        EVENTS.SAVE_TRUSTBADGE_CONFIGURATION,
                        'TRUSTBADGE CONFIGURATION NOT SAVED',
                        'error'
                    )
                }, 400)
            }
        },
        [EVENTS.GET_LOCATION_FOR_WIDGET]: () => {
            console.log('GET_LOCATION_FOR_WIDGET')
            dispatchAction({
                action: EVENTS.SET_LOCATION_FOR_WIDGET,
                payload: widgetLocations,
            })
        },
        [EVENTS.GET_AVAILABLE_PRODUCT_IDENTIFIERS]: (event) => {
            console.log('GET_AVAILABLE_PRODUCT_IDENTIFIERS', event.payload.salesChannelRef)
            dispatchAction({
                action: EVENTS.SET_AVAILABLE_PRODUCT_IDENTIFIERS,
                payload: productIdentifiers,
            })
        },
        [EVENTS.GET_WIDGET_PROVIDED]: (event) => {
            console.log('GET_WIDGET_PROVIDED')
            const shopId = event.payload.salesChannelRef
            const widgetsData = sessionStorage.getItem('widgets-' + shopId)
            setTimeout(() => {
                dispatchAction({
                    action: EVENTS.SET_WIDGET_PROVIDED,
                    payload: (widgetsData) ? JSON.parse(widgetsData) : { children: [] },
                })
            }, 3000)
        },
        [EVENTS.SAVE_WIDGET_CHANGES]: (event) => {
            try {
                console.log('SAVE_WIDGET_CHANGES')
                const shopId = event.payload.salesChannelRef
                sessionStorage.setItem('widgets-' + shopId, JSON.stringify(event.payload))
                setTimeout(() => {
                    dispatchAction({
                        action: EVENTS.SET_WIDGET_PROVIDED,
                        payload: event.payload,
                    })
                    sendingNotification(EVENTS.SAVE_WIDGET_CHANGES, 'WIDGET SAVED', 'success')
                }, 3000)
            } catch (error) {
                setTimeout(() => {
                    sendingNotification(EVENTS.SAVE_WIDGET_CHANGES, 'WIDGET NOT SAVED', 'error')
                }, 400)
            }
        },
        [EVENTS.GET_AVAILABLE_ORDER_STATUSES]: () => {
            console.log('GET_AVAILABLE_ORDER_STATUSE')
            dispatchAction({
                action: EVENTS.SET_AVAILABLE_ORDER_STATUSES,
                payload: availableOrderStatuses,
            })
        },
        [EVENTS.GET_USED_ORDER_STATUSES]: (event) => {
            console.log('GET_USED_ORDER_STATUSES')
            const shopId = event.payload.salesChannelRef
            const storedPayload = sessionStorage.getItem('getUsedOrderStatuses-' + shopId)
            setTimeout(() => {
                dispatchAction({
                    action: EVENTS.SET_USED_ORDER_STATUSES,
                    payload: (storedPayload) ? JSON.parse(storedPayload) : {},
                })
            }, 400)
        },
        [EVENTS.SAVE_USED_ORDER_STATUSES]: (event) => {
            try {
                console.log('SAVE_USED_ORDER_STATUSES', event.payload)
                const shopId = event.payload.salesChannelRef
                sessionStorage.setItem('getUsedOrderStatusesw-' + shopId, JSON.stringify(event.payload))
                setTimeout(() => {
                    dispatchAction({
                        action: EVENTS.SET_USED_ORDER_STATUSES,
                        payload: event.payload,
                    })
                    sendingNotification(
                        EVENTS.SAVE_USED_ORDER_STATUSES,
                        'USE ORDER STATUSES SAVED',
                        'success',
                        'save',
                    )
                }, 400)
            } catch (error) {
                setTimeout(() => {
                    sendingNotification(
                        EVENTS.SAVE_USED_ORDER_STATUSES,
                        'USE ORDER STATUSES SAVEDs',
                        'error',
                        'save',
                    )
                }, 400)
            }

        },
        [EVENTS.EXPORT_PREVIOUS_ORDER]: (event) => {
            console.log('EXPORT_PREVIOUS_ORDER', event.payload)
            setTimeout(() => {
                const link = document.createElement('a')
                link.download = `./invite_list.csv`
                const blob = new Blob(
                    ['email; reference; productName; productSku; productUrl; productImageUrl',
                     '\n',
                      '[email protected]; 101912; demo product; 234; shop.com/productName; shop.com/image/productName'],
                       { type: 'data:text/csv;charset=utf-8,' })
                link.href = URL.createObjectURL(blob)
                link.click()
                URL.revokeObjectURL(link.href)
                dispatchAction({
                    action: EVENTS.SET_EXPORT_PREVIOUS_ORDER,
                    payload: event.payload,
                })
            }, 400)
        },
    })
}

baseLayer()

What is an invite?

In this section, we take a look at the Review Invites tab.

In other, for a shop owner to collect reviews from their customers, a review invite must first be sent to the customer.
In this section, we will look at how to set up the dashboard to enable shop owners to configure their invite settings from within their e-commerce platform.

Enabling different parts of the review tab

The Review tab is divided into different sections as shown in the screenshot above.

To enable them we will need to expand the [GET_INFORMATION_OF_SYSTEM] which we created in initial baseLayer code

[EVENTS.GET_INFORMATION_OF_SYSTEM]: () => {
    console.log('GET_INFORMATION_OF_SYSTEM')
    dispatchAction({
        action: EVENTS.SET_INFORMATION_OF_SYSTEM,
        payload: {
            nameOfSystem: 'eCommercPlatformName',
            versionNumberOfSystem: 'v-0.0.1',
            versionNumberOfPlugin: 'v-0.0.1',
            allowsEstimatedDeliveryDate: true,
            allowsEventsByOrderStatus: true,
        },
    })
},

We have added two new attributes to our payload:

  • allowsEventsByOrderStatus: This will cause the section labeled 1 to show.

  • allowsEstimatedDeliveryDate: This needs to be set for all older versions (v1) of the plugin.

Product review collection

Shop owners who already have product review collection as an extra feature can collect customer feedback for a particular product right on their shopfront.

In step 4 of the integration we looked at the TrustBadge.

Collecting product reviews is a part of the Trustbadge functionality.

To activate this you will have to add specific HTML elements bearing the details of the products you will like to collect reviews for. There is a help centre article that better describes the structure of the HTML elements to embed.

Invite timing

By default, Review Invites are sent out using an optimal timing based on Trusted Shops’ data. You can however change this in the Control Centre.

Shop owners can select a point (touchpoints) in a customer's journey to send out invites.

They can set this from the section labeled 1 in the diagram above

Shop owners have the option to collect two types of reviews, namely:

  • Service reviews: These reviews cover the overall experience, thus, the shopping experience. Learn more
  • Product reviews: These are reviews pertaining to the product the customer purchased. Learn more


Lets now look at how to set this up in code:

...
const availableOrderStatuses = [ // touchpoints
    { name: 'Awaiting Payment', ID: '1' },
    { name: 'Payment accepted', ID: '2' },
    { name: 'Processing in progress', ID: '3' },
    { name: 'Shipped', ID: '4' },
    { name: 'Delivered', ID: '5' },
    { name: 'Canceled', ID: '6' },
    { name: 'Refunded', ID: '7' },
]

[EVENTS.GET_AVAILABLE_ORDER_STATUSES]: () => {
    console.log('GET_AVAILABLE_ORDER_STATUSE', availableOrderStatuses)
    dispatchAction({
        action: EVENTS.SET_AVAILABLE_ORDER_STATUSES,
        payload: availableOrderStatuses,
    })
},
[EVENTS.GET_USED_ORDER_STATUSES]: (event) => {
    console.log('GET_USED_ORDER_STATUSES')
    const shopId = event.payload.salesChannelRef
    const storedPayload = sessionStorage.getItem('getUsedOrderStatuses-' + shopId)
    setTimeout(() => {
        dispatchAction({
            action: EVENTS.SET_USED_ORDER_STATUSES,
            payload: (storedPayload) ? JSON.parse(storedPayload) : {},
        })
    }, 400)
},
[EVENTS.SAVE_USED_ORDER_STATUSES]: (event) => {
    try {
        console.log('SAVE_USED_ORDER_STATUSES', event.payload)
        const shopId = event.payload.salesChannelRef
        sessionStorage.setItem('getUsedOrderStatusesw-' + shopId, JSON.stringify(event.payload))
        setTimeout(() => {
            dispatchAction({
                action: EVENTS.SET_USED_ORDER_STATUSES,
                payload: event.payload,
            })
            sendingNotification(
                EVENTS.SAVE_USED_ORDER_STATUSES,
                'USE ORDER STATUSES SAVED',
                'success',
                'save',
            )
        }, 400)
    } catch (error) {
        setTimeout(() => {
            sendingNotification(
                EVENTS.SAVE_USED_ORDER_STATUSES,
                'USE ORDER STATUSES SAVEDs',
                'error',
                'save',
            )
        }, 400)
    }

},
...

Displaying touchpoints

The code snippet above begins by listing out several touchpoints stored in the variable availableOrderStatuses. Ideally this should come from your database and should be touchpoints implemented within the shop front.

To display the list of available touchpoints (As shown in the section labeled 2 in the image above) you will need to use the GET_AVAILABLE_ORDER_STATUSES event handler.

We then provide it with the list from the availableOrderStatuses variable to populate the dropdowns.

Persisting selected touchpoints to the database

When the user selects touchpoints,(dropdown labeled 2 in the image above), their choices need to be persisted as well as sent over to the eTrusted system.

Once the user selects their option and hits save the
SAVE_USED_ORDER_STATUSES event handler is triggered.
The handler provides a variable event.payload which contains the user's options.

We then need to persist this to the database as well as fire a dispatchAction with SET_USED_ORDER_STATUSES as the action.

See the code snippet above for a complete implementation.

Fetching the persisted touchpoint options from the database

Whenever the Review invites tab is loaded, the previously selected touchpoints will have to be shown as defaults under the Review invites tab.

That can be done using the GET_USED_ORDER_STATUSES event handler. Within the handler, the persisted touchpoints are fetched from the database and fed to dispatchAction with SET_USED_ORDER_STATUSES as the action.

See the code snippet above for a complete implementation.

Creating and exporting an invite list

Shop owners can prepare an invite list using their order data from their e-commerce platform (See the second section in the image above).

From the dashboard, the owner can select how far back they would like to go in terms of orders placed in X number of days. From there they hit the "export" button to export the list in CSV format.

They will then import this list to the eTrusted Control Centre to send out invites.

To handle this in code, you will use the [EXPORT_PREVIOUS_ORDER] event handler which is fired when the shop owner hits the "export" button.

The code below prepares a sample CSV data to be exported, you can modify this example and fill it out with real order data from the DB.

...
[EVENTS.EXPORT_PREVIOUS_ORDER]: (event) => {
    console.log('EXPORT_PREVIOUS_ORDER', event.payload)
    setTimeout(() => {
        const link = document.createElement('a')
        link.download = `./invite_list.csv`
        const blob = new Blob(
            ['email; reference; productName; productSku; productUrl; productImageUrl',
                '\n',
                '[email protected]; 101912; demo product; 234; shop.com/productName; shop.com/image/productName'],
                { type: 'data:text/csv;charset=utf-8,' })
        link.href = URL.createObjectURL(blob)
        link.click()
        URL.revokeObjectURL(link.href)
        dispatchAction({
            action: EVENTS.SET_EXPORT_PREVIOUS_ORDER,
            payload: event.payload,
        })
    }, 400)
},
...

The event.payload.numberOfDays attribute contains how far back in days to export.
When the Include product data (labeled 2 in the image above).
When the option is toggled on, event.payload.includeProductData will be set to true, You can then include extra product data elements.

Read the CSV structure help article to learn about the different data elements you can have.

Demo app reference

Below are the reference points if you are following along using the demo app

What's next?

We are almost through with the integration, in the next section, we will look at how shop owners can reset and reconfigure their setup if they need to.