CallDebugger Refactoring Implementation Plan¶
Date: 2026-02-04 Design Document: 2026-02-04-calldebugger-refactor-design.md Branch: debugger-dashboard
Overview¶
This plan breaks the CallDebugger refactoring into small, testable increments. Tasks are ordered to minimize breaking changes: backend changes first, then utilities, then frontend components, and finally cleanup/deletion of obsolete files.
Phase 1: Backend Changes¶
Task 1.1: Add new call lookup endpoint¶
Goal: Create GET /api/debug/db/calls/:id endpoint that supports both retell and arini call IDs.
Files to modify:
- /src/worker/modules/debug/router.ts - Add new route
- /src/worker/modules/debug/queries/calls.ts - Add/modify query function
Implementation:
1. Add new route GET /db/calls/:id in router.ts
2. Create getCallById query function that:
- Accepts either retell_call_id (call_xxx) or arini_call_id (UUID)
- Returns: { call_id, arini_call_id, date, env, agent_id, agent_name, direction, created_at }
- Uses single SQL query with WHERE retell_call_id = :id OR arini_call_id = :id
Testing: - Test with retell call ID (call_xxx) - Test with arini call ID (UUID) - Test with non-existent ID (should return 404)
Dependencies: None
Task 1.2: Update auth middleware¶
Goal: Remove the lookup endpoint from public paths since it's being replaced.
Files to modify:
- /src/worker/middleware/auth.ts - Remove /api/debug/db/calls/lookup from PUBLIC_PATHS
Testing:
- Verify /api/debug/db/calls/lookup now requires auth
- Verify /api/debug/db/calls/:id requires auth
Dependencies: Task 1.1
Task 1.3: Remove old lookup endpoint¶
Goal: Remove the /api/debug/db/calls/lookup endpoint.
Files to modify:
- /src/worker/modules/debug/router.ts - Remove the lookup route
- /src/worker/modules/debug/queries/calls.ts - Remove lookupCall function if no longer used
Testing: - Verify endpoint returns 404
Dependencies: Task 1.2, Task 3.1 (frontend must be updated first to use new endpoint)
Phase 2: Utility Files¶
Task 2.1: Create deduplicatedFetch utility¶
Goal: Create a simple request deduplication utility to replace callStore's dedupe logic.
Files to create:
- /src/ui/modules/debug/utils/deduplicatedFetch.ts
Implementation:
const pending = new Map<string, Promise<unknown>>()
export async function deduplicatedFetch<T>(
key: string,
fetcher: () => Promise<T>
): Promise<T> {
const existing = pending.get(key)
if (existing) return existing as Promise<T>
const promise = fetcher().finally(() => pending.delete(key))
pending.set(key, promise)
return promise
}
Testing: - Unit test: concurrent calls with same key return same promise - Unit test: key is removed after promise resolves/rejects
Dependencies: None
Task 2.2: Create dashboardState utility¶
Goal: Create sessionStorage helpers for preserving dashboard state when navigating to/from debugger.
Files to create:
- /src/ui/modules/debug/utils/dashboardState.ts
Implementation:
const STORAGE_KEY = 'debug:dashboard'
type DashboardState = {
date: string
env: Environment
orgId: string | null
locationId: string | null
agentId: string | null
}
export function saveDashboardState(state: DashboardState) { ... }
export function loadDashboardState(): DashboardState | null { ... }
export function clearDashboardState() { ... }
Testing: - Unit test: save/load/clear cycle works correctly - Unit test: returns null when no state saved
Dependencies: None
Phase 3: Frontend Components¶
Task 3.1: Create useCall hook¶
Goal: Create a hook to fetch single call metadata by ID using the new endpoint.
Files to create:
- /src/ui/modules/debug/hooks/useCall.ts
Files to modify:
- /src/ui/modules/debug/services/CallDebuggerService.ts - Add getCall(id) function
Implementation:
// In CallDebuggerService.ts
export async function getCall(id: string, signal?: AbortSignal): Promise<CallMetadata | null> {
const response = await fetch(`/api/debug/db/calls/${encodeURIComponent(id)}`, { signal })
if (response.status === 404) return null
if (!response.ok) throw new Error(`Failed to fetch call: ${response.statusText}`)
return response.json()
}
// In useCall.ts
export function useCall(callId: string | null) {
const [data, setData] = useState<CallMetadata | null>(null)
const [error, setError] = useState<Error | null>(null)
const [isLoading, setIsLoading] = useState(false)
useEffect(() => {
if (!callId) return
// Use deduplicatedFetch to fetch call metadata
// ...
}, [callId])
return { data, error, isLoading, notFound: error?.message === 'Call not found' }
}
Testing: - Hook returns loading state initially - Hook returns data on success - Hook returns notFound error on 404
Dependencies: Task 1.1, Task 2.1
Task 3.2: Create CallNotFound component¶
Goal: Create error state component with option to check dev environment.
Files to create:
- /src/ui/modules/debug/components/CallNotFound.tsx
Implementation: Per design document - shows error message with callId, button to check dev environment, and back to dashboard link.
Testing: - Component renders with callId - "Check Dev Environment" button triggers dev lookup - "Back to Dashboard" navigates to /debug
Dependencies: Task 3.1 (needs useCall or similar for dev check)
Task 3.3: Create BottomSheet component¶
Goal: Create mobile bottom sheet component for turn details.
Files to create:
- /src/ui/components/BottomSheet.tsx (shared component)
Implementation: Per design document: - Swipe down to dismiss - Drag handle at top - Max height 80vh - Backdrop overlay - Animation for open/close
Testing: - Component renders when open=true - Swipe gesture dismisses sheet - Backdrop click dismisses sheet - Content scrolls within sheet
Dependencies: None
Task 3.4: Update TurnExplorerView for mobile bottom sheet¶
Goal: Use BottomSheet for turn details on mobile instead of side panel.
Files to modify:
- /src/ui/modules/debug/components/TurnExplorerView.tsx
Implementation:
const isMobile = useIsMobile()
{selectedTurn && (
isMobile ? (
<BottomSheet open={!!selectedTurn} onClose={() => setSelectedTurn(null)}>
<TurnDetailsPanel turn={selectedTurn} />
</BottomSheet>
) : (
<div className="w-1/2 border-l">
<TurnDetailsPanel turn={selectedTurn} />
</div>
)
)}
Testing: - On mobile viewport: details appear in bottom sheet - On desktop viewport: details appear in side panel - Sheet dismisses on swipe/backdrop click
Dependencies: Task 3.3
Task 3.5: Update routes¶
Goal: Update route structure to /debug/call/:callId (simplified from /debug/call/:env/:date/:callId).
Files to modify:
- /src/ui/modules/debug/index.tsx - Update route definitions
Old routes:
{ path: "call/:env/:date/:callId", element: <CallDebugger /> }
{ path: "call", element: <CallDebugger /> }
{ path: ":callId", element: <CallLookupRedirect /> }
New routes:
Testing:
- /debug/call/call_xxx loads CallDebugger with correct callId
- /debug/call/uuid loads CallDebugger with correct callId
- Old routes redirect appropriately (or show 404)
Dependencies: Task 3.1
Task 3.6: Refactor CallDebugger component¶
Goal: Remove sidebar, simplify to route-based, use useCall hook.
Files to modify:
- /src/ui/modules/debug/pages/CallDebugger.tsx
Changes:
1. Remove all sidebar-related code:
- Remove CallIdSidebar import and usage
- Remove sidebarCollapsed state
- Remove handleToggleSidebar function
- Remove sidebar toggle button
- Remove mobile overlay mask
- Remove useResizable hook
- Remove legacy URL param handling:
- Remove
params.date,params.envusage -
Simplify to just
useParams<{ callId: string }>() -
Use new
useCallhook: - Replace existing call resolution logic
-
Extract
env,datefromuseCallresult -
Add CallNotFound error handling:
-
Show
CallNotFoundwhenuseCallreturns notFound -
Remove props (no longer embedded in CallsLandingPage):
- Remove
CallDebuggerPropstype - Component only reads from URL params
Testing:
- Direct navigation to /debug/call/:callId works
- Call metadata loads and displays correctly
- View switching (turns/raw/waterfall/data) works
- Mobile header collapse still works
- Filter controls still work
Dependencies: Task 3.1, Task 3.2, Task 3.5
Task 3.7: Update CallsLandingPage to navigate to new route¶
Goal: Update navigation from dashboard to use new route structure.
Files to modify:
- /src/ui/modules/debug/pages/CallsLandingPage.tsx
Changes:
1. Update handleSelectCall to navigate to /debug/call/:callId
2. Save dashboard state before navigation (using Task 2.2 utility)
3. Remove embedded CallDebugger rendering (no longer inline)
4. Remove isDebugging state and related logic
Testing:
- Clicking call in sidebar navigates to /debug/call/:callId
- Dashboard state is saved before navigation
- Back button returns to dashboard with state restored
Dependencies: Task 2.2, Task 3.5, Task 3.6
Task 3.8: Restore dashboard state on mount¶
Goal: Restore dashboard state from sessionStorage when returning from debugger.
Files to modify:
- /src/ui/modules/debug/pages/CallsLandingPage.tsx
Changes: 1. On mount, check for saved dashboard state 2. If found, restore date/env/orgId/locationId/agentId 3. Clear saved state after restore (one-time)
Testing: - Navigate to call, then back - state is restored - Refresh dashboard - no crash (handles missing state) - State is cleared after restoration
Dependencies: Task 2.2, Task 3.7
Phase 4: Cleanup¶
Task 4.1: Update DebugLayout¶
Goal: Remove CallDebuggerProvider since context is no longer needed.
Files to modify:
- /src/ui/modules/debug/DebugLayout.tsx
Changes:
// Before
export function DebugLayout() {
return (
<CallDebuggerProvider>
<Outlet />
</CallDebuggerProvider>
)
}
// After
export function DebugLayout() {
return <Outlet />
}
Dependencies: Task 3.6, Task 3.7 (ensure nothing uses context)
Task 4.2: Delete CallDebuggerContext¶
Goal: Remove the context file since it's no longer used.
Files to delete:
- /src/ui/modules/debug/contexts/CallDebuggerContext.tsx
Testing: - Build succeeds without errors - No runtime errors related to missing context
Dependencies: Task 4.1
Task 4.3: Delete callStore¶
Goal: Remove the data store file since hooks now manage their own state.
Files to delete:
- /src/ui/modules/debug/data/callStore.ts
Testing: - Build succeeds without errors
Dependencies: Task 4.1, Task 4.2
Task 4.4: Delete CallIdSidebar¶
Goal: Remove the old sidebar component and styles.
Files to delete:
- /src/ui/modules/debug/components/CallIdSidebar.tsx
- /src/ui/modules/debug/components/CallIdSidebar.css
Testing: - Build succeeds without errors
Dependencies: Task 3.6
Task 4.5: Delete useDBCalls hook¶
Goal: Remove the hook since it was only used by CallIdSidebar.
Files to delete:
- /src/ui/modules/debug/hooks/useDBCalls.ts
Testing: - Build succeeds without errors
Dependencies: Task 4.4
Task 4.6: Delete CallLookupRedirect¶
Goal: Remove the lookup redirect page and styles since lookup is handled by the new endpoint.
Files to delete:
- /src/ui/modules/debug/pages/CallLookupRedirect.tsx
- /src/ui/modules/debug/pages/CallLookupRedirect.css
Testing: - Build succeeds without errors
Dependencies: Task 3.5
Task 4.7: Clean up CallDebuggerService¶
Goal: Remove legacy functions no longer needed.
Files to modify:
- /src/ui/modules/debug/services/CallDebuggerService.ts
Functions to remove:
- lookupCall (replaced by getCall)
Testing: - Build succeeds without errors - Remaining functions work correctly
Dependencies: Task 1.3, Task 3.1
Task 4.8: Clean up types¶
Goal: Update types to reflect new API response shape.
Files to modify:
- /src/ui/modules/debug/types/debugger.ts
Changes:
- Update CallMetadata type to include env field
- Remove unused types if any
Dependencies: Task 3.1
Task Dependency Graph¶
Phase 1 (Backend):
1.1 ──────────────┬──> 1.2 ──> 1.3
│
Phase 2 (Utilities):│
2.1 ──────────────┼──> 3.1
2.2 ──────────────┼──> 3.7, 3.8
│
Phase 3 (Frontend): │
3.1 ──────────────┼──> 3.2, 3.5, 3.6
3.3 ──────────────┼──> 3.4
3.5 ──────────────┼──> 3.6, 3.7, 4.6
3.6 ──────────────┼──> 3.7, 4.1, 4.4
3.7 ──────────────┼──> 3.8, 4.1
│
Phase 4 (Cleanup): │
4.1 ──────────────┼──> 4.2, 4.3
4.2 ──────────────┤
4.3 ──────────────┤
4.4 ──────────────┼──> 4.5
4.5 ──────────────┤
4.6 ──────────────┤
1.3, 3.1 ─────────┼──> 4.7
3.1 ──────────────┼──> 4.8
Recommended Implementation Order¶
- Phase 2 first (utilities) - No dependencies, can be done immediately
- 2.1: deduplicatedFetch
-
2.2: dashboardState
-
Phase 1 (backend) - Independent of frontend
- 1.1: New endpoint
-
1.2: Update auth (after 1.1)
-
Phase 3 (frontend) - Core refactoring
- 3.1: useCall hook (needs 1.1, 2.1)
- 3.3: BottomSheet (no dependencies)
- 3.2: CallNotFound (needs 3.1)
- 3.4: TurnExplorerView update (needs 3.3)
- 3.5: Routes update (needs 3.1)
- 3.6: CallDebugger refactor (needs 3.1, 3.2, 3.5)
- 3.7: CallsLandingPage update (needs 2.2, 3.5, 3.6)
-
3.8: Dashboard state restore (needs 2.2, 3.7)
-
Phase 4 (cleanup) - After frontend is working
- 4.1: DebugLayout (needs 3.6, 3.7)
- 4.2: Delete context (needs 4.1)
- 4.3: Delete callStore (needs 4.1, 4.2)
- 4.4: Delete CallIdSidebar (needs 3.6)
- 4.5: Delete useDBCalls (needs 4.4)
- 4.6: Delete CallLookupRedirect (needs 3.5)
- 1.3: Remove old lookup endpoint (needs 3.1)
- 4.7: Clean up service (needs 1.3, 3.1)
- 4.8: Clean up types (needs 3.1)
Summary of File Changes¶
Create (7 files)¶
| File | Task |
|---|---|
src/ui/modules/debug/utils/deduplicatedFetch.ts |
2.1 |
src/ui/modules/debug/utils/dashboardState.ts |
2.2 |
src/ui/modules/debug/hooks/useCall.ts |
3.1 |
src/ui/modules/debug/components/CallNotFound.tsx |
3.2 |
src/ui/components/BottomSheet.tsx |
3.3 |
Modify (11 files)¶
| File | Tasks |
|---|---|
src/worker/modules/debug/router.ts |
1.1, 1.3 |
src/worker/modules/debug/queries/calls.ts |
1.1, 1.3 |
src/worker/middleware/auth.ts |
1.2 |
src/ui/modules/debug/services/CallDebuggerService.ts |
3.1, 4.7 |
src/ui/modules/debug/components/TurnExplorerView.tsx |
3.4 |
src/ui/modules/debug/index.tsx |
3.5 |
src/ui/modules/debug/pages/CallDebugger.tsx |
3.6 |
src/ui/modules/debug/pages/CallsLandingPage.tsx |
3.7, 3.8 |
src/ui/modules/debug/DebugLayout.tsx |
4.1 |
src/ui/modules/debug/types/debugger.ts |
4.8 |
Delete (7 files)¶
| File | Task |
|---|---|
src/ui/modules/debug/contexts/CallDebuggerContext.tsx |
4.2 |
src/ui/modules/debug/data/callStore.ts |
4.3 |
src/ui/modules/debug/components/CallIdSidebar.tsx |
4.4 |
src/ui/modules/debug/components/CallIdSidebar.css |
4.4 |
src/ui/modules/debug/hooks/useDBCalls.ts |
4.5 |
src/ui/modules/debug/pages/CallLookupRedirect.tsx |
4.6 |
src/ui/modules/debug/pages/CallLookupRedirect.css |
4.6 |
Risk Mitigation¶
-
Breaking changes: Backend changes (Phase 1) are done first and the old endpoint is kept until frontend is updated.
-
Rollback: Each task is a discrete commit. If issues arise, individual changes can be reverted.
-
Testing: Each task includes specific testing criteria. Run full test suite after each phase.
-
Mobile UX: BottomSheet (3.3) is built and tested independently before integrating with TurnExplorerView.
-
State preservation: dashboardState utility (2.2) is tested in isolation before integrating with navigation flow.