Review Widgets
Review Widgets
Note "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 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',
allowsEditIntegrationCode: 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)
}
}
})
}
baseLayer()
...
const widgetLocations = [ //line 2
{
id: 'wdg-loc-pn',
name: 'Product Title',
},
{
id: 'wdg-loc-ft',
name: 'Footer',
},
{
id: 'wdg-loc-hp',
name: 'Home Page',
},
]
const productIdentifiers = [ //line 17
{ id: 'data-sku', name: 'SKU' },
{ id: 'data-gtin', name: 'GTIN' },
{ id: 'data-mpn', name: 'MPN' },
]
[EVENTS.GET_LOCATION_FOR_WIDGET]: () => { //line 23
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) => { //line 37
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: [] }, //line 44
})
}, 3000)
},
[EVENTS.SAVE_WIDGET_CHANGES]: (event) => { //line 48
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)
}
}
...
What is a Trusted Shops Widget?
In this section, we look at the Widgets tab.
A Trusted Shops widget is a shopfront embeddable used to display customer reviews.
These widgets might take the form of star ratings or textual reviews or both.
Types of widgets
There are at least 5 different types of review widgets available:
- Trusted Stars
- Review Carousel
- Customer Voice Widget
- Full Review List
- Mini Stars
Please visit the Help Centre to learn about these widgets in detail.
Widgets Dashboard Setup
Widgets are much similar to the TrustBadge in the sense that both have a dashboard and shopfront components.
We start off our integration by looking at the [EVENTS.GET_WIDGET_PROVIDED]
event handler (line 37
) in the code above.
Here the goal is to fetch and display any widget configuration we have stored in our DB. In case there are no widget configurations yet the dashboard will render a default configuration by providing it with the base object { children: [] }
as seen on line 44
.
!!! Note "Note on activating widgets:"
Please note by clicking on the Create new widget
button shop owners are taken to the Control Centre where they can activate the widgets they will like to use.
Shop owners need to click on the Create new widget
button and then follow the instructions to add new widgets. Once they are done creating widgets on the Control Centre, they can return to the plugin dashboard and hit the "Reload list" button to see the list of activated widgets.
There is a Help Centre article that better illustrates the process of activating widgets.
Widget locations (placement)
One of the things a shop owner can set from the dashboard is where a widget should be placed (labeled 1 in the diagram above)
.
You the developer can organize the different locations as an array of objects.
For our example, this is the widgetLocations
variable declared on line 2
.
Each object has a name
which is displayed to the end user in the form of a dropdown. There is also an id
that you the developer can then use to prepare your logic on where a widget should be placed.
When a user hits the Save changes
button this configuration will be persisted for later use (displaying the widget).
The [EVENTS.GET_LOCATION_FOR_WIDGET]
event handler line 23
is the handler responsible for setting the widget's location.
The locations that are assigned to the widgetLocations
variable above are not entirely arbitrary. The IDs are predefined and convey positional information to Connector script.
For example, this ensures the proper language localization of the widgets. In the situation where no pre-defined locations are detected, the Connector script will interpret this as all positions have been implemented.
The next section shows a table with all prefined positions you can use.
Predefined widget positions
Main placement positions
Name | ID | Widget Type | Description |
---|---|---|---|
Homepage | wdg-loc-hp | Service Reviews |
Clearly visible, as a prominent, full-width separator between content blocks. Should be above or below an area where the user has the most interaction (experience).
Should have a minimum width of |
Left/Right Margin | wdg-loc-lrm | Service Reviews |
Used as a subtle separator between content blocks. Should be above or below an area where the user has the most interaction (experience).
Ideal for all pages where a margin (e.g. menu column) exists. |
Product Listings | wdg-loc-pl | Product Reviews & Service Reviews |
Ideal for product category pages (or any page where several products are presented) As an additional label, next to each product's essential information
Template variables for SKU, GTIN, MPN are mandatory. For an example, checkout the widget resulting script. |
Product Page | wdg-loc-pp | Product Reviews & Service reviews |
As a section/tab below your product description or as a column next to the product description with a minimum width of 320px
In addition, a label underneath the products name
Template variables for SKU, GTIN, MPN are mandatory. For an example, checkout the widget resulting script. |
Additional positions
Outside of the main locations described above, you may use these positions as well.
Name | ID | Widget Type | Description |
---|---|---|---|
Footer | wdg-loc-ft | Service Reviews |
Used within a content block inside the footer section of each page. This should be placed within a footer with a minimum width of 320px |
Header | wdg-loc-hd | Service Reviews |
Used within a content block inside the header section of each page. This should be placed within a header with a minimum width of 320px |
Product Description | wdg-loc-pd | Product Reviews & Service Reviews |
As a content block below your product description with a minimum width of 320px Template variables for SKU, GTIN, MPN mandatory |
Product Name | wdg-loc-pn | Product Reviews & Service reviews |
Ideally used as label underneath the products name Template variables for SKU, GTIN, MPN are mandatory. For an example, checkout the widget resulting script. |
Custom or Manual placement | wdg-loc-cst | Product Reviews & Service reviews |
A placeholder to use as flexible container for cutom positions or manual positioning, e.g. with WYSIWYG editors |
Product identifiers
Much like the widget locations, the product identifier can also be selected by the shop owner (labeled 2 in the diagram above)
. There are three main options namely SKU, GTIN, MPN.
These identifiers are information set during the review collection process and is used within the widget to pull reviews for specific products (acting like an ID).
To set and make these options available to the shop owner to select, we set it as a variable productIdentifiers
on line 17
and then set it to the dashboard using the [EVENTS.GET_AVAILABLE_PRODUCT_IDENTIFIERS]
event handler and EVENTS.SET_AVAILABLE_PRODUCT_IDENTIFIERS
dispatch action.
For products/items/articles with variates, for example, the same shirt with different colors or an insurance policy with options the parent Stock Keeping Unit (SKU) should be used.
In situations where any of the aforementioned identifiers (SKU, GTIN, MPN.) is not available, you should use a unique identifier within your system to identify the different products/items/articles. For example, the the ID of the item within the database is a good substitute.
Persisting widget changes
Finally, let's take a look at the handler responsible for persisting the modifications we make to widgets. This is the [EVENTS.SAVE_WIDGET_CHANGES]
event handler line 48
.
Whenever you make changes to the widgets and hit the "Save changes" this event will be fired up. The returned object i.e.: event.payload
will contain the latest configuration which you can then persist as well as dispatch to the [EVENTS.SET_WIDGET_PROVIDED]
action.
Displaying widgets on the shopfront
As mentioned earlier there are two parts to the integration. We will look at the second part which is the widget placements on the shopfront.
The widget data we stored above contains data that describe how to construct the HTML element and scripts we need to render the different widgets on the shopfront.
To help keep this simple, below is an example of a widget object stored in the DB and the resulting widget script:
- Stored widget object:
Click to view
```json { "children": [ { "tag": "script", "attributes": { "src": { "value": "https://integrations.etrusted.com/applications/widget.js/v2", "attributeName": "src" }, "async": { "attributeName": "async" }, "defer": { "attributeName": "defer" } }, "children": [ { "tag": "etrusted-widget", "attributes": { "id": { "value": "wdg-96f51bec-1ebb-xxx-921a-22d3f8382b57", "attributeName": "data-etrusted-widget-id" }, "productIdentifier": { "attributeName": "data-gtin" } }, "extensions": { "product_star": { "tag": "etrusted-product-review-list-widget-product-star-extension" } }, "widgetId": "wdg-96f51bec-1ebb-xxx-921a-22d3f8382b57", "applicationType": "product_review_list", "widgetLocation": { "id": "wdg-loc-pn", "name": "Product Title" } } ] } ], "id": "chl-e7335588-cb62-4c70-8dbe-82b62ee4077d", "eTrustedChannelRef": "chl-e7335588-cb62-4c70-8dbe-82b62ee4077d", "salesChannelRef": "shop-7e52920a-2722-4881-9908-ecec98c716e4" } ```- Resulting script:
<script src="https://integrations.etrusted.com/applications/widget.js/v2" async defer></script>
<div class="wdg-loc-pn">
<etrusted-widget data-etrusted-widget-id="wdg-96f51bec-1ebb-xxx-921a-22d3f8382b57" data-gtin="{{$gtinValueFromDB}}"></etrusted-widget>
<etrusted-product-review-list-widget-product-star-extension></etrusted-product-review-list-widget-product-star-extension>
</div>
The above is an example of a product review widget that is used to present reviews for a specific product. This is why the widget has a data-gtin
attribute. All other widget scripts look similar but without the aforementioned attribute.
The following Help Centre articles provide further explanation on widget placements on the shopfront:
Another good place to see examples is from your eTrusted control Centre (development account) if you have access to it. Thus marketing > widgets.
Disable Review Widgets tab
You may decide to not grant shop owners the ability to configure the review widget themselves, in this case, you can disable the tab entirely.
To do so you will need to set the allowsSupportWidgets
option to false
within the [EVENTS.GET_INFORMATION_OF_SYSTEM]
handler.
[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,
allowsSendReviewInvitesForPreviousOrders: true
allowsEditIntegrationCode: false,
allowsSupportWidgets: true, // Set this option to false to disable the Review Widgets tab
},
})
},
Demo app reference
Below are the reference points if you are following along using the demo app
What's next?
In the next section, we will look at Review invites.
Updated over 1 year ago