Dashboard 重新设计 — 时间与内容矩阵视图
Path: apps/web/src/pages/dashboard-v3/ · Created: 2026-05-24
这份文档是什么
这份文档是 Dashboard 单店深挖页 reshape 的 source of truth, 取代现有 mvp-v3 mockup (docs.retaintive.ai/ui-design/ui-prototype/mvp-v3) 里 Dashboard section 的 4 time tab 设计。
实施时按这份文档走, mvp-v3 HTML 不需要改 — 它是过渡 mockup, 这份文档是 final spec。生产实现位于 dashboard-v3/ (新版终态), dashboard/ 是 legacy, v3 完成后被替代。
vs mvp-v3 的变化:
Product Positioning
Dashboard 是 single-store deep dive 页, 是 My Stores fleet view 点店后的下一层。Manager-primary (每天工作面板), Owner 通过 Email Digest cliffhanger 偶尔点入复用同一页 — 没有单独的 Owner Dashboard。
不重发明信息, 不删 mvp-v3 已 design 好的块, 只 unify time × content 让用户切 time range 时认知不断裂。
Page Shell
页面 7 层结构 (从上到下):
- Deep-link banner(可选) — 仅当从 My Stores cell click 跳来时显示;详见末尾 "Deep-link from My Stores" 章节
- Header — "Dashboard" 标题 + Store switcher dropdown + Snapshot / Export 按钮
- Filter row — Time range picker + Voicemail toggle 同行
- Time range picker:复用 My Stores 同款 component。5 个预设 (Yesterday / Last Week / This Month / Last Month / Custom max 93 天)
- Voicemail toggle:
[✓] Include voicemail默认 ON,跟 My Stores 同款。Owner 可手动切换 / Deep-link 设置自动 apply
- AI Quick Scan — 顶部 1 段 ≤ 80 字 AI summary (按 time scale 重写)
- KPI 卡条(② block) — 9 个 KPI 卡跨 time / tab 一致
- Tab 切换 — 3 个 tab:
- Call deep-dive(④ Cancellation Reasons + ⑤ Complaint Reasons)
- Task deep-dive(⑥ Impacted Revenue + ⑦ Task by Reason — Yesterday only)
- Staff Performance(③ Staff 一行表 — 独立 tab,加 task 列以反映 staff 跨 call/task 双维度工作)
- 当前 tab 的内容(主体)
切 time range 不重置 store / sort / VM toggle / tab。默认时间 = Yesterday (Manager 早 huddle 心智)。Time picker + VM toggle 跨 3 tab 共享。
为什么 Staff 独立 tab(不在 Call deep-dive 下)? Staff 是"人维度"切片,跟 Cancellation Reasons / Complaint Reasons("原因维度"切片)是不同的 lens。Staff 同时做 call + task,放 Call tab 下语义错配。独立 tab 后可加 task 相关列(每个 staff close 了多少 task、close rate、task productivity),反映 staff 真实工作量。
① AI Quick Scan
1 段 ≤ 80 字 plain-text summary, 按当前 time range 重写。Owner 30 秒读完 + Manager 决定要不要深挖。
Sample (illustrative):
视觉: 顶部 1 段 plain text, 无 emoji 无 sentiment 标签, 文字本身已传达情绪 (urgent / stable / strong)。
Calculation (① AI Quick Scan)
非 SQL 算的。callytics-infrastructure cross-call AI Lambda 每天凌晨扫这家店当前 time range 内所有 call analysis, 综合写一句 ≤ 80 字 plain-text summary 存到 store-level summary table。Dashboard 直接读 string 字段 dashboardSummary[period], inline 显示。
V1 不带视觉状态标签。V2 视产品需要再加 sentiment classification。准确性问题 → issue 报到 callytics-infrastructure cross-call AI pipeline。
② KPI 卡条 (8 卡)
8 个数字卡 + trend % vs 上期。所有 time range 都展示同 8 卡 (单日 Impacted Revenue 数字小也显示, 数据真相)。
Cancel Save + Complaint Resolve 两类对称 KPI 卡 — 既然下方 ④/⑤ 有 Cancel Reasons + Complaint Reasons 两 detail block,顶部 KPI 也对应展示两组 close/total 数字,visual + 语义上下对称。Cancel Save % 严格只算 cancellation_risk 类 call(主动 cancel);Complaint Resolve % 算 retention 类 call(投诉/billing/服务争议)— 两类 schema 上 type_category 不同,KPI 上各自独立分母。
Sample (Last Week, illustrative):
视觉: trend ↑N% 绿色 (#16A34A) = 对店有利方向, ↓N% 红色 (#dc2626) = 不利方向, — 灰色 (#CBCED4) = 无对比数据。Membership Cancellation Calls 列 ↓ 是好事 (cancel 减少) → 绿色, 颜色按"对店家有利"判定方向而非数字大小。沿用 mvp-v3 + My Stores 同款 component。
Calculation Recipes (② KPI 卡条)
源表 calls + tasks (Neon) · Schema @retaintive/common/db/schema/{calls,tasks}.ts · Enum 常量 apps/api/src/constants/call-analysis.ts
所有公式默认前缀 WHERE store_id = ? AND start_time IN period, 下表只列额外条件。Trend % = (current - previous) / previous × 100, previous 自动按 time range 算同长度上期。
Sanity checks:
Inbound + Outbound = Total CallsCancel Save saved ≤ Cancellation Calls(saved 数 ≤ total)Complaint Resolve resolved ≤ Complaint Calls- Cancellation Calls 与 Complaint Calls 来自不同 subcategory(
membership_cancelvsmember_support/billing_issue/etc.),互不重叠 - 单日 Impacted Revenue 可能 = $0, 不是 bug
Backend 改动 — voicemail toggle 替代 filter 删除 (与 My Stores spec 一致):
apps/api/src/routes/v3/dashboard-multi-store.ts line 181 + 206 现有 call_state IS DISTINCT FROM 'voicemail' filter 不删,改成参数化:
- API 接
?includeVoicemail=true|false(默认true) true→ 移除 filter (含 VM)false→ 保留 filter (排除 VM)
Dashboard UI 顶部 voicemail toggle 跟 My Stores 同款,默认 ON。Owner 切换时重查。Deep-link 从 My Stores 跳来时,VM 设置经 query param 带过来(详见末尾 "Deep-link from My Stores")。
③ Staff Performance 表 (独立 tab)
按 SA 一行表格,反映 staff 跨 call + task 双维度工作。独立 tab(不在 Call deep-dive 下) — staff 是"人维度"切片,跟 Cancellation/Complaint Reasons("原因维度"切片)分开。
Sample (Last Week, illustrative):
视觉: Productivity vs avg +N 绿色, −N 红色, ±0 灰色。Coaching Flags % > 25% 黄色背景警示 (阈值由 product confirm)。
Calculation Recipes (③ Staff Performance)
源表 calls (聚合 by staff_name) + tasks (assigned_to field) · 跟 ② 同 base WHERE 加 AND staff_name = ?
Team Avg Productivity = SUM(numerator) / SUM(Total Calls) × 100 (不是 staff 平均的平均)
Sanity checks:
In + Out = Calls(per staff)Productivity % ≤ 100%vs avg偏差 =staff_productivity - team_avg
④ Cancellation Call Reasons
Cancel call 按 reason 拆分 + 各 reason 的 Save Rate。顶部 1 行 AI summary 指明 "哪个 reason 在 bleeding"。
Sample (Last Week, illustrative):
顶部 banner: "Where we're bleeding: Schedule conflict is the #1 cancel reason (5 calls) and we save only 20% — fix the scheduling-objection script."
视觉: Save Rate < 30% 红色 (#DC2626), 30-60% 黄色 (#CA8A04), > 60% 绿色 (#16A34A)。"Moved away" 标 — uncontrollable 灰色 (不计入 save rate 评估)。
Calculation Recipes (④ Cancellation Call Reasons)
源表 calls WHERE primary_subcategory = 'membership_cancel' · Reason 来自 AI prompt 的 cancellation_reason 字段 (callytics-infrastructure standard-v1.0.0.txt)
Sanity check: 各 reason Cancel Calls SUM = ② KPI 卡 Membership Cancellation Calls
⑤ Complaint Call Reasons
跟 ④ 同结构, complaint 按 reason 拆 + Resolve Rate。
Sample (Last Week, illustrative):
顶部 banner: "Where trust is slipping: Billing disputes are the #1 complaint (2 calls) and we resolve only 50% — tighten the billing-explanation script."
视觉: 同 ④ 颜色规则 (Resolve Rate < 50% 红, 50-80% 黄, > 80% 绿)。
Calculation Recipes (⑤ Complaint Call Reasons)
源表 calls WHERE primary_subcategory IN ('service_complaint', 'billing_complaint') · complaint_reason 字段同 AI prompt 分类。
公式结构同 ④, 替换:
cancellation_reason→complaint_reasonprimary_outcome_result = 'retained'→primary_outcome_result = 'resolved'Saved→ResolvedSave Rate→Resolve Rate
Sanity check: 各 reason Complaint Calls SUM = ② KPI 卡 Complaint Calls
⑥ Impacted Revenue Breakdown
9 类 MRR + 1 类 One-time, 每类 revenue + task count + trend %。Owner 验证 Retaintive ROI 核心视图。
Sample (Last Week, illustrative):
Recurring Revenue (MRR)
One-Time Revenue
| Total | $3,362 ↑11% | 41 | — |
Calculation Recipes (⑥ Impacted Revenue Breakdown)
源表 tasks WHERE status = 'closed' AND closed_at IN period · 按 close_result 分类 + 应用 V1 系数表
V1 现实: 只有 Conversion / Upgrade / Cancel Save / Win-back 有现成 close_result enum 算得出。Complaint Retention / Payment Recovery / Freeze Recovery / Promotion / Event 是 mvp-v3 mockup 已 design 但 schema 未支持的, 见 Pending Team Discussion · Decision 2。
Sanity check: SUM(all categories) = Total = ② KPI 卡 Impacted Revenue
⑦ Task by Reason — Created / Closed / Overdue
3 列并排表格, 每列按 11 reason 拆 task 数。Manager huddle 看"今天打谁电话"+ Owner 看"团队 follow-through 怎样"。所有 time range 都展示。
Sample (Yesterday, illustrative):
视觉: 数字 = 0 的 reason 标灰色 (#9CA3AF)。bar 长度按各列 max 缩放。Created 蓝 / Closed 绿 / Overdue 红 — 颜色 = 状态情绪。
Calculation Recipes (⑦ Task by Reason)
源表 tasks · 按 type_category 分组 (11 reason mapping 见下)
Reason mapping (mvp-v3 mockup 11 reason vs @retaintive/common schema 9 enum):
Sanity checks:
Tasks Created SUM = 顶部 Created total(例 = 6)Tasks Closed SUM = 顶部 Closed total(例 = 4)- Overdue 是 snapshot, 不受 period 影响
Interaction Rules
Deep-link from My Stores
Dashboard reshape 作为 My Stores cell click 的跳转目标,需要接受 my-stores 传过来的 4 个 query param。
支持的 query param:
Frontend 行为:
- 接到 deep-link 时,4 个 param 自动 apply 到对应 UI 控件
metric决定 default tab + 是否高亮:metric=calls / intro / membership / productivity→ default Call tab,顶部 KPI 卡条对应那张高亮(紫色边框 + ⤴ pill)metric=cancel→ default Call tab,scroll to ④ Cancellation Reasons block + KPI Cancel Save % 卡高亮metric=complaint→ default Call tab,scroll to ⑤ Complaint Reasons block + KPI Complaint Resolve % 卡高亮metric=tasks / revenue→ default Task tab,对应 block + KPI 卡高亮
- 顶部紫色 banner 显示 "⤴ From › My Stores ›
<Store>›<Metric>" + 一键‹ Back to My Stores按钮 ✕ Clear focus按钮 — 清掉 banner + 高亮,但保留 time / VM / tab 选择
V1 visual scope:V1 只对 3 个 metric 高亮 detail block(cancel → ④;complaint → ⑤;revenue → ⑥)。其他 5 个 metric 跳进来只显示顶部 banner + 对应 KPI 卡高亮,不 scroll(因为 dashboard reshape V1 没有对应的 detail block)。V2 补全 detail block 后再扩展。
Backend 行为:Dashboard 已经支持 store + period + voicemail param,无需新加 backend 字段。metric 是纯 frontend 路由信号,不影响 SQL。
Backend Implications
复用 apps/api/src/routes/v3/dashboard-multi-store.ts 改 single-store route (或新建 dashboard-single-store.ts), 扩展:
- Period enum 加
yesterday / last-week / this-month / last-month+startDate/endDateISO query params (custom 日历) - Response shape:
aiSummary: string(① 来自 cross-call AI Lambda)kpis: { totalCalls, inbound, outbound, introBooking, membershipsSold, cancellation, cancelSaved, complaintCalls, complaintRetention, impactedRevenue, ...trends }(②)staffPerformance: Array<{ staffName, calls, in, out, introBooking, sold, cancelSaved, tasks, coachingFlags, productivity, vsAvg }>(③) +teamAvgProductivitycancellationReasons: Array<{ reason, count, saved, saveRate }>(④)complaintReasons: Array<{ reason, count, resolved, resolveRate }>(⑤)impactedRevenueBreakdown: { recurring: [...], oneTime: [...], total }(⑥)taskByReason: Array<{ reason, created, closed, overdue }>(⑦)
- 删 voicemail filter (见 ② Backend 改动)
- Previous-period 同长度自动算 (用于 trend %)
Store 列表沿用现有 useStores composable + getAuthorizedStore 权限检查。多租户隔离: store_id。
Files to Change
Pending Team Discussion
V1 按下面"当前选择"实施, 不 block 开发。Team review 后如改方向, 同步更新对应 section。
Decision 1 — KPI 卡 ② 用哪 8 个
背景 — mvp-v3 mockup 选 8 卡, role-deep-dive Owner 5 问偏向不同集合, Manager M1 还要 Complaint Retention。这是产品 lens 决策, 不是技术决策。
决策人: (待填) · 决议日期: (待填)
Decision 2 — Impacted Revenue ⑥ 9 类系数缺 5 个
mvp-v3 mockup 展示 9 类 MRR (Conversion / Upgrade / Complaint Retention / Payment Recovery / Cancel Save / Freeze Recovery / Win-back / Promotion / Event), @retaintive/common v0.54 close_result 18 值只能算出 4 类 (Conversion / Upgrade / Cancel Save / Win-back)。
决策人: (待填) · 决议日期: (待填)
Decision 3 — Task by Reason ⑦ 11 vs 9 schema mismatch
mvp-v3 mockup 11 reason (含 Payment Recovery / Freeze Recovery / Event Promotion / Special Promotion), schema 只 9 enum。
决策人: (待填) · 决议日期: (待填)
Schema Source-of-Truth Chain
L2 详情:
apps/api/src/constants/call-analysis.ts— call enums (CallCategory/CallSubcategory/OutcomeResult/CallState/CallDirection)@retaintive/common/db/schema/tasks.ts—TASK_TYPE_CATEGORY(9 值) +TASK_CLOSE_RESULT(18 值, v0.54 起加'booked'+'cancelled')@retaintive/common/db/schema/task-ui.ts—CLOSE_RESULT_OPTIONS(Impacted Revenue 系数表 SoT)