Client Care Dashboard: Close attribution toggle (Scheduled By vs Quote Creator)
Objective
Add a toggle on the Employee Performance scheduler stats table (Client Care Dashboard) so users can switch close-rate attribution between:
- Scheduled By (default / current behavior) — all inspection-backed close components are credited to
inspection.scheduledBy. - Quote Creator — for inspections linked to a quote (
Quote._finalInspectionId), credit close components toquote.createdBy; if no linked quote, fall back toscheduledBy.
This lets CC leadership compare “who booked the job” vs “who originated the quote/lead” without changing revenue, upsell, cancel, or other non-close columns unless product decides otherwise.
Current behavior (as of investigation)
Backend: GET /cc-dashboard/scheduler-metrics in apps/backend/src/routes/ccDashboard.ts
- Main inspection aggregation groups by
(scheduledBy, _companyId)for revenue, created, confirmed, upsell, cancel, add-on, etc. - Open quotes in the close-rate denominator already use
quote.createdBy→ user mapping (Steps 5–5b). - Result: hybrid attribution today — confirmed inspections →
scheduledBy; orphan open quotes →createdBy.
Frontend: EmployeeTable.tsx renders the table inside DashboardBlock; filters live in the controller slot (Online Scheduler checkbox + scheduler multi-select). Data fetched from CCDashboardClient.tsx via cc-dashboard/scheduler-metrics.
Data model:
cc_inspection_baseview exposesscheduledBybut not quote linkage (createCcDashboardViews.ts).- Quote link:
Quote._finalInspectionId↔ inspection_id; quote originator:Quote.createdBy.
Proposed implementation
1. API — new query param
Add closeAttribution=scheduledBy|quoteCreator (default scheduledBy) to GET /cc-dashboard/scheduler-metrics.
When closeAttribution=quoteCreator, re-attribute close-related fields only:
closeRateordersCreated(denominator basis)ordersConfirmed(numerator)- Team average + per-company breakdown for those fields
Keep on scheduledBy (unless product expands scope): revenue, averageFee, upsellRate, oneCallCloseRate, selectedAddOnRate, cancel columns, # Core Confirmed.
2. Backend aggregation strategy
Recommended approach (no view migration required):
- Keep existing
scheduledByaggregation for non-close metrics. - Add a parallel close-only aggregation when
closeAttribution=quoteCreator:
$matchsame date/status filters as main query.$lookupquotes on{ localField: '_id', foreignField: '_finalInspectionId', as: 'quote' }.$addFields:closeAttributionEmp: { $ifNull: [{ $arrayElemAt: ['$quote.createdBy', 0] }, '$scheduledBy'] }.$groupby(closeAttributionEmp, _companyId)summing onlycreated(non-cancelled),confirmed, and cancelled counts needed for close math.
- Merge close buckets into existing
mergedMapby resolveduserId(sameemployeeToUser/ membership mapping as today). - Open-quote denominator logic stays as-is (
createdBy) — already matches Quote Creator mode.
Online Scheduler row: clarify product rule when scheduledBy is null but a linked quote has createdBy — likely move those counts off the Online row onto the quote creator’s row.
3. Frontend
- Add toggle in
EmployeeTablecontroller (alongside Online Scheduler / All Schedulers), e.g. segmented control: - Scheduled By | Quote Creator
- Thread state through
CCDashboardClient.tsx→fetchSchedulerMetricsparams. - Refetch scheduler metrics on toggle change (same pattern as
dateType). - Update empty-state copy if needed (currently mentions
scheduledByonly).
4. Saved views (optional but recommended)
Follow dateType / globalCategoryMatch pattern:
- Add
closeAttributionto@attik/types/ccDashboardView.types, DB model, saved-view API, and save payload inCCDashboardClient.
Edge cases / open questions
- [ ] Should One Call Close follow the same attribution toggle? (User request mentions close rate specifically.)
- [ ] Inspection booked online (
scheduledBy: null) with internal quote creator — counts leave Online Scheduler row? - [ ] Quote exists but
createdByis missing — confirm fallback toscheduledBy. - [ ] Reconcile with ATT-1270 semantics so
# Createdstill matches close-rate denominator in both modes.
Files to touch
Backend
apps/backend/src/routes/ccDashboard.ts— param parsing, close-only re-aggregation, merge logic- (Optional) integration test covering both modes with fixture inspections + quotes
Frontend
apps/frontend/src/app/admin/client-care-dashboard/_components/EmployeeTable.tsx— toggle UIapps/frontend/src/app/admin/client-care-dashboard/CCDashboardClient.tsx— state + fetch paramapps/frontend/src/app/admin/client-care-dashboard/_types.ts— type for param- (Optional) saved view types/model if persisting preference
Acceptance criteria
- [ ] Toggle visible on Employee Performance block; default = Scheduled By (no behavior change for existing users).
- [ ] Quote Creator mode: confirmed inspection linked to quote counts toward quote
createdBy(mapped to user via membership); unlinked inspections still usescheduledBy. - [ ] Open quotes continue to count toward
createdByin both modes. - [ ] Close Rate =
# Confirmed / # Createdremains internally consistent per row in both modes. - [ ] Revenue / upsell / cancel columns unchanged when toggling (unless product expands scope).
- [ ] Team Average row recalculates correctly for close fields in both modes.
Test plan
- Pick a CSR who creates quotes but has another person schedule confirmations — verify close rate shifts between modes.
- One-call / direct-book inspection (no quote) — identical in both modes.
- Online Scheduler row with mix of self-booked vs quote-originated jobs.
- Multi-company view: same user merged across companies; per-company expand still correct.
- Saved view reload preserves toggle (if implemented).
Log in to comment and vote
No comments yet
Be the first to share your thoughts.