From ad58888514a39a213ea0d3dbdf2d0429d016d279 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Tue, 24 Mar 2026 13:22:31 +0530 Subject: [PATCH] docs: supervisor module spec + implementation plan Co-Authored-By: Claude Opus 4.6 (1M context) --- .../plans/2026-03-24-supervisor-module.md | 531 ++++++++++++++++++ .../specs/2026-03-24-supervisor-module.md | 191 +++++++ 2 files changed, 722 insertions(+) create mode 100644 docs/superpowers/plans/2026-03-24-supervisor-module.md create mode 100644 docs/superpowers/specs/2026-03-24-supervisor-module.md diff --git a/docs/superpowers/plans/2026-03-24-supervisor-module.md b/docs/superpowers/plans/2026-03-24-supervisor-module.md new file mode 100644 index 0000000..3d9dbdb --- /dev/null +++ b/docs/superpowers/plans/2026-03-24-supervisor-module.md @@ -0,0 +1,531 @@ +# Supervisor Module Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build the supervisor module with team performance dashboard (PP-5), live call monitor (PP-6), master data pages, and admin sidebar restructure. + +**Architecture:** Frontend pages query platform GraphQL directly for entity data (calls, appointments, leads, agents). Sidecar provides Ozonetel-specific data (agent time breakdown, active calls via event subscription). No hardcoded/mock data anywhere. + +**Tech Stack:** React + Tailwind + ECharts (frontend), NestJS sidecar (Ozonetel integration), Fortytwo platform GraphQL + +**Spec:** `docs/superpowers/specs/2026-03-24-supervisor-module.md` + +--- + +## File Map + +### Frontend (`helix-engage/src/`) + +| File | Action | Responsibility | +|------|--------|----------------| +| `pages/team-performance.tsx` | Create | PP-5 full dashboard | +| `pages/live-monitor.tsx` | Create | PP-6 active call table | +| `pages/call-recordings.tsx` | Create | Calls with recordings master | +| `pages/missed-calls.tsx` | Create | Missed calls master (supervisor view) | +| `components/layout/sidebar.tsx` | Modify | Admin nav restructure | +| `main.tsx` | Modify | Add new routes | + +### Sidecar (`helix-engage-server/src/`) + +| File | Action | Responsibility | +|------|--------|----------------| +| `supervisor/supervisor.service.ts` | Create | Team perf aggregation + active call tracking | +| `supervisor/supervisor.controller.ts` | Create | REST endpoints | +| `supervisor/supervisor.module.ts` | Create | Module registration | +| `app.module.ts` | Modify | Import SupervisorModule | + +--- + +## Task 1: Admin Sidebar Nav + Routes + +**Files:** +- Modify: `helix-engage/src/components/layout/sidebar.tsx` +- Modify: `helix-engage/src/main.tsx` + +- [ ] **Step 1: Add new icon imports to sidebar** + +In `sidebar.tsx`, add to the FontAwesome imports: + +```typescript +import { + // existing imports... + faRadio, + faFileAudio, + faPhoneMissed, + faChartLine, +} from '@fortawesome/pro-duotone-svg-icons'; +``` + +Add icon wrappers: + +```typescript +const IconRadio = faIcon(faRadio); +const IconFileAudio = faIcon(faFileAudio); +const IconPhoneMissed = faIcon(faPhoneMissed); +const IconChartLine = faIcon(faChartLine); +``` + +- [ ] **Step 2: Restructure admin nav** + +Replace the admin nav section (currently has Overview + Management + Admin groups) with: + +```typescript +if (role === 'admin') { + return [ + { label: 'Supervisor', items: [ + { label: 'Dashboard', href: '/', icon: IconGrid2 }, + { label: 'Team Performance', href: '/team-performance', icon: IconChartLine }, + { label: 'Live Call Monitor', href: '/live-monitor', icon: IconRadio }, + ]}, + { label: 'Data & Reports', items: [ + { label: 'Lead Master', href: '/leads', icon: IconUsers }, + { label: 'Patient Master', href: '/patients', icon: IconHospitalUser }, + { label: 'Appointment Master', href: '/appointments', icon: IconCalendarCheck }, + { label: 'Call Log Master', href: '/call-history', icon: IconClockRewind }, + { label: 'Call Recordings', href: '/call-recordings', icon: IconFileAudio }, + { label: 'Missed Calls', href: '/missed-calls', icon: IconPhoneMissed }, + ]}, + { label: 'Admin', items: [ + { label: 'Settings', href: '/settings', icon: IconGear }, + ]}, + ]; +} +``` + +- [ ] **Step 3: Add routes in main.tsx** + +Import new page components (they'll be created in later tasks — use placeholder components for now): + +```typescript +import { TeamPerformancePage } from "@/pages/team-performance"; +import { LiveMonitorPage } from "@/pages/live-monitor"; +import { CallRecordingsPage } from "@/pages/call-recordings"; +import { MissedCallsPage } from "@/pages/missed-calls"; +``` + +Add routes: + +```typescript +} /> +} /> +} /> +} /> +``` + +- [ ] **Step 4: Create placeholder pages** + +Create minimal placeholder files for each new page so the build doesn't fail: + +```typescript +// src/pages/team-performance.tsx +export const TeamPerformancePage = () =>
Team Performance — coming soon
; + +// src/pages/live-monitor.tsx +export const LiveMonitorPage = () =>
Live Call Monitor — coming soon
; + +// src/pages/call-recordings.tsx +export const CallRecordingsPage = () =>
Call Recordings — coming soon
; + +// src/pages/missed-calls.tsx +export const MissedCallsPage = () =>
Missed Calls — coming soon
; +``` + +- [ ] **Step 5: Verify build** + +```bash +cd helix-engage && npm run build +``` + +- [ ] **Step 6: Commit** + +```bash +git add src/components/layout/sidebar.tsx src/main.tsx src/pages/team-performance.tsx src/pages/live-monitor.tsx src/pages/call-recordings.tsx src/pages/missed-calls.tsx +git commit -m "feat: admin sidebar restructure + placeholder pages for supervisor module" +``` + +--- + +## Task 2: Call Recordings Page + +**Files:** +- Modify: `helix-engage/src/pages/call-recordings.tsx` + +- [ ] **Step 1: Implement call recordings page** + +Query platform for calls with recordings. Reuse patterns from `call-history.tsx`. + +```typescript +// Query: calls where recording primaryLinkUrl is not empty +const QUERY = `{ calls(first: 100, filter: { + recording: { primaryLinkUrl: { neq: "" } } +}, orderBy: [{ startedAt: DescNullsLast }]) { edges { node { + id direction callStatus callerNumber { primaryPhoneNumber } + agentName startedAt durationSec disposition + recording { primaryLinkUrl primaryLinkLabel } +} } } }`; +``` + +Table columns: Agent, Caller (PhoneActionCell), Type (In/Out badge), Date, Duration, Disposition, Recording (play button). + +Search by agent name or phone number. Date filter optional. + +- [ ] **Step 2: Verify build** + +```bash +cd helix-engage && npm run build +``` + +- [ ] **Step 3: Commit** + +```bash +git add src/pages/call-recordings.tsx +git commit -m "feat: call recordings master page" +``` + +--- + +## Task 3: Missed Calls Page (Supervisor View) + +**Files:** +- Modify: `helix-engage/src/pages/missed-calls.tsx` + +- [ ] **Step 1: Implement missed calls page** + +Query platform for all missed calls — no agent filter (supervisor sees all). + +```typescript +const QUERY = `{ calls(first: 100, filter: { + callStatus: { eq: MISSED } +}, orderBy: [{ startedAt: DescNullsLast }]) { edges { node { + id callerNumber { primaryPhoneNumber } agentName + startedAt callsourcenumber callbackstatus missedcallcount callbackattemptedat +} } } }`; +``` + +Table columns: Caller (PhoneActionCell), Date/Time, Branch (`callsourcenumber`), Agent, Callback Status (badge), SLA (computed from `startedAt`). + +Tabs: All | Pending (`PENDING_CALLBACK`) | Attempted (`CALLBACK_ATTEMPTED`) | Completed (`CALLBACK_COMPLETED` + `WRONG_NUMBER`). + +Search by phone or agent. + +- [ ] **Step 2: Verify build** + +```bash +cd helix-engage && npm run build +``` + +- [ ] **Step 3: Commit** + +```bash +git add src/pages/missed-calls.tsx +git commit -m "feat: missed calls master page for supervisors" +``` + +--- + +## Task 4: Sidecar — Supervisor Module + +**Files:** +- Create: `helix-engage-server/src/supervisor/supervisor.service.ts` +- Create: `helix-engage-server/src/supervisor/supervisor.controller.ts` +- Create: `helix-engage-server/src/supervisor/supervisor.module.ts` +- Modify: `helix-engage-server/src/app.module.ts` + +- [ ] **Step 1: Create supervisor service** + +```typescript +// supervisor.service.ts +// Two responsibilities: +// 1. Aggregate Ozonetel agent summary across all agents +// 2. Track active calls from Ozonetel real-time events + +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { PlatformGraphqlService } from '../platform/platform-graphql.service'; +import { OzonetelAgentService } from '../ozonetel/ozonetel-agent.service'; + +type ActiveCall = { + ucid: string; + agentId: string; + callerNumber: string; + callType: string; + startTime: string; + status: 'active' | 'on-hold'; +}; + +@Injectable() +export class SupervisorService implements OnModuleInit { + private readonly logger = new Logger(SupervisorService.name); + private readonly activeCalls = new Map(); + + constructor( + private platform: PlatformGraphqlService, + private ozonetel: OzonetelAgentService, + private config: ConfigService, + ) {} + + async onModuleInit() { + // Subscribe to Ozonetel events (fire and forget) + // Will be implemented when webhook URL is configured + this.logger.log('Supervisor service initialized'); + } + + // Called by webhook when Ozonetel pushes call events + handleCallEvent(event: any) { + const { action, ucid, agent_id, caller_id, call_type, event_time } = event; + if (action === 'Answered' || action === 'Calling') { + this.activeCalls.set(ucid, { + ucid, agentId: agent_id, callerNumber: caller_id, + callType: call_type, startTime: event_time, status: 'active', + }); + } else if (action === 'Disconnect') { + this.activeCalls.delete(ucid); + } + } + + // Called by webhook when Ozonetel pushes agent events + handleAgentEvent(event: any) { + this.logger.log(`Agent event: ${event.agentId} → ${event.action}`); + } + + getActiveCalls(): ActiveCall[] { + return Array.from(this.activeCalls.values()); + } + + // Aggregate time breakdown across all agents + async getTeamPerformance(date: string): Promise { + // Get all agent IDs from platform + const agentData = await this.platform.query( + `{ agents(first: 20) { edges { node { + id name ozonetelagentid npsscore maxidleminutes minnpsthreshold minconversionpercent + } } } }`, + ); + const agents = agentData?.agents?.edges?.map((e: any) => e.node) ?? []; + + // Fetch Ozonetel summary per agent + const summaries = await Promise.all( + agents.map(async (agent: any) => { + try { + const summary = await this.ozonetel.getAgentSummary(agent.ozonetelagentid, date); + return { ...agent, timeBreakdown: summary }; + } catch { + return { ...agent, timeBreakdown: null }; + } + }), + ); + + return { date, agents: summaries }; + } +} +``` + +- [ ] **Step 2: Create supervisor controller** + +```typescript +// supervisor.controller.ts +import { Controller, Get, Post, Body, Query, Logger } from '@nestjs/common'; +import { SupervisorService } from './supervisor.service'; + +@Controller('api/supervisor') +export class SupervisorController { + private readonly logger = new Logger(SupervisorController.name); + + constructor(private readonly supervisor: SupervisorService) {} + + @Get('active-calls') + getActiveCalls() { + return this.supervisor.getActiveCalls(); + } + + @Get('team-performance') + async getTeamPerformance(@Query('date') date?: string) { + const targetDate = date ?? new Date().toISOString().split('T')[0]; + return this.supervisor.getTeamPerformance(targetDate); + } + + @Post('call-event') + handleCallEvent(@Body() body: any) { + // Ozonetel pushes events here + const event = body.data ?? body; + this.logger.log(`Call event: ${event.action} ucid=${event.ucid} agent=${event.agent_id}`); + this.supervisor.handleCallEvent(event); + return { received: true }; + } + + @Post('agent-event') + handleAgentEvent(@Body() body: any) { + const event = body.data ?? body; + this.logger.log(`Agent event: ${event.action} agent=${event.agentId}`); + this.supervisor.handleAgentEvent(event); + return { received: true }; + } +} +``` + +- [ ] **Step 3: Create supervisor module and register** + +```typescript +// supervisor.module.ts +import { Module } from '@nestjs/common'; +import { PlatformModule } from '../platform/platform.module'; +import { OzonetelAgentModule } from '../ozonetel/ozonetel-agent.module'; +import { SupervisorController } from './supervisor.controller'; +import { SupervisorService } from './supervisor.service'; + +@Module({ + imports: [PlatformModule, OzonetelAgentModule], + controllers: [SupervisorController], + providers: [SupervisorService], +}) +export class SupervisorModule {} +``` + +Add to `app.module.ts`: +```typescript +import { SupervisorModule } from './supervisor/supervisor.module'; +// Add to imports array +``` + +- [ ] **Step 4: Verify sidecar build** + +```bash +cd helix-engage-server && npm run build +``` + +- [ ] **Step 5: Commit** + +```bash +git add src/supervisor/ src/app.module.ts +git commit -m "feat: supervisor module with team performance + active calls endpoints" +``` + +--- + +## Task 5: Team Performance Dashboard (PP-5) + +**Files:** +- Modify: `helix-engage/src/pages/team-performance.tsx` + +This is the largest task. The page queries platform directly for calls/appointments/leads and the sidecar for time breakdown. + +- [ ] **Step 1: Build the full page** + +The page has 6 sections. Use `apiClient.graphql()` for platform data and `apiClient.get()` for sidecar data. + +**Queries needed:** +- Calls by date range: `calls(first: 500, filter: { startedAt: { gte: "...", lte: "..." } })` +- Appointments by date range: `appointments(first: 200, filter: { scheduledAt: { gte: "...", lte: "..." } })` +- Leads: `leads(first: 200)` +- Follow-ups: `followUps(first: 200)` +- Agents with thresholds: `agents(first: 20) { ... npsscore maxidleminutes minnpsthreshold minconversionpercent }` +- Sidecar: `GET /api/supervisor/team-performance?date=YYYY-MM-DD` + +**Date range logic:** +- Today: today start → now +- Week: Monday of current week → now +- Month: 1st of current month → now +- Year: Jan 1 → now +- Custom: user-selected range + +**Sections to implement:** +1. Key Metrics bar (6 cards in a row) +2. Call Breakdown Trends (2 ECharts line charts side by side) +3. Agent Performance table (sortable) +4. Time Breakdown (team average + per-agent stacked bars) +5. NPS + Conversion Metrics (donut + cards) +6. Performance Alerts (threshold comparison) + +Check if ECharts is already installed: +```bash +grep echarts helix-engage/package.json +``` +If not, install: `npm install echarts echarts-for-react` + +Follow the existing My Performance page (`my-performance.tsx`) for ECharts patterns. + +- [ ] **Step 2: Verify build** + +```bash +cd helix-engage && npm run build +``` + +- [ ] **Step 3: Test locally** + +```bash +cd helix-engage && npm run dev +``` + +Navigate to `/team-performance` as admin user. Verify all 6 sections render with real data. + +- [ ] **Step 4: Commit** + +```bash +git add src/pages/team-performance.tsx package.json package-lock.json +git commit -m "feat: team performance dashboard (PP-5) with 6 data sections" +``` + +--- + +## Task 6: Live Call Monitor (PP-6) + +**Files:** +- Modify: `helix-engage/src/pages/live-monitor.tsx` + +- [ ] **Step 1: Build the live monitor page** + +Page polls `GET /api/supervisor/active-calls` every 5 seconds. + +**Structure:** +1. TopBar: "Live Call Monitor" with subtitle "Listen, whisper, or barge into active calls" +2. Three KPI cards: Active Calls, On Hold, Avg Duration +3. Active Calls table: Agent, Caller, Type, Department, Duration (live counter), Status, Actions +4. Actions: Listen / Whisper / Barge buttons — all disabled with tooltip "Coming soon — pending Ozonetel API" +5. Empty state: headphones icon + "No active calls" + +Duration should be a live counter — calculated client-side from `startTime` in the active call data. Use `setInterval` to update every second. + +Caller name: attempt to match `callerNumber` against leads from `useData()`. If matched, show lead name + phone. If not, show phone only. + +- [ ] **Step 2: Verify build** + +```bash +cd helix-engage && npm run build +``` + +- [ ] **Step 3: Test locally** + +Navigate to `/live-monitor`. Verify empty state renders. If Ozonetel events are flowing, verify active calls appear. + +- [ ] **Step 4: Commit** + +```bash +git add src/pages/live-monitor.tsx +git commit -m "feat: live call monitor page (PP-6) with polling + KPI cards" +``` + +--- + +## Task 7: Local Testing + Final Verification + +- [ ] **Step 1: Run both locally** + +Terminal 1: `cd helix-engage-server && npm run start:dev` +Terminal 2: `cd helix-engage && npm run dev` + +- [ ] **Step 2: Test admin login** + +Login as admin (sanjay.marketing@globalhospital.com). Verify: +- Sidebar shows new nav structure (Supervisor + Data & Reports sections) +- Dashboard loads +- Team Performance shows data from platform +- Live Monitor shows empty state or active calls +- All master data pages load (Lead, Patient, Appointment, Call Log, Call Recordings, Missed Calls) + +- [ ] **Step 3: Commit any fixes** + +- [ ] **Step 4: Push to Azure** + +```bash +cd helix-engage && git push origin dev +cd helix-engage-server && git push origin dev +``` diff --git a/docs/superpowers/specs/2026-03-24-supervisor-module.md b/docs/superpowers/specs/2026-03-24-supervisor-module.md new file mode 100644 index 0000000..be8963b --- /dev/null +++ b/docs/superpowers/specs/2026-03-24-supervisor-module.md @@ -0,0 +1,191 @@ +# Supervisor Module — Team Performance, Live Call Monitor, Master Data + +**Date**: 2026-03-24 +**Jira**: PP-5 (Team Performance), PP-6 (Live Call Monitor) +**Status**: Approved design + +--- + +## Principle + +No hardcoded/mock data. All data from Ozonetel APIs or platform GraphQL queries. + +--- + +## 1. Admin Sidebar Nav Restructure + +``` +SUPERVISOR + Dashboard → / (existing team-dashboard.tsx — summary) + Team Performance → /team-performance (new — full PP-5) + Live Call Monitor → /live-monitor (new — PP-6) + +DATA & REPORTS + Lead Master → /leads (existing all-leads.tsx) + Patient Master → /patients (existing patients.tsx) + Appointment Master → /appointments (existing appointments.tsx) + Call Log Master → /call-history (existing call-history.tsx) + Call Recordings → /call-recordings (new — filtered calls with recordings) + Missed Calls → /missed-calls (new — standalone missed call table) +``` + +**Files**: `sidebar.tsx` (admin nav config), `main.tsx` (routes) + +--- + +## 2. Team Performance Dashboard (PP-5) + +**Route**: `/team-performance` +**Page**: `src/pages/team-performance.tsx` + +### Section 1: Key Metrics Bar +- Active Agents / On Call Now → sidecar (from active calls tracking) +- Total Calls → platform `calls` count by date range +- Appointments → platform `appointments` count +- Missed Calls → platform `calls` where `callStatus: MISSED` +- Conversion Rate → appointments / total calls +- Time filter: Today | Week | Month | Year | Custom + +### Section 2: Call Breakdown Trends +- Left: Inbound vs Outbound line chart (ECharts) by day +- Right: Leads vs Missed vs Follow-ups by day +- Data: platform `calls` grouped by date + direction + +### Section 3: Agent Performance Table +| Column | Source | +|--------|--------| +| Agent | Agent entity `name` | +| Calls | Platform `calls` filtered by `agentName` | +| Inbound | Platform `calls` where `direction: INBOUND` | +| Missed | Platform `calls` where `callStatus: MISSED` | +| Follow-ups | Platform `followUps` filtered by `assignedAgent` | +| Leads | Platform `leads` filtered by `assignedAgent` | +| Conv% | Derived: appointments / calls | +| NPS | Agent entity `npsscore` | +| Idle | Ozonetel `getAgentSummary` API | + +Sortable columns. Own time filter (Today/Week/Month/Year/Custom). + +### Section 4: Time Breakdown +- Team average: Active / Wrap / Idle / Break totals +- Per-agent horizontal stacked bars +- Data: Ozonetel `getAgentSummary` per agent +- Agents with idle > `maxidleminutes` threshold highlighted red + +### Section 5: NPS + Conversion Metrics +- NPS donut chart (average of all agents' `npsscore`) +- Per-agent NPS horizontal bars +- Call→Appointment % card (big number) +- Lead→Contact % card (big number) +- Per-agent conversion breakdown below cards + +### Section 6: Performance Alerts +- Compare actual metrics vs Agent entity thresholds: + - `maxidleminutes` → "Excessive Idle Time" + - `minnpsthreshold` → "Low NPS" + - `minconversionpercent` → "Low Lead-to-Contact" +- Red-highlighted alert cards with agent name, alert type, value + +### Sidecar Endpoint +`GET /api/supervisor/team-performance?date=YYYY-MM-DD` +- Aggregates Ozonetel `getAgentSummary` across all agents +- Returns per-agent time breakdown (active/wrap/idle/break in minutes) +- Uses Agent entity to get list of all agent IDs + +--- + +## 3. Live Call Monitor (PP-6) + +**Route**: `/live-monitor` +**Page**: `src/pages/live-monitor.tsx` + +### KPI Cards +- Active Calls count +- On Hold count +- Avg Duration + +### Active Calls Table +| Column | Source | +|--------|--------| +| Agent | Ozonetel event `agent_id` → mapped to Agent entity name | +| Caller | Event `caller_id` → matched against platform leads/patients | +| Type | Event `call_type` (InBound/Manual) | +| Department | From matched lead's `interestedService` or "—" | +| Duration | Live counter from `event_time` | +| Status | active / on-hold | +| Actions | Listen / Whisper / Barge buttons (disabled until API confirmed) | + +### Data Flow +1. Sidecar subscribes to Ozonetel real-time events on startup + - `POST https://subscription.ozonetel.com/events/subscribe` + - Body: `{ callEventsURL: "", agentEventsURL: "" }` +2. Sidecar receives events at `POST /webhooks/ozonetel/call-event` +3. In-memory map: `ucid → { agentId, callerNumber, callType, startTime, status }` + - `Calling` / `Answered` → add/update entry + - `Disconnect` → remove entry +4. `GET /api/supervisor/active-calls` → returns current map +5. Frontend polls every 5 seconds + +### Sidecar Changes +- New module: `src/supervisor/` + - `supervisor.controller.ts` — team-performance + active-calls endpoints + - `supervisor.service.ts` — Ozonetel event subscription, active call tracking + - `supervisor.module.ts` +- New webhook: `POST /webhooks/ozonetel/call-event` +- Ozonetel event subscription on `onModuleInit` + +--- + +## 4. Master Data Pages + +### Call Recordings (`/call-recordings`) +**Page**: `src/pages/call-recordings.tsx` +- Query: platform `calls` where `recording` is not null +- Table: Agent, Caller, Type, Date, Duration, Recording Player +- Search by agent/phone + date filter + +### Missed Calls (`/missed-calls`) +**Page**: `src/pages/missed-calls.tsx` +- Query: platform `calls` where `callStatus: MISSED` +- Table: Caller, Date/Time, Branch (`callsourcenumber`), Agent, Callback Status, SLA +- Tabs: All | Pending | Attempted | Completed (filter by `callbackstatus`) +- Not filtered by agent — supervisor sees all + +--- + +## 5. Agent Entity Fields (Already Configured) + +| GraphQL Field | Type | Purpose | +|---|---|---| +| `ozonetelagentid` | Text | Ozonetel agent ID | +| `sipextension` | Text | SIP extension | +| `sippassword` | Text | SIP password | +| `campaignname` | Text | Ozonetel campaign | +| `npsscore` | Number | Agent NPS score | +| `maxidleminutes` | Number | Idle time alert threshold | +| `minnpsthreshold` | Number | NPS alert threshold | +| `minconversionpercent` | Number | Conversion alert threshold | + +All custom fields use **all-lowercase** GraphQL names. + +--- + +## 6. File Map + +### New Files +| File | Purpose | +|------|---------| +| `helix-engage/src/pages/team-performance.tsx` | PP-5 dashboard | +| `helix-engage/src/pages/live-monitor.tsx` | PP-6 active call monitor | +| `helix-engage/src/pages/call-recordings.tsx` | Call recordings master | +| `helix-engage/src/pages/missed-calls.tsx` | Missed calls master | +| `helix-engage-server/src/supervisor/supervisor.controller.ts` | Supervisor endpoints | +| `helix-engage-server/src/supervisor/supervisor.service.ts` | Event subscription + active calls | +| `helix-engage-server/src/supervisor/supervisor.module.ts` | Module registration | + +### Modified Files +| File | Change | +|------|--------| +| `helix-engage/src/components/layout/sidebar.tsx` | Admin nav restructure | +| `helix-engage/src/main.tsx` | New routes | +| `helix-engage-server/src/app.module.ts` | Import SupervisorModule |