From 136e5504231467a221593c537339ca8dde638c13 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Mon, 7 Jul 2025 11:49:05 +0530 Subject: [PATCH 1/9] fix(ui-metrics): @webex/ui-metrics package --- .../contact-center/cc-components/package.json | 2 +- .../contact-center/cc-widgets/package.json | 2 +- .../contact-center/station-login/package.json | 3 +- .../station-login/src/station-login/index.tsx | 7 +- packages/contact-center/store/package.json | 2 +- packages/contact-center/task/package.json | 3 +- .../task/src/CallControl/index.tsx | 5 +- .../task/src/CallControlCAD/index.tsx | 4 +- .../task/src/IncomingTask/index.tsx | 4 +- .../task/src/OutdialCall/index.tsx | 4 +- .../task/src/TaskList/index.tsx | 4 +- .../contact-center/ui-metrics/package.json | 35 +++++++++ .../contact-center/ui-metrics/src/index.ts | 6 ++ .../ui-metrics/src/metricsLogger.ts | 72 +++++++++++++++++++ .../ui-metrics/src/withMetrics.tsx | 47 ++++++++++++ .../contact-center/ui-metrics/tsconfig.json | 11 +++ .../ui-metrics/webpack.config.js | 12 ++++ .../contact-center/user-state/package.json | 3 +- .../user-state/src/user-state/index.tsx | 4 +- .../cc/samples-cc-react-app/webpack.config.js | 1 + yarn.lock | 24 +++++++ 21 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 packages/contact-center/ui-metrics/package.json create mode 100644 packages/contact-center/ui-metrics/src/index.ts create mode 100644 packages/contact-center/ui-metrics/src/metricsLogger.ts create mode 100644 packages/contact-center/ui-metrics/src/withMetrics.tsx create mode 100644 packages/contact-center/ui-metrics/tsconfig.json create mode 100644 packages/contact-center/ui-metrics/webpack.config.js diff --git a/packages/contact-center/cc-components/package.json b/packages/contact-center/cc-components/package.json index eab9d347e..0755dc3e5 100644 --- a/packages/contact-center/cc-components/package.json +++ b/packages/contact-center/cc-components/package.json @@ -65,4 +65,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/cc-widgets/package.json b/packages/contact-center/cc-widgets/package.json index 6d8971840..02a603aa3 100644 --- a/packages/contact-center/cc-widgets/package.json +++ b/packages/contact-center/cc-widgets/package.json @@ -86,4 +86,4 @@ "^.+\\.(css|less|scss)$": "babel-jest" } } -} \ No newline at end of file +} diff --git a/packages/contact-center/station-login/package.json b/packages/contact-center/station-login/package.json index fe3211825..1b46870a1 100644 --- a/packages/contact-center/station-login/package.json +++ b/packages/contact-center/station-login/package.json @@ -22,6 +22,7 @@ "dependencies": { "@webex/cc-components": "workspace:*", "@webex/cc-store": "workspace:*", + "@webex/ui-metrics": "workspace:*", "mobx-react-lite": "^4.1.0" }, "devDependencies": { @@ -63,4 +64,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/station-login/src/station-login/index.tsx b/packages/contact-center/station-login/src/station-login/index.tsx index 98293bef4..cd7a60bf2 100644 --- a/packages/contact-center/station-login/src/station-login/index.tsx +++ b/packages/contact-center/station-login/src/station-login/index.tsx @@ -5,6 +5,7 @@ import {observer} from 'mobx-react-lite'; import {StationLoginComponent} from '@webex/cc-components'; import {useStationLogin} from '../helper'; import {StationLoginProps} from './station-login.types'; +import {withMetrics} from '@webex/ui-metrics'; const StationLogin: React.FunctionComponent = observer( ({onLogin, onLogout, onCCSignOut, profileMode, onSaveStart, onSaveEnd, doStationLogout}) => { @@ -22,6 +23,7 @@ const StationLogin: React.FunctionComponent = observer( teamId, setTeamId, } = store; + const result = useStationLogin({ cc, onLogin, @@ -38,6 +40,7 @@ const StationLogin: React.FunctionComponent = observer( }); const dialNumberRegex = cc?.agentConfig?.regexUS; + const props = { ...result, setDeviceType, @@ -54,7 +57,9 @@ const StationLogin: React.FunctionComponent = observer( logger, profileMode, }; - return ; + + const StationLoginWithMetrics = withMetrics(StationLoginComponent, 'StationLogin'); + return ; } ); diff --git a/packages/contact-center/store/package.json b/packages/contact-center/store/package.json index 01e7b3731..0934a1a67 100644 --- a/packages/contact-center/store/package.json +++ b/packages/contact-center/store/package.json @@ -57,4 +57,4 @@ "webpack-cli": "5.1.4", "webpack-merge": "6.0.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/task/package.json b/packages/contact-center/task/package.json index 07c4583ad..c52cfce8b 100644 --- a/packages/contact-center/task/package.json +++ b/packages/contact-center/task/package.json @@ -22,6 +22,7 @@ "dependencies": { "@webex/cc-components": "workspace:*", "@webex/cc-store": "workspace:*", + "@webex/ui-metrics": "workspace:*", "mobx-react-lite": "^4.1.0" }, "devDependencies": { @@ -62,4 +63,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/task/src/CallControl/index.tsx b/packages/contact-center/task/src/CallControl/index.tsx index 82929a599..992faac03 100644 --- a/packages/contact-center/task/src/CallControl/index.tsx +++ b/packages/contact-center/task/src/CallControl/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {observer} from 'mobx-react-lite'; +import {withMetrics} from '@webex/ui-metrics'; import store from '@webex/cc-store'; import {useCallControl} from '../helper'; import {CallControlProps} from '../task.types'; @@ -45,7 +46,9 @@ const CallControl: React.FunctionComponent = observer( allowConsultToQueue, logger, }; - return ; + + const CallControlWithMetrics = withMetrics(CallControlComponent, 'CallControl'); + return ; } ); diff --git a/packages/contact-center/task/src/CallControlCAD/index.tsx b/packages/contact-center/task/src/CallControlCAD/index.tsx index 29d395a1f..7c0f6f0a3 100644 --- a/packages/contact-center/task/src/CallControlCAD/index.tsx +++ b/packages/contact-center/task/src/CallControlCAD/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {observer} from 'mobx-react-lite'; +import {withMetrics} from '@webex/ui-metrics'; import store from '@webex/cc-store'; import {useCallControl} from '../helper'; import {CallControlProps} from '../task.types'; @@ -47,7 +48,8 @@ const CallControlCAD: React.FunctionComponent = observer( logger, }; - return ; + const CallControlCADWithMetrics = withMetrics(CallControlCADComponent, 'CallControlCAD'); + return ; } ); diff --git a/packages/contact-center/task/src/IncomingTask/index.tsx b/packages/contact-center/task/src/IncomingTask/index.tsx index 6dfe5d4db..7d3aa7338 100644 --- a/packages/contact-center/task/src/IncomingTask/index.tsx +++ b/packages/contact-center/task/src/IncomingTask/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {observer} from 'mobx-react-lite'; +import {withMetrics} from '@webex/ui-metrics'; import store from '@webex/cc-store'; import {useIncomingTask} from '../helper'; import {IncomingTaskComponent} from '@webex/cc-components'; @@ -15,7 +16,8 @@ const IncomingTask: React.FunctionComponent = observer(({inco logger, }; - return ; + const IncomingTaskWithMetrics = withMetrics(IncomingTaskComponent, 'IncomingTask'); + return ; }); export {IncomingTask}; diff --git a/packages/contact-center/task/src/OutdialCall/index.tsx b/packages/contact-center/task/src/OutdialCall/index.tsx index aa6eff781..5d8506f16 100644 --- a/packages/contact-center/task/src/OutdialCall/index.tsx +++ b/packages/contact-center/task/src/OutdialCall/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import store from '@webex/cc-store'; import {observer} from 'mobx-react-lite'; +import {withMetrics} from '@webex/ui-metrics'; import {OutdialCallComponent} from '@webex/cc-components'; import {useOutdialCall} from '../helper'; @@ -12,7 +13,8 @@ const OutdialCall: React.FunctionComponent = observer(() => { ...result, }; - return ; + const OutdialCallWithMetrics = withMetrics(OutdialCallComponent, 'OutdialCall'); + return ; }); export {OutdialCall}; diff --git a/packages/contact-center/task/src/TaskList/index.tsx b/packages/contact-center/task/src/TaskList/index.tsx index 6d3ed878d..b7f22655f 100644 --- a/packages/contact-center/task/src/TaskList/index.tsx +++ b/packages/contact-center/task/src/TaskList/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import store from '@webex/cc-store'; import {observer} from 'mobx-react-lite'; +import {withMetrics} from '@webex/ui-metrics'; import {TaskListComponent} from '@webex/cc-components'; import {useTaskList} from '../helper'; @@ -17,7 +18,8 @@ const TaskList: React.FunctionComponent = observer( logger, }; - return ; + const TaskListWithMetrics = withMetrics(TaskListComponent, 'TaskList'); + return ; } ); diff --git a/packages/contact-center/ui-metrics/package.json b/packages/contact-center/ui-metrics/package.json new file mode 100644 index 000000000..391eeb1d8 --- /dev/null +++ b/packages/contact-center/ui-metrics/package.json @@ -0,0 +1,35 @@ +{ + "name": "@webex/ui-metrics", + "version": "1.0.0", + "description": "UI metrics tracking for Webex widgets", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build:src": "webpack --mode=development", + "clean": "rm -rf dist", + "clean:dist": "rm -rf dist", + "test:unit": "jest", + "test:styles": "echo 'No styles to test'" + }, + "dependencies": { + "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/uuid": "^9.0.0", + "typescript": "^5.6.3", + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "keywords": [ + "webex", + "widgets", + "metrics", + "analytics", + "ui-tracking" + ], + "author": "devsupport@webex.com", + "license": "MIT" +} diff --git a/packages/contact-center/ui-metrics/src/index.ts b/packages/contact-center/ui-metrics/src/index.ts new file mode 100644 index 000000000..dcf8a1914 --- /dev/null +++ b/packages/contact-center/ui-metrics/src/index.ts @@ -0,0 +1,6 @@ +import withMetrics from './withMetrics'; +import {WidgetMetrics} from './metricsLogger'; +import {logMetrics, havePropsChanged, sanitizePropsForLogging} from './metricsLogger'; + +export {logMetrics, havePropsChanged, sanitizePropsForLogging, withMetrics}; +export type {WidgetMetrics}; diff --git a/packages/contact-center/ui-metrics/src/metricsLogger.ts b/packages/contact-center/ui-metrics/src/metricsLogger.ts new file mode 100644 index 000000000..6a683ba82 --- /dev/null +++ b/packages/contact-center/ui-metrics/src/metricsLogger.ts @@ -0,0 +1,72 @@ +export type WidgetMetrics = { + widgetName: string; + event: + | 'WIDGET_LOADED' + | 'PROP_RECEIVED' + | 'INTERACTION' + | 'ERROR' + | 'WIDGET_INITIALIZED' + | 'WIDGET_PROPS_RECEIVED' + | 'WIDGET_RENDER_START' + | 'WIDGET_RENDER_COMPLETE' + | 'WIDGET_UNMOUNTED' + | 'WIDGET_PROP_UPDATED'; + props?: Record; + timestamp: number; + additionalContext?: Record; +}; + +export const logMetrics = (metric: WidgetMetrics) => { + console.log('[WIDGET_METRICS]', metric, null, 2); + // Optional: send to Amplitude or a log endpoint +}; + +export function havePropsChanged(prev: any, next: any): boolean { + if (prev === next) return false; + + // Do shallow comparison first + if (typeof prev !== typeof next) return true; + if (!prev || !next) return prev !== next; + + const prevKeys = Object.keys(prev); + const nextKeys = Object.keys(next); + + if (prevKeys.length !== nextKeys.length) return true; + + // Check if any primitive values changed + for (const key of prevKeys) { + const prevVal = prev[key]; + const nextVal = next[key]; + + if (prevVal === nextVal) continue; + if (typeof prevVal !== 'object' || prevVal === null) return true; + if (typeof nextVal !== 'object' || nextVal === null) return true; + } + + // Fall back to deep comparison only for objects that passed shallow check + try { + return JSON.stringify(prev) !== JSON.stringify(next); + } catch { + return true; // fallback to log if circular structure + } +} + +export function sanitizePropsForLogging(obj: Record, depth = 0): Record { + if (depth > 2) return {}; // prevent deep nesting issues + + const result: Record = {}; + + for (const key in obj) { + const value = obj[key]; + + if (typeof value === 'function') { + result[key] = '[Function]'; + } else if (value && typeof value === 'object') { + result[key] = sanitizePropsForLogging(value, depth + 1); + } else { + result[key] = value; + } + } + + return result; +} diff --git a/packages/contact-center/ui-metrics/src/withMetrics.tsx b/packages/contact-center/ui-metrics/src/withMetrics.tsx new file mode 100644 index 000000000..320c624d2 --- /dev/null +++ b/packages/contact-center/ui-metrics/src/withMetrics.tsx @@ -0,0 +1,47 @@ +import React, {useEffect, useRef} from 'react'; +import {havePropsChanged, logMetrics} from './metricsLogger'; + +export default function withMetrics

(Component: any, widgetName: string) { + return React.memo( + (props: P) => { + const previousProps = useRef

(); + + // Handle mount and unmount events + useEffect(() => { + logMetrics({ + widgetName, + event: 'WIDGET_INITIALIZED', + props, + timestamp: Date.now(), + }); + + return () => { + logMetrics({ + widgetName, + event: 'WIDGET_UNMOUNTED', + timestamp: Date.now(), + }); + }; + }, []); + + // Handle prop updates + useEffect(() => { + if (previousProps.current && havePropsChanged(previousProps.current, props)) { + logMetrics({ + widgetName, + event: 'WIDGET_PROP_UPDATED', + props, + timestamp: Date.now(), + }); + } + previousProps.current = props; + }); + + return ; + }, + (prevProps, nextProps) => { + const hasChanged = havePropsChanged(prevProps, nextProps); + return !hasChanged; + } + ); +} diff --git a/packages/contact-center/ui-metrics/tsconfig.json b/packages/contact-center/ui-metrics/tsconfig.json new file mode 100644 index 000000000..50b68ce6a --- /dev/null +++ b/packages/contact-center/ui-metrics/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.json", + "include": [ + "./src" + ], + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "declarationDir": "./dist/types" + }, +} \ No newline at end of file diff --git a/packages/contact-center/ui-metrics/webpack.config.js b/packages/contact-center/ui-metrics/webpack.config.js new file mode 100644 index 000000000..247cf2783 --- /dev/null +++ b/packages/contact-center/ui-metrics/webpack.config.js @@ -0,0 +1,12 @@ +const {merge} = require('webpack-merge'); +const path = require('path'); + +const baseConfig = require('../../../webpack.config'); + +module.exports = merge(baseConfig, { + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', // Set the output filename to index.js + libraryTarget: 'commonjs2', + }, +}); diff --git a/packages/contact-center/user-state/package.json b/packages/contact-center/user-state/package.json index cc4f46cdc..23b86ce1e 100644 --- a/packages/contact-center/user-state/package.json +++ b/packages/contact-center/user-state/package.json @@ -22,6 +22,7 @@ "dependencies": { "@webex/cc-components": "workspace:*", "@webex/cc-store": "workspace:*", + "@webex/ui-metrics": "workspace:*", "mobx-react-lite": "^4.1.0", "typescript": "5.6.3" }, @@ -61,4 +62,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/user-state/src/user-state/index.tsx b/packages/contact-center/user-state/src/user-state/index.tsx index 6e03ad675..046597202 100644 --- a/packages/contact-center/user-state/src/user-state/index.tsx +++ b/packages/contact-center/user-state/src/user-state/index.tsx @@ -5,6 +5,7 @@ import {observer} from 'mobx-react-lite'; import {useUserState} from '../helper'; import {UserStateComponent, IUserState} from '@webex/cc-components'; import {IUserStateProps} from '../user-state.types'; +import {withMetrics} from '@webex/ui-metrics'; const UserState: React.FunctionComponent = observer(({onStateChange}) => { const { @@ -35,7 +36,8 @@ const UserState: React.FunctionComponent = observer(({onStateCh logger, }; - return ; + const UserStateWithMetrics = withMetrics(UserStateComponent, 'UserState'); + return ; }); export {UserState}; diff --git a/widgets-samples/cc/samples-cc-react-app/webpack.config.js b/widgets-samples/cc/samples-cc-react-app/webpack.config.js index cfd17aaf2..ef43552bc 100644 --- a/widgets-samples/cc/samples-cc-react-app/webpack.config.js +++ b/widgets-samples/cc/samples-cc-react-app/webpack.config.js @@ -17,6 +17,7 @@ module.exports = merge(baseConfig, { '@webex/cc-user-state': path.resolve(__dirname, '../../../packages/contact-center/user-state/src'), '@webex/cc-task': path.resolve(__dirname, '../../../packages/contact-center/task/src'), '@webex/cc-components': path.resolve(__dirname, '../../../packages/contact-center/cc-components/src'), + '@webex/ui-metrics': path.resolve(__dirname, '../../../packages/contact-center/ui-metrics/src'), }, }, module: { diff --git a/yarn.lock b/yarn.lock index f84429f86..5ebbc7357 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8516,6 +8516,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^9.0.0": + version: 9.0.8 + resolution: "@types/uuid@npm:9.0.8" + checksum: 10c0/b411b93054cb1d4361919579ef3508a1f12bf15b5fdd97337d3d351bece6c921b52b6daeef89b62340fd73fd60da407878432a1af777f40648cbe53a01723489 + languageName: node + linkType: hard + "@types/validator@npm:^13.1.3": version: 13.12.2 resolution: "@types/validator@npm:13.12.2" @@ -9446,6 +9453,7 @@ __metadata: "@types/react-test-renderer": "npm:18" "@webex/cc-components": "workspace:*" "@webex/cc-store": "workspace:*" + "@webex/ui-metrics": "workspace:*" babel-jest: "npm:29.7.0" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -9531,6 +9539,7 @@ __metadata: "@types/react-test-renderer": "npm:18" "@webex/cc-components": "workspace:*" "@webex/cc-store": "workspace:*" + "@webex/ui-metrics": "workspace:*" babel-jest: "npm:29.7.0" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -9576,6 +9585,7 @@ __metadata: "@types/react-test-renderer": "npm:18" "@webex/cc-components": "workspace:*" "@webex/cc-store": "workspace:*" + "@webex/ui-metrics": "workspace:*" babel-jest: "npm:29.7.0" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -12295,6 +12305,20 @@ __metadata: languageName: node linkType: hard +"@webex/ui-metrics@workspace:*, @webex/ui-metrics@workspace:packages/contact-center/ui-metrics": + version: 0.0.0-use.local + resolution: "@webex/ui-metrics@workspace:packages/contact-center/ui-metrics" + dependencies: + "@types/uuid": "npm:^9.0.0" + typescript: "npm:^5.6.3" + uuid: "npm:^9.0.0" + webpack: "npm:^5.96.1" + webpack-cli: "npm:^5.1.4" + peerDependencies: + react: ">=16.8.0" + languageName: unknown + linkType: soft + "@webex/web-capabilities@npm:^1.1.0, @webex/web-capabilities@npm:^1.4.0, @webex/web-capabilities@npm:^1.4.1": version: 1.4.1 resolution: "@webex/web-capabilities@npm:1.4.1" From 032fd092fb48c5242bc93642cd6254f51c8717f8 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Mon, 21 Jul 2025 11:28:41 +0530 Subject: [PATCH 2/9] fix(ui-metrics): metrics rerendering issue --- .../contact-center/cc-components/package.json | 3 +- .../components/StationLogin/station-login.tsx | 4 +- .../src/components/UserState/user-state.tsx | 4 +- .../task/CallControl/call-control.tsx | 4 +- .../task/CallControlCAD/call-control-cad.tsx | 4 +- .../task/IncomingTask/incoming-task.tsx | 4 +- .../task/OutdialCall/outdial-call.tsx | 4 +- .../components/task/TaskList/task-list.tsx | 4 +- .../contact-center/station-login/package.json | 1 - .../station-login/src/station-login/index.tsx | 4 +- packages/contact-center/task/package.json | 1 - .../task/src/CallControl/index.tsx | 4 +- .../task/src/CallControlCAD/index.tsx | 4 +- .../task/src/IncomingTask/index.tsx | 4 +- .../task/src/OutdialCall/index.tsx | 4 +- .../task/src/TaskList/index.tsx | 4 +- .../contact-center/ui-metrics/package.json | 1 + .../contact-center/ui-metrics/src/index.ts | 3 +- .../ui-metrics/src/metricsLogger.ts | 39 ++++--------------- .../ui-metrics/src/withMetrics.tsx | 22 +---------- .../contact-center/user-state/package.json | 1 - .../user-state/src/user-state/index.tsx | 4 +- webpack.config.js | 15 ++++--- yarn.lock | 5 +-- 24 files changed, 52 insertions(+), 95 deletions(-) diff --git a/packages/contact-center/cc-components/package.json b/packages/contact-center/cc-components/package.json index e3d114fbc..eea2fee23 100644 --- a/packages/contact-center/cc-components/package.json +++ b/packages/contact-center/cc-components/package.json @@ -26,7 +26,8 @@ "dependencies": { "@momentum-ui/illustrations": "^1.24.0", "@r2wc/react-to-web-component": "2.0.3", - "@webex/cc-store": "workspace:*" + "@webex/cc-store": "workspace:*", + "@webex/ui-metrics": "workspace:*" }, "devDependencies": { "@babel/core": "7.25.2", diff --git a/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx b/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx index e0ff392fe..adbc2b353 100644 --- a/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx +++ b/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx @@ -16,6 +16,7 @@ import { updateDialNumberLabel, handleCCSignoutKeyDown, } from './station-login.utils'; +import {withMetrics} from '@webex/ui-metrics'; const StationLoginComponent: React.FunctionComponent = (props) => { const { @@ -343,4 +344,5 @@ const StationLoginComponent: React.FunctionComponent ); }; -export default StationLoginComponent; +const StationLoginComponentWithMetrics = withMetrics(StationLoginComponent, 'StationLogin'); +export default StationLoginComponentWithMetrics; 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..74eb13a1d 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 @@ -17,6 +17,7 @@ import { getSelectedKey, buildDropdownItems, } from './user-state.utils'; +import {withMetrics} from '@webex/ui-metrics'; const UserStateComponent: React.FunctionComponent = (props) => { const { @@ -103,4 +104,5 @@ const UserStateComponent: React.FunctionComponent = (props) => { ); }; -export default UserStateComponent; +const UserStateComponentWithMetrics = withMetrics(UserStateComponent, 'UserState'); +export default UserStateComponentWithMetrics; diff --git a/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx b/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx index b8afefe27..22c202db5 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx @@ -22,6 +22,7 @@ import { SELECT, SUBMIT_WRAP_UP, } from '../constants'; +import {withMetrics} from '@webex/ui-metrics'; function CallControlComponent(props: CallControlComponentProps) { const [selectedWrapupReason, setSelectedWrapupReason] = useState(null); @@ -399,4 +400,5 @@ function CallControlComponent(props: CallControlComponentProps) { ); } -export default CallControlComponent; +const CallControlComponentWithMetrics = withMetrics(CallControlComponent, 'CallControl'); +export default CallControlComponentWithMetrics; diff --git a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx index fdacfd384..df9551578 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx @@ -19,6 +19,7 @@ import { CUSTOMER_NAME, RONA, } from '../constants'; +import {withMetrics} from '@webex/ui-metrics'; const CallControlCADComponent: React.FC = (props) => { const { @@ -214,4 +215,5 @@ const CallControlCADComponent: React.FC = (props) => ); }; -export default CallControlCADComponent; +const CallControlCADComponentWithMetrics = withMetrics(CallControlCADComponent, 'CallControlCAD'); +export default CallControlCADComponentWithMetrics; diff --git a/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx b/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx index c71e36f60..76053c044 100644 --- a/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx +++ b/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {IncomingTaskComponentProps, MEDIA_CHANNEL} from '../task.types'; import Task from '../Task'; +import {withMetrics} from '@webex/ui-metrics'; const IncomingTaskComponent: React.FunctionComponent = (props) => { const {incomingTask, isBrowser, accept, reject, logger} = props; @@ -64,4 +65,5 @@ const IncomingTaskComponent: React.FunctionComponent ); }; -export default IncomingTaskComponent; +const IncomingTaskComponentWithMetrics = withMetrics(IncomingTaskComponent, 'IncomingTask'); +export default IncomingTaskComponentWithMetrics; diff --git a/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx b/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx index e75abd183..c74486dcb 100644 --- a/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx +++ b/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx @@ -1,6 +1,7 @@ import React, {useState} from 'react'; import {OutdialCallComponentProps} from '../task.types'; import './outdial-call.style.scss'; +import {withMetrics} from '@webex/ui-metrics'; const OutdialCallComponent: React.FunctionComponent = (props) => { const {startOutdial} = props; @@ -49,4 +50,5 @@ const OutdialCallComponent: React.FunctionComponent = ); }; -export default OutdialCallComponent; +const OutdialCallComponentWithMetrics = withMetrics(OutdialCallComponent, 'OutdialCall'); +export default OutdialCallComponentWithMetrics; diff --git a/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx b/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx index da77d11a2..03f242e01 100644 --- a/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx +++ b/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {TaskListComponentProps, MEDIA_CHANNEL} from '../task.types'; import Task from '../Task'; import './styles.scss'; +import {withMetrics} from '@webex/ui-metrics'; const TaskListComponent: React.FunctionComponent = (props) => { const {currentTask, taskList, acceptTask, declineTask, isBrowser, onTaskSelect, logger} = props; @@ -74,4 +75,5 @@ const TaskListComponent: React.FunctionComponent = (prop ); }; -export default TaskListComponent; +const TaskListComponentWithMetrics = withMetrics(TaskListComponent, 'TaskList'); +export default TaskListComponentWithMetrics; diff --git a/packages/contact-center/station-login/package.json b/packages/contact-center/station-login/package.json index 4e667aca5..a3eb9d378 100644 --- a/packages/contact-center/station-login/package.json +++ b/packages/contact-center/station-login/package.json @@ -22,7 +22,6 @@ "dependencies": { "@webex/cc-components": "workspace:*", "@webex/cc-store": "workspace:*", - "@webex/ui-metrics": "workspace:*", "mobx-react-lite": "^4.1.0" }, "devDependencies": { diff --git a/packages/contact-center/station-login/src/station-login/index.tsx b/packages/contact-center/station-login/src/station-login/index.tsx index cd7a60bf2..5ce1067da 100644 --- a/packages/contact-center/station-login/src/station-login/index.tsx +++ b/packages/contact-center/station-login/src/station-login/index.tsx @@ -5,7 +5,6 @@ import {observer} from 'mobx-react-lite'; import {StationLoginComponent} from '@webex/cc-components'; import {useStationLogin} from '../helper'; import {StationLoginProps} from './station-login.types'; -import {withMetrics} from '@webex/ui-metrics'; const StationLogin: React.FunctionComponent = observer( ({onLogin, onLogout, onCCSignOut, profileMode, onSaveStart, onSaveEnd, doStationLogout}) => { @@ -58,8 +57,7 @@ const StationLogin: React.FunctionComponent = observer( profileMode, }; - const StationLoginWithMetrics = withMetrics(StationLoginComponent, 'StationLogin'); - return ; + return ; } ); diff --git a/packages/contact-center/task/package.json b/packages/contact-center/task/package.json index 19e24713d..01037139f 100644 --- a/packages/contact-center/task/package.json +++ b/packages/contact-center/task/package.json @@ -22,7 +22,6 @@ "dependencies": { "@webex/cc-components": "workspace:*", "@webex/cc-store": "workspace:*", - "@webex/ui-metrics": "workspace:*", "mobx-react-lite": "^4.1.0" }, "devDependencies": { diff --git a/packages/contact-center/task/src/CallControl/index.tsx b/packages/contact-center/task/src/CallControl/index.tsx index 992faac03..ba78f948f 100644 --- a/packages/contact-center/task/src/CallControl/index.tsx +++ b/packages/contact-center/task/src/CallControl/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {observer} from 'mobx-react-lite'; -import {withMetrics} from '@webex/ui-metrics'; import store from '@webex/cc-store'; import {useCallControl} from '../helper'; import {CallControlProps} from '../task.types'; @@ -47,8 +46,7 @@ const CallControl: React.FunctionComponent = observer( logger, }; - const CallControlWithMetrics = withMetrics(CallControlComponent, 'CallControl'); - return ; + return ; } ); diff --git a/packages/contact-center/task/src/CallControlCAD/index.tsx b/packages/contact-center/task/src/CallControlCAD/index.tsx index 7c0f6f0a3..29d395a1f 100644 --- a/packages/contact-center/task/src/CallControlCAD/index.tsx +++ b/packages/contact-center/task/src/CallControlCAD/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {observer} from 'mobx-react-lite'; -import {withMetrics} from '@webex/ui-metrics'; import store from '@webex/cc-store'; import {useCallControl} from '../helper'; import {CallControlProps} from '../task.types'; @@ -48,8 +47,7 @@ const CallControlCAD: React.FunctionComponent = observer( logger, }; - const CallControlCADWithMetrics = withMetrics(CallControlCADComponent, 'CallControlCAD'); - return ; + return ; } ); diff --git a/packages/contact-center/task/src/IncomingTask/index.tsx b/packages/contact-center/task/src/IncomingTask/index.tsx index 7d3aa7338..6dfe5d4db 100644 --- a/packages/contact-center/task/src/IncomingTask/index.tsx +++ b/packages/contact-center/task/src/IncomingTask/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {observer} from 'mobx-react-lite'; -import {withMetrics} from '@webex/ui-metrics'; import store from '@webex/cc-store'; import {useIncomingTask} from '../helper'; import {IncomingTaskComponent} from '@webex/cc-components'; @@ -16,8 +15,7 @@ const IncomingTask: React.FunctionComponent = observer(({inco logger, }; - const IncomingTaskWithMetrics = withMetrics(IncomingTaskComponent, 'IncomingTask'); - return ; + return ; }); export {IncomingTask}; diff --git a/packages/contact-center/task/src/OutdialCall/index.tsx b/packages/contact-center/task/src/OutdialCall/index.tsx index 5d8506f16..aa6eff781 100644 --- a/packages/contact-center/task/src/OutdialCall/index.tsx +++ b/packages/contact-center/task/src/OutdialCall/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import store from '@webex/cc-store'; import {observer} from 'mobx-react-lite'; -import {withMetrics} from '@webex/ui-metrics'; import {OutdialCallComponent} from '@webex/cc-components'; import {useOutdialCall} from '../helper'; @@ -13,8 +12,7 @@ const OutdialCall: React.FunctionComponent = observer(() => { ...result, }; - const OutdialCallWithMetrics = withMetrics(OutdialCallComponent, 'OutdialCall'); - return ; + return ; }); export {OutdialCall}; diff --git a/packages/contact-center/task/src/TaskList/index.tsx b/packages/contact-center/task/src/TaskList/index.tsx index b7f22655f..6d3ed878d 100644 --- a/packages/contact-center/task/src/TaskList/index.tsx +++ b/packages/contact-center/task/src/TaskList/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import store from '@webex/cc-store'; import {observer} from 'mobx-react-lite'; -import {withMetrics} from '@webex/ui-metrics'; import {TaskListComponent} from '@webex/cc-components'; import {useTaskList} from '../helper'; @@ -18,8 +17,7 @@ const TaskList: React.FunctionComponent = observer( logger, }; - const TaskListWithMetrics = withMetrics(TaskListComponent, 'TaskList'); - return ; + return ; } ); diff --git a/packages/contact-center/ui-metrics/package.json b/packages/contact-center/ui-metrics/package.json index 391eeb1d8..c172d51b7 100644 --- a/packages/contact-center/ui-metrics/package.json +++ b/packages/contact-center/ui-metrics/package.json @@ -12,6 +12,7 @@ "test:styles": "echo 'No styles to test'" }, "dependencies": { + "@webex/cc-store": "workspace:*", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/packages/contact-center/ui-metrics/src/index.ts b/packages/contact-center/ui-metrics/src/index.ts index dcf8a1914..522b06d0b 100644 --- a/packages/contact-center/ui-metrics/src/index.ts +++ b/packages/contact-center/ui-metrics/src/index.ts @@ -1,6 +1,5 @@ import withMetrics from './withMetrics'; import {WidgetMetrics} from './metricsLogger'; -import {logMetrics, havePropsChanged, sanitizePropsForLogging} from './metricsLogger'; -export {logMetrics, havePropsChanged, sanitizePropsForLogging, withMetrics}; +export {withMetrics}; export type {WidgetMetrics}; diff --git a/packages/contact-center/ui-metrics/src/metricsLogger.ts b/packages/contact-center/ui-metrics/src/metricsLogger.ts index 6a683ba82..36a0ead8f 100644 --- a/packages/contact-center/ui-metrics/src/metricsLogger.ts +++ b/packages/contact-center/ui-metrics/src/metricsLogger.ts @@ -1,23 +1,18 @@ +import store from '@webex/cc-store'; + export type WidgetMetrics = { widgetName: string; - event: - | 'WIDGET_LOADED' - | 'PROP_RECEIVED' - | 'INTERACTION' - | 'ERROR' - | 'WIDGET_INITIALIZED' - | 'WIDGET_PROPS_RECEIVED' - | 'WIDGET_RENDER_START' - | 'WIDGET_RENDER_COMPLETE' - | 'WIDGET_UNMOUNTED' - | 'WIDGET_PROP_UPDATED'; + event: 'WIDGET_MOUNTED' | 'ERROR' | 'WIDGET_UNMOUNTED'; props?: Record; timestamp: number; additionalContext?: Record; }; export const logMetrics = (metric: WidgetMetrics) => { - console.log('[WIDGET_METRICS]', metric, null, 2); + store.logger.log(`CC-Widgets: UI Metrics: ${JSON.stringify(metric, null, 2)}`, { + module: 'metricsLogger.tsx', + method: 'logMetrics', + }); // Optional: send to Amplitude or a log endpoint }; @@ -50,23 +45,3 @@ export function havePropsChanged(prev: any, next: any): boolean { return true; // fallback to log if circular structure } } - -export function sanitizePropsForLogging(obj: Record, depth = 0): Record { - if (depth > 2) return {}; // prevent deep nesting issues - - const result: Record = {}; - - for (const key in obj) { - const value = obj[key]; - - if (typeof value === 'function') { - result[key] = '[Function]'; - } else if (value && typeof value === 'object') { - result[key] = sanitizePropsForLogging(value, depth + 1); - } else { - result[key] = value; - } - } - - return result; -} diff --git a/packages/contact-center/ui-metrics/src/withMetrics.tsx b/packages/contact-center/ui-metrics/src/withMetrics.tsx index 320c624d2..5bad8d91f 100644 --- a/packages/contact-center/ui-metrics/src/withMetrics.tsx +++ b/packages/contact-center/ui-metrics/src/withMetrics.tsx @@ -6,12 +6,10 @@ export default function withMetrics

(Component: any, widgetName (props: P) => { const previousProps = useRef

(); - // Handle mount and unmount events useEffect(() => { logMetrics({ widgetName, - event: 'WIDGET_INITIALIZED', - props, + event: 'WIDGET_MOUNTED', timestamp: Date.now(), }); @@ -24,24 +22,8 @@ export default function withMetrics

(Component: any, widgetName }; }, []); - // Handle prop updates - useEffect(() => { - if (previousProps.current && havePropsChanged(previousProps.current, props)) { - logMetrics({ - widgetName, - event: 'WIDGET_PROP_UPDATED', - props, - timestamp: Date.now(), - }); - } - previousProps.current = props; - }); - return ; }, - (prevProps, nextProps) => { - const hasChanged = havePropsChanged(prevProps, nextProps); - return !hasChanged; - } + (prevProps, nextProps) => !havePropsChanged(prevProps, nextProps) ); } diff --git a/packages/contact-center/user-state/package.json b/packages/contact-center/user-state/package.json index e6b05cef9..76050ed28 100644 --- a/packages/contact-center/user-state/package.json +++ b/packages/contact-center/user-state/package.json @@ -22,7 +22,6 @@ "dependencies": { "@webex/cc-components": "workspace:*", "@webex/cc-store": "workspace:*", - "@webex/ui-metrics": "workspace:*", "mobx-react-lite": "^4.1.0", "typescript": "5.6.3" }, diff --git a/packages/contact-center/user-state/src/user-state/index.tsx b/packages/contact-center/user-state/src/user-state/index.tsx index 046597202..6e03ad675 100644 --- a/packages/contact-center/user-state/src/user-state/index.tsx +++ b/packages/contact-center/user-state/src/user-state/index.tsx @@ -5,7 +5,6 @@ import {observer} from 'mobx-react-lite'; import {useUserState} from '../helper'; import {UserStateComponent, IUserState} from '@webex/cc-components'; import {IUserStateProps} from '../user-state.types'; -import {withMetrics} from '@webex/ui-metrics'; const UserState: React.FunctionComponent = observer(({onStateChange}) => { const { @@ -36,8 +35,7 @@ const UserState: React.FunctionComponent = observer(({onStateCh logger, }; - const UserStateWithMetrics = withMetrics(UserStateComponent, 'UserState'); - return ; + return ; }); export {UserState}; diff --git a/webpack.config.js b/webpack.config.js index 3bbe67e5d..9d020e6b3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,7 +16,7 @@ module.exports = { vm: require.resolve('vm-browserify'), util: require.resolve('util/'), url: require.resolve('url/'), - } + }, }, plugins: [ new webpack.ProvidePlugin({ @@ -38,12 +38,15 @@ module.exports = { { test: /\.scss$/, use: [ - "style-loader", // Injects styles into DOM - "css-loader", // Turns CSS into CommonJS - "sass-loader" // Compiles Sass to CSS + 'style-loader', // Injects styles into DOM + 'css-loader', // Turns CSS into CommonJS + 'sass-loader', // Compiles Sass to CSS ], - exclude: /node_modules/ - } + exclude: /node_modules/, + }, ], }, + stats: { + warningsFilter: [/sass-loader/], + }, }; diff --git a/yarn.lock b/yarn.lock index ba87ce679..660d723a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9409,6 +9409,7 @@ __metadata: "@types/jest": "npm:29.5.14" "@types/react-test-renderer": "npm:18" "@webex/cc-store": "workspace:*" + "@webex/ui-metrics": "workspace:*" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" eslint-config-prettier: "npm:^10.0.1" @@ -9453,7 +9454,6 @@ __metadata: "@types/react-test-renderer": "npm:18" "@webex/cc-components": "workspace:*" "@webex/cc-store": "workspace:*" - "@webex/ui-metrics": "workspace:*" babel-jest: "npm:29.7.0" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -9539,7 +9539,6 @@ __metadata: "@types/react-test-renderer": "npm:18" "@webex/cc-components": "workspace:*" "@webex/cc-store": "workspace:*" - "@webex/ui-metrics": "workspace:*" babel-jest: "npm:29.7.0" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -9585,7 +9584,6 @@ __metadata: "@types/react-test-renderer": "npm:18" "@webex/cc-components": "workspace:*" "@webex/cc-store": "workspace:*" - "@webex/ui-metrics": "workspace:*" babel-jest: "npm:29.7.0" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -12310,6 +12308,7 @@ __metadata: resolution: "@webex/ui-metrics@workspace:packages/contact-center/ui-metrics" dependencies: "@types/uuid": "npm:^9.0.0" + "@webex/cc-store": "workspace:*" typescript: "npm:^5.6.3" uuid: "npm:^9.0.0" webpack: "npm:^5.96.1" From e800f38e7a0fd1f7df462455f9169f5b74d539a1 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Tue, 22 Jul 2025 14:07:45 +0530 Subject: [PATCH 3/9] feat(cc-ui-metrics): change name to cc-ui-metrics --- .../contact-center/cc-components/package.json | 2 +- .../components/StationLogin/station-login.tsx | 2 +- .../src/components/UserState/user-state.tsx | 2 +- .../task/CallControl/call-control.tsx | 2 +- .../task/CallControlCAD/call-control-cad.tsx | 2 +- .../task/IncomingTask/incoming-task.tsx | 2 +- .../task/OutdialCall/outdial-call.tsx | 2 +- .../components/task/TaskList/task-list.tsx | 2 +- .../cc/samples-cc-react-app/webpack.config.js | 2 +- yarn.lock | 47 ++++++++++--------- 10 files changed, 33 insertions(+), 32 deletions(-) diff --git a/packages/contact-center/cc-components/package.json b/packages/contact-center/cc-components/package.json index eea2fee23..23ad630d5 100644 --- a/packages/contact-center/cc-components/package.json +++ b/packages/contact-center/cc-components/package.json @@ -27,7 +27,7 @@ "@momentum-ui/illustrations": "^1.24.0", "@r2wc/react-to-web-component": "2.0.3", "@webex/cc-store": "workspace:*", - "@webex/ui-metrics": "workspace:*" + "@webex/cc-ui-metrics": "workspace:*" }, "devDependencies": { "@babel/core": "7.25.2", diff --git a/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx b/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx index adbc2b353..9d48901ef 100644 --- a/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx +++ b/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx @@ -16,7 +16,7 @@ import { updateDialNumberLabel, handleCCSignoutKeyDown, } from './station-login.utils'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; const StationLoginComponent: React.FunctionComponent = (props) => { const { 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 74eb13a1d..38d00604e 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 @@ -17,7 +17,7 @@ import { getSelectedKey, buildDropdownItems, } from './user-state.utils'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; const UserStateComponent: React.FunctionComponent = (props) => { const { diff --git a/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx b/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx index 22c202db5..fac96a8f2 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx @@ -22,7 +22,7 @@ import { SELECT, SUBMIT_WRAP_UP, } from '../constants'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; function CallControlComponent(props: CallControlComponentProps) { const [selectedWrapupReason, setSelectedWrapupReason] = useState(null); diff --git a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx index df9551578..784c77fd3 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx @@ -19,7 +19,7 @@ import { CUSTOMER_NAME, RONA, } from '../constants'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; const CallControlCADComponent: React.FC = (props) => { const { diff --git a/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx b/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx index 76053c044..a25aae918 100644 --- a/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx +++ b/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {IncomingTaskComponentProps, MEDIA_CHANNEL} from '../task.types'; import Task from '../Task'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; const IncomingTaskComponent: React.FunctionComponent = (props) => { const {incomingTask, isBrowser, accept, reject, logger} = props; diff --git a/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx b/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx index c74486dcb..b40fcb979 100644 --- a/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx +++ b/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx @@ -1,7 +1,7 @@ import React, {useState} from 'react'; import {OutdialCallComponentProps} from '../task.types'; import './outdial-call.style.scss'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; const OutdialCallComponent: React.FunctionComponent = (props) => { const {startOutdial} = props; diff --git a/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx b/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx index 03f242e01..7a33e8084 100644 --- a/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx +++ b/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {TaskListComponentProps, MEDIA_CHANNEL} from '../task.types'; import Task from '../Task'; import './styles.scss'; -import {withMetrics} from '@webex/ui-metrics'; +import {withMetrics} from '@webex/cc-ui-metrics'; const TaskListComponent: React.FunctionComponent = (props) => { const {currentTask, taskList, acceptTask, declineTask, isBrowser, onTaskSelect, logger} = props; diff --git a/widgets-samples/cc/samples-cc-react-app/webpack.config.js b/widgets-samples/cc/samples-cc-react-app/webpack.config.js index ef43552bc..b0ac80296 100644 --- a/widgets-samples/cc/samples-cc-react-app/webpack.config.js +++ b/widgets-samples/cc/samples-cc-react-app/webpack.config.js @@ -17,7 +17,7 @@ module.exports = merge(baseConfig, { '@webex/cc-user-state': path.resolve(__dirname, '../../../packages/contact-center/user-state/src'), '@webex/cc-task': path.resolve(__dirname, '../../../packages/contact-center/task/src'), '@webex/cc-components': path.resolve(__dirname, '../../../packages/contact-center/cc-components/src'), - '@webex/ui-metrics': path.resolve(__dirname, '../../../packages/contact-center/ui-metrics/src'), + '@webex/cc-ui-metrics': path.resolve(__dirname, '../../../packages/contact-center/ui-metrics/src'), }, }, module: { diff --git a/yarn.lock b/yarn.lock index 660d723a0..60fdb3a39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8516,13 +8516,6 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:^9.0.0": - version: 9.0.8 - resolution: "@types/uuid@npm:9.0.8" - checksum: 10c0/b411b93054cb1d4361919579ef3508a1f12bf15b5fdd97337d3d351bece6c921b52b6daeef89b62340fd73fd60da407878432a1af777f40648cbe53a01723489 - languageName: node - linkType: hard - "@types/validator@npm:^13.1.3": version: 13.12.2 resolution: "@types/validator@npm:13.12.2" @@ -9409,7 +9402,7 @@ __metadata: "@types/jest": "npm:29.5.14" "@types/react-test-renderer": "npm:18" "@webex/cc-store": "workspace:*" - "@webex/ui-metrics": "workspace:*" + "@webex/cc-ui-metrics": "workspace:*" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" eslint-config-prettier: "npm:^10.0.1" @@ -9568,6 +9561,29 @@ __metadata: languageName: unknown linkType: soft +"@webex/cc-ui-metrics@workspace:*, @webex/cc-ui-metrics@workspace:packages/contact-center/ui-metrics": + version: 0.0.0-use.local + resolution: "@webex/cc-ui-metrics@workspace:packages/contact-center/ui-metrics" + dependencies: + "@testing-library/dom": "npm:10.4.0" + "@testing-library/jest-dom": "npm:6.6.2" + "@testing-library/react": "npm:16.0.1" + "@types/jest": "npm:29.5.14" + "@types/react-test-renderer": "npm:18" + "@webex/cc-store": "workspace:*" + babel-jest: "npm:29.7.0" + jest: "npm:29.7.0" + jest-environment-jsdom: "npm:29.7.0" + typescript: "npm:^5.6.3" + uuid: "npm:^9.0.0" + webpack: "npm:^5.96.1" + webpack-cli: "npm:^5.1.4" + peerDependencies: + react: ">=18.3.1" + react-dom: ">=18.3.1" + languageName: unknown + linkType: soft + "@webex/cc-user-state@workspace:*, @webex/cc-user-state@workspace:packages/contact-center/user-state": version: 0.0.0-use.local resolution: "@webex/cc-user-state@workspace:packages/contact-center/user-state" @@ -12303,21 +12319,6 @@ __metadata: languageName: node linkType: hard -"@webex/ui-metrics@workspace:*, @webex/ui-metrics@workspace:packages/contact-center/ui-metrics": - version: 0.0.0-use.local - resolution: "@webex/ui-metrics@workspace:packages/contact-center/ui-metrics" - dependencies: - "@types/uuid": "npm:^9.0.0" - "@webex/cc-store": "workspace:*" - typescript: "npm:^5.6.3" - uuid: "npm:^9.0.0" - webpack: "npm:^5.96.1" - webpack-cli: "npm:^5.1.4" - peerDependencies: - react: ">=16.8.0" - languageName: unknown - linkType: soft - "@webex/web-capabilities@npm:^1.1.0, @webex/web-capabilities@npm:^1.4.0, @webex/web-capabilities@npm:^1.4.1": version: 1.4.1 resolution: "@webex/web-capabilities@npm:1.4.1" From c4982b2cc27a1668e23a4598abaa08681b191c08 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Tue, 22 Jul 2025 14:08:14 +0530 Subject: [PATCH 4/9] fix(ui-metrics): add uts --- .../contact-center/ui-metrics/bable.config.js | 3 + .../contact-center/ui-metrics/jest.config.js | 6 + .../contact-center/ui-metrics/package.json | 31 ++-- .../ui-metrics/src/metricsLogger.ts | 5 +- .../ui-metrics/src/withMetrics.tsx | 2 - .../ui-metrics/tests/metricsLogger.test.ts | 103 +++++++++++++ .../ui-metrics/tests/withMetrics.test.tsx | 137 ++++++++++++++++++ .../ui-metrics/webpack.config.js | 5 + 8 files changed, 273 insertions(+), 19 deletions(-) create mode 100644 packages/contact-center/ui-metrics/bable.config.js create mode 100644 packages/contact-center/ui-metrics/jest.config.js create mode 100644 packages/contact-center/ui-metrics/tests/metricsLogger.test.ts create mode 100644 packages/contact-center/ui-metrics/tests/withMetrics.test.tsx diff --git a/packages/contact-center/ui-metrics/bable.config.js b/packages/contact-center/ui-metrics/bable.config.js new file mode 100644 index 000000000..0eaef236c --- /dev/null +++ b/packages/contact-center/ui-metrics/bable.config.js @@ -0,0 +1,3 @@ +const baseConfig = require('../../../babel.config.js'); + +module.exports = baseConfig; diff --git a/packages/contact-center/ui-metrics/jest.config.js b/packages/contact-center/ui-metrics/jest.config.js new file mode 100644 index 000000000..4f2ecff91 --- /dev/null +++ b/packages/contact-center/ui-metrics/jest.config.js @@ -0,0 +1,6 @@ +const jestConfig = require('../../../jest.config.js'); + +jestConfig.rootDir = '../../../'; +jestConfig.testMatch = ['**/ui-metrics/tests/**/*.ts', '**/ui-metrics/tests/**/*.tsx']; + +module.exports = jestConfig; diff --git a/packages/contact-center/ui-metrics/package.json b/packages/contact-center/ui-metrics/package.json index c172d51b7..883c411e7 100644 --- a/packages/contact-center/ui-metrics/package.json +++ b/packages/contact-center/ui-metrics/package.json @@ -1,5 +1,5 @@ { - "name": "@webex/ui-metrics", + "name": "@webex/cc-ui-metrics", "version": "1.0.0", "description": "UI metrics tracking for Webex widgets", "main": "dist/index.js", @@ -8,29 +8,28 @@ "build:src": "webpack --mode=development", "clean": "rm -rf dist", "clean:dist": "rm -rf dist", - "test:unit": "jest", + "test:unit": "jest --coverage", "test:styles": "echo 'No styles to test'" }, "dependencies": { - "@webex/cc-store": "workspace:*", - "uuid": "^9.0.0" + "@webex/cc-store": "workspace:*" }, "devDependencies": { - "@types/uuid": "^9.0.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", + "babel-jest": "29.7.0", + "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", "typescript": "^5.6.3", + "uuid": "^9.0.0", "webpack": "^5.96.1", "webpack-cli": "^5.1.4" }, "peerDependencies": { - "react": ">=16.8.0" - }, - "keywords": [ - "webex", - "widgets", - "metrics", - "analytics", - "ui-tracking" - ], - "author": "devsupport@webex.com", - "license": "MIT" + "react": ">=18.3.1", + "react-dom": ">=18.3.1" + } } diff --git a/packages/contact-center/ui-metrics/src/metricsLogger.ts b/packages/contact-center/ui-metrics/src/metricsLogger.ts index 36a0ead8f..d59a25712 100644 --- a/packages/contact-center/ui-metrics/src/metricsLogger.ts +++ b/packages/contact-center/ui-metrics/src/metricsLogger.ts @@ -9,11 +9,14 @@ export type WidgetMetrics = { }; export const logMetrics = (metric: WidgetMetrics) => { + if (!store.logger) { + console.log('CC-Widgets: UI Metrics: No logger found'); + return; + } store.logger.log(`CC-Widgets: UI Metrics: ${JSON.stringify(metric, null, 2)}`, { module: 'metricsLogger.tsx', method: 'logMetrics', }); - // Optional: send to Amplitude or a log endpoint }; export function havePropsChanged(prev: any, next: any): boolean { diff --git a/packages/contact-center/ui-metrics/src/withMetrics.tsx b/packages/contact-center/ui-metrics/src/withMetrics.tsx index 5bad8d91f..12156e429 100644 --- a/packages/contact-center/ui-metrics/src/withMetrics.tsx +++ b/packages/contact-center/ui-metrics/src/withMetrics.tsx @@ -4,8 +4,6 @@ import {havePropsChanged, logMetrics} from './metricsLogger'; export default function withMetrics

(Component: any, widgetName: string) { return React.memo( (props: P) => { - const previousProps = useRef

(); - useEffect(() => { logMetrics({ widgetName, diff --git a/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts b/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts new file mode 100644 index 000000000..5bfcc28ab --- /dev/null +++ b/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts @@ -0,0 +1,103 @@ +import store from '@webex/cc-store'; +import {logMetrics, havePropsChanged, WidgetMetrics} from '../src/metricsLogger'; + +// Mock the store +jest.mock('@webex/cc-store', () => ({ + logger: { + log: jest.fn(), + }, +})); + +describe('metricsLogger', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('logMetrics', () => { + it('should log metrics when logger is available', () => { + const metric: WidgetMetrics = { + widgetName: 'TestWidget', + event: 'WIDGET_MOUNTED', + timestamp: 1234567890, + props: {test: 'prop'}, + additionalContext: {context: 'test'}, + }; + + logMetrics(metric); + + expect(store.logger.log).toHaveBeenCalledWith(`CC-Widgets: UI Metrics: ${JSON.stringify(metric, null, 2)}`, { + module: 'metricsLogger.tsx', + method: 'logMetrics', + }); + }); + + it('should handle case when logger is not available', () => { + const consoleSpy = jest.spyOn(console, 'log'); + (store as any).logger = null; + + const metric: WidgetMetrics = { + widgetName: 'TestWidget', + event: 'WIDGET_MOUNTED', + timestamp: 1234567890, + }; + + logMetrics(metric); + + expect(consoleSpy).toHaveBeenCalledWith('CC-Widgets: UI Metrics: No logger found'); + consoleSpy.mockRestore(); + }); + }); + + describe('havePropsChanged', () => { + it('should return false for identical primitives', () => { + expect(havePropsChanged(1, 1)).toBe(false); + expect(havePropsChanged('test', 'test')).toBe(false); + expect(havePropsChanged(true, true)).toBe(false); + }); + + it('should return true for different primitives', () => { + expect(havePropsChanged(1, 2)).toBe(true); + expect(havePropsChanged('test', 'test2')).toBe(true); + expect(havePropsChanged(true, false)).toBe(true); + }); + + it('should return true for different types', () => { + expect(havePropsChanged(1, '1')).toBe(true); + expect(havePropsChanged({}, [])).toBe(true); + expect(havePropsChanged(null, undefined)).toBe(true); + }); + + it('should return true when object keys differ', () => { + const obj1 = {a: 1, b: 2}; + const obj2 = {a: 1}; + expect(havePropsChanged(obj1, obj2)).toBe(true); + }); + + it('should return true when nested values differ', () => { + const obj1 = {a: {b: 1}}; + const obj2 = {a: {b: 2}}; + expect(havePropsChanged(obj1, obj2)).toBe(true); + }); + + it('should return false for identical objects', () => { + const obj1 = {a: 1, b: {c: 2}}; + const obj2 = {a: 1, b: {c: 2}}; + expect(havePropsChanged(obj1, obj2)).toBe(false); + }); + + it('should handle null and undefined', () => { + expect(havePropsChanged(null, null)).toBe(false); + expect(havePropsChanged(undefined, undefined)).toBe(false); + expect(havePropsChanged(null, undefined)).toBe(true); + }); + + it('should handle circular references gracefully', () => { + const obj1: any = {a: 1}; + const obj2: any = {a: 1}; + obj1.self = obj1; + obj2.self = obj2; + + expect(havePropsChanged(obj1, obj2)).toBe(true); + }); + }); +}); diff --git a/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx b/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx new file mode 100644 index 000000000..11c5cdaf3 --- /dev/null +++ b/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx @@ -0,0 +1,137 @@ +import React from 'react'; +import {render} from '@testing-library/react'; +import '@testing-library/jest-dom'; +import withMetrics from '../src/withMetrics'; +import * as metricsLogger from '../src/metricsLogger'; + +interface TestComponentProps { + name?: string; + [key: string]: any; +} + +describe('withMetrics HOC', () => { + const logMetricsSpy = jest.spyOn(metricsLogger, 'logMetrics'); + + const TestComponent: React.FC = (props) =>

Test Component {props.name}
; + const WrappedComponent = withMetrics(TestComponent, 'TestWidget'); + + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should log metrics on mount', () => { + const mockTime = 1234567890; + jest.setSystemTime(mockTime); + + render(); + + expect(logMetricsSpy).toHaveBeenCalledWith({ + widgetName: 'TestWidget', + event: 'WIDGET_MOUNTED', + timestamp: mockTime, + }); + }); + + it('should log metrics on unmount', () => { + const mockTime = 1234567890; + jest.setSystemTime(mockTime); + + const {unmount} = render(); + + // Clear the mount log + logMetricsSpy.mockClear(); + + // Unmount the component + unmount(); + + expect(logMetricsSpy).toHaveBeenCalledWith({ + widgetName: 'TestWidget', + event: 'WIDGET_UNMOUNTED', + timestamp: mockTime, + }); + }); + + it('should pass through props to wrapped component', () => { + const {getByText} = render(); + expect(getByText('Test Component test-name')).toBeInTheDocument(); + }); + + it('should not re-render when props have not changed', () => { + const renderSpy = jest.fn(); + const SpyComponent: React.FC = (props) => { + renderSpy(); + return
Test Component {props.name}
; + }; + + const WrappedSpy = withMetrics(SpyComponent, 'TestWidget'); + + const {rerender} = render(); + expect(renderSpy).toHaveBeenCalledTimes(1); + + // Re-render with same props + rerender(); + expect(renderSpy).toHaveBeenCalledTimes(1); + }); + + it('should re-render when props have changed', () => { + const renderSpy = jest.fn(); + const SpyComponent: React.FC = (props) => { + renderSpy(); + return
Test Component {props.name}
; + }; + + const WrappedSpy = withMetrics(SpyComponent, 'TestWidget'); + + const {rerender} = render(); + expect(renderSpy).toHaveBeenCalledTimes(1); + + // Re-render with different props + rerender(); + expect(renderSpy).toHaveBeenCalledTimes(2); + }); + + it('should handle complex prop changes', () => { + const renderSpy = jest.fn(); + const SpyComponent: React.FC = (props) => { + renderSpy(); + return
Test Component
; + }; + + const WrappedSpy = withMetrics(SpyComponent, 'TestWidget'); + + const initialProps = { + obj: {a: 1, b: 2}, + arr: [1, 2, 3], + }; + + const {rerender} = render(); + expect(renderSpy).toHaveBeenCalledTimes(1); + + // Re-render with same structure but different reference + rerender( + + ); + expect(renderSpy).toHaveBeenCalledTimes(1); + + // Re-render with different values + rerender( + + ); + expect(renderSpy).toHaveBeenCalledTimes(2); + }); +}); diff --git a/packages/contact-center/ui-metrics/webpack.config.js b/packages/contact-center/ui-metrics/webpack.config.js index 247cf2783..fa737ac93 100644 --- a/packages/contact-center/ui-metrics/webpack.config.js +++ b/packages/contact-center/ui-metrics/webpack.config.js @@ -9,4 +9,9 @@ module.exports = merge(baseConfig, { filename: 'index.js', // Set the output filename to index.js libraryTarget: 'commonjs2', }, + externals: { + react: 'react', + 'react-dom': 'react-dom', + '@webex/cc-store': '@webex/cc-store', + }, }); From a5bfab93da590c576c6b76940078294a0373fa9d Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Tue, 22 Jul 2025 14:43:05 +0530 Subject: [PATCH 5/9] fix(ui-metrics): merged latest and typed properly --- packages/contact-center/ui-metrics/package.json | 1 + .../ui-metrics/tests/metricsLogger.test.ts | 16 ++++++++-------- .../ui-metrics/tests/withMetrics.test.tsx | 8 ++++++++ .../contact-center/ui-metrics/tsconfig.test.json | 9 +++++++++ yarn.lock | 1 + 5 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 packages/contact-center/ui-metrics/tsconfig.test.json diff --git a/packages/contact-center/ui-metrics/package.json b/packages/contact-center/ui-metrics/package.json index 883c411e7..b98dfb4bf 100644 --- a/packages/contact-center/ui-metrics/package.json +++ b/packages/contact-center/ui-metrics/package.json @@ -20,6 +20,7 @@ "@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", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", diff --git a/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts b/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts index 5bfcc28ab..c986cfa7c 100644 --- a/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts +++ b/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts @@ -1,14 +1,14 @@ import store from '@webex/cc-store'; import {logMetrics, havePropsChanged, WidgetMetrics} from '../src/metricsLogger'; -// Mock the store -jest.mock('@webex/cc-store', () => ({ - logger: { - log: jest.fn(), - }, -})); - describe('metricsLogger', () => { + store.store.logger = { + log: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + trace: jest.fn(), + }; beforeEach(() => { jest.clearAllMocks(); }); @@ -33,7 +33,7 @@ describe('metricsLogger', () => { it('should handle case when logger is not available', () => { const consoleSpy = jest.spyOn(console, 'log'); - (store as any).logger = null; + store.store.logger = undefined; const metric: WidgetMetrics = { widgetName: 'TestWidget', diff --git a/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx b/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx index 11c5cdaf3..f01718d31 100644 --- a/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx +++ b/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {render} from '@testing-library/react'; import '@testing-library/jest-dom'; import withMetrics from '../src/withMetrics'; +import store from '@webex/cc-store'; import * as metricsLogger from '../src/metricsLogger'; interface TestComponentProps { @@ -10,6 +11,13 @@ interface TestComponentProps { } describe('withMetrics HOC', () => { + store.store.logger = { + log: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + trace: jest.fn(), + }; const logMetricsSpy = jest.spyOn(metricsLogger, 'logMetrics'); const TestComponent: React.FC = (props) =>
Test Component {props.name}
; diff --git a/packages/contact-center/ui-metrics/tsconfig.test.json b/packages/contact-center/ui-metrics/tsconfig.test.json new file mode 100644 index 000000000..40bd84f5a --- /dev/null +++ b/packages/contact-center/ui-metrics/tsconfig.test.json @@ -0,0 +1,9 @@ +// This config is to do type checking in our files while running tests. +{ + "extends": "./tsconfig.json", + "include": ["./tests"], + "exclude": ["node_modules"], + "compilerOptions": { + "noEmit": true // Don't output any files + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 868292180..5f6a65502 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9575,6 +9575,7 @@ __metadata: "@types/jest": "npm:29.5.14" "@types/react-test-renderer": "npm:18" "@webex/cc-store": "workspace:*" + "@webex/test-fixtures": "workspace:*" babel-jest: "npm:29.7.0" jest: "npm:29.7.0" jest-environment-jsdom: "npm:29.7.0" From 979f3d0d50bd4cf3db42d6c863c06a2ada5e225a Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Mon, 4 Aug 2025 13:38:11 +0530 Subject: [PATCH 6/9] fix(cc-loggin): review-comments --- .../contact-center/cc-components/package.json | 2 +- .../components/StationLogin/station-login.tsx | 2 +- .../src/components/UserState/user-state.tsx | 2 +- .../task/CallControl/call-control.tsx | 2 +- .../task/CallControlCAD/call-control-cad.tsx | 2 +- .../task/IncomingTask/incoming-task.tsx | 2 +- .../task/OutdialCall/outdial-call.tsx | 2 +- .../components/task/TaskList/task-list.tsx | 2 +- .../contact-center/ui-metrics/bable.config.js | 3 - .../contact-center/ui-metrics/package.json | 2 +- .../ui-metrics/src/metricsLogger.ts | 70 ++++++++++++++++--- .../ui-metrics/src/withMetrics.tsx | 2 + .../ui-metrics/tests/metricsLogger.test.ts | 21 +----- .../ui-metrics/tests/withMetrics.test.tsx | 40 ----------- webpack.config.js | 2 + .../cc/samples-cc-react-app/webpack.config.js | 2 +- yarn.lock | 6 +- 17 files changed, 80 insertions(+), 84 deletions(-) delete mode 100644 packages/contact-center/ui-metrics/bable.config.js diff --git a/packages/contact-center/cc-components/package.json b/packages/contact-center/cc-components/package.json index 8df918b72..8adaef1b7 100644 --- a/packages/contact-center/cc-components/package.json +++ b/packages/contact-center/cc-components/package.json @@ -36,7 +36,7 @@ "@momentum-ui/illustrations": "^1.24.0", "@r2wc/react-to-web-component": "2.0.3", "@webex/cc-store": "workspace:*", - "@webex/cc-ui-metrics": "workspace:*" + "@webex/cc-ui-logging": "workspace:*" }, "devDependencies": { "@babel/core": "7.25.2", diff --git a/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx b/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx index 47c29ecf7..e75da3564 100644 --- a/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx +++ b/packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx @@ -17,7 +17,7 @@ import { updateDialNumberLabel, handleCCSignoutKeyDown, } from './station-login.utils'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; const StationLoginComponent: React.FunctionComponent = (props) => { const { 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 7f6e20160..84b557116 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 @@ -17,7 +17,7 @@ import { getSelectedKey, buildDropdownItems, } from './user-state.utils'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; const UserStateComponent: React.FunctionComponent = (props) => { const { diff --git a/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx b/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx index c404a4660..290ddc49e 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControl/call-control.tsx @@ -25,7 +25,7 @@ import { MUTE_CALL, UNMUTE_CALL, } from '../constants'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; function CallControlComponent(props: CallControlComponentProps) { const [selectedWrapupReason, setSelectedWrapupReason] = useState(null); diff --git a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx index 64d4d40fc..db2c6d423 100644 --- a/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx +++ b/packages/contact-center/cc-components/src/components/task/CallControlCAD/call-control-cad.tsx @@ -19,7 +19,7 @@ import { CUSTOMER_NAME, RONA, } from '../constants'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; const CallControlCADComponent: React.FC = (props) => { const { diff --git a/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx b/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx index 53a11cd66..d893ede65 100644 --- a/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx +++ b/packages/contact-center/cc-components/src/components/task/IncomingTask/incoming-task.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {IncomingTaskComponentProps, MEDIA_CHANNEL} from '../task.types'; import Task from '../Task'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; const IncomingTaskComponent: React.FunctionComponent = (props) => { const {incomingTask, isBrowser, accept, reject, logger} = props; diff --git a/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx b/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx index b40fcb979..284520907 100644 --- a/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx +++ b/packages/contact-center/cc-components/src/components/task/OutdialCall/outdial-call.tsx @@ -1,7 +1,7 @@ import React, {useState} from 'react'; import {OutdialCallComponentProps} from '../task.types'; import './outdial-call.style.scss'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; const OutdialCallComponent: React.FunctionComponent = (props) => { const {startOutdial} = props; diff --git a/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx b/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx index 45ab31e1e..d7308bf08 100644 --- a/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx +++ b/packages/contact-center/cc-components/src/components/task/TaskList/task-list.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {TaskListComponentProps, MEDIA_CHANNEL} from '../task.types'; import Task from '../Task'; import './styles.scss'; -import {withMetrics} from '@webex/cc-ui-metrics'; +import {withMetrics} from '@webex/cc-ui-logging'; const TaskListComponent: React.FunctionComponent = (props) => { const {currentTask, taskList, acceptTask, declineTask, isBrowser, onTaskSelect, logger} = props; diff --git a/packages/contact-center/ui-metrics/bable.config.js b/packages/contact-center/ui-metrics/bable.config.js deleted file mode 100644 index 0eaef236c..000000000 --- a/packages/contact-center/ui-metrics/bable.config.js +++ /dev/null @@ -1,3 +0,0 @@ -const baseConfig = require('../../../babel.config.js'); - -module.exports = baseConfig; diff --git a/packages/contact-center/ui-metrics/package.json b/packages/contact-center/ui-metrics/package.json index b98dfb4bf..729eb6562 100644 --- a/packages/contact-center/ui-metrics/package.json +++ b/packages/contact-center/ui-metrics/package.json @@ -1,5 +1,5 @@ { - "name": "@webex/cc-ui-metrics", + "name": "@webex/cc-ui-logging", "version": "1.0.0", "description": "UI metrics tracking for Webex widgets", "main": "dist/index.js", diff --git a/packages/contact-center/ui-metrics/src/metricsLogger.ts b/packages/contact-center/ui-metrics/src/metricsLogger.ts index d59a25712..02f2b8d87 100644 --- a/packages/contact-center/ui-metrics/src/metricsLogger.ts +++ b/packages/contact-center/ui-metrics/src/metricsLogger.ts @@ -2,15 +2,40 @@ import store from '@webex/cc-store'; export type WidgetMetrics = { widgetName: string; - event: 'WIDGET_MOUNTED' | 'ERROR' | 'WIDGET_UNMOUNTED'; + event: 'WIDGET_MOUNTED' | 'ERROR' | 'WIDGET_UNMOUNTED' | 'PROPS_UPDATED'; props?: Record; timestamp: number; additionalContext?: Record; }; +/** + * Logs UI metrics for contact center widgets. + * + * This function logs widget lifecycle events and errors to help monitor + * widget performance and user interactions. If no logger is available, + * it will emit a warning and skip logging. + * + * @param metric - The metrics data to be logged + * @param metric.widgetName - Name of the widget generating the metric + * @param metric.event - Type of event being logged + * @param metric.props - Optional properties associated with the widget + * @param metric.timestamp - Unix timestamp when the event occurred + * @param metric.additionalContext - Optional additional context data + * + * @example + * ```typescript + * logMetrics({ + * widgetName: 'CallControl', + * event: 'WIDGET_MOUNTED', + * props: { callId: '123' }, + * timestamp: Date.now(), + * additionalContext: { userId: 'user123' } + * }); + * ``` + */ export const logMetrics = (metric: WidgetMetrics) => { if (!store.logger) { - console.log('CC-Widgets: UI Metrics: No logger found'); + console.warn('CC-Widgets: UI Metrics: No logger found'); return; } store.logger.log(`CC-Widgets: UI Metrics: ${JSON.stringify(metric, null, 2)}`, { @@ -19,10 +44,41 @@ export const logMetrics = (metric: WidgetMetrics) => { }); }; +/** + * Determines if props have changed between two objects using shallow comparison. + * + * This function performs a shallow comparison between two objects to detect changes. + * It compares object keys and primitive values, but does not recursively compare + * nested objects. This is useful for determining when to log metrics based on prop changes. + * + * @param prev - The previous props object + * @param next - The next props object to compare against + * @returns `true` if the props have changed, `false` otherwise + * + * @example + * ```typescript + * const oldProps = { name: 'John', age: 30 }; + * const newProps = { name: 'John', age: 31 }; + * + * if (havePropsChanged(oldProps, newProps)) { + * // Props have changed, log metrics + * logMetrics({ + * widgetName: 'UserProfile', + * event: 'WIDGET_MOUNTED', + * props: newProps, + * timestamp: Date.now() + * }); + * } * ``` + * + * @remarks + * The function is important as we dont sanitize our props right now. + * Once we start sanitizing we can do a deep comparison. This is used to only re-render + * the HOC if the props have changed. + */ export function havePropsChanged(prev: any, next: any): boolean { if (prev === next) return false; - // Do shallow comparison first + // Do shallow comparison if (typeof prev !== typeof next) return true; if (!prev || !next) return prev !== next; @@ -41,10 +97,6 @@ export function havePropsChanged(prev: any, next: any): boolean { if (typeof nextVal !== 'object' || nextVal === null) return true; } - // Fall back to deep comparison only for objects that passed shallow check - try { - return JSON.stringify(prev) !== JSON.stringify(next); - } catch { - return true; // fallback to log if circular structure - } + // All shallow comparisons passed, consider props unchanged + return false; } diff --git a/packages/contact-center/ui-metrics/src/withMetrics.tsx b/packages/contact-center/ui-metrics/src/withMetrics.tsx index 12156e429..77354d3dd 100644 --- a/packages/contact-center/ui-metrics/src/withMetrics.tsx +++ b/packages/contact-center/ui-metrics/src/withMetrics.tsx @@ -20,6 +20,8 @@ export default function withMetrics

(Component: any, widgetName }; }, []); + // TODO: https://jira-eng-sjc12.cisco.com/jira/browse/CAI-6890 PROPS_UPDATED event + return ; }, (prevProps, nextProps) => !havePropsChanged(prevProps, nextProps) diff --git a/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts b/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts index c986cfa7c..f7ecf15e5 100644 --- a/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts +++ b/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts @@ -32,7 +32,7 @@ describe('metricsLogger', () => { }); it('should handle case when logger is not available', () => { - const consoleSpy = jest.spyOn(console, 'log'); + const consoleSpy = jest.spyOn(console, 'warn'); store.store.logger = undefined; const metric: WidgetMetrics = { @@ -56,14 +56,12 @@ describe('metricsLogger', () => { }); it('should return true for different primitives', () => { - expect(havePropsChanged(1, 2)).toBe(true); expect(havePropsChanged('test', 'test2')).toBe(true); expect(havePropsChanged(true, false)).toBe(true); }); it('should return true for different types', () => { expect(havePropsChanged(1, '1')).toBe(true); - expect(havePropsChanged({}, [])).toBe(true); expect(havePropsChanged(null, undefined)).toBe(true); }); @@ -73,15 +71,9 @@ describe('metricsLogger', () => { expect(havePropsChanged(obj1, obj2)).toBe(true); }); - it('should return true when nested values differ', () => { + it('should return false when nested values differ', () => { const obj1 = {a: {b: 1}}; const obj2 = {a: {b: 2}}; - expect(havePropsChanged(obj1, obj2)).toBe(true); - }); - - it('should return false for identical objects', () => { - const obj1 = {a: 1, b: {c: 2}}; - const obj2 = {a: 1, b: {c: 2}}; expect(havePropsChanged(obj1, obj2)).toBe(false); }); @@ -90,14 +82,5 @@ describe('metricsLogger', () => { expect(havePropsChanged(undefined, undefined)).toBe(false); expect(havePropsChanged(null, undefined)).toBe(true); }); - - it('should handle circular references gracefully', () => { - const obj1: any = {a: 1}; - const obj2: any = {a: 1}; - obj1.self = obj1; - obj2.self = obj2; - - expect(havePropsChanged(obj1, obj2)).toBe(true); - }); }); }); diff --git a/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx b/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx index f01718d31..23ec71691 100644 --- a/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx +++ b/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx @@ -102,44 +102,4 @@ describe('withMetrics HOC', () => { rerender(); expect(renderSpy).toHaveBeenCalledTimes(2); }); - - it('should handle complex prop changes', () => { - const renderSpy = jest.fn(); - const SpyComponent: React.FC = (props) => { - renderSpy(); - return

Test Component
; - }; - - const WrappedSpy = withMetrics(SpyComponent, 'TestWidget'); - - const initialProps = { - obj: {a: 1, b: 2}, - arr: [1, 2, 3], - }; - - const {rerender} = render(); - expect(renderSpy).toHaveBeenCalledTimes(1); - - // Re-render with same structure but different reference - rerender( - - ); - expect(renderSpy).toHaveBeenCalledTimes(1); - - // Re-render with different values - rerender( - - ); - expect(renderSpy).toHaveBeenCalledTimes(2); - }); }); diff --git a/webpack.config.js b/webpack.config.js index 9d020e6b3..922c4b2ba 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -47,6 +47,8 @@ module.exports = { ], }, stats: { + // While building and running the sample app when sass-loader is used we get a lot of deprecation warnings + // This is a workaround to suppress them untill we move away from sass-loader warningsFilter: [/sass-loader/], }, }; diff --git a/widgets-samples/cc/samples-cc-react-app/webpack.config.js b/widgets-samples/cc/samples-cc-react-app/webpack.config.js index b0ac80296..9b6a8f850 100644 --- a/widgets-samples/cc/samples-cc-react-app/webpack.config.js +++ b/widgets-samples/cc/samples-cc-react-app/webpack.config.js @@ -17,7 +17,7 @@ module.exports = merge(baseConfig, { '@webex/cc-user-state': path.resolve(__dirname, '../../../packages/contact-center/user-state/src'), '@webex/cc-task': path.resolve(__dirname, '../../../packages/contact-center/task/src'), '@webex/cc-components': path.resolve(__dirname, '../../../packages/contact-center/cc-components/src'), - '@webex/cc-ui-metrics': path.resolve(__dirname, '../../../packages/contact-center/ui-metrics/src'), + '@webex/cc-ui-logging': path.resolve(__dirname, '../../../packages/contact-center/ui-metrics/src'), }, }, module: { diff --git a/yarn.lock b/yarn.lock index 5f6a65502..9e45002a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9402,7 +9402,7 @@ __metadata: "@types/jest": "npm:29.5.14" "@types/react-test-renderer": "npm:18" "@webex/cc-store": "workspace:*" - "@webex/cc-ui-metrics": "workspace:*" + "@webex/cc-ui-logging": "workspace:*" "@webex/test-fixtures": "workspace:*" babel-loader: "npm:9.2.1" eslint: "npm:^9.20.1" @@ -9565,9 +9565,9 @@ __metadata: languageName: unknown linkType: soft -"@webex/cc-ui-metrics@workspace:*, @webex/cc-ui-metrics@workspace:packages/contact-center/ui-metrics": +"@webex/cc-ui-logging@workspace:*, @webex/cc-ui-logging@workspace:packages/contact-center/ui-metrics": version: 0.0.0-use.local - resolution: "@webex/cc-ui-metrics@workspace:packages/contact-center/ui-metrics" + resolution: "@webex/cc-ui-logging@workspace:packages/contact-center/ui-metrics" dependencies: "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.2" From cab57af9f8e3d9b8ad6599edf89a188dffe65792 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Fri, 8 Aug 2025 10:33:29 +0530 Subject: [PATCH 7/9] fix(ui-loggin): change folder name to logging --- packages/contact-center/{ui-metrics => ui-logging}/jest.config.js | 0 packages/contact-center/{ui-metrics => ui-logging}/package.json | 0 packages/contact-center/{ui-metrics => ui-logging}/src/index.ts | 0 .../{ui-metrics => ui-logging}/src/metricsLogger.ts | 0 .../contact-center/{ui-metrics => ui-logging}/src/withMetrics.tsx | 0 .../{ui-metrics => ui-logging}/tests/metricsLogger.test.ts | 0 .../{ui-metrics => ui-logging}/tests/withMetrics.test.tsx | 0 packages/contact-center/{ui-metrics => ui-logging}/tsconfig.json | 0 .../contact-center/{ui-metrics => ui-logging}/tsconfig.test.json | 0 .../contact-center/{ui-metrics => ui-logging}/webpack.config.js | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename packages/contact-center/{ui-metrics => ui-logging}/jest.config.js (100%) rename packages/contact-center/{ui-metrics => ui-logging}/package.json (100%) rename packages/contact-center/{ui-metrics => ui-logging}/src/index.ts (100%) rename packages/contact-center/{ui-metrics => ui-logging}/src/metricsLogger.ts (100%) rename packages/contact-center/{ui-metrics => ui-logging}/src/withMetrics.tsx (100%) rename packages/contact-center/{ui-metrics => ui-logging}/tests/metricsLogger.test.ts (100%) rename packages/contact-center/{ui-metrics => ui-logging}/tests/withMetrics.test.tsx (100%) rename packages/contact-center/{ui-metrics => ui-logging}/tsconfig.json (100%) rename packages/contact-center/{ui-metrics => ui-logging}/tsconfig.test.json (100%) rename packages/contact-center/{ui-metrics => ui-logging}/webpack.config.js (100%) diff --git a/packages/contact-center/ui-metrics/jest.config.js b/packages/contact-center/ui-logging/jest.config.js similarity index 100% rename from packages/contact-center/ui-metrics/jest.config.js rename to packages/contact-center/ui-logging/jest.config.js diff --git a/packages/contact-center/ui-metrics/package.json b/packages/contact-center/ui-logging/package.json similarity index 100% rename from packages/contact-center/ui-metrics/package.json rename to packages/contact-center/ui-logging/package.json diff --git a/packages/contact-center/ui-metrics/src/index.ts b/packages/contact-center/ui-logging/src/index.ts similarity index 100% rename from packages/contact-center/ui-metrics/src/index.ts rename to packages/contact-center/ui-logging/src/index.ts diff --git a/packages/contact-center/ui-metrics/src/metricsLogger.ts b/packages/contact-center/ui-logging/src/metricsLogger.ts similarity index 100% rename from packages/contact-center/ui-metrics/src/metricsLogger.ts rename to packages/contact-center/ui-logging/src/metricsLogger.ts diff --git a/packages/contact-center/ui-metrics/src/withMetrics.tsx b/packages/contact-center/ui-logging/src/withMetrics.tsx similarity index 100% rename from packages/contact-center/ui-metrics/src/withMetrics.tsx rename to packages/contact-center/ui-logging/src/withMetrics.tsx diff --git a/packages/contact-center/ui-metrics/tests/metricsLogger.test.ts b/packages/contact-center/ui-logging/tests/metricsLogger.test.ts similarity index 100% rename from packages/contact-center/ui-metrics/tests/metricsLogger.test.ts rename to packages/contact-center/ui-logging/tests/metricsLogger.test.ts diff --git a/packages/contact-center/ui-metrics/tests/withMetrics.test.tsx b/packages/contact-center/ui-logging/tests/withMetrics.test.tsx similarity index 100% rename from packages/contact-center/ui-metrics/tests/withMetrics.test.tsx rename to packages/contact-center/ui-logging/tests/withMetrics.test.tsx diff --git a/packages/contact-center/ui-metrics/tsconfig.json b/packages/contact-center/ui-logging/tsconfig.json similarity index 100% rename from packages/contact-center/ui-metrics/tsconfig.json rename to packages/contact-center/ui-logging/tsconfig.json diff --git a/packages/contact-center/ui-metrics/tsconfig.test.json b/packages/contact-center/ui-logging/tsconfig.test.json similarity index 100% rename from packages/contact-center/ui-metrics/tsconfig.test.json rename to packages/contact-center/ui-logging/tsconfig.test.json diff --git a/packages/contact-center/ui-metrics/webpack.config.js b/packages/contact-center/ui-logging/webpack.config.js similarity index 100% rename from packages/contact-center/ui-metrics/webpack.config.js rename to packages/contact-center/ui-logging/webpack.config.js From 3c85248394f9c5ae726891670e59b903a527ff2a Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Mon, 11 Aug 2025 11:22:00 +0530 Subject: [PATCH 8/9] fix(ui-logging): build issue --- packages/contact-center/ui-logging/jest.config.js | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contact-center/ui-logging/jest.config.js b/packages/contact-center/ui-logging/jest.config.js index 4f2ecff91..bef75ab06 100644 --- a/packages/contact-center/ui-logging/jest.config.js +++ b/packages/contact-center/ui-logging/jest.config.js @@ -1,6 +1,6 @@ const jestConfig = require('../../../jest.config.js'); jestConfig.rootDir = '../../../'; -jestConfig.testMatch = ['**/ui-metrics/tests/**/*.ts', '**/ui-metrics/tests/**/*.tsx']; +jestConfig.testMatch = ['**/ui-logging/tests/**/*.ts', '**/ui-logging/tests/**/*.tsx']; module.exports = jestConfig; diff --git a/yarn.lock b/yarn.lock index 9e45002a8..280e4455c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9565,9 +9565,9 @@ __metadata: languageName: unknown linkType: soft -"@webex/cc-ui-logging@workspace:*, @webex/cc-ui-logging@workspace:packages/contact-center/ui-metrics": +"@webex/cc-ui-logging@workspace:*, @webex/cc-ui-logging@workspace:packages/contact-center/ui-logging": version: 0.0.0-use.local - resolution: "@webex/cc-ui-logging@workspace:packages/contact-center/ui-metrics" + resolution: "@webex/cc-ui-logging@workspace:packages/contact-center/ui-logging" dependencies: "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.6.2" From 40ded9898b7842f92992b001ea305506c776c3c5 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Mon, 11 Aug 2025 14:43:56 +0530 Subject: [PATCH 9/9] fix(samples): fix samples building issues --- widgets-samples/cc/samples-cc-react-app/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets-samples/cc/samples-cc-react-app/webpack.config.js b/widgets-samples/cc/samples-cc-react-app/webpack.config.js index 9b6a8f850..86fb2c134 100644 --- a/widgets-samples/cc/samples-cc-react-app/webpack.config.js +++ b/widgets-samples/cc/samples-cc-react-app/webpack.config.js @@ -17,7 +17,7 @@ module.exports = merge(baseConfig, { '@webex/cc-user-state': path.resolve(__dirname, '../../../packages/contact-center/user-state/src'), '@webex/cc-task': path.resolve(__dirname, '../../../packages/contact-center/task/src'), '@webex/cc-components': path.resolve(__dirname, '../../../packages/contact-center/cc-components/src'), - '@webex/cc-ui-logging': path.resolve(__dirname, '../../../packages/contact-center/ui-metrics/src'), + '@webex/cc-ui-logging': path.resolve(__dirname, '../../../packages/contact-center/ui-logging/src'), }, }, module: {