> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# My Stores 仪表板 — 两个标签重设计

**Path**: `apps/web/src/pages/overview/` · **Created**: 2026-05-24

## 这份文档是什么

这份文档是 **My Stores 页面 V2 设计的 source of truth**, 取代现有 mvp-v3 mockup ([docs.retaintive.ai/ui-design/ui-prototype/mvp-v3](https://docs.retaintive.ai/ui-design/ui-prototype/mvp-v3)) 里的 My Stores section。

实施时按这份文档走,mvp-v3 HTML 不需要改 — 它是过渡 mockup, 这份文档是 final spec。

**vs mvp-v3 的变化**:

| 维度                       | mvp-v3 (旧)                                                                      | 本文档 (新)                                                          |
| ------------------------ | ------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| 时间选择                     | 4 个 time tab (Yesterday / Last Week / This Month / Last Month),每个 tab 不同 column | **统一 time range picker** (8 预设 + Custom max 93 天),复用现有 component |
| 视图切换                     | 时间 tab 内单 table mix call + task 数据                                              | **2 个 functional tab** — Call Activity / Outcomes,语义清晰分离         |
| Column 结构                | Daily / Weekly 两套 column 不同                                                     | 同一 tab 跨所有 time range 保持 column 一致                               |
| 趋势展示 (`↑N%` 绿 / `↓N%` 红) | 保留                                                                              | 保留,沿用现有 component                                                |

## Product Positioning

Studio 是 **call-data observation tool**,不是 CRM。看到什么展示什么 — 不重建客户真相,不替 owner 决定哪些信号该藏。voicemail / no\_answer 算 call (客人确实拨过来了);funnel 各阶段分开计数,同客人同事件可能在 Tab 1 和 Tab 2 都出现 — owner 自己 connect。

***

## Page Shell

页面 5 层结构 (从上到下):

1. **Header** — "My Stores" 标题 + Snapshot / Export 按钮
2. **Store grid** — 8 家店卡片 (复用 mvp-v3 Store row 组件,显示店名 + Open/Closed 状态)
3. **Filter row** — Time range picker + Voicemail toggle 同行
   - Time range picker:复用现有 component,8 个预设 (Today / 7d / 14d / 28d / This Week / Last Week / This Month / Last Month) + Custom (max 93 天)
   - **Voicemail toggle**:`[✓] Include voicemail` 默认 ON。Owner 可手动切换。切换会重新查询并把 Call Volume 重算
4. **Tab 切换** — Call Activity / Outcomes
5. **当前 tab 的 multi-store table** (主体)

切 tab 不重置 time range / VM toggle。默认时间 = This Week。Time picker + VM toggle 跨 tab 共享,这是相比 mvp-v3 (per-time-tab table) 最大的标准化改进。

***

## Tab 1 — Call Activity

> Call activity — 数字格式 `closed / total`。一个客人为同一件事打多通电话被算多次。

**Sample (illustrative numbers, not live data)**:

| # | Store     | Call Volume                                  | Intro Booking (c/t) | Membership (c/t) | Cancel Save % (s/t) | Complaint Resolve % (r/t) | Team Productivity |
| - | --------- | -------------------------------------------- | ------------------- | ---------------- | ------------------- | ------------------------- | ----------------- |
| 1 | Harlem    | 87 ↑5% <br/><small>35 out · 52 in</small>    | 14 / 87 ↑12%        | 5 / 45 ↑8%       | 4 / 6 (67%) ↓20%    | 3 / 5 (60%) ↑10%          | 16% ↑3%           |
| 2 | Totowa    | 71 ↑12% <br/><small>36 out · 35 in</small>   | 17 / 71 ↑18%        | 6 / 38 ↑10%      | 2 / 2 (100%) —      | 2 / 3 (67%) —             | 23% ↑5%           |
| 3 | Westside  | 42 ↓15% <br/><small>7 out · 35 in</small>    | 6 / 42 ↓22%         | 1 / 22 —         | 1 / 12 (8%) ↑50%    | 1 / 3 (33%) ↓40%          | 9% ↓12%           |
| — | **TOTAL** | 425 ↑4% <br/><small>172 out · 253 in</small> | 89 / 425 ↑6%        | 31 / 180 ↑5%     | 12 / 28 (43%) ↑12%  | 9 / 18 (50%) ↑5%          | 19% (avg)         |

**视觉**: trend `↑N%` 绿色 (`#16A34A`) 表示同比上升, `↓N%` 红色 (`#dc2626`) 表示下降, `—` 灰色 (`#CBCED4`) 表示无对比数据。Cancel Save / Complaint Resolve 列 ↑ = 救回 / 解决率上升 = 好事 = 绿色。沿用现有 component。

**Design philosophy** — "看苦劳 → 看功劳":Call Volume 是 activity metric(折叠到 subtext),3 个 outcome 列(Intro / Membership / Cancel Save)+ Complaint Resolve(投诉处理)是 outcome metrics(占主体),Team Productivity 是 rate metric。Voicemail toggle 控制 Call Volume 是否含 voicemail。**Cancel Save % 严格只算 `cancellation_risk` 类 call**(客人主动要走的);**Complaint Resolve % 单独一列算 `retention` 类 call**(投诉/billing/服务争议)— 两类语义不同,各自独立分母不混。

### Calculation Recipes (Tab 1)

源表 `calls` (Neon) · Schema `@retaintive/common/db/schema/calls.ts` · Enum 常量 `apps/api/src/constants/call-analysis.ts` (`CallCategory` / `CallSubcategory` / `OutcomeResult` / `CallState` / `CallDirection`)

所有公式默认前缀 `WHERE store_id = ? AND start_time IN period`,下表只列额外条件。

| 列                              | 公式 (在 base WHERE 上追加)                                                                                                                              | 含义 / 备注                                                                                                   |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Call Volume** (主数字)          | `COUNT(*)` — 无追加 filter                                                                                                                            | 所有 call\_state 都算,含 voicemail / no\_answer                                                                |
| · Outbound (subtext)           | `AND direction = 'Outbound'`                                                                                                                       | RingCentral enum 首字母大写,SQL 里写 `'Outbound'` 不是 `'outbound'`                                                |
| · Inbound (subtext)            | `AND direction = 'Inbound'`                                                                                                                        | Outbound + Inbound = Call Volume (direction 只有 2 值)                                                       |
| **Intro Booking** total        | `AND primary_subcategory = 'intro_booking'`                                                                                                        | 新客人 first-time,free / $12 区间;$25+ 促销 / 已 booked online 的 follow-up 不算                                     |
| **Intro Booking** closed       | total + `AND primary_outcome_result = 'success'`                                                                                                   | 客人确认了 class 时间                                                                                            |
| **Membership** total           | `AND primary_subcategory IN ('membership_purchase_related', 'reactivation_purchase')`                                                              | 新签 + 询价 + winback。**不含** `member_upgrade` / `member_package` (待定决策见 Pending Team Discussion · Decision 1) |
| **Membership** closed          | total + `AND primary_outcome_result = 'success'`                                                                                                   | 签了新会员 / winback 成功                                                                                        |
| **Cancel Save** total          | `AND primary_subcategory = 'membership_cancel'`                                                                                                    | 客人**主动要 cancel/freeze/downgrade**(完全终止 membership)。`membership_freeze` 不算                                 |
| **Cancel Save** saved          | total + `AND primary_outcome_result = 'retained'`                                                                                                  | `retained` 是 `membership_cancel` 专用 outcome                                                               |
| **Cancel Save** % saved        | `saved / total × 100`                                                                                                                              | 前端 compute;`total = 0` 显示 `—`。**严格不混 complaint** — 投诉处理在下一列单算                                             |
| **Complaint Resolve** total    | `AND primary_subcategory IN ('member_support', 'billing_issue', 'cancellation_fee_dispute', 'complaint_feedback')`                                 | 客人**没要 cancel**,但有投诉 / billing 争议 / 服务质量问题。语义独立于 Cancel Save                                              |
| **Complaint Resolve** resolved | total + `AND primary_outcome_result = 'resolved'`                                                                                                  | 投诉处理掉了,客人继续留                                                                                              |
| **Complaint Resolve** %        | `resolved / total × 100`                                                                                                                           | 前端 compute;`total = 0` 显示 `—`。Owner 通过 Cancel Save vs Complaint Resolve 两个独立列判断 "店里是拦客人强还是处理投诉强"          |
| **Team Productivity**          | `(Intro_closed + Membership_closed + Cancel_saved + Complaint_resolved) / (Intro_total + Membership_total + Cancel_total + Complaint_total) × 100` | 分母只算 revenue-impacting 四大类(不算 Call Volume)                                                                |

**Sanity checks** (实施时验证):

- `Outbound + Inbound = Call Volume`
- `closed ≤ total` (每列)
- 任何 `closed > total` = SQL bug
- Cancel Save total 与 Complaint Resolve total **互不重叠**(不同 subcategory,语义独立)

**Backend 改动 — voicemail toggle 替代 filter 删除**:
`apps/api/src/routes/v3/dashboard-multi-store.ts` line 181 + 206 现有 `call_state IS DISTINCT FROM 'voicemail'` filter — **不删,改为受 `includeVoicemail` query param 控制**:

- API 接 `?includeVoicemail=true|false` (默认 `true`)
- `includeVoicemail=true` → 移除 filter (含 VM)
- `includeVoicemail=false` → 保留 filter (排除 VM)

UI 顶部 toggle 默认 ON,Owner 切换时重查。这避免删 filter 后 Owner 第一次打开 dashboard 数字一夜 1.77x 暴涨的 baseline shift(verify 数据:Main 7d 含 VM 560 vs 不含 317,VM 占 43%)。

***

## Tab 2 — Outcomes

> Task outcomes — 数字格式 `closed / total`。Task = 一客一事件 (去重,一人多通电话只算 1 个 task)。**Base = `closed_at IN period`**(本期 close 了多少 task)— 对齐 Tab 1 `start_time IN period` 口径,与 HubSpot "Tasks Completed" pattern 一致。Carryover task(上期建本期 close)归入"本期 close 数",反映本期工作量,但 Volume 不再包含 pending backlog(backlog 警示放别处)。

**Sample (illustrative numbers, not live data)**:

| # | Store     | Task Volume                                  | Intro Booking (c/t) | Membership (c/t) | Cancel Save % (s/t) | Complaint Resolve % (r/t) | Task Productivity | Impacted Revenue | AI Summary                         |
| - | --------- | -------------------------------------------- | ------------------- | ---------------- | ------------------- | ------------------------- | ----------------- | ---------------- | ---------------------------------- |
| 1 | Harlem    | 18 ↑10% <br/><small>closed this week</small> | 10 / 12 ↑8%         | 4 / 6 —          | 3 / 4 (75%) ↑5%     | 1 / 2 (50%) —             | 77% ↑3%           | $1,950 ↑5%       | Stable week, cancel saves on track |
| 2 | Totowa    | 15 ↑15% <br/><small>closed this week</small> | 12 / 14 ↑13%        | 5 / 7 ↑20%       | 3 / 3 (100%) —      | 2 / 3 (67%) ↑10%          | 83% ↑8%           | $2,400 ↑18%      | All four metrics trending up       |
| 3 | Westside  | 8 ↓20% <br/><small>closed this week</small>  | 2 / 4 ↓25%          | 1 / 3 ↓15%       | 1 / 6 (17%) ↓30%    | 0 / 2 (0%) ↓100%          | 31% ↓40%          | $450 ↓60%        | Urgent: cancel saves below avg     |
| — | **TOTAL** | 95 ↑5% <br/><small>closed this week</small>  | 60 / 76 ↑8%         | 23 / 36 ↑5%      | 12 / 23 (52%) ↑6%   | 9 / 18 (50%) ↑5%          | 67% (avg)         | $12,600 ↑8%      | —                                  |

镜像 Tab 1 结构:同 6 列 outcome / productivity,unit 从 call 换 task。**Cancel Save % 和 Complaint Resolve % 是两个独立分母列,不混**(对应 schema 的 `cancellation_risk` 和 `retention` 两个独立 `type_category`)。AI Summary 是 LLM 生成的一句话,不是固定 emoji 状态。

### Calculation Recipes (Tab 2)

源表 `tasks` (Neon) · Schema `@retaintive/common/db/schema/tasks.ts` (`TASK_TYPE_CATEGORY` 9 值,`TASK_CLOSE_RESULT` 18 值 — v0.54 起加 `'booked'` + `'cancelled'`) · UI label + category (positive 7 / neutral 6 / negative 3) `@retaintive/common/db/schema/task-ui.ts` `CLOSE_RESULT_OPTIONS` · Task 创建规则 `callytics-infrastructure/lambda/contacts-analyzer/src/core/prompt-builder.ts`

所有公式默认前缀 `WHERE store_id = ? AND closed_at IN period AND status = 'closed'`,下表只列额外条件。Task 去重靠 schema 约束 (同 contact\_phone + 同 type\_category 同时只能 1 个 pending,见 `prompt-builder.ts` CONSTRAINTS)。

> **Base 决策(reviewer claim #5 verify 后)**:Tab 2 不再用 `created_at IN period OR closed_at IN period` 这种 "OR base"。原口径会把 carryover task 进 base(Main 7d:188 created + 25 closed-old = 213),Owner 看到 Tab 2 数字虚高于 Tab 1。改成 closed-only 后,Tab 1 (本期 calls)和 Tab 2 (本期 closed tasks)口径平行可比,且贴 HubSpot/Salesforce "Tasks Completed" pattern。Pending backlog 信号放页面顶部 alert banner(待 V2 spec),不混进 Volume。

| 列                              | 公式 (在 base WHERE 上追加)                                                                                                                              | 含义 / 备注                                                                                                                               |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Task Volume** (主数字)          | `COUNT(*)` — 无追加 filter                                                                                                                            | 本期 close 的 task 数。pending backlog 不进 base                                                                                             |
| · Closed this period (subtext) | 同主数字                                                                                                                                               | 显式标注 "closed this week / month / etc."                                                                                                |
| **Intro Booking** total        | `AND type_category = 'lead_follow_up'`                                                                                                             | Lead funnel 内 task,按 `prompt-builder.ts` line 590                                                                                     |
| **Intro Booking** closed       | total + `AND close_result IN ('converted', 'booked')`                                                                                              | `converted` = task objective achieved;`booked` = v0.54 显式 enum                                                                        |
| **Membership** total           | `AND type_category = 'booked_not_converted'`                                                                                                       | "已 booked / showed / trialed 但未签" 客人的拉回 task,按 line 591                                                                               |
| **Membership** closed          | total + `AND close_result = 'converted'`                                                                                                           | 在 `booked_not_converted` 语境下 = 转成 paying member。**不**算 `lead_follow_up + converted`(那是 booked intro,不是签会员,已在 Intro Booking closed 算过) |
| **Cancel Save** total          | `AND type_category = 'cancellation_risk'`                                                                                                          | 客人**主动要 cancel/freeze/downgrade**(prompt-builder.ts line 594)                                                                         |
| **Cancel Save** saved          | total + `AND close_result = 'cancel_saved'`                                                                                                        | 把客人从"要走"边缘救回                                                                                                                          |
| **Cancel Save** % saved        | `saved / total × 100`                                                                                                                              | 前端 compute;`total = 0` 显示 `—`                                                                                                         |
| **Complaint Resolve** total    | `AND type_category = 'retention'`                                                                                                                  | 客人**没要 cancel**,但有投诉 / billing 争议 / 服务质量问题(prompt-builder.ts line 595)                                                                |
| **Complaint Resolve** resolved | total + `AND close_result = 'issue_resolved'`                                                                                                      | 处理掉投诉,客人继续留                                                                                                                           |
| **Complaint Resolve** %        | `resolved / total × 100`                                                                                                                           | 前端 compute;`total = 0` 显示 `—`                                                                                                         |
| **Task Productivity**          | `(Intro_closed + Membership_closed + Cancel_saved + Complaint_resolved) / (Intro_total + Membership_total + Cancel_total + Complaint_total) × 100` | 跟 Tab 1 Team Productivity 同构                                                                                                          |
| **Impacted Revenue**           | `SUM(coefficient(close_result))` over `status='closed' AND closed_at IN period`                                                                    | 见下方 V1 系数表;Tooltip 必须明示"估算,非 POS 入账"                                                                                                  |

**Sanity checks**:

- `closed ≤ total`(每列)
- Intro / Membership / Cancel Save / Complaint Resolve 四类 task **互不重叠**(不同 `type_category`)
- `Impacted Revenue ≥ 0`,与 Tab 2 closed/saved 总数 directionally 一致

**两个 Productivity 联合诊断** (Tab 1 Team Prod + Tab 2 Task Prod 一起看):

| Team Prod | Task Prod | 诊断                                        |
| --------- | --------- | ----------------------------------------- |
| 低         | 高         | 接到机会就 close,但 lead 量不够 → **marketing 问题** |
| 高         | 低         | 机会多但 close 不掉 → **staff 执行问题**            |
| 低         | 低         | 系统性问题(店关了 / 长期病)                          |
| 高         | 高         | 一切健康                                      |

#### Impacted Revenue V1 系数表

SoT `@retaintive/common/db/schema/task-ui.ts` `CLOSE_RESULT_OPTIONS` (v0.54 起 18 值)。

| close\_result              | task-ui category | V1 系数   | 含义                                                                                                                                                  |
| -------------------------- | ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `converted`                | positive         | $450    | 新签 paying member / 真预约上                                                                                                                             |
| `cancel_saved`             | positive         | $450    | 从 cancel 边缘救回                                                                                                                                       |
| `win_back`                 | positive         | $300    | Former member 重新加入                                                                                                                                  |
| `referral_obtained`        | positive         | $300    | 现有 member 介绍朋友                                                                                                                                      |
| `renewed`                  | positive         | $200    | 续费 / 复活冻结账号                                                                                                                                         |
| `upgraded`                 | positive         | $200    | 升级 tier                                                                                                                                             |
| `booked`                   | (v0.54 新增)       | $150    | Intro booked 但未签会员 (跟 `converted` 区别:可能 no-show)                                                                                                    |
| `issue_resolved`           | positive         | $0 (V1) | Service complaint resolved。V2 重评估                                                                                                                   |
| `cancelled`                | (v0.54 新增)       | $0      | Cancellation 已批准 / 取消单已发,确认流失。**待决策**: V2 是否给负系数反映流失成本                                                                                              |
| Neutral (6) + Negative (3) | —                | $0      | `attempted` / `no_answer` / `left_voicemail` / `callback_later` / `already_member` / `other` / `not_interested` / `wrong_number` / `do_not_contact` |

**V1 系数硬编码**,V2 下放 store-level config。

#### AI Summary

不是 SQL 算的。`callytics-infrastructure` cross-call AI Lambda 每天凌晨扫这家店当天所有 call analysis,综合写一句 ≤ 80 字 plain-text summary 存到 store-level summary table。Dashboard 直接读 string 字段,inline 显示。

**示例 narrative**:

- "Stable week, cancel saves on track"
- "Intro booking +20% vs last week, momentum strong"
- "Membership conversion below avg, review SA scripts"
- "Urgent: 8/9 cancel calls unsaved, immediate intervention needed"

V1 不带视觉状态标签 (无 emoji 无 color tag) — 文字本身已传达情绪 (urgent / stable / strong)。V2 视产品需要再加 sentiment classification。

准确性靠 cross-call AI prompt 质量,与 dashboard 本身无关。Owner 反馈 "对不上" → issue 报到 `callytics-infrastructure` cross-call AI pipeline。

***

## Interaction Rules

| 行为                        | 跳转                                                                                                                                                                                                                                   |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 点 **Store name**          | `/store-detail/:storeId?period=<current>&voicemail=<current>` 裸跳单店 dashboard,带 time + VM 设置                                                                                                                                          |
| 点 **KPI cell**(任何数字 cell) | `/store-detail/:storeId?metric=<col>&period=<current>&voicemail=<current>` 带 metric 信号跳店内 deep-dive。dashboard reshape 接到 `metric` 后 default scroll 到对应 detail block(详见 `2026-05-24-dashboard-reshape.md` "Deep-link from My Stores") |
| 切 tab                     | 不重置 time range / VM toggle / sort                                                                                                                                                                                                    |
| 切 VM toggle               | 立即重查 + 重渲染数字(Call Volume 受影响最直接;含 VM time ≈ 排除 VM time × 1.77 based on Main 7d verify)                                                                                                                                               |
| Hover ⓘ                   | 显示 formula tooltip                                                                                                                                                                                                                   |
| AI Summary 列              | inline 显示,不 expand                                                                                                                                                                                                                   |

**Deep-link query param spec(发给 store-detail)**:

| Param       | Values                                                                                                            | 含义                                                                                       |
| ----------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `metric`    | `calls` \| `intro` \| `membership` \| `cancel` \| `complaint` \| `productivity` \| `tasks` \| `revenue`           | Owner 在 my-stores 点的具体 cell。store-detail 接到后 default focus 对应 block(若该 metric 有对应 block) |
| `period`    | `today` \| `7d` \| `14d` \| `28d` \| `this-week` \| `last-week` \| `this-month` \| `last-month` \| ISO date range | 与 my-stores 当前 time picker 一致                                                            |
| `voicemail` | `include` \| `exclude`                                                                                            | 与 my-stores VM toggle 一致                                                                 |

***

## Backend Implications

复用 `apps/api/src/routes/v3/dashboard-multi-store.ts`,扩展:

- **Period enum** 加 `today / 7d / 14d / 28d / this-week` + `startDate` / `endDate` ISO query params(custom 日历)
- **`includeVoicemail` query param** 新增 — default `true`,控制 line 181 + 206 现有 `call_state IS DISTINCT FROM 'voicemail'` filter 是否应用。**不删 filter,改成参数化**(避免 VM 含/不含数字一夜 1.77x 跳变,Owner 失去 baseline)
- **Tab 2 response fields**: `totalTasks`(closed in period), `introBookingClosed/Total`, `membershipClosed/Total`, `cancelSavedSaved/Total`, `complaintResolvedResolved/Total`, `taskProductivity`, `impactedRevenue`, `aiSummary`
- **Tab 1 response 重构** from flat fields 成 closed/total 对:`introBookingClosed/Total`, `membershipCallClosed/Total`, `cancelSaveSaved/Total`, `complaintResolveResolved/Total`, `teamProductivity`
- **Tab 2 SQL base 改 closed-only**:`AND closed_at IN period AND status = 'closed'` (替代原 `created_at OR closed_at`)
- 现有 `tasksCreated / tasksClosed / tasksOverdue` 字段 V1 保留但不展示

Store 列表沿用 `rc_stores + store_members + store_config(activated_at IS NOT NULL)`(line 139-150)。多租户隔离:`store_id` (见 `apps/api/CLAUDE.md` Store-Level 隔离)。

***

## Files to Change

| 文件                                                                                 | 改动                                                                                                       |
| ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `apps/web/src/pages/overview/index.vue`                                            | Tab 切换 + Tab 2 表格                                                                                        |
| `apps/web/src/pages/overview/components/PeriodTabs.vue`                            | 扩展为 popup picker (8 个选项 + 日历)                                                                            |
| `apps/web/src/pages/overview/components/CallActivityTable.vue`                     | 新建 — Tab 1 表格抽离                                                                                          |
| `apps/web/src/pages/overview/components/OutcomesTable.vue`                         | 新建 — Tab 2 表格                                                                                            |
| `apps/web/src/composables/queries/useMultiStore.ts`                                | 扩展 response type                                                                                         |
| `apps/api/src/routes/v3/dashboard-multi-store.ts`                                  | period enum / task outcome aggregate / Impacted Revenue / Productivity / AI Summary / 删 voicemail filter |
| `apps/api/docs/api-technical-reference.md` + `apps/api/docs/frontend-api-guide.md` | 文档同步(pre-commit hook 强制)                                                                                 |
| `CHANGELOG.md` (root + apps/web + apps/api)                                        | 各加 entry                                                                                                 |

***

## Pending Team Discussion

V1 按下面"当前选择"实施,不 block 开发。Team review 后如改方向,同步更新 Tab 1 Calculation Recipes 表格 + Defaults section。

### Decision 2 — Complaint subcategory list 范围

Tab 1 Complaint Resolve total 公式当前列了 4 个 subcategory:
`member_support` + `billing_issue` + `cancellation_fee_dispute` + `complaint_feedback`

Scarsdale 30d 真实分布:`member_support` 39(最大)+ `billing_issue` 16 + `cancellation_fee_dispute` 4 + `complaint_feedback` 1 = 60。

但 `member_support` 这 39 通里可能很多只是"客人问问题"(not actual complaint)。如果含 `member_support`,Complaint Resolve total = 60;不含 = 21。两个数字差 3 倍。

**待 product 拍板** 哪些 subcategory 算"complaint"。

- 含 `member_support` = 范围大,可能噪声
- 不含 `member_support` = 范围窄,数据稀疏但语义干净

**决策人**: _(待填)_ · **决议日期**: _(待填)_

### Decision 1 — Tab 1 Membership 用 2 值还是 4 值

| 选项             | 包含 subcategory                                          | Lens                         | 利弊                                                                          |
| -------------- | ------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------- |
| **2 值** (当前选择) | `membership_purchase_related` + `reactivation_purchase` | "卖一个新 membership"            | 跟 Tab 2 `booked_not_converted` 同语义;upgrade/package 是 add-on,V2 可单开列         |
| **4 值**        | 上面 2 个 + `member_upgrade` + `member_package`            | "所有 membership-related 营收对话" | observation tool 全展示;现有 member 升级 / 加包也算 "membership activity",但跟 Tab 2 不对称 |

**背景** — Prompt + schema 把 4 个都列为 `revenue_impacting`,**没规定**列怎么 group。这是产品 lens 决策,不是技术决策。

**决策人**: _(待填)_  ·  **决议日期**: _(待填)_

***

## Schema Source-of-Truth Chain

| 层                      | 位置                                                                                                  |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| L1 Drizzle schema      | `@retaintive/common/src/db/schema/{tasks,calls}.ts` (API `^1.0.0`, web `^0.54.0` — 2026-05-24 bump) |
| L2 Enum constants      | 见下方 sub-list                                                                                        |
| L3 Neon test live data | `restless-boat-70724564` (org `org-long-math-59935021`),`npx neonctl connection-string` 拿 conn      |
| L4 Existing route      | `apps/api/src/routes/v3/dashboard-multi-store.ts` (Tab 1 SQL 已写,line 165-208)                       |

**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)
