diff --git a/docs/docs/feature-library/custom-transfer-directory.md b/docs/docs/feature-library/custom-transfer-directory.md index b4877c472..474628b0b 100644 --- a/docs/docs/feature-library/custom-transfer-directory.md +++ b/docs/docs/feature-library/custom-transfer-directory.md @@ -40,9 +40,11 @@ Enable the feature in the flex-config asset for your environment. ```javascript "custom_transfer_directory": { "enabled": true, // globally enable or disable the feature + "max_items": 200, // max number of items to show (search field allows accessing the remaining items) "worker" : { "enabled": true, // enable the custom worker tab - "show_only_available_workers": false + "show_only_available_workers": false, + "max_taskrouter_workers": 15000 // the maximum "Registered Workers per Workspace" (Max-Named-Workers-Count) value in the TaskRouter Limits section of Twilio Console; 15000 for most accounts. }, "queue" : { "enabled": true, // enable the custom queue tab diff --git a/flex-config/ui_attributes.common.json b/flex-config/ui_attributes.common.json index ffa453e05..05a511101 100644 --- a/flex-config/ui_attributes.common.json +++ b/flex-config/ui_attributes.common.json @@ -205,9 +205,11 @@ }, "custom_transfer_directory": { "enabled": true, + "max_items": 200, "worker": { "enabled": true, - "show_only_available_workers": false + "show_only_available_workers": false, + "max_taskrouter_workers": 15000 }, "queue": { "enabled": true, diff --git a/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/config.ts b/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/config.ts index f0a65f1a9..547defdc0 100644 --- a/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/config.ts +++ b/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/config.ts @@ -6,6 +6,7 @@ import CustomTransferDirectoryConfig from './types/ServiceConfiguration'; const { enabled = false, + max_items = 200, queue: queue_config, worker: worker_config, external_directory: external_directory_config, @@ -20,7 +21,11 @@ const { global_exclude_filter = '', } = queue_config || {}; -const { enabled: workerEnabled = false, show_only_available_workers = false } = worker_config || {}; +const { + enabled: workerEnabled = false, + show_only_available_workers = false, + max_taskrouter_workers = 15000, +} = worker_config || {}; const { enabled: externalDirectoryEnabled = false, @@ -39,6 +44,10 @@ export const isFeatureEnabled = (): boolean => { return enabled; }; +export const getMaxItems = (): number => { + return max_items; +}; + export const isCustomQueueTransferEnabled = (): boolean => { return isFeatureEnabled() && queueEnabled; }; @@ -102,6 +111,10 @@ export const showOnlyAvailableWorkers = (): boolean => { return show_only_available_workers; }; +export const getMaxTaskRouterWorkers = (): number => { + return max_taskrouter_workers; +}; + export const isNativeDigitalXferEnabled = (): boolean => { return Manager.getInstance().store.getState().flex.featureFlags?.transfersConfig?.enabled === true; }; diff --git a/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryItem.tsx b/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryItem.tsx index 0ce9b4d37..1caa3bc92 100644 --- a/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryItem.tsx +++ b/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryItem.tsx @@ -1,9 +1,8 @@ -import * as React from 'react'; -import { TaskHelper, ITask, templates } from '@twilio/flex-ui'; +import React, { useState } from 'react'; +import { TaskHelper, ITask, styled, templates } from '@twilio/flex-ui'; import { Box } from '@twilio-paste/core/box'; import { ButtonGroup } from '@twilio-paste/core/button-group'; import { Button } from '@twilio-paste/core/button'; -import { Flex } from '@twilio-paste/core/flex'; import { Tooltip } from '@twilio-paste/core/tooltip'; import { Text } from '@twilio-paste/core/text'; import { AgentIcon } from '@twilio-paste/icons/esm/AgentIcon'; @@ -23,8 +22,17 @@ export interface DirectoryItemProps { onTransferClick: (options: any) => void; } +const DirectoryItemContainer = styled('div')` + padding-inline: 0.5rem; + min-height: 40px; + display: flex; + justify-content: flex-start; + align-items: center; +`; + const DirectoryItem = (props: DirectoryItemProps) => { const { entry, task, onTransferClick } = props; + const [isHovered, setIsHovered] = useState(false); const onWarmTransferClick = () => { onTransferClick({ mode: 'WARM' }); @@ -36,7 +44,7 @@ const DirectoryItem = (props: DirectoryItemProps) => { const renderIcon = (): React.JSX.Element => { if (entry.icon) { - return entry.icon; + return entry.icon(); } switch (entry.type) { @@ -51,7 +59,9 @@ const DirectoryItem = (props: DirectoryItemProps) => { const renderLabel = (): React.JSX.Element => ( - {entry.labelComponent || ( + {entry.labelComponent ? ( + entry.labelComponent() + ) : ( {entry.label} @@ -60,16 +70,15 @@ const DirectoryItem = (props: DirectoryItemProps) => { ); return ( - setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} key={`directory-item-container-${entry.type}-${entry.key}`} > {renderIcon()} - {entry.tooltip ? ( + {isHovered && entry.tooltip ? ( { renderLabel() )} - - {entry.warm_transfer_enabled ? ( - - - - ) : ( -
- )} - {entry.cold_transfer_enabled ? ( - - + + ) : ( +
+ )} + {entry.cold_transfer_enabled ? ( + - {task && TaskHelper.isChatBasedTask(task) ? ( - - ) : ( - - )} - - - ) : ( -
- )} -
-
+ + + ) : ( +
+ )} + + )} + ); }; diff --git a/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryTab.tsx b/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryTab.tsx index 2991025ec..597147679 100644 --- a/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryTab.tsx +++ b/plugin-flex-ts-template-v2/src/feature-library/custom-transfer-directory/custom-components/DirectoryTab.tsx @@ -1,7 +1,9 @@ import { Alert } from '@twilio-paste/core/alert'; +import { Button } from '@twilio-paste/core/button'; import { Flex } from '@twilio-paste/core/flex'; import { Spinner } from '@twilio-paste/core/spinner'; -import { withTaskContext, ITask, Actions, Template, templates } from '@twilio/flex-ui'; +import { LoadingIcon } from '@twilio-paste/icons/esm/LoadingIcon'; +import { withTaskContext, ITask, Actions, styled, Template, templates } from '@twilio/flex-ui'; import { useState, useRef, useEffect } from 'react'; import debounce from 'lodash/debounce'; @@ -9,6 +11,7 @@ import DirectoryItem from './DirectoryItem'; import SearchBox from './SearchBox'; import { StringTemplates } from '../flex-hooks/strings/CustomTransferDirectory'; import { DirectoryEntry } from '../types/DirectoryEntry'; +import { getMaxItems } from '../config'; export interface TransferClickPayload { mode: 'WARM' | 'COLD'; @@ -20,8 +23,18 @@ export interface OwnProps { isLoading: boolean; noEntriesMessage?: string; onTransferClick: (entry: DirectoryEntry, transferOptions: TransferClickPayload) => void; + onReloadClick?: () => void; } +const SearchRow = styled('div')` + display: flex; + align-items: center; + column-gap: 1rem; + padding: 1rem; + padding-top: 1.25rem; + width: 100%; +`; + const DirectoryTab = (props: OwnProps) => { const [filteredDirectory, setFilteredDirectory] = useState([] as Array); @@ -58,7 +71,14 @@ const DirectoryTab = (props: OwnProps) => { return ( - + + + {props.onReloadClick && ( + + )} + {props.isLoading && ( @@ -76,7 +96,9 @@ const DirectoryTab = (props: OwnProps) => { /> ) : ( - Array.from(filteredDirectory).map((entry: DirectoryEntry) => { + !props.isLoading && + Array.from(filteredDirectory).map((entry, index) => { + if (index >= getMaxItems()) return null; return ( { ); }) )} + {filteredDirectory.length > getMaxItems() && ( + +