Create custom Insight Panel result actions
Create custom Insight Panel result actions
When building a custom Insight Panel, you can enhance user workflows by implementing custom result actions. This article covers two distinct and compatible approaches:
-
Use the Quick Action API: With the Salesforce Quick Action API and the Coveo
QuanticResultAction
component, you can create actions like posting results to Chatter or sending emails. -
With the built-in Apex classes included in Coveo for Salesforce, you can create actions such as attaching or detaching search results to and from Salesforce Cases.
Use the Quick Action API
This section shows how to implement custom result actions that integrate with Salesforce’s Quick Action API. It provides examples of posting results to the Chatter feed and sending results as emails.
In either case, you need a custom Lightning Web Component (LWC) that uses the QuanticResultAction
component, which serves as a base for rendering your custom actions in the target result template.
This custom LWC dispatches a custom event when the user interacts with it.
Also, until Salesforce makes the Salesforce Quick Action API available for Lightning Web Components, you need to create an Aura wrapper component that listens to the custom event dispatched by your LWC and invokes the appropriate Quick Action from the Aura Quick Action API.
You can navigate to the Coveo Quantic repository to find all the code samples explained in this section:
Aura wrapper component
-
Create a new Aura component named
ExampleInsightPanelWrapper
. -
Add the following code to the
ExampleInsightPanelWrapper.cmp
:<aura:component implements="force:hasRecordId,force:hasSObjectName,flexipage:availableForRecordHome" access="global">
<lightning:quickActionAPI aura:id="quickActionAPI" />
<article> <c:exampleInsightPanel
oninsightpanel_auraposttofeed="{!c.handleQuickAction}"
oninsightpanel_aurasendasemail="{!c.handleQuickAction}"> </c:exampleInsightPanel> </article> </aura:component>
These interfaces let the component access the record ID, the SObject
name, and make the component available on the Record home page.The lightning:quickActionAPI
component lets your component call the Quick Action API.Wrap your custom Insight Panel component. Listen to the insightpanel_auraposttofeed
andinsightpanel_aurasendasemail
events, which will be emitted by the LWCs. Handle these events in thehandleQuickAction
controller method defined in the Aura component’s JavaScript controller. -
Add the following code to
ExampleInsightPanelWrapperController.js
to handle the custom events dispatched by the embedded Lightning Web Component:({ handleQuickAction: function (component, event, helper) { helper.executeQuickActionFromEvent(component, event);
}, });
This method delegates the event handling logic to the executeQuickActionFromEvent
helper function, keeping the controller clean and maintainable. -
Add the following code to
ExampleInsightPanelWrapperHelper.js
to handle the custom event sent from the LWC and call the Quick Action API:({ /** * Executes a Quick Action using the QuickActionAPI with parameters coming from * the event details. * @param {Aura.Component} component * @param {Event} event Custom Event. */ executeQuickActionFromEvent: function (component, event) { const quickActionsApi = component.find('quickActionAPI');
const targetFields = event.getParam('targetFields');
const actionName = event.getParam('actionName');
const resultPromiseResolve = event.getParam('resultPromiseResolve');
const resultPromiseReject = event.getParam('resultPromiseReject');
if (quickActionsApi && targetFields && actionName) { quickActionsApi .setActionFieldValues({
actionName, targetFields, }) .then((data) => { if (resultPromiseResolve) {
resultPromiseResolve(data); } }) .catch((error) => {
if (resultPromiseReject) { resultPromiseReject(error); } }); } }, });
The lightning:quickActionAPI
component lets your Aura component call the Quick Action API.Extract the name of the Quick Action to call from the event payload sent by the LWC. Extract the fields and values to pass to the Quick Action from the event payload sent by the LWC. Extract the promise resolve function from the event payload sent by the LWC. Extract the promise reject function from the event payload sent by the LWC. The setActionFieldValues
method of the Quick Action API lets you set the parameters of the Quick Action.If the Quick Action API call is successful, the promise resolves with the data returned by the API. If the Quick Action API call fails, the promise rejects with the error information. -
Optionally, configure the CSS styles in
ExampleInsightPanelWrapper.css
for your custom action components..THIS {
border-width: var( --slds-c-card-sizing-border,
var(--sds-c-card-sizing-border, var(--lwc-borderWidthThin, 1px)) ); border-style: solid; border-color: var( --slds-c-card-color-border, var( --sds-c-card-color-border, var( --slds-g-color-border-base-1, var(--lwc-cardColorBorder, rgb(201, 201, 201)) ) ) ); border-radius: var( --slds-c-card-radius-border, var(--sds-c-card-radius-border, var(--lwc-borderRadiusMedium, 0.25rem)) ); }
The .THIS
selector targets the root element of theExampleInsightPanelWrapper
Aura component.Use the Salesforce Lightning Design System (SLDS) CSS custom properties (with fallbacks) for consistent theming and Lightning Experience compatibility.
Post to feed LWC
This section guides you through the implementation of a custom LWC that lets users post a Coveo result to the Salesforce Chatter feed.
-
Create a new LWC named
resultPostToFeed
. -
Add the following code to
resultPostToFeed.html
:<template> <c-quantic-result-action event-name={eventName} result={result} icon-name={iconName} label={label} loading={_isLoading}>
</c-quantic-result-action> </template>
Set the QuanticResultAction
component properties. In particular, the event specified ineventName
is dispatched when the user clicks the action button. You’ll create an event listener for this event in the JavaScript file. -
In
resultPostToFeed.js
, emit the Quick Action custom event when the user clicks the custom action button as in the following code sample, which relies on promises to establish a communication channel between the LWC and the Aura wrapper component.This code sample has been abbreviated to focus on the relevant parts of the implementation. For the full sample, see resultPostToFeed.js.
// ... export default class ResultPostToFeed extends LightningElement { actionName = 'FeedItem.TextPost';
// ... @api textTemplate =
'<a href="${clickUri}">${title}</a><br/><br/><quote>${excerpt}</quote>'; // ... eventName = 'insightpanel__posttofeed';
connectedCallback() { registerComponentForInit(this, this.engineId); this.addEventListener(this.eventName, this.handlePostToFeedClick);
} // ... postResultToFeed() { this._isLoading = true; this._actionHandled = false; let resultPromiseResolve; let resultPromiseReject; const resultPromise = new Promise((resolve, reject) => {
resultPromiseResolve = resolve; resultPromiseReject = reject; }); resultPromise.catch(this.handleResultPromiseFailure).finally(() => {
clearTimeout(this.timeout); this._actionHandled = true; this._isLoading = false; }); this.timeout = setTimeout(() => {
if (!this._actionHandled) { resultPromiseReject({auraWrapperMissing: true}); } }, 2000); const postToFeedEvent = new CustomEvent('insightpanel_auraposttofeed',
{ detail: { targetFields: { Body: { value: buildTemplateTextFromResult(this.textTemplate, this.result), insertType: this.insertType, }, }, actionName: this.actionName, resultPromiseResolve, resultPromiseReject, }, bubbles: true, composed: true, }); this.dispatchEvent(postToFeedEvent); } handlePostToFeedClick = (event) => { event.stopPropagation(); this.engine.dispatch(this.actions.logFeedItemTextPost(this.result));
this.postResultToFeed(); }; handleResultPromiseFailure = (error) => {
const {auraWrapperMissing} = error; const message = auraWrapperMissing ? this.labels.actionIsUnavailable : `[${this.actionName}] ${error?.errors?.[0] ?? this.labels.errorWithQuickAction}`; console.error(message); }; }
Target the FeedItem object’s TextPost
action.The textTemplate
is a template used to generate the text to insert in the body of the chatter post. In this case, it defaults to a template that includes the result’sclickUri
,title
, andexcerpt
, but users can customize it.Define the name of the custom event that the c-quantic-result-action
component dispatches when the user clicks the action button. This is the event that yourResultPostToFeed
LWC listens to in theconnectedCallback
method.Listen to the custom event dispatched by the c-quantic-result-action
component, and call thehandlePostToFeedClick
method when the event is triggered. Mainly,handlePostToFeedClick
will callpostResultToFeed
, defined next.The postResultToFeed
method creates a promise that resolves or rejects based on the success or failure of the Quick Action API call.If there’s an error, call the handleResultPromiseFailure
to handle it. And regardless of success or failure, thefinally
block clears the timeout and sets the_actionHandled
and_isLoading
flags.The timeout is set to reject the promise after 2 seconds if no Aura component handles the event. Dispatch the CustomEvent
with the nameinsightpanel_auraposttofeed
, which the Aura wrapper listens to.The logFeedItemTextPost
action logs the analytics event for posting a result to the feed.The handleResultPromiseFailure
method handles the error from the Quick Action API call and logs an appropriate message. -
Include the
resultPostToFeed
LWC in the target result templates.<!--- main/lwc/exampleInsightPanel/resultTemplates/defaultResultTemplate.html ---> <template> <div class="result-action_container slds-is-relative slds-var-p-horizontal_medium" > <c-quantic-result-template is-any-preview-open={isAnyPreviewOpen} result-preview-should-not-be-accessible={resultPreviewShouldNotBeAccessible} > <!-- ... ---> <div slot="actions"> <template if:true={isHovered}> <c-quantic-result-action-bar> <!-- ... ---> <c-result-post-to-feed result={result} engine-id={engineId}></c-result-post-to-feed>
</c-quantic-result-action-bar> </template> </div> <!-- ... ---> </c-quantic-result-template> </div> </template>
The resultPostToFeed
LWC.
Send as email LWC
Another custom result action you can implement would be to send a Coveo result as an email.
The logic is similar to the Post to feed action.
For the full code sample, see resultSendAsEmail
LWC.
Use Apex classes
This section guides you through the implementation of a custom result action that lets users attach or detach a Coveo result to/from a Salesforce Case.
At a high level, you need to:
-
Create a
resultAttachToCase
LWC that displays an attach/detach button in the result template and handles the user interaction. -
Create an
attachToCaseService
LWC to connect to the Apex classes. -
Initialize attached results in your custom Insight Panel.
-
Leverage the
resultAttachToCase
LWC in your result templates.
|
The code samples explained in this section are available in the Coveo Quantic repository: |
Create a resultAttachToCase
LWC
-
Create a new LWC named
resultAttachToCase
. -
In
resultAttachToCase.html
, copy the code from the resultAttachToCase.html file in the Quantic repository. -
In
resultAttachToCase.js
, copy the code from the resultAttachToCase.js file in the Quantic repository.This code relies on utilities and services that you’ll create in the next steps.
Create an attachToCaseUtils
LWC
-
Create a new LWC named
attachToCaseUtils
. -
In
attachToCaseUtils.js
, copy the code from the attachToCaseUtils.js file in the Quantic repository.
Create an attachToCaseService
LWC to connect to the Apex classes
While you could call the Apex classes directly from the resultAttachToCase
and exampleInsightPanel
LWCs, we recommend creating a dedicated LWC named attachToCaseService
to centralize the communication with the Apex classes.
-
Create a new LWC named
attachToCaseService
. -
Add the following code to
attachToCaseService.js
to wrap theAttachToCaseController
methodsAttachToCase
,DetachFromCase
, andgetAttachedResults
.// @ts-ignore import AttachToCase from '@salesforce/apex/CoveoV2.AttachToCaseController.AuraAttachToCase'; // @ts-ignore import DetachFromCase from '@salesforce/apex/CoveoV2.AttachToCaseController.AuraDetachFromCase'; // @ts-ignore import getAttachedResults from '@salesforce/apex/CoveoV2.AttachToCaseController.getAttachedResults'; export function attachToCase(resultToAttach) { return AttachToCase(resultToAttach); } export function detachFromCase(resultToDetach) { return DetachFromCase(resultToDetach); } export function getAllAttachedResults(caseId) { return getAttachedResults({ caseId, }); }
Initialize attached results
For the resultAttachToCase
LWC to display the correct state of the result (attached or not), you need to initialize it with the current state of the result.
-
Open the
exampleInsightPanel.js
file, where you define the custom Insight Panel. Add the following code (you can find the complete file in the Coveo Quantic repository: exampleInsightPanel.js).import { getHeadlessBundle, registerComponentForInit, initializeWithHeadless, } from 'c/quanticHeadlessLoader'; import {buildAttachedResultsPayloadHeadless} from 'c/attachToCaseUtils'; import {getAllAttachedResults} from 'c/attachToCaseService'; // ... export default class ExampleInsightPanel extends LightningElement { // ... @api caseId;
isInitAttachedResults = false;
loadAttachedResults() { getAllAttachedResults(this.caseId)
.then((data) => { const dataParsed = JSON.parse(data); if ( dataParsed?.succeeded && Array.isArray(dataParsed.attachedResults) ) { this._attachedResults = dataParsed.attachedResults;
this.initAttachedResults(); } else { console.warn(dataParsed?.message); } }) .catch((error) => { console.warn(error?.body?.message); }); } connectedCallback() { // ... this.loadAttachedResults();
registerComponentForInit(this, this.engineId); } renderedCallback() { initializeWithHeadless(this, this.engineId, this.initialize);
} initialize = (engine) => { this.headless = getHeadlessBundle(this.engineId); this.engine = engine; this.insightInterface = this.headless.buildInsightInterface(engine); this.searchStatus = this.headless.buildSearchStatus(engine); this.actions = {
...this.headless.loadCaseContextActions(engine), ...this.headless.loadAttachedResultsActions(engine), }; this.engine.dispatch(this.actions.setCaseId(this.caseId));
this.initAttachedResults(); }; initAttachedResults = () => { if (!this.isInitAttachedResults) { if (this.engine && Array.isArray(this._attachedResults)) { this.isInitAttachedResults = true; if (this.actions.setAttachedResults) { this.engine.dispatch(
this.actions.setAttachedResults({ results: buildAttachedResultsPayloadHeadless( this._attachedResults ), loading: false, }) ); } } } }; // ... }
The case ID property that identifies which Salesforce Case to load attached results for. It’s passed from the parent component, such as ExampleInsightPanelWrapper
.Flag to ensure attached results are only initialized once during the component lifecycle. Call the service to retrieve all results currently attached to this case. Store the attached results data locally for later use in initializing the Headless state. Load attached results when the component connects to the DOM. Initialize the Headless engine when the component renders. Load the necessary Headless action creators for managing case context and attached results. Set the case ID in the Headless engine’s state. Dispatch the attached results to the Headless engine, converting them to the expected format. -
Pass the
caseId
to your Insight Panel. When you use theexampleInsightPanel
LWC, pass thecaseId
property to it. In theExampleInsightPanelWrapper.cmp
, edit thecaseId
property as follows:<c:exampleInsightPanel // ... other properties ... // caseId="{!v.recordId}"> </c:exampleInsightPanel>
Leverage the resultAttachToCase
LWC in your result templates
We recommend including your result-attach-to-case
component as a result action in the quantic-result-action-bar
of your result templates.
<c-quantic-result-action-bar>
<!-- ... --->
<c-result-attach-to-case result={result} engine-id={engineId}></c-result-attach-to-case>
</c-quantic-result-action-bar>
Also, you can include result-attach-to-case
component as a read-only icon indicating whether the result is already attached.
We recommend inserting it near the title and result link.
<c-quantic-result-template>
<div slot="title" class="slds-truncate">
<c-quantic-result-link
result={result}
engine-id={engineId}
></c-quantic-result-link>
<c-result-attach-to-case read-only engine-id={engineId} result={result}></c-result-attach-to-case>
</div>
<!-- ... --->
</c-quantic-result-template>
For a full example, see defaultResultTemplate.html in the Quantic repository.