--- title: Create custom Insight Panel result actions slug: latest-custom-ip-result-actions canonical_url: https://docs.coveo.com/en/quantic/latest/usage/custom-ip-result-actions/ collection: quantic source_format: adoc --- # Create custom Insight Panel result actions When [building a custom Insight Panel](https://docs.coveo.com/en/quantic/latest/usage/custom-insight-panels/), you can enhance user workflows by implementing custom result actions. This article covers two distinct and compatible approaches: * [Use the Quick Action API](#use-the-quick-action-api): With the Salesforce [Quick Action API](https://developer.salesforce.com/docs/component-library/bundle/lightning:quickActionAPI/documentation) and the Coveo [`QuanticResultAction`](https://docs.coveo.com/en/quantic/latest/usage/create-custom-result-actions/) component, you can create actions like posting results to Chatter or sending emails. * With the built-in [Apex classes](https://docs.coveo.com/en/2986/) 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](#post-to-feed-lwc) and [sending results as emails](#send-as-email-lwc). In either case, you need a custom [Lightning Web Component (LWC)](https://developer.salesforce.com/developer-centers/lightning-web-components) 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](https://developer.salesforce.com/docs/component-library/bundle/lightning:quickActionAPI/documentation) available for Lightning Web Components, create an [Aura wrapper component](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/intro_components.htm) 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: * [`resultPostToFeed` LWC](https://github.com/coveo/ui-kit/tree/main/packages/quantic/force-app/solutionExamples/main/lwc/resultPostToFeed) * [`resultSendAsEmail` LWC](https://github.com/coveo/ui-kit/tree/main/packages/quantic/force-app/solutionExamples/main/lwc/resultSendAsEmail) * [`ExampleInsightPanelWrapper` Aura component](https://github.com/coveo/ui-kit/tree/main/packages/quantic/force-app/solutionExamples/main/aura/ExampleInsightPanelWrapper) ### Aura wrapper component . Create a new Aura component named `ExampleInsightPanelWrapper`. . Add the following code to the `ExampleInsightPanelWrapper.cmp`: ```html <1> <2>
oninsightpanel_auraposttofeed="{!c.handleQuickAction}" <4> oninsightpanel_aurasendasemail="{!c.handleQuickAction}">
``` <1> These [interfaces](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/oo_interfaces.htm) let the component access the record ID, the `SObject` name, and make the component available on the Record home page. <2> The [`lightning:quickActionAPI`](https://developer.salesforce.com/docs/component-library/bundle/lightning:quickActionAPI/documentation) component lets your component call the Quick Action API. <3> Wrap your [custom Insight Panel](https://docs.coveo.com/en/quantic/latest/usage/custom-insight-panels/) component. <4> Listen to the `insightpanel_auraposttofeed` and `insightpanel_aurasendasemail` events, which will be emitted by the LWCs. Handle these events in the `handleQuickAction` 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: ```javascript ({ handleQuickAction: function (component, event, helper) { helper.executeQuickActionFromEvent(component, event); <1> }, }); ``` <1> 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: ```javascript ({ /** * 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'); <1> const targetFields = event.getParam('targetFields'); <2> const actionName = event.getParam('actionName'); <3> const resultPromiseResolve = event.getParam('resultPromiseResolve'); <4> const resultPromiseReject = event.getParam('resultPromiseReject'); <5> if (quickActionsApi && targetFields && actionName) { quickActionsApi .setActionFieldValues({ <6> actionName, targetFields, }) .then((data) => { if (resultPromiseResolve) { <7> resultPromiseResolve(data); } }) .catch((error) => { <8> if (resultPromiseReject) { resultPromiseReject(error); } }); } }, }); ``` <1> The [`lightning:quickActionAPI`](https://developer.salesforce.com/docs/component-library/bundle/lightning:quickActionAPI/documentation) component lets your Aura component call the Quick Action API. <2> Extract the name of the Quick Action to call from the event payload sent by the LWC. <3> Extract the fields and values to pass to the Quick Action from the event payload sent by the LWC. <4> Extract the promise resolve function from the event payload sent by the LWC. <5> Extract the promise reject function from the event payload sent by the LWC. <6> The `setActionFieldValues` method of the Quick Action API lets you set the parameters of the Quick Action. <7> If the Quick Action API call is successful, the promise resolves with the data returned by the API. <8> 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. ```css .THIS { <1> border-width: var( --slds-c-card-sizing-border, <2> 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)) ); } ``` <1> The `.THIS` selector targets the root element of the `ExampleInsightPanelWrapper` Aura component. <2> Use the [Salesforce Lightning Design System](https://www.lightningdesignsystem.com/) (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`: ```html ``` <1> Set the [`QuanticResultAction` component properties](https://docs.coveo.com/en/quantic/latest/reference/insight-panel-components/insight-panel-result-action#properties). In particular, the event specified in `eventName` 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](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/resultPostToFeed/resultPostToFeed.js). ```javascript // ... export default class ResultPostToFeed extends LightningElement { actionName = 'FeedItem.TextPost'; <1> // ... @api textTemplate = <2> '${title}

$[excerpt](https://docs.coveo.com/en/3310/)'; // ... eventName = 'insightpanel__posttofeed'; <3> connectedCallback() { registerComponentForInit(this, this.engineId); this.addEventListener(this.eventName, this.handlePostToFeedClick); <4> } // ... postResultToFeed() { this._isLoading = true; this._actionHandled = false; let resultPromiseResolve; let resultPromiseReject; const resultPromise = new Promise((resolve, reject) => { <5> resultPromiseResolve = resolve; resultPromiseReject = reject; }); resultPromise.catch(this.handleResultPromiseFailure).finally(() => { <6> clearTimeout(this.timeout); this._actionHandled = true; this._isLoading = false; }); this.timeout = setTimeout(() => { <7> if (!this._actionHandled) { resultPromiseReject({auraWrapperMissing: true}); } }, 2000); const postToFeedEvent = new CustomEvent('insightpanel_auraposttofeed', <8> { 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)); <9> this.postResultToFeed(); }; handleResultPromiseFailure = (error) => { <10> const {auraWrapperMissing} = error; const message = auraWrapperMissing ? this.labels.actionIsUnavailable : `[${this.actionName}] ${error?.errors?.[0] ?? this.labels.errorWithQuickAction}`; console.error(message); }; } ``` <1> Target the [FeedItem](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_feeditem.htm) object's `TextPost` action. <2> 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's `clickUri`, `title`, and `excerpt`, but users can customize it. <3> 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 your `ResultPostToFeed` LWC listens to in the `connectedCallback` method. <4> Listen to the custom event dispatched by the `c-quantic-result-action` component, and call the `handlePostToFeedClick` method when the event is triggered. Mainly, `handlePostToFeedClick` will call `postResultToFeed`, defined next. <5> The `postResultToFeed` method creates a promise that resolves or rejects based on the success or failure of the Quick Action API call. <6> If there's an error, call the `handleResultPromiseFailure` to handle it. And regardless of success or failure, the `finally` block clears the timeout and sets the `_actionHandled` and `_isLoading` flags. <7> The timeout is set to reject the promise after 2 seconds if no Aura component handles the event. <8> Dispatch the `CustomEvent` with the name `insightpanel_auraposttofeed`, which the Aura wrapper listens to. <9> The [`logFeedItemTextPost`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Insight.InsightAnalyticsActionCreators.html#logfeeditemtextpost) action logs the analytics event for posting a result to the feed. <10> 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. ```html ``` <1> 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](https://github.com/coveo/ui-kit/tree/main/packages/quantic/force-app/solutionExamples/main/lwc/resultSendAsEmail). ## 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, do the following: * [Create a `resultAttachToCase` LWC](#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](#create-an-attachtocaseservice-lwc-to-connect-to-the-apex-classes). * [Initialize attached results](#initialize-attached-results) in your custom Insight Panel. * [Leverage the `resultAttachToCase` LWC in your result templates](#leverage-the-resultattachtocase-lwc-in-your-result-templates). > **Tip** > > The code samples explained in this section are available in the Coveo Quantic repository: > > * [`resultAttachToCase` LWC](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/resultAttachToCase/) > > * [`attachToCaseService` LWC](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/attachToCaseService/) > > * [`attachToCaseUtils` LWC](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/attachToCaseUtils/) > > * [`exampleInsightPanel` LWC](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/exampleInsightPanel) > > * [`defaultResultTemplate.html`](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/exampleInsightPanel/resultTemplates/defaultResultTemplate.html) ### Create a `resultAttachToCase` LWC . Create a new LWC named `resultAttachToCase`. . In `resultAttachToCase.html`, copy the code from the [resultAttachToCase.html](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/resultAttachToCase/resultAttachToCase.html) file in the Quantic repository. . In `resultAttachToCase.js`, copy the code from the [resultAttachToCase.js](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/resultAttachToCase/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](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/attachToCaseUtils/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 the [`AttachToCaseController`](https://docs.coveo.com/en/p6bd0251/) methods [`AttachToCase`](https://docs.coveo.com/en/p6bd0251#attachtocase), [`DetachFromCase`](https://docs.coveo.com/en/p6bd0251#detachfromcase), and [`getAttachedResults`](https://docs.coveo.com/en/p6bd0251#getattachedresults). ```javascript // @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), 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](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/exampleInsightPanel/exampleInsightPanel.js)). ```javascript 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; <1> isInitAttachedResults = false; <2> loadAttachedResults() { getAllAttachedResults(this.caseId) <3> .then((data) => { const dataParsed = JSON.parse(data); if ( dataParsed?.succeeded && Array.isArray(dataParsed.attachedResults) ) { this._attachedResults = dataParsed.attachedResults; <4> this.initAttachedResults(); } else { console.warn(dataParsed?.message); } }) .catch((error) => { console.warn(error?.body?.message); }); } connectedCallback() { // ... this.loadAttachedResults(); <5> registerComponentForInit(this, this.engineId); } renderedCallback() { initializeWithHeadless(this, this.engineId, this.initialize); <6> } initialize = (engine) => { this.headless = getHeadlessBundle(this.engineId); this.engine = engine; this.insightInterface = this.headless.buildInsightInterface(engine); this.searchStatus = this.headless.buildSearchStatus(engine); this.actions = { <7> ...this.headless.loadCaseContextActions(engine), ...this.headless.loadAttachedResultsActions(engine), }; this.engine.dispatch(this.actions.setCaseId(this.caseId)); <8> this.initAttachedResults(); }; initAttachedResults = () => { if (!this.isInitAttachedResults) { if (this.engine && Array.isArray(this._attachedResults)) { this.isInitAttachedResults = true; if (this.actions.setAttachedResults) { this.engine.dispatch( <9> this.actions.setAttachedResults({ results: buildAttachedResultsPayloadHeadless( this._attachedResults ), loading: false, }) ); } } } }; // ... } ``` <1> The case ID property that identifies which Salesforce Case to load attached results for. It's passed from the parent component, such as `ExampleInsightPanelWrapper`. <2> Flag to ensure attached results are only initialized once during the component lifecycle. <3> Call the [service](#create-an-attachtocaseservice-lwc-to-connect-to-the-apex-classes) to retrieve all results currently attached to this case. <4> Store the attached results data locally for later use in initializing the Headless state. <5> Load attached results when the component connects to the DOM. <6> Initialize the Headless engine when the component renders. <7> Load the necessary Headless action creators for managing case context and attached results. <8> Set the case ID in the Headless engine's state. <9> Dispatch the attached results to the Headless engine, converting them to the expected format. . Pass the `caseId` to your Insight Panel. When you use the `exampleInsightPanel` LWC, pass the `caseId` property to it. In the [`ExampleInsightPanelWrapper.cmp`](#aura-wrapper-component), edit the `caseId` property as follows: ``` ``` ### Leverage the `resultAttachToCase` LWC in your result templates Include your `result-attach-to-case` component as a result action in the `quantic-result-action-bar` of your result templates. ```html <2> ``` Also, you can include `result-attach-to-case` component as a read-only icon indicating whether the result is already attached. Insert it near the title and result link. ```html
<1>
``` For a full example, see [defaultResultTemplate.html](https://github.com/coveo/ui-kit/blob/master/packages/quantic/force-app/solutionExamples/main/lwc/exampleInsightPanel/resultTemplates/defaultResultTemplate.html) in the Quantic repository.