Skip to content
Open
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,4 @@ workflows:
- unit_tests
filters:
branches:
only: ccwidgets
only: ccconnectors
4 changes: 2 additions & 2 deletions .releaserc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"branches": [
"master",
{
"name": "ccwidgets",
"prerelease": "ccwidgets"
"name": "ccconnectors",
"prerelease": "ccconnectors"
}
],
"plugins": [
Expand Down
8 changes: 3 additions & 5 deletions packages/contact-center/cc-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,9 @@
},
"stableVersion": "1.28.0-ccwidgets.8",
"peerDependencies": {
"@momentum-ui/core": ">=19.16.0",
"@momentum-ui/icons": ">=8.28.5",
"@momentum-ui/tokens": ">=1.7.1",
"@momentum-ui/utils": ">=6.2.15",
"@momentum-ui/web-components": ">=2.16.16",
"@emotion/react": ">=11.14.0",
"@emotion/styled": ">=11.14.0",
"@mui/material": ">=6.4.2",
"react": ">=18.3.1",
"react-dom": ">=18.3.1"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import {IncomingTaskPresentationalProps} from '../task.types';
import {IncomingTaskPresentationalProps} from './task.types';

const styles: {[key: string]: React.CSSProperties} = {
box: {
Expand Down Expand Up @@ -164,7 +164,7 @@ const IncomingTaskPresentational: React.FunctionComponent<IncomingTaskPresentati
{isBrowser && (
<div style={styles.buttonsWrapper}>
<button style={styles.answerButton} onClick={accept}>
Answer
Answers
</button>
<button style={styles.declineButton} onClick={decline}>
Decline
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import {ITask, IContactCenter} from '@webex/plugin-cc';
import {ILogger, WrapupCodes} from '@webex/cc-store';

/**
* Interface representing the TaskProps of a user.
*/
export interface TaskProps {
/**
* currentTask of the agent.
*/
currentTask: ITask;

/**
* Incoming task on the incoming task widget
*/
incomingTask: ITask;

/**
* CC SDK Instance.
*/
cc: IContactCenter;

/**
* Handler for task accepted
*/
onAccepted?: () => void;

/**
* Handler for task declined
*/
onDeclined?: () => void;

/**
* Handler for task accepted in TaskList
*/
onTaskAccepted?: (task: ITask) => void;

/**
* Handler for task declined in TaskList
*/
onTaskDeclined?: (task: ITask) => void;

/**
* accept incoming task action
*/
accept: () => void;

/**
* decline incoming task action
*/
decline: () => void;

/**
* accept task from task list
*/
acceptTask: (task: ITask) => void;

/**
* decline task from tasklist
*/
declineTask: (task: ITask) => void;

/**
* Flag to determine if the user is logged in with a browser option
*/
isBrowser: boolean;

/**
* Flag to determine if the task is answered
*/
isAnswered: boolean;

/**
* Flag to determine if the task is ended
*/
isEnded: boolean;

/**
* Selected login option
*/
selectedLoginOption: string;

/**
* List of tasks
*/
taskList: ITask[];

/**
* The logger instance from SDK
*/
logger: ILogger;
}

export type UseTaskProps = Pick<TaskProps, 'cc' | 'onAccepted' | 'onDeclined' | 'selectedLoginOption' | 'logger'>;
export type UseTaskListProps = Pick<
TaskProps,
'cc' | 'selectedLoginOption' | 'onTaskAccepted' | 'onTaskDeclined' | 'logger'
>;
export type IncomingTaskPresentationalProps = Pick<
TaskProps,
'incomingTask' | 'isBrowser' | 'isAnswered' | 'isEnded' | 'accept' | 'decline'
>;
export type IncomingTaskProps = Pick<TaskProps, 'onAccepted' | 'onDeclined'>;
export type TaskListProps = Pick<TaskProps, 'onTaskAccepted' | 'onTaskDeclined'>;

export type TaskListPresentationalProps = Pick<
TaskProps,
'currentTask' | 'taskList' | 'isBrowser' | 'acceptTask' | 'declineTask'
>;
export enum TASK_EVENTS {
TASK_INCOMING = 'task:incoming',
TASK_ASSIGNED = 'task:assigned',
TASK_MEDIA = 'task:media',
TASK_HOLD = 'task:hold',
TASK_UNHOLD = 'task:unhold',
TASK_CONSULT = 'task:consult',
TASK_CONSULT_END = 'task:consultEnd',
TASK_CONSULT_ACCEPT = 'task:consultAccepted',
TASK_PAUSE = 'task:pause',
TASK_RESUME = 'task:resume',
TASK_END = 'task:end',
TASK_WRAPUP = 'task:wrapup',
} // TODO: remove this once cc sdk exports this enum

/**
* Interface representing the properties for control actions on a task.
*/
export interface ControlProps {
/**
* Audio reference
*/
audioRef: React.RefObject<HTMLAudioElement>;
/**
* The current task being handled.
*/
currentTask: ITask;

/**
* Function to handle hold/resume actions.
*/
onHoldResume: () => void;

/**
* Function to handle ending the task.
*/
onEnd: () => void;

/**
* Function to handle wrapping up the task.
*/
onWrapUp: () => void;

/**
* Logger instance for logging purposes.
*/
logger: ILogger;

/**
* Array of wrap-up codes.
* TODO: Expose this type from SDK.
*/
wrapupCodes: WrapupCodes[];

/**
* Indicates if wrap-up is required.
*/
wrapupRequired: boolean;

/**
* Function to handle hold/resume actions with a boolean parameter.
* @param hold - Boolean indicating whether to hold (true) or resume (false).
*/
toggleHold: (hold: boolean) => void;

/**
* Function to handle pause/resume recording actions with a boolean parameter.
* @param pause - Boolean indicating whether to pause (true) or resume (false) recording.
*/
toggleRecording: (pause: boolean) => void;

/**
* Function to handle ending the call.
*/
endCall: () => void;

/**
* Function to handle wrapping up the call with a reason and ID.
* @param wrapupReason - The reason for wrapping up the call.
* @param wrapupId - The ID associated with the wrap-up reason.
*/
wrapupCall: (wrapupReason: string, wrapupId: string) => void;
}

export type CallControlProps = Pick<ControlProps, 'onHoldResume' | 'onEnd' | 'onWrapUp'>;

export type CallControlPresentationalProps = Pick<
ControlProps,
| 'currentTask'
| 'audioRef'
| 'wrapupCodes'
| 'wrapupRequired'
| 'toggleHold'
| 'toggleRecording'
| 'endCall'
| 'wrapupCall'
>;

export type useCallControlProps = Pick<ControlProps, 'currentTask' | 'onHoldResume' | 'onEnd' | 'onWrapUp' | 'logger'>;
Original file line number Diff line number Diff line change
@@ -1,52 +1,55 @@
import React from 'react';

import {IUserState} from './user-state.types';
import {formatTime} from '../../utils'
import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import Button from '@mui/material/Button';
import { IUserState } from './user-state.types';
import { formatTime } from '../../utils';

import './user-state.scss';

const UserStateComponent: React.FunctionComponent<IUserState> = (props) => {
const {idleCodes,setAgentStatus,isSettingAgentStatus, errorMessage, elapsedTime, currentState, currentTheme} = props;
const UserStateComponent: React.FunctionComponent<Omit<IUserState, "setCurrentState">> = (props) => {
const { idleCodes, setAgentStatus, isSettingAgentStatus, errorMessage, elapsedTime, currentState, currentTheme } = props;

return (
<div className={`box ${currentTheme === 'DARK' ? 'dark-theme' : 'light-theme'}`}>
<section className='sectionBox'>
<fieldset className='fieldset'>
<legend data-testid='user-state-title' className='legendBox'>Agent State</legend>
<div style={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }} />
<select
id="idleCodes"
value={currentState.id}
className='select'
onChange={
(event) => {
const code = idleCodes?.filter(code => code.id === event.target.value)[0];

<FormControl variant="outlined" fullWidth disabled={isSettingAgentStatus} className='formControl'>
<InputLabel id="idleCodes-label">Idle Codes</InputLabel>
<Select
labelId="idleCodes-label"
id="idleCodes"
value={currentState?.id || ''}
onChange={(event) => {
const code = idleCodes?.find(code => code.id === event.target.value);
setAgentStatus(code);
}
}
disabled={isSettingAgentStatus}
>
{idleCodes?.filter(code => !code.isSystem).map((code) => {
return (
<option
key={code.id}
value={code.id}
>
{code.name}
</option>
);
})}
</select>
{/* @ts-ignore */}
<md-button color={`${currentTheme === 'DARK' ? 'white' : 'dark-grey'}`}>Test Button</md-button>
<div className={`elapsedTime ${isSettingAgentStatus ? 'elapsedTime-disabled' : ''}`}>{formatTime(elapsedTime)}</div>
{
errorMessage && <div style={{color: 'red'}}>{errorMessage}</div>
}
}}
label="Idle Codes"
>
{idleCodes?.filter(code => !code.isSystem).map((code) => (
<MenuItem key={code.id} value={code.id}>
{code.name}
</MenuItem>
))}
</Select>
</FormControl>

<Button style={{ color: currentTheme === 'DARK' ? 'white' : 'dark-grey' }}>
Test Button
</Button>
<div className={`elapsedTime ${isSettingAgentStatus ? 'elapsedTime-disabled' : ''}`}>
{formatTime(elapsedTime)}
</div>
{errorMessage && <div style={{ color: 'red' }}>{errorMessage}</div>}
</fieldset>
</section>
</div>
);
};

export default UserStateComponent;
export default UserStateComponent;
3 changes: 0 additions & 3 deletions packages/contact-center/cc-components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import UserStateComponent from './components/UserState/user-state';
import {IUserState} from './components/UserState/user-state.types';
import '@momentum-ui/icons/css/momentum-ui-icons.min.css';
import '@momentum-ui/core/css/momentum-ui.min.css';
import '@momentum-ui/web-components';

export {UserStateComponent, type IUserState};
3 changes: 0 additions & 3 deletions packages/contact-center/cc-components/src/wc.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import r2wc from '@r2wc/react-to-web-component';
import UserStateComponent from './components/UserState/user-state';
import '@momentum-ui/web-components';
import '@momentum-ui/icons/css/momentum-ui-icons.min.css';
import '@momentum-ui/core/css/momentum-ui.min.css';

const WebUserState = r2wc(UserStateComponent);

Expand Down
Loading