Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add action to select CRM tab #654

Merged
merged 3 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
};