Team Accountability table — .sb-acct · engineering spec (PRESCRIPTIVE — Sales Analytics arc, design-ahead)
What it is: the Analytics centerpiece — one table answering "is my team doing the work, and is it turning into sales." It is an INSTANCE of the corrected CustomTable grammar (comp-table): gray-07 header, top-pinned aggregate row, circled hover-only sort on metric columns — extended, never forked. Whole row = one tap target (opens the rep drilldown — note only, no page yet).
1 · Anatomy — section card · rep cell · aggregate row · default sort
OPEN QUESTION: data-point mix under review — Taylor.
Team Accountability
Rep Tasks Today Overdue Calls Texts Emails Avg Response Appts Shown Sold Last Active
All (team)3610871696415m2414
1Sonya WestClient Advisor
6143298m644m ago
2Jake MartinSalesperson
42921522m5338m ago
3Karla SellersSalesperson
511181212m432m ago
4Devon BrooksBDC
812241156m3212m ago
5Tara McCannBDC
718361114m211h ago
6Marcus LeeFleet Manager
234631h 12m21Yesterday 6:45pm
7Priya NairClient Advisor
3712831m13h ago
8Shane DusangSalesperson
142311Tuesday 2:10pm
Daisi (AI) AI
2201841m1m ago
Totals are humans; AI reported separately.
This is also the zero-config default state whenever no bdc_agent exists on the roster. Section card (FIDELITY-3): gray-07 TITLE BAR ("Team Accountability" 14px semibold + info icon, r5 top) → WHITE body carrying the table → gray-07 FOOTER BAR (r5 bottom) with the humans/AI microtext. This IS the sales column set — behavior columns (Tasks Today → Avg Response) then sales outcomes (Appts Shown, Sold) then Last Active. Showing an appointment is a sales-role outcome whether or not a BDC set it, so a BDC band is purely additive: it appears above when the roster carries a bdc_agent and never changes how sales is measured. No vertical column lines (production tables have ONLY horizontal separators). Data rows = WHITE token bg; "All (team)" row gray-06; header row transparent-on-white. Rep cell: index box + name (semiBoldParagraph gray-00) + role subtitle (mediumIndicator gray-03) — no avatar (Taylor: the small avatars distorted into ovals and added no value; removed). Default sort = Sold descending (circled active glyph). Whole row = one tap target → the rep coaching drilldown. Exact geometry (FIDELITY-2A): 1px gray-06 outer border r5; header h40 (13px bold gray-02); rows FLUSH with 1px gray-06 separators, min content height 48; each rep row leads with a 24×24 INDEX BOX (12px medium gray-02), cell pad-left 8, 8px gaps; metric headers AND values CENTERED 13px bold with trailing 32px sort boxes (hover/active only) + invisible leading boxes; the AI row keeps the geometry with an em-dash index box.
1b · Role bands — BDC + Sales (when a BDC exists)
Team Accountability
BDC3 repsTasks TodayOverdueCallsTextsEmailsAvg ResponseAppts CreatedAppts ShownLast Active
All (BDC)21358104349m4118
1Devon BrooksBDC
812241156m18812m ago
2Tara McCannBDC
718361114m1461h ago
3Renee OkaforBDC Agent
621827818m9426m ago
Sales5 repsTasks TodayOverdueCallsTextsEmailsAvg ResponseAppts ShownSoldLast Active
All (sales)18940803017m3312
1Sonya WestClient Advisor
6143298m1144m ago
2Jake MartinSalesperson
42921522m9338m ago
3Karla SellersSalesperson
511181212m832m ago
4Marcus LeeFleet Manager
234631h 12m311d ago
5Shane DusangSalesperson
14231213d ago
Daisi (AI) AI
2201841m1m ago
Totals are humans; AI reported separately.
Zero config. Everyone carries the sales_person default via roundRobinRoles; the BDC band appears only when the roster says at least one person is marked bdc_agent — most stores today have nobody marked, and render the single table in specimen 1. One grid, two headers: both bands live in ONE section card on identical column tracks, and each band’s own header row carries the role label (role name + rep count in the row-key cell, no chevron — it is a static subgroup label, not a picker) so no extra full-width divider row is needed. Appts Shown is the SAME metric with the SAME definition in both bands — a shared column, not a role-specific one: appointments scheduled inside the range, marked complete, credited to the customer's assigned dealer. What actually differs per role is the OTHER outcome column: Appts Created for BDC (the appointments that actor created, credited to the creator, anchored on the created date) and Sold for Sales. The two BDC columns do not chain. Created is creator-attributed on a created-date anchor; Shown is assignment-attributed on a scheduled-date anchor — so Shown is not a subset of Created and the pair is never a ratio or a funnel. A BDC rep who books an appointment for a customer assigned to a salesperson sees it in that salesperson's Appts Shown, never their own: intended, because Shown measures who owned the customer when they walked in. BDC sits on top and Sales below; each band opens with its own aggregate row and there is no whole-floor total. Each band sorts by its final outcome column descending. Counts, never a percent: because Shown requires marked-complete, the 39% of appointments left unresolved by the silent hourly sweep never count — Appts Shown undercounts, so no show-rate percentage may be derived from it. Membership: a bdc_agent marking beats the sales_person default everyone carries, so dual-role people land in BDC; you appear if you were assigned a lead OR logged any activity in range — a GM who works no leads vanishes naturally, while a rep with assignments and zero activity still shows as the zero row this table exists to catch. Daisi appears once per card after the Sales band, ruled off as always, and is governed by the AI Included/Excluded toggle.
2 · Threshold cells — values get color, never rows
Overdue: red-01 when >0 · em-dash gray-04 at zero. Avg Response: default fg below 15m · orange-01 15–60m · red-01 above 60m (the grounded AFT bands — calibrated on live first-touch distributions). Last Active: relative time boldTiny; turns orange-01 when older than 24h (e.g. "Yesterday 6:45pm"). Values only get color — never whole rows. Zero-data cells render an em-dash, not zero-noise.
3 · Pinned AI row
Bottom-pinned, visually ruled off from humans: gray-07 fill + a 2px gray-06 top rule; name "Daisi (AI)" + an "AI" RoundedText capsule; values in the SAME columns but the identity text is gray-02 — muted identity, honest numbers. The AI row is OUTSIDE the "All (team)" totals; the microtext under the table states it: totals are humans; AI reported separately. AI bucket (ruling 1): automation-flagged sends UNION the DAISI dealer actor — one bucket, one "AI" label.
4 · Exclude AI — quick-filter CHIP (FIDELITY-4: the CustomSwitch form is RETIRED; control form PROVISIONAL — Taylor may want something else)
OFF (default) — the AI row is visible, pinned bottom.
Touchpoints
41
humans only
ON — the AI row is hidden AND every stat value carries a small "humans only" scope annotation (shown here on a StatsCard) so scoped numbers are never mistaken for totals. Control form is PROVISIONAL — the chip replaces the retired CustomSwitch; Taylor may rule a different form.
5 · States — loading · single-rep store · compact · exclude-AI on
Loading — skeleton rows
Single-rep store — 1 row + totals
Rep CallsTextsSold
All (team)14324
1Sonya WestClient Advisor
14324
Compact density
Rep OverdueAvg ResponseSold
All (team)1015m14
1Sonya West
8m4
2Jake Martin
222m3
Compact: 32px rows, role subtitle hidden. Thresholds unchanged.
Exclude AI on — AI row hidden
Rep TextsEmailsSold
All (team)1696414
1Sonya West
3294
2Karla Sellers
18123
No AI row; scoped stats carry "humans only" (section 4).
6 · Mobile — card-list transform (showroom mobile pattern)
Sonya West
Client Advisor
4m ago
Tasks Today6
Overdue
Calls14
Texts32
Emails9
Avg Response8m
6 Shown4 Sold
Marcus Lee
Fleet Manager
Yesterday 6:45pm
Tasks Today2
Overdue3
Calls4
Texts6
Emails3
Avg Response1h 12m
2 Shown1 Sold
Each rep becomes a card: index + name + role on top, Last Active right-aligned (orange when >24h), a 2×3 metric mini-grid below with the SAME thresholdld colors, Sold as a green count chip. The AI row becomes the last card, same ruled-off treatment. Whole card = one tap target.
7 · Data contract — column → backend metric id
// Column → metric id (POST /v2/dashboard/sales + dealerjourney) Tasks Today = TasksTodo Overdue = TasksOverdue Calls = OutboundCalls Texts = OutboundTexts Emails = OutboundEmails Avg Response = AvgResponseTime // MEDIAN — label it honestly wherever surfaced Appts Shown = AppointmentsShown // SHARED by both bands, identical definition: // appointments SCHEDULED inside the range, MARKED COMPLETE, // credited to the customer's ASSIGNED dealer (not the creator). // Date anchor: the appointment's scheduled date. Appts Created = AppointmentsCreated // BDC band only: // appointments THAT ACTOR CREATED — attribution is the CREATOR. // Date anchor: appointments created inside the range. // The two are INDEPENDENTLY ATTRIBUTED over DIFFERENT date anchors — Shown is NOT a subset of // Created. Never present them as a ratio, a funnel, or a show rate. Sold = DealsClosedWon Last Active = NEW metric — MAX(datetime) per dealer from dealerjourney // ROLE MARKER — the band split dealers."roundRobinRoles" text[] NOT NULL DEFAULT ARRAY['sales_person'] // bdc_agent BEATS the sales_person default everyone carries — dual-role people land in BDC. // A role is a marker on the USER, not their job title: the free-text job title under each name is // display only and does NOT drive the split (specimen 1 shows reps whose title reads "BDC" in a // store with no bdc_agent marker — that is the point). // BAND MEMBERSHIP — a rep appears if they were ASSIGNED A LEAD or LOGGED ANY ACTIVITY in the range. // A manager who works no leads drops out naturally; a rep with assignments and zero activity still // shows as the zero row this table exists to catch. There is no clean manager field to exclude on. // APPOINTMENT ATTRIBUTION — no creator column exists. Attribution needs the customernotifications // proxy at ~90% coverage; ~30% of "dealer" creates are actually DAISI; ~31% of appointments are // customer self-scheduled with no rep at all; ~39% sit unresolved via a silent hourly sweep. // Therefore ship COUNTS, never a bare show-rate percentage. And because Appts Shown requires // MARKED COMPLETE, the appointments left unresolved by that sweep never count — Appts Shown // UNDERCOUNTS by construction, so no show-rate percentage may be derived from it. // AI bucket (ruling 1): automation-flagged sends UNION the DAISI dealer actor // — one bucket, one "AI" label; never blended into human rows or the All (team) totals. // Row example { "dealerId": "dlr_8842", "name": "Sonya West", "role": "Client Advisor", "TasksTodo": 6, "TasksOverdue": 0, "OutboundCalls": 14, "OutboundTexts": 32, "OutboundEmails": 9, "AvgResponseTime": 480, // seconds, median "AppointmentsShown": 6, "AppointmentsCreated": 11, "DealsClosedWon": 4, "roundRobinRoles": ["sales_person"], "lastActive": "2026-07-10T13:52:00Z", // MAX(datetime) — drives the Last Active cell "isAi": false }
8 · Rules — engineering checklist
No leaderboards or rank celebration — threshold callouts only.
Behaviors before outcomes — behavior columns run first, then the role's outcomes, then Last Active.
Role bands are the one group-band exception — the BDC band appears only when a bdc_agent exists on the roster; a store without one renders the single sales table.
Both bands use identical column tracks — Appts Shown is shared (same definition, assigned-dealer attributed); only the other outcome differs (BDC: Appts Created, creator-attributed · Sales: Sold). The two BDC outcomes never form a ratio.
Each band carries its own aggregate row and sorts by its own final outcome column.
No whole-floor total row — summing different column sets is meaningless.
The Daisi row stays ONE row for the whole card, after the last band.
Every number drills down (the R0 drilldown grammar — read-only rows).
Medians labeled as medians — AvgResponseTime is a median; say so.
AI labeled, never blended — outside totals, ruled off, "AI" capsule.
Zero-data columns render an em-dash, not zero-noise.
Values get threshold color — never whole rows.
Rep cell = index + name + role — no avatar (Taylor: the small avatars distorted, removed).
The table is a CustomTable instance — extend .sb-table, never fork it.