From 4bf4b0e8e7adef48e5a15a266a5e2d5ac4c2d030 Mon Sep 17 00:00:00 2001 From: Rankush Kumar Date: Thu, 11 Dec 2025 17:01:53 +0530 Subject: [PATCH 1/3] docs(ui-logging): add AI documentation - AGENTS.md and ARCHITECTURE.md --- .../ui-logging/ai-docs/AGENTS.md | 403 ++++++++++++++ .../ui-logging/ai-docs/ARCHITECTURE.md | 497 ++++++++++++++++++ 2 files changed, 900 insertions(+) create mode 100644 packages/contact-center/ui-logging/ai-docs/AGENTS.md create mode 100644 packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md diff --git a/packages/contact-center/ui-logging/ai-docs/AGENTS.md b/packages/contact-center/ui-logging/ai-docs/AGENTS.md new file mode 100644 index 000000000..9380913a1 --- /dev/null +++ b/packages/contact-center/ui-logging/ai-docs/AGENTS.md @@ -0,0 +1,403 @@ +# UI Logging - Metrics Tracking Utility + +## Overview + +UI Logging is a lightweight utility package that provides metrics tracking capabilities for contact center widgets. It includes a Higher-Order Component (HOC) called `withMetrics` that automatically tracks widget lifecycle events, and a `logMetrics` function for custom event logging. + +**Package:** `@webex/cc-ui-logging` + +**Version:** See [package.json](../package.json) + +--- + +## Why and What is This Package Used For? + +### Purpose + +The UI Logging package enables observability and monitoring for contact center widgets. It: +- **Tracks widget lifecycle** - Automatically logs mount, unmount, and updates +- **Provides HOC wrapper** - Easy integration with minimal code changes +- **Logs to store logger** - Integrates with existing logging infrastructure +- **Supports custom metrics** - Log custom events with additional context +- **Optimizes re-renders** - Includes shallow props comparison for performance + +### Key Capabilities + +- **withMetrics HOC**: Wraps components to auto-track lifecycle events +- **logMetrics Function**: Manually log custom events +- **havePropsChanged Utility**: Shallow comparison to prevent unnecessary re-renders +- **Type-Safe**: Full TypeScript support with WidgetMetrics type +- **Store Integration**: Uses store.logger for centralized logging + +--- + +## Examples and Use Cases + +### Getting Started + +#### Basic HOC Usage + +```typescript +import { withMetrics } from '@webex/cc-ui-logging'; +import MyWidget from './MyWidget'; + +// Wrap your widget with metrics tracking +const MyWidgetWithMetrics = withMetrics(MyWidget, 'MyWidget'); + +// Use the wrapped component +function App() { + return ; +} + +// Automatically logs: +// - WIDGET_MOUNTED when component mounts +// - WIDGET_UNMOUNTED when component unmounts +``` + +#### Manual Metrics Logging + +```typescript +import { logMetrics } from '@webex/cc-ui-logging'; + +function MyComponent() { + const handleButtonClick = () => { + // Log custom event + logMetrics({ + widgetName: 'MyComponent', + event: 'ERROR', + timestamp: Date.now(), + additionalContext: { + errorCode: 'LOGIN_FAILED', + reason: 'Invalid credentials' + } + }); + }; + + return ; +} +``` + +### Common Use Cases + +#### 1. Tracking Widget Lifecycle + +```typescript +import { withMetrics } from '@webex/cc-ui-logging'; +import { StationLogin } from './StationLogin'; + +// Automatically tracks mount/unmount +const StationLoginWithMetrics = withMetrics( + StationLogin, + 'StationLogin' +); + +// When used in app: + + +// Logs on mount: +// { +// widgetName: 'StationLogin', +// event: 'WIDGET_MOUNTED', +// timestamp: 1700000000000 +// } + +// Logs on unmount: +// { +// widgetName: 'StationLogin', +// event: 'WIDGET_UNMOUNTED', +// timestamp: 1700000100000 +// } +``` + +#### 2. Logging Errors + +```typescript +import { logMetrics } from '@webex/cc-ui-logging'; + +function UserState() { + const handleStateChange = async (newState) => { + try { + await updateState(newState); + } catch (error) { + // Log error with context + logMetrics({ + widgetName: 'UserState', + event: 'ERROR', + timestamp: Date.now(), + props: { attemptedState: newState }, + additionalContext: { + error: error.message, + stack: error.stack + } + }); + } + }; + + return ; +} +``` + +#### 3. Performance Tracking + +```typescript +import { logMetrics } from '@webex/cc-ui-logging'; +import { useEffect } from 'react'; + +function TaskList({ tasks }) { + useEffect(() => { + const startTime = performance.now(); + + // Render tasks + renderTasks(tasks); + + const endTime = performance.now(); + + // Log render performance + logMetrics({ + widgetName: 'TaskList', + event: 'WIDGET_MOUNTED', + timestamp: Date.now(), + additionalContext: { + renderTime: endTime - startTime, + taskCount: tasks.length + } + }); + }, [tasks]); + + return
{/* task list */}
; +} +``` + +#### 4. User Interaction Tracking + +```typescript +import { logMetrics } from '@webex/cc-ui-logging'; + +function CallControl({ task }) { + const handleHold = () => { + logMetrics({ + widgetName: 'CallControl', + event: 'WIDGET_MOUNTED', // Using WIDGET_MOUNTED for custom events + timestamp: Date.now(), + props: { taskId: task.id }, + additionalContext: { + action: 'hold_clicked', + callDuration: task.duration + } + }); + + // Perform hold action + task.hold(); + }; + + return ; +} +``` + +### Integration Patterns + +#### With Widget Components + +```typescript +import { withMetrics } from '@webex/cc-ui-logging'; +import { observer } from 'mobx-react-lite'; +import { UserStateComponent } from '@webex/cc-components'; +import store from '@webex/cc-store'; + +// 1. Create internal component +const UserStateInternal = observer(({ onStateChange }) => { + const props = { + idleCodes: store.idleCodes, + currentState: store.currentState, + setAgentStatus: (code) => store.setCurrentState(code), + onStateChange, + }; + + return ; +}); + +// 2. Wrap with metrics HOC +const UserState = withMetrics(UserStateInternal, 'UserState'); + +export { UserState }; +``` + +#### With Error Boundaries + +```typescript +import { logMetrics } from '@webex/cc-ui-logging'; +import { ErrorBoundary } from 'react-error-boundary'; + +function Widget(props) { + const handleError = (error: Error) => { + // Log error via metrics + logMetrics({ + widgetName: 'MyWidget', + event: 'ERROR', + timestamp: Date.now(), + additionalContext: { + error: error.message, + componentStack: error.stack + } + }); + }; + + return ( + + + + ); +} +``` + +#### Custom Metrics in Hooks + +```typescript +import { logMetrics } from '@webex/cc-ui-logging'; +import { useEffect } from 'react'; + +function useCustomHook(widgetName: string) { + useEffect(() => { + // Log when hook initializes + logMetrics({ + widgetName, + event: 'WIDGET_MOUNTED', + timestamp: Date.now(), + additionalContext: { + hookInitialized: true + } + }); + + return () => { + // Log when hook cleans up + logMetrics({ + widgetName, + event: 'WIDGET_UNMOUNTED', + timestamp: Date.now() + }); + }; + }, [widgetName]); +} +``` + +--- + +## Dependencies + +**Note:** For exact versions, see [package.json](../package.json) + +### Runtime Dependencies + +| Package | Purpose | +|---------|---------| +| `@webex/cc-store` | Access to store.logger for logging | + +### Peer Dependencies + +| Package | Purpose | +|---------|---------| +| `react` | React framework (for HOC) | +| `react-dom` | React DOM (for HOC) | + +### Development Dependencies + +Key development tools (see [package.json](../package.json) for versions): +- TypeScript +- Jest (testing) +- Webpack (bundling) + +--- + +## API Reference + +### withMetrics HOC + +```typescript +function withMetrics

( + Component: React.ComponentType

, + widgetName: string +): React.MemoExoticComponent> +``` + +**Parameters:** +- `Component` - React component to wrap +- `widgetName` - Name for metric identification + +**Returns:** Memoized component with automatic metrics tracking + +**Behavior:** +- Wraps component with React.memo +- Uses custom comparison function (`havePropsChanged`) +- Logs WIDGET_MOUNTED on mount +- Logs WIDGET_UNMOUNTED on unmount + +--- + +### logMetrics Function + +```typescript +function logMetrics(metric: WidgetMetrics): void + +type WidgetMetrics = { + widgetName: string; + event: 'WIDGET_MOUNTED' | 'ERROR' | 'WIDGET_UNMOUNTED' | 'PROPS_UPDATED'; + props?: Record; + timestamp: number; + additionalContext?: Record; +}; +``` + +**Parameters:** +- `metric.widgetName` - Widget identifier +- `metric.event` - Event type +- `metric.props` - Optional widget props snapshot +- `metric.timestamp` - Unix timestamp +- `metric.additionalContext` - Optional additional data + +**Behavior:** +- Checks if `store.logger` exists +- Logs warning if no logger available +- Calls `store.logger.log()` with formatted JSON + +--- + +### havePropsChanged Function + +```typescript +function havePropsChanged(prev: any, next: any): boolean +``` + +**Parameters:** +- `prev` - Previous props object +- `next` - Next props object + +**Returns:** `true` if props have changed, `false` otherwise + +**Behavior:** +- Performs shallow comparison +- Compares object keys length +- Compares primitive values +- Does NOT deep compare nested objects +- Used by React.memo to prevent re-renders + +--- + +## Installation + +```bash +# Install as development or runtime dependency +yarn add @webex/cc-ui-logging + +# Used internally by widgets, usually not directly installed +``` + +--- + +## Additional Resources + +For detailed HOC implementation, metrics flow, and performance optimization, see [architecture.md](./architecture.md). + +--- + +_Last Updated: 2025-11-26_ + diff --git a/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md b/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md new file mode 100644 index 000000000..9a33299d7 --- /dev/null +++ b/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md @@ -0,0 +1,497 @@ +# UI Logging - Architecture + +## Component Overview + +UI Logging is a utility package that provides metrics tracking through a Higher-Order Component (HOC) pattern and direct logging functions. It integrates with the store's logger to provide centralized metrics collection. + +### Module Table + +| Module | File | Exports | Purpose | Dependencies | +|--------|------|---------|---------|--------------| +| **withMetrics HOC** | `src/withMetrics.tsx` | `withMetrics` (default) | Wraps components with lifecycle tracking | React, metricsLogger | +| **metricsLogger** | `src/metricsLogger.ts` | `logMetrics`, `havePropsChanged`, `WidgetMetrics` (type) | Logging functions and utilities | @webex/cc-store | +| **Package Entry** | `src/index.ts` | All exports | Main package export | Both modules above | + +### File Structure + +``` +ui-logging/ +├── src/ +│ ├── index.ts # Package exports +│ ├── metricsLogger.ts # Logging functions +│ └── withMetrics.tsx # HOC implementation +├── tests/ +│ ├── metricsLogger.test.ts # Logger tests +│ └── withMetrics.test.tsx # HOC tests +├── dist/ +│ ├── index.js # Build output +│ └── types/ +│ ├── index.d.ts +│ ├── metricsLogger.d.ts +│ └── withMetrics.d.ts +├── package.json +├── tsconfig.json +└── webpack.config.js +``` + +--- + +## Data Flows + +### Metrics Logging Flow + +```mermaid +graph LR + subgraph "Widget/Component" + Component[React Component] + Event[User Event/Lifecycle] + end + + subgraph "UI Logging" + HOC[withMetrics HOC] + LogFn[logMetrics Function] + end + + subgraph "Store" + Logger[store.logger] + end + + subgraph "Backend/Console" + Output[Log Output] + end + + Component -->|Wrapped by| HOC + HOC -->|Mount/Unmount| LogFn + Event -->|Custom logging| LogFn + LogFn -->|JSON metrics| Logger + Logger -->|Formatted logs| Output + + style HOC fill:#e1f5ff + style LogFn fill:#ffe1e1 + style Logger fill:#fff4e1 +``` + +### HOC Lifecycle Flow + +```mermaid +sequenceDiagram + participant App as Application + participant HOC as withMetrics HOC + participant Component as Wrapped Component + participant Logger as logMetrics + participant Store as store.logger + + App->>HOC: Render withMetrics(Component) + activate HOC + + HOC->>HOC: useEffect (mount) + HOC->>Logger: logMetrics({event: 'WIDGET_MOUNTED'}) + activate Logger + Logger->>Store: Check store.logger exists + alt Logger exists + Logger->>Store: logger.log(metrics) + Store-->>Logger: Logged + else No logger + Logger->>Logger: console.warn('No logger found') + end + deactivate Logger + + HOC->>Component: Render with props + activate Component + Component-->>HOC: Rendered + deactivate Component + + HOC-->>App: Rendered widget + deactivate HOC + + Note over App,Store: Component unmounts + + App->>HOC: Unmount + activate HOC + HOC->>HOC: useEffect cleanup + HOC->>Logger: logMetrics({event: 'WIDGET_UNMOUNTED'}) + activate Logger + Logger->>Store: logger.log(metrics) + Store-->>Logger: Logged + deactivate Logger + deactivate HOC +``` + +--- + +## Implementation Details + +### withMetrics HOC + +**File:** `src/withMetrics.tsx` + +The HOC wraps components to track lifecycle events: + +```typescript +export default function withMetrics

( + Component: any, + widgetName: string +) { + return React.memo( + (props: P) => { + // Track mount and unmount + useEffect(() => { + logMetrics({ + widgetName, + event: 'WIDGET_MOUNTED', + timestamp: Date.now(), + }); + + return () => { + logMetrics({ + widgetName, + event: 'WIDGET_UNMOUNTED', + timestamp: Date.now(), + }); + }; + }, []); + + return ; + }, + // Custom comparison function + (prevProps, nextProps) => !havePropsChanged(prevProps, nextProps) + ); +} +``` + +**Key Features:** +- Uses `React.memo` for performance optimization +- Custom props comparison via `havePropsChanged` +- Single `useEffect` with cleanup for lifecycle tracking +- Props passed through transparently + +--- + +### logMetrics Function + +**File:** `src/metricsLogger.ts` + +Logs metrics to store.logger: + +```typescript +export const logMetrics = (metric: WidgetMetrics) => { + if (!store.logger) { + console.warn('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', + } + ); +}; +``` + +**Behavior:** +- Checks for `store.logger` existence +- Warns to console if logger missing (doesn't throw) +- Formats metrics as JSON string +- Includes module/method context + +--- + +### havePropsChanged Function + +**File:** `src/metricsLogger.ts` + +Performs shallow comparison to detect prop changes: + +```typescript +export function havePropsChanged(prev: any, next: any): boolean { + if (prev === next) return false; + + // Type check + if (typeof prev !== typeof next) return true; + if (!prev || !next) return prev !== next; + + // Compare keys + const prevKeys = Object.keys(prev); + const nextKeys = Object.keys(next); + if (prevKeys.length !== nextKeys.length) return true; + + // Compare primitive values (shallow) + 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; + } + + return false; +} +``` + +**Logic:** +- Reference equality check first (fastest) +- Type comparison +- Key count comparison +- Shallow primitive comparison +- **Does NOT** deep compare nested objects (intentional for performance) + +**Use Case:** +Used by `React.memo` to prevent unnecessary re-renders when props haven't actually changed. + +--- + +## Metrics Events + +### Event Types + +| Event | When Fired | Use Case | +|-------|-----------|----------| +| `WIDGET_MOUNTED` | Component mounted to DOM | Track widget usage, initialization time | +| `WIDGET_UNMOUNTED` | Component unmounted from DOM | Track session duration, cleanup | +| `ERROR` | Error occurred | Track failures, debug issues | +| `PROPS_UPDATED` | Props changed (future) | Track configuration changes | + +### Metrics Data Structure + +```typescript +type WidgetMetrics = { + widgetName: string; // e.g., 'StationLogin' + event: string; // e.g., 'WIDGET_MOUNTED' + props?: Record; // Optional props snapshot + timestamp: number; // Unix timestamp + additionalContext?: Record; // Custom data +}; +``` + +**Example Logged Metric:** + +```json +{ + "widgetName": "StationLogin", + "event": "WIDGET_MOUNTED", + "timestamp": 1700000000000, + "props": { + "profileMode": false, + "teamId": "team123" + }, + "additionalContext": { + "userAgent": "Chrome/120.0", + "sessionId": "session-abc" + } +} +``` + +--- + +## Performance Optimization + +### React.memo with Custom Comparison + +The HOC uses `React.memo` with `havePropsChanged` to optimize re-renders: + +```mermaid +graph TD + Start[Props Update] + Compare{havePropsChanged?} + Rerender[Re-render Component] + Skip[Skip Re-render] + + Start --> Compare + Compare -->|true| Rerender + Compare -->|false| Skip + + style Compare fill:#ffe1e1 + style Skip fill:#e1ffe1 +``` + +**Benefits:** +- Prevents unnecessary re-renders +- Reduces PROPS_UPDATED events +- Improves performance for widgets with frequent parent updates + +**Trade-off:** +- Shallow comparison only (nested object changes might be missed) +- Intentional design choice to avoid deep comparison overhead + +--- + +## Store Integration + +### Logger Dependency + +The package relies on `store.logger` being initialized: + +```typescript +// store.logger must be set before using ui-logging +import store from '@webex/cc-store'; + +store.setLogger({ + log: (...args) => console.log(...args), + error: (...args) => console.error(...args), + warn: (...args) => console.warn(...args), + info: (...args) => console.info(...args), +}); + +// Now logMetrics will work +logMetrics({ ... }); +``` + +**Graceful Degradation:** +- If `store.logger` is undefined, logs warning to console +- Does NOT throw error (allows widgets to work without logger) + +--- + +## Troubleshooting Guide + +### Common Issues + +#### 1. Metrics Not Logging + +**Symptoms:** +- No metrics appearing in logs +- Silent failures + +**Possible Causes:** +- `store.logger` not initialized +- Logger object missing methods + +**Solutions:** + +```typescript +// Check if logger exists +import store from '@webex/cc-store'; +console.log('Logger exists:', store.logger !== undefined); + +// Set logger if missing +store.setLogger({ + log: console.log, + error: console.error, + warn: console.warn, + info: console.info, +}); + +// Verify logging works +import { logMetrics } from '@webex/cc-ui-logging'; +logMetrics({ + widgetName: 'Test', + event: 'WIDGET_MOUNTED', + timestamp: Date.now() +}); +``` + +#### 2. Component Re-rendering Too Often + +**Symptoms:** +- Component re-renders on every parent update +- Performance degradation + +**Possible Causes:** +- Props comparison not working +- Passing new object/function references + +**Solutions:** + +```typescript +// Ensure stable prop references +import { useCallback, useMemo } from 'react'; + +const Parent = () => { + // ✅ Memoized callback + const handleChange = useCallback(() => {}, []); + + // ✅ Memoized object + const config = useMemo(() => ({ option: 'value' }), []); + + return ; +}; + +// ❌ Avoid inline functions/objects + {}} // New function every render + config={{ option: 'value' }} // New object every render +/> +``` + +#### 3. TypeScript Type Errors + +**Symptoms:** +- Type errors with WidgetMetrics +- Event type not recognized + +**Possible Causes:** +- Using incorrect event type +- Missing type import + +**Solutions:** + +```typescript +// Import type +import type { WidgetMetrics } from '@webex/cc-ui-logging'; + +// Use correct event types +const metric: WidgetMetrics = { + widgetName: 'MyWidget', + event: 'WIDGET_MOUNTED', // Must be one of the allowed event types + timestamp: Date.now() +}; + +// For custom events, use WIDGET_MOUNTED with additionalContext +const customMetric: WidgetMetrics = { + widgetName: 'MyWidget', + event: 'WIDGET_MOUNTED', + timestamp: Date.now(), + additionalContext: { + customEvent: 'button_clicked' + } +}; +``` + +#### 4. HOC Not Tracking Unmount + +**Symptoms:** +- WIDGET_MOUNTED logged +- WIDGET_UNMOUNTED never logged + +**Possible Causes:** +- Component never unmounted +- Cleanup function not running +- Page refreshed before unmount + +**Solutions:** + +```typescript +// Verify component actually unmounts +useEffect(() => { + console.log('Component mounted'); + + return () => { + console.log('Component cleanup'); // Should see this + }; +}, []); + +// For navigation/page changes +window.addEventListener('beforeunload', () => { + // Log before page unload + logMetrics({ + widgetName: 'MyWidget', + event: 'WIDGET_UNMOUNTED', + timestamp: Date.now() + }); +}); +``` + +--- + +## Related Documentation + +- [Agent Documentation](./agent.md) - Usage examples and API +- [React Patterns](../../../../ai-docs/patterns/react-patterns.md) - HOC patterns +- [CC Store Documentation](../../store/ai-docs/agent.md) - Logger configuration + +--- + +_Last Updated: 2025-11-26_ + From 06f37ca95ead8d323fb4427fad172035bb8f0b14 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Tue, 3 Feb 2026 10:20:21 +0530 Subject: [PATCH 2/3] docs(ui-logging): ;3Breview --- .../ui-logging/ai-docs/AGENTS.md | 28 +------ .../ui-logging/ai-docs/ARCHITECTURE.md | 83 +------------------ 2 files changed, 4 insertions(+), 107 deletions(-) diff --git a/packages/contact-center/ui-logging/ai-docs/AGENTS.md b/packages/contact-center/ui-logging/ai-docs/AGENTS.md index 9380913a1..c26927626 100644 --- a/packages/contact-center/ui-logging/ai-docs/AGENTS.md +++ b/packages/contact-center/ui-logging/ai-docs/AGENTS.md @@ -2,7 +2,7 @@ ## Overview -UI Logging is a lightweight utility package that provides metrics tracking capabilities for contact center widgets. It includes a Higher-Order Component (HOC) called `withMetrics` that automatically tracks widget lifecycle events, and a `logMetrics` function for custom event logging. +UI Logging is a lightweight utility package that provides logging and metrics tracking capabilities for contact center widgets. It includes a Higher-Order Component (HOC) called `withMetrics` that automatically tracks widget lifecycle events, and a `logMetrics` function for custom event logging. **Package:** `@webex/cc-ui-logging` @@ -19,7 +19,7 @@ The UI Logging package enables observability and monitoring for contact center w - **Provides HOC wrapper** - Easy integration with minimal code changes - **Logs to store logger** - Integrates with existing logging infrastructure - **Supports custom metrics** - Log custom events with additional context -- **Optimizes re-renders** - Includes shallow props comparison for performance +- **[WIP]Optimizes re-renders** - Includes shallow props comparison for performance ### Key Capabilities @@ -83,7 +83,7 @@ function MyComponent() { ```typescript import { withMetrics } from '@webex/cc-ui-logging'; -import { StationLogin } from './StationLogin'; +import { StationLogin } from '@webex/cc-widget'; // Automatically tracks mount/unmount const StationLoginWithMetrics = withMetrics( @@ -327,7 +327,6 @@ function withMetrics

( **Behavior:** - Wraps component with React.memo -- Uses custom comparison function (`havePropsChanged`) - Logs WIDGET_MOUNTED on mount - Logs WIDGET_UNMOUNTED on unmount @@ -361,27 +360,6 @@ type WidgetMetrics = { --- -### havePropsChanged Function - -```typescript -function havePropsChanged(prev: any, next: any): boolean -``` - -**Parameters:** -- `prev` - Previous props object -- `next` - Next props object - -**Returns:** `true` if props have changed, `false` otherwise - -**Behavior:** -- Performs shallow comparison -- Compares object keys length -- Compares primitive values -- Does NOT deep compare nested objects -- Used by React.memo to prevent re-renders - ---- - ## Installation ```bash diff --git a/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md b/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md index 9a33299d7..5f7d38b75 100644 --- a/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md +++ b/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md @@ -2,7 +2,7 @@ ## Component Overview -UI Logging is a utility package that provides metrics tracking through a Higher-Order Component (HOC) pattern and direct logging functions. It integrates with the store's logger to provide centralized metrics collection. +UI Logging is a utility package that provides logging and metrics tracking through a Higher-Order Component (HOC) pattern and direct logging functions. It integrates with the store's logger to provide centralized metrics collection. ### Module Table @@ -26,7 +26,6 @@ ui-logging/ ├── dist/ │ ├── index.js # Build output │ └── types/ -│ ├── index.d.ts │ ├── metricsLogger.d.ts │ └── withMetrics.d.ts ├── package.json @@ -195,53 +194,6 @@ export const logMetrics = (metric: WidgetMetrics) => { - Formats metrics as JSON string - Includes module/method context ---- - -### havePropsChanged Function - -**File:** `src/metricsLogger.ts` - -Performs shallow comparison to detect prop changes: - -```typescript -export function havePropsChanged(prev: any, next: any): boolean { - if (prev === next) return false; - - // Type check - if (typeof prev !== typeof next) return true; - if (!prev || !next) return prev !== next; - - // Compare keys - const prevKeys = Object.keys(prev); - const nextKeys = Object.keys(next); - if (prevKeys.length !== nextKeys.length) return true; - - // Compare primitive values (shallow) - 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; - } - - return false; -} -``` - -**Logic:** -- Reference equality check first (fastest) -- Type comparison -- Key count comparison -- Shallow primitive comparison -- **Does NOT** deep compare nested objects (intentional for performance) - -**Use Case:** -Used by `React.memo` to prevent unnecessary re-renders when props haven't actually changed. - ---- - ## Metrics Events ### Event Types @@ -284,39 +236,6 @@ type WidgetMetrics = { ``` --- - -## Performance Optimization - -### React.memo with Custom Comparison - -The HOC uses `React.memo` with `havePropsChanged` to optimize re-renders: - -```mermaid -graph TD - Start[Props Update] - Compare{havePropsChanged?} - Rerender[Re-render Component] - Skip[Skip Re-render] - - Start --> Compare - Compare -->|true| Rerender - Compare -->|false| Skip - - style Compare fill:#ffe1e1 - style Skip fill:#e1ffe1 -``` - -**Benefits:** -- Prevents unnecessary re-renders -- Reduces PROPS_UPDATED events -- Improves performance for widgets with frequent parent updates - -**Trade-off:** -- Shallow comparison only (nested object changes might be missed) -- Intentional design choice to avoid deep comparison overhead - ---- - ## Store Integration ### Logger Dependency From b818fbef9bdedf790a5c9176bb53a2d80cb39979 Mon Sep 17 00:00:00 2001 From: Shreyas Sharma Date: Tue, 3 Feb 2026 15:46:00 +0530 Subject: [PATCH 3/3] docs(ui-logging):review --- .../ui-logging/ai-docs/AGENTS.md | 209 ++++-------------- .../ui-logging/ai-docs/ARCHITECTURE.md | 106 ++++----- 2 files changed, 93 insertions(+), 222 deletions(-) diff --git a/packages/contact-center/ui-logging/ai-docs/AGENTS.md b/packages/contact-center/ui-logging/ai-docs/AGENTS.md index c26927626..555b19255 100644 --- a/packages/contact-center/ui-logging/ai-docs/AGENTS.md +++ b/packages/contact-center/ui-logging/ai-docs/AGENTS.md @@ -1,5 +1,17 @@ # UI Logging - Metrics Tracking Utility +## AI Agent Routing (Do Not Start Here) + +If you are an AI assistant or tool reading this file **as your first entry point**, do **not** start your reasoning or code generation workflow from here. + +- **Primary entrypoint:** Always begin with the **nearest parent** contact-center AI docs `AGENTS.md` (for example, the root `ai-docs/AGENTS.md` at the repository root). +- **Process:** + - Load and follow the instructions and templates in that parent `AGENTS.md`. + - Only after a parent `AGENTS.md` explicitly routes you to this file should you treat this document as package-specific guidance. +- **Never** skip the parent `AGENTS.md` even if the user prompt directly mentions this specific package or file. + +Once you have gone through the parent `AGENTS.md` and been routed here, you can use the rest of this file as the authoritative reference for the `@webex/cc-ui-logging` package. + ## Overview UI Logging is a lightweight utility package that provides logging and metrics tracking capabilities for contact center widgets. It includes a Higher-Order Component (HOC) called `withMetrics` that automatically tracks widget lifecycle events, and a `logMetrics` function for custom event logging. @@ -15,6 +27,7 @@ UI Logging is a lightweight utility package that provides logging and metrics tr ### Purpose The UI Logging package enables observability and monitoring for contact center widgets. It: + - **Tracks widget lifecycle** - Automatically logs mount, unmount, and updates - **Provides HOC wrapper** - Easy integration with minimal code changes - **Logs to store logger** - Integrates with existing logging infrastructure @@ -54,29 +67,6 @@ function App() { // - WIDGET_UNMOUNTED when component unmounts ``` -#### Manual Metrics Logging - -```typescript -import { logMetrics } from '@webex/cc-ui-logging'; - -function MyComponent() { - const handleButtonClick = () => { - // Log custom event - logMetrics({ - widgetName: 'MyComponent', - event: 'ERROR', - timestamp: Date.now(), - additionalContext: { - errorCode: 'LOGIN_FAILED', - reason: 'Invalid credentials' - } - }); - }; - - return ; -} -``` - ### Common Use Cases #### 1. Tracking Widget Lifecycle @@ -87,7 +77,7 @@ import { StationLogin } from '@webex/cc-widget'; // Automatically tracks mount/unmount const StationLoginWithMetrics = withMetrics( - StationLogin, + StationLogin, 'StationLogin' ); @@ -109,91 +99,6 @@ const StationLoginWithMetrics = withMetrics( // } ``` -#### 2. Logging Errors - -```typescript -import { logMetrics } from '@webex/cc-ui-logging'; - -function UserState() { - const handleStateChange = async (newState) => { - try { - await updateState(newState); - } catch (error) { - // Log error with context - logMetrics({ - widgetName: 'UserState', - event: 'ERROR', - timestamp: Date.now(), - props: { attemptedState: newState }, - additionalContext: { - error: error.message, - stack: error.stack - } - }); - } - }; - - return ; -} -``` - -#### 3. Performance Tracking - -```typescript -import { logMetrics } from '@webex/cc-ui-logging'; -import { useEffect } from 'react'; - -function TaskList({ tasks }) { - useEffect(() => { - const startTime = performance.now(); - - // Render tasks - renderTasks(tasks); - - const endTime = performance.now(); - - // Log render performance - logMetrics({ - widgetName: 'TaskList', - event: 'WIDGET_MOUNTED', - timestamp: Date.now(), - additionalContext: { - renderTime: endTime - startTime, - taskCount: tasks.length - } - }); - }, [tasks]); - - return

{/* task list */}
; -} -``` - -#### 4. User Interaction Tracking - -```typescript -import { logMetrics } from '@webex/cc-ui-logging'; - -function CallControl({ task }) { - const handleHold = () => { - logMetrics({ - widgetName: 'CallControl', - event: 'WIDGET_MOUNTED', // Using WIDGET_MOUNTED for custom events - timestamp: Date.now(), - props: { taskId: task.id }, - additionalContext: { - action: 'hold_clicked', - callDuration: task.duration - } - }); - - // Perform hold action - task.hold(); - }; - - return ; -} -``` - ### Integration Patterns #### With Widget Components @@ -224,62 +129,32 @@ export { UserState }; #### With Error Boundaries -```typescript -import { logMetrics } from '@webex/cc-ui-logging'; +````typescript +import { withMetrics } from '@webex/cc-ui-logging'; import { ErrorBoundary } from 'react-error-boundary'; +const UserStateInternal = observer(({ onStateChange }) => { + const props = { + idleCodes: store.idleCodes, + currentState: store.currentState, + setAgentStatus: (code) => store.setCurrentState(code), + onStateChange, + }; + + return ; +}); + function Widget(props) { const handleError = (error: Error) => { // Log error via metrics - logMetrics({ - widgetName: 'MyWidget', - event: 'ERROR', - timestamp: Date.now(), - additionalContext: { - error: error.message, - componentStack: error.stack - } - }); }; return ( - + ); } -``` - -#### Custom Metrics in Hooks - -```typescript -import { logMetrics } from '@webex/cc-ui-logging'; -import { useEffect } from 'react'; - -function useCustomHook(widgetName: string) { - useEffect(() => { - // Log when hook initializes - logMetrics({ - widgetName, - event: 'WIDGET_MOUNTED', - timestamp: Date.now(), - additionalContext: { - hookInitialized: true - } - }); - - return () => { - // Log when hook cleans up - logMetrics({ - widgetName, - event: 'WIDGET_UNMOUNTED', - timestamp: Date.now() - }); - }; - }, [widgetName]); -} -``` - --- ## Dependencies @@ -288,20 +163,21 @@ function useCustomHook(widgetName: string) { ### Runtime Dependencies -| Package | Purpose | -|---------|---------| +| Package | Purpose | +| ----------------- | ---------------------------------- | | `@webex/cc-store` | Access to store.logger for logging | ### Peer Dependencies -| Package | Purpose | -|---------|---------| -| `react` | React framework (for HOC) | -| `react-dom` | React DOM (for HOC) | +| Package | Purpose | +| ----------- | ------------------------- | +| `react` | React framework (for HOC) | +| `react-dom` | React DOM (for HOC) | ### Development Dependencies Key development tools (see [package.json](../package.json) for versions): + - TypeScript - Jest (testing) - Webpack (bundling) @@ -316,16 +192,18 @@ Key development tools (see [package.json](../package.json) for versions): function withMetrics

( Component: React.ComponentType

, widgetName: string -): React.MemoExoticComponent> -``` +): React.MemoExoticComponent>; +```` **Parameters:** + - `Component` - React component to wrap - `widgetName` - Name for metric identification **Returns:** Memoized component with automatic metrics tracking **Behavior:** + - Wraps component with React.memo - Logs WIDGET_MOUNTED on mount - Logs WIDGET_UNMOUNTED on unmount @@ -335,7 +213,7 @@ function withMetrics

( ### logMetrics Function ```typescript -function logMetrics(metric: WidgetMetrics): void +function logMetrics(metric: WidgetMetrics): void; type WidgetMetrics = { widgetName: string; @@ -347,6 +225,7 @@ type WidgetMetrics = { ``` **Parameters:** + - `metric.widgetName` - Widget identifier - `metric.event` - Event type - `metric.props` - Optional widget props snapshot @@ -354,6 +233,7 @@ type WidgetMetrics = { - `metric.additionalContext` - Optional additional data **Behavior:** + - Checks if `store.logger` exists - Logs warning if no logger available - Calls `store.logger.log()` with formatted JSON @@ -363,8 +243,8 @@ type WidgetMetrics = { ## Installation ```bash -# Install as development or runtime dependency -yarn add @webex/cc-ui-logging +# Install as dependency +yarn add @webex/cc-widget # Used internally by widgets, usually not directly installed ``` @@ -378,4 +258,3 @@ For detailed HOC implementation, metrics flow, and performance optimization, see --- _Last Updated: 2025-11-26_ - diff --git a/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md b/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md index 5f7d38b75..5e9267fc8 100644 --- a/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md +++ b/packages/contact-center/ui-logging/ai-docs/ARCHITECTURE.md @@ -6,11 +6,11 @@ UI Logging is a utility package that provides logging and metrics tracking throu ### Module Table -| Module | File | Exports | Purpose | Dependencies | -|--------|------|---------|---------|--------------| -| **withMetrics HOC** | `src/withMetrics.tsx` | `withMetrics` (default) | Wraps components with lifecycle tracking | React, metricsLogger | -| **metricsLogger** | `src/metricsLogger.ts` | `logMetrics`, `havePropsChanged`, `WidgetMetrics` (type) | Logging functions and utilities | @webex/cc-store | -| **Package Entry** | `src/index.ts` | All exports | Main package export | Both modules above | +| Module | File | Exports | Purpose | Dependencies | +| ------------------- | ---------------------- | -------------------------------------------------------- | ---------------------------------------- | -------------------- | +| **withMetrics HOC** | `src/withMetrics.tsx` | `withMetrics` (default) | Wraps components with lifecycle tracking | React, metricsLogger | +| **metricsLogger** | `src/metricsLogger.ts` | `logMetrics`, `havePropsChanged`, `WidgetMetrics` (type) | Logging functions and utilities | @webex/cc-store | +| **Package Entry** | `src/index.ts` | All exports | Main package export | Both modules above | ### File Structure @@ -45,26 +45,26 @@ graph LR Component[React Component] Event[User Event/Lifecycle] end - + subgraph "UI Logging" HOC[withMetrics HOC] LogFn[logMetrics Function] end - + subgraph "Store" Logger[store.logger] end - + subgraph "Backend/Console" Output[Log Output] end - + Component -->|Wrapped by| HOC HOC -->|Mount/Unmount| LogFn Event -->|Custom logging| LogFn LogFn -->|JSON metrics| Logger Logger -->|Formatted logs| Output - + style HOC fill:#e1f5ff style LogFn fill:#ffe1e1 style Logger fill:#fff4e1 @@ -82,7 +82,7 @@ sequenceDiagram App->>HOC: Render withMetrics(Component) activate HOC - + HOC->>HOC: useEffect (mount) HOC->>Logger: logMetrics({event: 'WIDGET_MOUNTED'}) activate Logger @@ -94,17 +94,17 @@ sequenceDiagram Logger->>Logger: console.warn('No logger found') end deactivate Logger - + HOC->>Component: Render with props activate Component Component-->>HOC: Rendered deactivate Component - + HOC-->>App: Rendered widget deactivate HOC Note over App,Store: Component unmounts - + App->>HOC: Unmount activate HOC HOC->>HOC: useEffect cleanup @@ -151,14 +151,13 @@ export default function withMetrics

( }, []); return ; - }, - // Custom comparison function - (prevProps, nextProps) => !havePropsChanged(prevProps, nextProps) + } ); } ``` **Key Features:** + - Uses `React.memo` for performance optimization - Custom props comparison via `havePropsChanged` - Single `useEffect` with cleanup for lifecycle tracking @@ -178,17 +177,15 @@ export const logMetrics = (metric: WidgetMetrics) => { console.warn('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', - } - ); + store.logger.log(`CC-Widgets: UI Metrics: ${JSON.stringify(metric, null, 2)}`, { + module: 'metricsLogger.tsx', + method: 'logMetrics', + }); }; ``` **Behavior:** + - Checks for `store.logger` existence - Warns to console if logger missing (doesn't throw) - Formats metrics as JSON string @@ -198,22 +195,20 @@ export const logMetrics = (metric: WidgetMetrics) => { ### Event Types -| Event | When Fired | Use Case | -|-------|-----------|----------| -| `WIDGET_MOUNTED` | Component mounted to DOM | Track widget usage, initialization time | -| `WIDGET_UNMOUNTED` | Component unmounted from DOM | Track session duration, cleanup | -| `ERROR` | Error occurred | Track failures, debug issues | -| `PROPS_UPDATED` | Props changed (future) | Track configuration changes | +| Event | When Fired | Use Case | +| ------------------ | ---------------------------- | --------------------------------------- | +| `WIDGET_MOUNTED` | Component mounted to DOM | Track widget usage, initialization time | +| `WIDGET_UNMOUNTED` | Component unmounted from DOM | Track session duration, cleanup | +| `ERROR` | Error occurred | Track failures, debug issues | +| `PROPS_UPDATED` | Props changed (future) | Track configuration changes | ### Metrics Data Structure ```typescript type WidgetMetrics = { - widgetName: string; // e.g., 'StationLogin' - event: string; // e.g., 'WIDGET_MOUNTED' - props?: Record; // Optional props snapshot - timestamp: number; // Unix timestamp - additionalContext?: Record; // Custom data + widgetName: string; // e.g., 'StationLogin' + event: string; // e.g., 'WIDGET_MOUNTED' + timestamp: number; // Unix timestampdata }; ``` @@ -223,19 +218,12 @@ type WidgetMetrics = { { "widgetName": "StationLogin", "event": "WIDGET_MOUNTED", - "timestamp": 1700000000000, - "props": { - "profileMode": false, - "teamId": "team123" - }, - "additionalContext": { - "userAgent": "Chrome/120.0", - "sessionId": "session-abc" - } + "timestamp": 1700000000000 } ``` --- + ## Store Integration ### Logger Dependency @@ -258,6 +246,7 @@ logMetrics({ ... }); ``` **Graceful Degradation:** + - If `store.logger` is undefined, logs warning to console - Does NOT throw error (allows widgets to work without logger) @@ -270,10 +259,12 @@ logMetrics({ ... }); #### 1. Metrics Not Logging **Symptoms:** + - No metrics appearing in logs - Silent failures **Possible Causes:** + - `store.logger` not initialized - Logger object missing methods @@ -293,22 +284,23 @@ store.setLogger({ }); // Verify logging works -import { logMetrics } from '@webex/cc-ui-logging'; +import {logMetrics} from '@webex/cc-ui-logging'; logMetrics({ widgetName: 'Test', event: 'WIDGET_MOUNTED', - timestamp: Date.now() + timestamp: Date.now(), }); ``` #### 2. Component Re-rendering Too Often **Symptoms:** + - Component re-renders on every parent update - Performance degradation **Possible Causes:** -- Props comparison not working + - Passing new object/function references **Solutions:** @@ -320,7 +312,7 @@ import { useCallback, useMemo } from 'react'; const Parent = () => { // ✅ Memoized callback const handleChange = useCallback(() => {}, []); - + // ✅ Memoized object const config = useMemo(() => ({ option: 'value' }), []); @@ -328,7 +320,7 @@ const Parent = () => { }; // ❌ Avoid inline functions/objects - {}} // New function every render config={{ option: 'value' }} // New object every render /> @@ -337,10 +329,12 @@ const Parent = () => { #### 3. TypeScript Type Errors **Symptoms:** + - Type errors with WidgetMetrics - Event type not recognized **Possible Causes:** + - Using incorrect event type - Missing type import @@ -348,13 +342,13 @@ const Parent = () => { ```typescript // Import type -import type { WidgetMetrics } from '@webex/cc-ui-logging'; +import type {WidgetMetrics} from '@webex/cc-ui-logging'; // Use correct event types const metric: WidgetMetrics = { widgetName: 'MyWidget', event: 'WIDGET_MOUNTED', // Must be one of the allowed event types - timestamp: Date.now() + timestamp: Date.now(), }; // For custom events, use WIDGET_MOUNTED with additionalContext @@ -362,19 +356,18 @@ const customMetric: WidgetMetrics = { widgetName: 'MyWidget', event: 'WIDGET_MOUNTED', timestamp: Date.now(), - additionalContext: { - customEvent: 'button_clicked' - } }; ``` #### 4. HOC Not Tracking Unmount **Symptoms:** + - WIDGET_MOUNTED logged - WIDGET_UNMOUNTED never logged **Possible Causes:** + - Component never unmounted - Cleanup function not running - Page refreshed before unmount @@ -385,7 +378,7 @@ const customMetric: WidgetMetrics = { // Verify component actually unmounts useEffect(() => { console.log('Component mounted'); - + return () => { console.log('Component cleanup'); // Should see this }; @@ -397,7 +390,7 @@ window.addEventListener('beforeunload', () => { logMetrics({ widgetName: 'MyWidget', event: 'WIDGET_UNMOUNTED', - timestamp: Date.now() + timestamp: Date.now(), }); }); ``` @@ -413,4 +406,3 @@ window.addEventListener('beforeunload', () => { --- _Last Updated: 2025-11-26_ -