Skip to content

Commit

Permalink
Add action to select CRM tab (#654)
Browse files Browse the repository at this point in the history
  • Loading branch information
dremin authored Dec 9, 2024
1 parent 34dd7e6 commit df13066
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 8 deletions.
5 changes: 4 additions & 1 deletion docs/docs/feature-library/enhanced-crm-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This feature replaces the OOTB [CRMContainer](https://assets.flex.twilio.com/doc
This feature provides the following functionality:
- Extensible tabbed interface
- Other features can register tabs via the `beforeLoadCRMContainerTabs` action
- Other features can select a specific tab via the `SelectCRMContainerTab` action
- Tabs can receive task context, including if there is no task
- Configurable IFrame allowing you to specify a URL to display, including task and worker attribute interpolation
- Can optionally display an alternate URL when there are no tasks
Expand Down Expand Up @@ -77,4 +78,6 @@ export const actionHook = function addToEnhancedCRM(flex: typeof Flex, manager:
};
```

When the enhanced CRM component mounts, it adds a listener for `afterLoadCRMContainerTabs`, then invokes the `LoadCRMContainerTabs` action with the task in its payload. The `afterLoadCRMContainerTabs` action receives the components array in the payload, and renders those components. It immediately unregisters the listener to prevent receiving payloads for other tasks.
When the enhanced CRM component mounts, it adds a listener for `afterLoadCRMContainerTabs`, then invokes the `LoadCRMContainerTabs` action with the task in its payload. The `afterLoadCRMContainerTabs` action receives the components array in the payload, and renders those components. It immediately unregisters the listener to prevent receiving payloads for other tasks.

To select a CRM tab programmatically, other features may invoke the `SelectCRMContainerTab` action, providing a `title` property in the action payload. The tab with the specified title will be selected if present.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Actions, ITask } from '@twilio/flex-ui';
import { Flex } from '@twilio-paste/core/flex';
import { Tabs, TabList, Tab, TabPanels, TabPanel } from '@twilio-paste/core/tabs';
import { Tabs, TabList, Tab, TabPanels, TabPanel, useTabState } from '@twilio-paste/core/tabs';

export interface Props {
thisTask?: ITask; // task assigned to component
Expand All @@ -13,6 +13,10 @@ interface LoadCRMContainerTabsPayload {
components: CRMComponent[];
}

interface SelectCRMContainerTabPayload {
title: string;
}

interface CRMComponent {
title: string;
component: React.ComponentType;
Expand All @@ -22,6 +26,8 @@ interface CRMComponent {
export const TabbedCRMTask = ({ thisTask, task }: Props) => {
const [customComponents, setCustomComponents] = useState<CRMComponent[] | null>(null);

const tabState = useTabState({ baseId: 'enhanced-crm-tabs' });

// This allows short-lived tasks (e.g. callback tasks) to share/show
// the same components as their parent task so CRM work can continue after
// the short-lived task completes and disappears. This is done by rendering
Expand All @@ -33,29 +39,41 @@ export const TabbedCRMTask = ({ thisTask, task }: Props) => {

const handleCustomComponent = (payload: LoadCRMContainerTabsPayload) => {
// The action can be invoked multiple times at once. Ensure we handle the correct invocation.
if (payload.task !== thisTask) {
if (payload.task?.taskSid !== thisTask?.taskSid) {
return;
}

// Remove listener so that this function is not executed again for this instance
Actions.removeListener('afterLoadCRMContainerTabs', handleCustomComponent);

if (payload.components) {
setCustomComponents(payload.components.sort((a, b) => (a.order ?? 999) - (b.order ?? 999)));
}
};

const handleSelectTab = (payload: SelectCRMContainerTabPayload) => {
if (!payload.title) {
return;
}

tabState.select(`crm-tab-${payload.title}`);
};

useEffect(() => {
Actions.addListener('afterLoadCRMContainerTabs', handleCustomComponent);
Actions.addListener('afterSelectCRMContainerTab', handleSelectTab);
Actions.invokeAction('LoadCRMContainerTabs', {
task: thisTask,
components: [],
});

return () => {
// Remove the listeners when we unmount
Actions.removeListener('afterLoadCRMContainerTabs', handleCustomComponent);
Actions.removeListener('afterSelectCRMContainerTab', handleSelectTab);
};
}, []);

return (
<div style={{ display, flex: '1 0 auto' }}>
<Tabs baseId="enhanced-crm-tabs" element="CRM_TABS">
<Tabs state={tabState} element="CRM_TABS">
<TabList aria-label="CRM tabs" element="CRM_TAB_LIST">
{customComponents &&
customComponents.map((component) => <Tab key={`crm-tab-${component.title}`}>{component.title}</Tab>)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Actions } from '@twilio/flex-ui';

export const registerSelectCRMContainerTabAction = () => {
Actions.registerAction('SelectCRMContainerTab', async () => {
// Do nothing! The TabbedCRMTask component adds a listener to afterSelectCRMContainerTab to handle the payload.
});
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { FlexEvent } from '../../../../types/feature-loader';
import { registerLoadCRMContainerTabsAction } from '../custom-action/loadCRMContainerTabs';
import { registerSelectCRMContainerTabAction } from '../custom-action/selectCRMContainerTab';

export const eventName = FlexEvent.pluginsInitialized;
export const eventHook = function registerCRMAction() {
export const eventHook = function registerCRMActions() {
registerLoadCRMContainerTabsAction();
registerSelectCRMContainerTabAction();
};

0 comments on commit df13066

Please sign in to comment.