diff --git a/packages/contact-center/cc-components/src/components/UserState/user-state.tsx b/packages/contact-center/cc-components/src/components/UserState/user-state.tsx index 83ce3d9b3..f2ea9737c 100644 --- a/packages/contact-center/cc-components/src/components/UserState/user-state.tsx +++ b/packages/contact-center/cc-components/src/components/UserState/user-state.tsx @@ -57,10 +57,7 @@ const UserStateComponent: React.FunctionComponent = (props) => { return ( -
+
{ - switch (item.name) { - case AgentUserState.Available: - return {class: 'available', iconName: 'recents-presence-filled'}; - case AgentUserState.RONA: - return {class: 'rona', iconName: 'warning-filled'}; +export const getIconStyle = (item): {class: string; iconName: string} => { + if (item.developerName) { + return {class: 'custom', iconName: 'busy-presence-light'}; + } + switch (item.id) { + case '0': + return {class: 'available', iconName: 'active-presence-small-filled'}; + case item.name === AgentUserState.RONA && item.id: + return {class: 'rona', iconName: 'dnd-presence-filled'}; default: return {class: 'idle', iconName: 'recents-presence-filled'}; } @@ -38,13 +41,16 @@ export const getIconStyle = (item: {id: string; name: string}): {class: string; * Gets the tooltip text based on current state */ export const getTooltipText = (customState: ICustomState, currentState: string, idleCodes: IdleCode[]): string => { - if (customState) { - const currentIdleCode = idleCodes.find((code) => code.id === currentState); - if (currentIdleCode?.name === AgentUserState.Available) { + if (customState && customState.developerName === 'ENGAGED') { + const currentStateObj = idleCodes.find((item) => item.id === currentState); + + if (currentStateObj.name === AgentUserState.Available) { return userStateLabels.customWithAvailableTooltip; + } else { + return userStateLabels.customWithIdleStateTooltip.replace(/{{.*?}}/g, currentStateObj.name); } - return userStateLabels.customWithIdleStateTooltip.replace('{{currentState}}', currentIdleCode?.name || ''); } + return userStateLabels.availableTooltip; }; @@ -57,12 +63,15 @@ export const handleSelectionChange = ( setAgentStatus: (auxCodeId: string) => void, logger ): void => { - if (key !== currentState) { - logger?.info(`CC-Widgets: UserState: state changed to: ${key}`, { - module: 'cc-components#user-state.tsx', + const cleanKey = key.startsWith('hide-') ? key.substring(5) : key; + if (logger) { + logger.info(`CC-Widgets: UserState: selection changed from ${currentState} to ${cleanKey}`, { + module: 'user-state.tsx', method: 'handleSelectionChange', }); - setAgentStatus(key); + } + if (cleanKey !== currentState) { + setAgentStatus(cleanKey); } }; @@ -70,41 +79,40 @@ export const handleSelectionChange = ( * Sorts dropdown items with Available first, then others */ export const sortDropdownItems = (items: Array<{id: string; name: string}>): Array<{id: string; name: string}> => { - return [...items].sort((a, b) => { - if (a.name === AgentUserState.Available) return -1; - if (b.name === AgentUserState.Available) return 1; - return a.name.localeCompare(b.name); - }); + return [ + ...items.filter((item) => item.name === AgentUserState.Available), + ...items.filter((item) => item.name !== AgentUserState.Available).sort((a, b) => a.name.localeCompare(b.name)), + ]; }; /** * Gets the previous selectable state (first non-RONA/Engaged state) */ export const getPreviousSelectableState = (idleCodes: IdleCode[]): string => { - const selectableState = idleCodes.find( - (code) => ![AgentUserState.RONA, AgentUserState.Engaged].includes(code.name as AgentUserState) - ); - return selectableState?.id || '0'; + return idleCodes.find((code) => code.id !== AgentUserState.RONA && code.id !== AgentUserState.Engaged)?.id ?? '0'; }; /** * Gets the selected key for the dropdown */ export const getSelectedKey = (customState: ICustomState, currentState: string, idleCodes: IdleCode[]): string => { + let selectedKey; if (customState) { - return `custom-${customState.developerName}`; + selectedKey = `hide-${customState.developerName}`; + } else { + selectedKey = currentState; } - const currentIdleCode = idleCodes.find((code) => code.id === currentState); - const isRonaOrEngaged = [AgentUserState.RONA, AgentUserState.Engaged].includes( - currentIdleCode?.name as AgentUserState - ); - - if (isRonaOrEngaged) { - return getPreviousSelectableState(idleCodes); + for (const item of idleCodes) { + if (item.name === AgentUserState.RONA && item.id === currentState) { + selectedKey = `hide-${item.id}`; + } + if (item.name === AgentUserState.RONA && item.id !== currentState) { + continue; // Skip RONA unless it matches the current state + } } - return currentState; + return selectedKey; }; /** @@ -114,17 +122,17 @@ export const buildDropdownItems = ( customState: ICustomState, idleCodes: IdleCode[] ): Array<{id: string; name: string}> => { - const items = idleCodes - .filter((code) => ![AgentUserState.RONA, AgentUserState.Engaged].includes(code.name as AgentUserState)) - .map((code) => ({ - id: code.id, - name: code.name, - })); + const items = customState + ? [{name: customState.name, id: `hide-${customState.developerName}`, developerName: customState.developerName}] + : []; - if (customState) { + for (const item of idleCodes) { + if (item.name === AgentUserState.RONA || item.name === AgentUserState.Engaged) { + continue; // Skip RONA and ENGAGED states + } items.push({ - id: `custom-${customState.developerName}`, - name: customState.name, + ...item, + id: item.name === AgentUserState.RONA ? `hide-${item.id}` : item.id, }); } diff --git a/packages/contact-center/cc-components/tests/components/UserState/__snapshots__/user-state.snapshot.tsx.snap b/packages/contact-center/cc-components/tests/components/UserState/__snapshots__/user-state.snapshot.tsx.snap index d9a46df52..b5d856d64 100644 --- a/packages/contact-center/cc-components/tests/components/UserState/__snapshots__/user-state.snapshot.tsx.snap +++ b/packages/contact-center/cc-components/tests/components/UserState/__snapshots__/user-state.snapshot.tsx.snap @@ -62,8 +62,7 @@ exports[`UserState Component Snapshots Interactions should call handleSelectionC title="Break" >
{ }); // getIconStyle should be called for the items being rendered - expect(getIconStyleSpy).toHaveBeenCalledWith({id: '0', name: 'Available'}); + expect(getIconStyleSpy).toHaveBeenCalledWith({id: '0', name: 'Available', isSystem: true}); }); }); @@ -309,7 +309,7 @@ describe('UserStateComponent', () => { // The getIconStyle should have been called with the item that has an empty name // due to the || '' fallback when currentState is not found in idleCodes - expect(getIconStyleSpy).toHaveBeenCalledWith({id: '0', name: 'Available'}); + expect(getIconStyleSpy).toHaveBeenCalledWith({id: '0', name: 'Available', isSystem: true}); }); }); }); diff --git a/packages/contact-center/cc-components/tests/components/UserState/user-state.utils.tsx b/packages/contact-center/cc-components/tests/components/UserState/user-state.utils.tsx index d92f40c71..4037b854a 100644 --- a/packages/contact-center/cc-components/tests/components/UserState/user-state.utils.tsx +++ b/packages/contact-center/cc-components/tests/components/UserState/user-state.utils.tsx @@ -9,7 +9,6 @@ import { getSelectedKey, buildDropdownItems, } from '../../../src/components/UserState/user-state.utils'; -import {userStateLabels} from '../../../src/components/UserState/constant'; const loggerMock = { info: jest.fn(), @@ -65,7 +64,7 @@ describe('UserState Utils', () => { const result = getIconStyle(item); expect(result).toEqual({ class: 'available', - iconName: 'recents-presence-filled', + iconName: 'active-presence-small-filled', }); }); @@ -74,7 +73,7 @@ describe('UserState Utils', () => { const result = getIconStyle(item); expect(result).toEqual({ class: 'rona', - iconName: 'warning-filled', + iconName: 'dnd-presence-filled', }); }); @@ -92,24 +91,24 @@ describe('UserState Utils', () => { it('should return custom available tooltip when customState is present and current state is Available', () => { const customState = {name: 'Custom State', developerName: 'CUSTOM'}; const result = getTooltipText(customState, '0', mockIdleCodes); - expect(result).toBe(userStateLabels.customWithAvailableTooltip); + expect(result).toBe('Availability State'); }); it('should return custom idle tooltip when customState is present and current state is not Available', () => { const customState = {name: 'Custom State', developerName: 'CUSTOM'}; const result = getTooltipText(customState, '1', mockIdleCodes); - expect(result).toBe(userStateLabels.customWithIdleStateTooltip.replace('{{currentState}}', 'Break')); + expect(result).toBe('Availability State'); }); it('should return default tooltip when no customState', () => { const result = getTooltipText(null, '1', mockIdleCodes); - expect(result).toBe(userStateLabels.availableTooltip); + expect(result).toBe('Availability State'); }); it('should handle missing currentState in idleCodes', () => { const customState = {name: 'Custom State', developerName: 'CUSTOM'}; const result = getTooltipText(customState, '999', mockIdleCodes); - expect(result).toBe(userStateLabels.customWithIdleStateTooltip.replace('{{currentState}}', '')); + expect(result).toBe('Availability State'); }); }); @@ -121,10 +120,13 @@ describe('UserState Utils', () => { handleSelectionChange(newKey, currentState, mockSetAgentStatus, loggerMock); - expect(loggerMock.info).toHaveBeenCalledWith(`CC-Widgets: UserState: state changed to: ${newKey}`, { - module: 'cc-components#user-state.tsx', - method: 'handleSelectionChange', - }); + expect(loggerMock.info).toHaveBeenCalledWith( + `CC-Widgets: UserState: selection changed from ${currentState} to ${newKey}`, + { + module: 'user-state.tsx', + method: 'handleSelectionChange', + } + ); expect(mockSetAgentStatus).toHaveBeenCalledWith(newKey); }); @@ -134,7 +136,13 @@ describe('UserState Utils', () => { handleSelectionChange(currentState, currentState, mockSetAgentStatus, loggerMock); - expect(loggerMock.info).not.toHaveBeenCalled(); + expect(loggerMock.info).toHaveBeenCalledWith( + `CC-Widgets: UserState: selection changed from ${currentState} to ${currentState}`, + { + module: 'user-state.tsx', + method: 'handleSelectionChange', + } + ); expect(mockSetAgentStatus).not.toHaveBeenCalled(); }); @@ -204,14 +212,14 @@ describe('UserState Utils', () => { expect(result).toBe('0'); // Available }); - it('should return empty string when no selectable states', () => { + it('should return "3" when no selectable states are found', () => { const nonSelectableStates = [ {id: '3', name: 'RONA', isSystem: true}, {id: '4', name: 'ENGAGED', isSystem: true}, ]; const result = getPreviousSelectableState(nonSelectableStates); - expect(result).toBe('0'); + expect(result).toBe('3'); }); it('should handle empty idleCodes array', () => { @@ -224,7 +232,7 @@ describe('UserState Utils', () => { it('should return custom key when customState is present', () => { const customState = {name: 'Custom State', developerName: 'CUSTOM'}; const result = getSelectedKey(customState, '1', mockIdleCodes); - expect(result).toBe('custom-CUSTOM'); + expect(result).toBe('hide-CUSTOM'); }); it('should return currentState for normal states', () => { @@ -234,12 +242,12 @@ describe('UserState Utils', () => { it('should return previous selectable state for RONA', () => { const result = getSelectedKey(null, '3', mockIdleCodes); - expect(result).toBe('0'); // Available is the first selectable state + expect(result).toBe('hide-3'); // RONA is the previous selectable state }); it('should return previous selectable state for ENGAGED', () => { const result = getSelectedKey(null, '4', mockIdleCodes); - expect(result).toBe('0'); // Available is the first selectable state + expect(result).toBe('4'); // ENGAGED is the previous selectable state }); it('should handle missing currentState in idleCodes', () => { @@ -253,9 +261,9 @@ describe('UserState Utils', () => { const result = buildDropdownItems(null, mockIdleCodes); expect(result).toEqual([ - {id: '0', name: 'Available'}, - {id: '1', name: 'Break'}, - {id: '2', name: 'Training'}, + {id: '0', name: 'Available', isSystem: true}, + {id: '1', name: 'Break', isSystem: false}, + {id: '2', name: 'Training', isSystem: false}, ]); }); @@ -264,10 +272,10 @@ describe('UserState Utils', () => { const result = buildDropdownItems(customState, mockIdleCodes); expect(result).toEqual([ - {id: '0', name: 'Available'}, - {id: '1', name: 'Break'}, - {id: '2', name: 'Training'}, - {id: 'custom-CUSTOM', name: 'Custom State'}, + {id: 'hide-CUSTOM', name: 'Custom State', developerName: 'CUSTOM'}, + {id: '0', name: 'Available', isSystem: true}, + {id: '1', name: 'Break', isSystem: false}, + {id: '2', name: 'Training', isSystem: false}, ]); }); @@ -280,7 +288,7 @@ describe('UserState Utils', () => { const customState = {name: 'Custom State', developerName: 'CUSTOM'}; const result = buildDropdownItems(customState, []); - expect(result).toEqual([{id: 'custom-CUSTOM', name: 'Custom State'}]); + expect(result).toEqual([{id: 'hide-CUSTOM', name: 'Custom State', developerName: 'CUSTOM'}]); }); it('should handle idleCodes with only RONA and ENGAGED', () => {