Team View Filters allow supervisors to search or filter their agents by name or activity. You can also use custom, programmatically defined filter criteria, like teams or skills. This page describes the main concepts involved in customizing Team View Filters, and includes some sample code you can use to get started with the feature. For more information about using Team View Filters, check out the End User Guide!
This feature is in pilot between version 1.18 and version 1.26.3 of the Flex UI. You can enable team view filters on the pre-release features page within Flex Admin. This feature is enabled by default for version 1.27.0 and upper.
Flex uses the following objects and components to generate Team View Filters:
The portion of the Flex UI that shows the Team Filters. It is a child component of the Supervisor UI. By default, Flex allows you to filter by agent Activity, but you can add custom filters to the panel by modifying the defaultProps, like so:
_10Flex.TeamsView.defaultProps.filters = [_10 Flex.TeamsView.activitiesFilter,_10 // ... custom filters here_10];
A FilterDefinition is a structure that tells Flex how to render the new filter label and field, and what to query the field value against. Each field will add a new condition to the Sync query that will render the agents (or workers) list to the UI. A FilterDefinition has the following structure:
_10interface FilterDefinition {_10 id: string;_10 title: React.ReactChild;_10 fieldName: string;_10 type?: FiltersListItemType;_10 options?: Array;_10 customStructure?: CustomFilterItemStructure;_10 condition?: string;_10}
Field | Description |
---|---|
id | Required. A string representing the subject of your query. This identifies the values to filter for in the Flex app state based on your specified attribute or field. For example, "data.attributes.full_name" or "data.activity_name". |
title | Required. A string that is rendered as the filter display name on the Filters side panel. For example, "Names". |
fieldName | Required. A string representing the input name passed to the predefined filters. For example, "full_name". |
type | Required if customStructure isn't defined. Currently only supports "multiValue". This renders a list of checkboxes for each option you provide. |
options | Required if type is "multiValue". An array of filter definition options used for rendering the checkbox fields, their values, their labels, and whether they are selected by default. |
customStructure | Required if type isn't set. An object of type CustomFilterItemStructure used for rendering custom fields. This lets you add your own custom fields. |
condition | An optional parameter that represents the query comparison operator such as IN , NOT_IN , CONTAINS . See Query Operators to learn about other possible condition values. This represents the condition to be used in building the query. For instance, data.activity_name IN ["available"] or data.attributes.full_name CONTAINS "John" . In the latter example, "data.attributes.full_name" is the id , "CONTAINS" is the condition , and "John" is the value to filter for. |
Describes the available options which can be used as a filter. For example, if you wanted to filter by agent languages, you could create an option for each language spoken in your contact center.
_10interface FilterDefinitionOption {_10 value: string;_10 label: string;_10 default?: boolean;_10}
Field | Description |
---|---|
value | Value of the filter option. When using IN or NOT_IN to build your query and you are providing an array of options, you need to omit the first and the last instances of double quotes in your string. See example in Add a multivalue field |
label | Friendly name of the option. |
default | A Boolean value indicating whether the filter option should be selected by default. |
To add a custom field and label to a filter, you'll need an object with a select field or an input field passed to the CustomFilterItemStructure
.
_10interface CustomFilterItemStructure {_10 field: React.ReactElement;_10 label: React.ReactElement;_10}
field
is a React element that should render an input usable by the final customer. It inherits a few custom props from Flex.
Field | Description |
---|---|
name | The field name set in the FilterDefinition. |
handleChange | A function that gets invoked on this custom field change event. It requires the new value of the filter to be passed as an argument, either as an array of strings or a string. |
options | The same options passed in the FilterDefinition, if provided. |
currentValue | The current value of the filter. It can either be an array of strings or a string, depending on what the props.handleChange function receives once invoked. |
label is another React element that serves as the label of the filter. It should indicate the content of the filter when the accordion item is closed. It receives the following properties:
Field | Description |
---|---|
currentValue | The current value of the filter. |
activeOption | is the options array that is provided and returns the entire selected option. It will contain currentValue as value. |
The filters array also accepts FiltersDefinitionFactories, which are functions that return a FilterDefinition. You can write a FilterDefinitionFactory that fetches data from the app state and dynamically renders values or options of a field.
Your factory must accept two arguments:
Argument | Description |
---|---|
appState | The entire Flex Redux state. Use this to access information about activities, session, and more details about the current state of your Flex instance. |
ownProps | The props received by the TeamFiltersPanel. |
The TeamFiltersPanel includes activitiesFilter
by default. To add a new filter to the TeamsFiltersPanel, you can overwrite the defaultProps.filters
property with a new array of filter definitions before rendering your Flex instance.
This array can contain both FilterDefinitions and FilterDefinition factories. In this example, we are reading the default activity filter as the first item of the array.
_10Flex.TeamsView.defaultProps.filters = [_10 Flex.TeamsView.activitiesFilter,_10 yourFilterDefinitionHere,_10 your2ndFilterDefinitionHere,_10 your3rdFilterDefinitionHere,_10];
Initially, the FilterDefinition has one predefined type, which will output checkboxes: MultiValue. With custom input fields, you can add a different type of input, like a custom text input field.
An example of a custom input field, name, where you can type and filter by names based on the condition that you specify
The following file describes the HTML for your custom field, as well as the JavaScript associated with its behavior. In this sample, the filter functionality is extended in the extendFilters function
_49import React from "react";_49import { TeamsView } from "@twilio/flex-ui";_49_49// Define an Input component; returns an input HTML element with logic to handle changes when users enter input_49const Input = ({ handleChange, currentValue = "", fieldName }) => {_49 const _handleChange = (e) => {_49 e.preventDefault();_49 handleChange(e.target.value);_49 };return (_49 <input_49 className="CustomInput"_49 type="text"_49 onChange={_handleChange}_49 value={currentValue}_49 name={fieldName}_49 />_49 )_49};_49_49// Define the label that supervisors will see when using our custom filter_49const CustomLabel = ({ currentValue }) => (_49 <>{currentValue && currentValue.length ? `Containing "${currentValue}"` : "Any"}</>_49);_49_49// Define a new filter that uses the custom field_49const nameFilter = {_49 id: "data.attributes.full_name",_49 fieldName: "full_name",_49 title: "Names",_49 customStructure: {_49 field: <Input />,_49 label: <CustomLabel />,_49 },_49 condition: "CONTAINS"_49};_49_49// Export a function to be invoked in plugin's main entry-point_49export const extendFilter = (manager) => {_49 manager.updateConfig({_49 componentProps: {_49 TeamsView: {_49 filters:[_49 TeamsView.activitiesFilter,_49 nameFilter,_49 ]_49 }_49 }_49 })_49};
The following example will filter by agents located in any of the options specified in the value
field:
_30import React from "react";_30import { TeamsView } from "@twilio/flex-ui";_30import { FiltersListItemType } from "@twilio/flex-ui";_30_30// Define a new filter that uses the custom field_30_30const teamFilter = {_30 fieldName: "team_location",_30 title: 'Team',_30 id: 'data.attributes.squad',_30 type: FiltersListItemType.multiValue,_30 options: [_30 { label: 'Canada', value: 'CAN-1", "CAN-2", "ARC'}_30 ],_30 condition: 'IN',_30};_30_30// Export a function to be invoked in plugin's main entry-point_30export const extendFilter = (manager) => {_30 manager.updateConfig({_30 componentProps: {_30 TeamsView: {_30 filters: [_30 TeamsView.activitiesFilter,_30 teamFilter,_30 ]_30 }_30 }_30 })_30};
You can use a filter definition factory to dynamically render the options of the predefined checkboxes fields. For example, you could render the activities that are currently available on in the Flex Redux store.
_34import React from "react";_34_34import { FiltersListItemType } from "@twilio/flex-ui";_34_34const customActivityFilter = (appState, teamFiltersPanelProps) => {_34 const activitiesArray = Array.from(appState.worker.activities.values());_34_34 const activities = (activitiesArray).map((activity) => ({_34 value: activity.name,_34 label: activity.name,_34 default: !!activity.available,_34 }));_34_34 return {_34 id: "data.activity_name",_34 fieldName: "custom_activity_name",_34 type: FiltersListItemType.multiValue,_34 title: "Activities",_34 options: activities_34 };_34};_34_34// Export a function to be invoked in plugin's main entry-point_34export const extendFilter = (manager) => {_34 manager.updateConfig({_34 componentProps: {_34 TeamsView: {_34 filters: [_34 customActivityFilter,_34 ]_34 }_34 }_34 })_34};
You can use custom filter input components and filter definition factories together. The following code sample dynamically renders the options of a select field from the current Redux store.
_64import React from "react";_64_64// Create your custom field_64const CustomField = ({ handleChange, currentValue, fieldName, options }) => {_64 const _handleChange = (e) => {_64 e.preventDefault();_64 handleChange(e.target.value);_64 };_64_64 return (_64 <select_64 className="CustomInput"_64 onChange={_handleChange}_64 value={currentValue}_64 name={fieldName}_64 >_64 <option value="" key="default">All activities</option>_64 {options.map(opt => (_64 <option value={opt.value} key={opt.value}>{opt.label}</option>_64 ))}_64 </select>_64 )_64};_64_64// Define the label that will be displayed when filter is active_64const CustomLabel = ({ currentValue }) => (_64 <>{currentValue || "All activities"}</>_64);_64_64// Define the available properties upon which to filter based on application state_64const customActivityFilter = (appState, teamFiltersPanelProps) => {_64_64 const activitiesArray = Array.from(appState.worker.activities.values());_64_64 const activities = (activitiesArray).map((activity) => ({_64 value: activity.name,_64 label: activity.name,_64 default: !!activity.available,_64 }));_64_64 return {_64 id: "data.activity_name",_64 fieldName: "custom_activity_name",_64 title: "Activities",_64 customStructure: {_64 label: <CustomLabel />,_64 field: <CustomField />,_64 },_64 options: activities_64 };_64};_64_64// Add new filter to TeamFiltersPanel in the Flex UI_64export const extendFilter = (manager) => {_64 manager.updateConfig({_64 componentProps: {_64 TeamsView: {_64 filters: [_64 customActivityFilter,_64 ]_64 }_64 }_64 })_64};
Once you've configured your filter(s), you can enable them in Flex manager from within the base Plugin file. In this example, the extendFilters
function is imported and passed to the Flex Manager.
_20import Flex from '@twilio/flex-ui';_20import { FlexPlugin } from 'flex-plugin';_20import { extendFilter } from "./CustomFilters";_20_20const PLUGIN_NAME = 'SamplePlugin';_20_20export default class SamplePlugin extends FlexPlugin {_20 constructor() {_20 super(PLUGIN_NAME);_20 }_20_20 /**_20 *_20 * @param flex_20 * @param {Manager} manager_20 */_20 init(flex, manager) {_20 extendFilter(manager);_20 }_20}
The following code samples demonstrate writing and calling the same extendFilters
functions, but in the context of self-hosted Flex.
_46import React from "react";_46import Flex from "@twilio/flex-ui";_46_46// Define an Input component; returns a simple input HTML element with logic to handle changes when users enter input_46const Input = ({ handleChange, currentValue = "", fieldName }) => {_46 const _handleChange = (e) => {_46 e.preventDefault();_46 handleChange(e.target.value);_46 };_46_46 return (_46 <input_46 className="CustomInput"_46 type="text"_46 onChange={_handleChange}_46 value={currentValue}_46 name={fieldName}_46 />_46 )_46};_46_46// Define the label that supervisors will see when using our custom filter_46const CustomLabel = ({ currentValue }) => (_46 <>{currentValue && currentValue.length ? `Containing "${currentValue}"` : "Any"}</>_46);_46_46_46// Define a new filter that uses the custom field_46const nameFilter = {_46 id: "data.attributes.full_name",_46 fieldName: "full_name",_46 title: "Names",_46 customStructure: {_46 field: <Input />,_46 label: <CustomLabel />,_46 },_46 condition: "CONTAINS"_46};_46_46// Add the filter to the list of filters in the TeamFiltersPanel_46export const extendFilter = () => {_46 Flex.TeamsView.defaultProps.filters = [_46 Flex.TeamsView.activitiesFilter,_46 nameFilter,_46 ];_46};
_27import React from "react";_27import Flex from "@twilio/flex-ui";_27_27const customActivityFilter = (appState, teamFiltersPanelProps) => {_27 const activitiesArray = Array.from(appState.worker.activities.values());_27_27 const activities = (activitiesArray).map((activity) => ({_27 value: activity.name,_27 label: activity.name,_27 default: !!activity.available,_27 }));_27_27 return {_27 id: "data.activity_name",_27 fieldName: "custom_activity_name",_27 type: Flex.FiltersListItemType.multiValue,_27 title: "Activities",_27 options: activities_27 };_27};_27_27_27export const extendFilter = () => {_27 Flex.TeamsView.defaultProps.filters = [_27 customActivityFilter,_27 ];_27};
_59import React from "react";_59import Flex from "@twilio/flex-ui";_59_59// Create your custom field_59const CustomField = ({ handleChange, currentValue, fieldName, options }) => {_59 const _handleChange = (e) => {_59 e.preventDefault();_59 handleChange(e.target.value);_59 };_59_59 return (_59 <select_59 className="CustomInput"_59 onChange={_handleChange}_59 value={currentValue}_59 name={fieldName}_59 >_59 <option value="" key="default">All activities</option>_59 {options.map(opt => (_59 <option value={opt.value} key={opt.value}>{opt.label}</option>_59 ))}_59 </select>_59 )_59};_59_59// Define the label that will be displayed when filter is active_59const CustomLabel = ({ currentValue }) => (_59 <>{currentValue || "All activities"}</>_59);_59_59// Define the available properties upon which to filter based on application state_59const customActivityFilter = (appState, teamFiltersPanelProps) => {_59_59 const activitiesArray = Array.from(appState.worker.activities.values());_59_59 const activities = (activitiesArray).map((activity) => ({_59 value: activity.name,_59 label: activity.name,_59 default: !!activity.available,_59 }));_59_59 return {_59 id: "data.activity_name",_59 fieldName: "custom_activity_name",_59 title: "Activities",_59 customStructure: {_59 label: <CustomLabel />,_59 field: <CustomField />,_59 },_59 options: activities_59 };_59};_59_59// Add new filter to TeamFiltersPanel in the Flex UI_59export const extendFilter = () => {_59 Flex.TeamsView.defaultProps.filters = [_59 customActivityFilter,_59 ];_59};
_25import { extendFilter } from "./CustomFilters";_25_25export function run(config) {_25 const container = document.getElementById("container");_25_25 return Flex_25 .progress("#container")_25 .provideLoginInfo(config, "#container")_25 .then(() => Flex.Manager.create(config))_25 .then(manager => {_25_25 // Extending the filter functionality_25 extendFilter();_25_25 ReactDOM.render(_25 <Flex.ContextProvider manager={manager}>_25 <Flex.RootContainer />_25 </Flex.ContextProvider>,_25 container_25 );_25 })_25 .catch((e) => {_25 console.log("Failed to run Flex", e);_25 });_25}