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
3 changes: 3 additions & 0 deletions packages/contact-center/ai-assistant/bable.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const baseConfig = require('../../../babel.config.js');

module.exports = baseConfig;
20 changes: 20 additions & 0 deletions packages/contact-center/ai-assistant/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from 'typescript-eslint';
import pluginReact from 'eslint-plugin-react';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintConfigPrettier from 'eslint-config-prettier';

export default [
{files: ['**/src/**/*.{js,mjs,cjs,ts,jsx,tsx}']},
{ignores: ['.babelrc.js', '*config.{js,ts}', 'dist', 'node_modules', 'coverage']},
{languageOptions: {globals: globals.browser}},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
settings: {react: {version: 'detect'}},
},
eslintPluginPrettierRecommended,
eslintConfigPrettier,
];
6 changes: 6 additions & 0 deletions packages/contact-center/ai-assistant/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const jestConfig = require('../../../jest.config.js');

jestConfig.rootDir = '../../../';
jestConfig.testMatch = ['**/ai-assistant/tests/**/*.ts', '**/ai-assistant/tests/**/*.tsx'];

module.exports = jestConfig;
70 changes: 70 additions & 0 deletions packages/contact-center/ai-assistant/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "@webex/cc-ai-assistant",
"description": "Webex Contact Center Widgets: AI Assistant",
"license": "Cisco's General Terms (https://www.cisco.com/site/us/en/about/legal/contract-experience/index.html)",
"version": "1.28.0-ccwidgets.126",
"main": "dist/index.js",
"types": "dist/types/index.d.ts",
"publishConfig": {
"access": "public"
},
"files": [
"dist/",
"package.json"
],
"scripts": {
"clean": "rm -rf dist && rm -rf node_modules",
"clean:dist": "rm -rf dist",
"build": "yarn run -T tsc",
"build:src": "yarn run clean:dist && webpack",
"build:watch": "webpack --watch",
"test:unit": "tsc --project tsconfig.test.json && jest --coverage",
"test:styles": "eslint",
"deploy:npm": "yarn npm publish"
},
"dependencies": {
"@webex/cc-components": "workspace:*",
"@webex/cc-store": "workspace:*",
"@webex/cc-ui-logging": "workspace:*",
"mobx-react-lite": "^4.1.0",
"react-error-boundary": "^6.0.0",
"typescript": "5.6.3"
},
"devDependencies": {
"@babel/core": "7.25.2",
"@babel/preset-env": "7.25.4",
"@babel/preset-react": "7.24.7",
"@babel/preset-typescript": "7.25.9",
"@eslint/js": "^9.20.0",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.6.2",
"@testing-library/react": "16.0.1",
"@types/jest": "29.5.14",
"@types/react-test-renderer": "18",
"@webex/test-fixtures": "workspace:*",
"babel-jest": "29.7.0",
"babel-loader": "9.2.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.0.1",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.37.4",
"file-loader": "6.2.0",
"globals": "^16.0.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"prettier": "^3.5.1",
"ts-loader": "9.5.1",
"typescript-eslint": "^8.24.1",
"webpack": "5.94.0",
"webpack-cli": "5.1.4",
"webpack-merge": "6.0.1"
},
"peerDependencies": {
"react": ">=18.3.1",
"react-dom": ">=18.3.1"
}
}
24 changes: 24 additions & 0 deletions packages/contact-center/ai-assistant/src/ai-assistant.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type {SuggestedResponsePayload} from '@webex/cc-store';

/**
* Public props for the `AIAssistant` widget. All callbacks are optional —
* the host can opt into any subset.
*/
export interface IAIAssistantProps {
/** Fired when the launcher is clicked and the panel opens. */
onOpen?: () => void;
/** Fired when the agent minimizes the panel to its collapsed bar. */
onMinimize?: () => void;
/** Fired when the minimized bar is restored to the full panel. */
onRestore?: () => void;
/** Fired when the panel is closed back to the launcher. */
onClose?: () => void;
/** Fired when the chat is reset (suggestions, drafts, session flags wiped). */
onClearChat?: () => void;
/** Fired when the fullscreen affordance is toggled. Host owns layout. */
onFullScreenToggle?: (isFullScreen: boolean) => void;
/** Fired each time a fresh assistant suggestion arrives for the active task. */
onSuggestionReceived?: (payload: SuggestedResponsePayload) => void;
/** Optional extra class applied to the widget root. */
className?: string;
}
61 changes: 61 additions & 0 deletions packages/contact-center/ai-assistant/src/ai-assistant/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, {useCallback} from 'react';
import store from '@webex/cc-store';
import {observer} from 'mobx-react-lite';
import {ErrorBoundary} from 'react-error-boundary';

import {AIAssistantComponent} from '@webex/cc-components';
import type {AIAssistantFeedbackEvent} from '@webex/cc-components';
import type {SuggestedResponsePayload} from '@webex/cc-store';
import {useAiAssistant, SUGGESTED_RESPONSES_FLAG} from '../helper';
import {IAIAssistantProps} from '../ai-assistant.types';

const AIAssistantInternal: React.FunctionComponent<IAIAssistantProps> = observer((props) => {
const {currentTask, agentId, featureFlags, suggestedResponses} = store;
const interactionId = currentTask?.data?.interactionId;
const isFeatureEnabled = Boolean(featureFlags?.[SUGGESTED_RESPONSES_FLAG]);
const suggestions = interactionId ? suggestedResponses?.[interactionId] || [] : [];

const hookProps = useAiAssistant({
...props,
interactionId,
agentId,
isFeatureEnabled,
suggestions,
});

const handleSuggestionFeedback = useCallback(
(event: AIAssistantFeedbackEvent, suggestion: SuggestedResponsePayload) => {
if (!interactionId) return;
store.sendSuggestionFeedback?.({
interactionId,
adaptiveCardId: suggestion?.data?.adaptiveCardId,
trackingId: suggestion?.data?.trackingId,
actionId: event.actionId,
actionType: 'Action.Submit',
});
},
[interactionId]
);

return (
<AIAssistantComponent
{...hookProps}
isFeatureEnabled={isFeatureEnabled}
className={props.className}
onSuggestionFeedback={handleSuggestionFeedback}
/>
);
});

const AIAssistant: React.FunctionComponent<IAIAssistantProps> = (props) => (
<ErrorBoundary
fallbackRender={() => <></>}
onError={(error: Error) => {
if (store.onErrorCallback) store.onErrorCallback('AIAssistant', error);
}}
>
<AIAssistantInternal {...props} />
</ErrorBoundary>
);

export {AIAssistant};
Loading
Loading