From 3c70549e7cc49e357bdafac3bcd2de17d043408c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 6 Aug 2026 14:40:09 +0000 Subject: [PATCH 1/2] docs(analytics): write the landing page to source and retire the cube vocabulary (#976, #977) `content/docs/analytics/index.mdx` is the first screen a reader of the analytics docs sees, and every count on it named something else. Six claims, all three locales, rewritten against `src/`: - 4 cubes (Sales, Pipeline, Service, Marketing) -> no cube of this app's own; the semantic layer is the nine datasets in `src/datasets/`, compiled into cubes by the analytics service (ADR-0021). The four names hit nothing. - 4 dashboards -> five; Sales Activity was missing. - "10+ standard reports across leads, deals, cases, contracts" -> exactly ten, and none of them is a contract report: no dataset reads `crm_contract`, so none can be built. - a rep running *Pipeline by Stage* -> **Opportunities by Stage**, with the name's two real identities (a shared dashboard tile, a chart title) written the way PR #975 wrote them on the reports page. - the **Analytics** navigation group -> **Insights**, with the five entries the app pins to it; nothing in the sidebar is called Dashboards/Reports/Cubes. - "the AI Copilot can read directly from cubes" -> what this app declares (no skill names a dataset, cube or measure; the data-question skill aggregates over records), leaving the platform side undecided rather than asserted. The `/docs/ai-copilot/sales-copilot` link the issue reported as dangling is not: #589 / PR #611 renamed the link text and the target page has been there throughout. Left as is, and pinned. Eight further pages carried the same retired vocabulary (#977): whats-new now lists the nine datasets; for-developers draws `datasets/` instead of the `src/cubes/` directory removed in #492; performance-and-limits and faq state the refresh cadence the app really declares (each dashboard's own `refreshInterval` - 60s / 180s / 300s) instead of an "every 5 min incremental, nightly full" figure nothing in `src/` configures, and no longer promise a manual refresh button no dashboard declares; pipeline-management, introduction, fork-hotcrm and sandbox-and-releases were renames only. The glossary's definition of the cube concept is deliberately untouched. `test/docs-analytics-vocabulary.test.ts` derives every count and product name on these pages from `src/` and fails when the app and the page disagree. Reverse verification: 25 of its 39 rules go red on the pre-fix tree, green after. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa --- .../analytics-landing-page-cube-vocabulary.md | 41 ++ .../administration/sandbox-and-releases.mdx | 2 +- .../sandbox-and-releases.zh-Hans.mdx | 2 +- .../sandbox-and-releases.zh-Hant.mdx | 2 +- content/docs/analytics/index.mdx | 40 +- content/docs/analytics/index.zh-Hans.mdx | 40 +- content/docs/analytics/index.zh-Hant.mdx | 40 +- .../docs/getting-started/for-developers.mdx | 2 +- .../for-developers.zh-Hans.mdx | 2 +- .../for-developers.zh-Hant.mdx | 2 +- content/docs/getting-started/introduction.mdx | 2 +- .../getting-started/introduction.zh-Hans.mdx | 2 +- .../getting-started/introduction.zh-Hant.mdx | 2 +- content/docs/marketplace/fork-hotcrm.mdx | 2 +- .../docs/marketplace/fork-hotcrm.zh-Hans.mdx | 2 +- .../docs/marketplace/fork-hotcrm.zh-Hant.mdx | 2 +- content/docs/reference/faq.mdx | 4 +- content/docs/reference/faq.zh-Hans.mdx | 4 +- content/docs/reference/faq.zh-Hant.mdx | 4 +- .../docs/reference/performance-and-limits.mdx | 5 +- .../performance-and-limits.zh-Hans.mdx | 5 +- .../performance-and-limits.zh-Hant.mdx | 5 +- content/docs/sales/pipeline-management.mdx | 2 +- .../sales/pipeline-management.zh-Hans.mdx | 2 +- .../sales/pipeline-management.zh-Hant.mdx | 2 +- content/docs/whats-new.mdx | 2 +- content/docs/whats-new.zh-Hans.mdx | 2 +- content/docs/whats-new.zh-Hant.mdx | 2 +- test/docs-analytics-vocabulary.test.ts | 428 ++++++++++++++++++ 29 files changed, 568 insertions(+), 84 deletions(-) create mode 100644 .changeset/analytics-landing-page-cube-vocabulary.md create mode 100644 test/docs-analytics-vocabulary.test.ts diff --git a/.changeset/analytics-landing-page-cube-vocabulary.md b/.changeset/analytics-landing-page-cube-vocabulary.md new file mode 100644 index 00000000..efa1fe72 --- /dev/null +++ b/.changeset/analytics-landing-page-cube-vocabulary.md @@ -0,0 +1,41 @@ +--- +'hotcrm': patch +--- + +Write the analytics landing page to source, and retire the "four built-in cubes" +vocabulary from the eight pages that still carried it. + +`content/docs/analytics/index.mdx` is the first screen a reader of the analytics +docs sees, and every count on it named something else. It advertised **4 cubes** +(*Sales*, *Pipeline*, *Service*, *Marketing* — four names that exist nowhere in +the app; the semantic layer is the nine datasets in `src/datasets/`, which the +analytics service compiles into cubes internally, ADR-0021), **4 dashboards** +(there are five — *Sales Activity* was missing), **10+ reports across leads, +deals, cases, contracts** (there are exactly ten, and none of them is a contract +report: no dataset reads `crm_contract`, so none can be built), a report called +*Pipeline by Stage* (a shared dashboard tile and a chart title — the report is +**Opportunities by Stage**) and an **Analytics** navigation group (the group is +called **Insights**, and it holds CRM Overview, Forecasts, Pipeline Coverage, +Lead Inflow and SLA Performance — nothing in it is called *Dashboards*, +*Reports* or *Cubes*). The "AI Copilot reads directly from cubes" claim now says +what this app declares — no skill names a dataset, cube or measure, and the one +that answers data questions aggregates over records — and leaves the platform +side undecided rather than asserting it in either direction. + +The same retired vocabulary is gone from eight further pages: `whats-new` now +lists the nine datasets it actually shipped, `getting-started/for-developers` +draws `datasets/` instead of the `src/cubes/` directory removed in #492, and +`reference/performance-and-limits` plus `reference/faq` state the refresh cadence +the app really declares — each dashboard's own `refreshInterval`, 60 s on +Customer Service, 180 s on Sales Performance, 300 s on the other three — in place +of an "every 5 min incremental / nightly full" figure nothing in `src/` +configures, and of a manual refresh button no dashboard declares. +`sales/pipeline-management`, `getting-started/introduction`, +`marketplace/fork-hotcrm` and `administration/sandbox-and-releases` were renames +only. The glossary's definition of the *cube* concept is deliberately untouched: +the concept is real, and only its misapplication to this app was not. + +Documentation only in all three locales; no metadata changed. +`test/docs-analytics-vocabulary.test.ts` now derives every count and product name +on these pages from `src/` and fails the build when the app and the page disagree. +Refs #976 #977. diff --git a/content/docs/administration/sandbox-and-releases.mdx b/content/docs/administration/sandbox-and-releases.mdx index 9c504311..1f169552 100644 --- a/content/docs/administration/sandbox-and-releases.mdx +++ b/content/docs/administration/sandbox-and-releases.mdx @@ -90,7 +90,7 @@ A **change package** captures a set of metadata changes (objects, fields, valida | Flows | ✅ | | Sharing rules, profiles, permission sets | ✅ | | Pages, list views, dashboards | ✅ | -| Reports, cubes | ✅ | +| Reports, datasets | ✅ | | AI skills, knowledge base configs | ✅ | | Approval processes | ✅ | | Email templates | ✅ | diff --git a/content/docs/administration/sandbox-and-releases.zh-Hans.mdx b/content/docs/administration/sandbox-and-releases.zh-Hans.mdx index ab8e63cd..1cf2e7de 100644 --- a/content/docs/administration/sandbox-and-releases.zh-Hans.mdx +++ b/content/docs/administration/sandbox-and-releases.zh-Hans.mdx @@ -90,7 +90,7 @@ description: 安全地进行配置变更 —— 沙箱租户、变更包、提 | 流程 | ✅ | | 共享规则、权限档案、权限集 | ✅ | | 页面、列表视图、仪表板 | ✅ | -| 报表、立方体 | ✅ | +| 报表、dataset | ✅ | | AI 技能、知识库配置 | ✅ | | 审批流程 | ✅ | | 电子邮件模板 | ✅ | diff --git a/content/docs/administration/sandbox-and-releases.zh-Hant.mdx b/content/docs/administration/sandbox-and-releases.zh-Hant.mdx index caddefad..956cf224 100644 --- a/content/docs/administration/sandbox-and-releases.zh-Hant.mdx +++ b/content/docs/administration/sandbox-and-releases.zh-Hant.mdx @@ -90,7 +90,7 @@ description: 安全地進行設定變更 —— 沙箱租戶、變更包、提 | 流程 | ✅ | | 共用規則、權限設定檔、權限集 | ✅ | | 頁面、清單檢視、儀表板 | ✅ | -| 報表、立方體 | ✅ | +| 報表、dataset | ✅ | | AI 技能、知識庫設定 | ✅ | | 審批流程 | ✅ | | 電子郵件範本 | ✅ | diff --git a/content/docs/analytics/index.mdx b/content/docs/analytics/index.mdx index 1d155c88..e480171f 100644 --- a/content/docs/analytics/index.mdx +++ b/content/docs/analytics/index.mdx @@ -1,6 +1,6 @@ --- title: Analytics -description: Dashboards, reports, and the data cubes that power them — how HotCRM turns daily activity into insight. +description: Dashboards, reports, and the semantic layer under them — how HotCRM turns daily activity into insight. --- # Analytics @@ -9,15 +9,17 @@ HotCRM Analytics turns your CRM activity into **decisions**. Three layers work t - **Dashboards** — visual at-a-glance views for execs, managers, and reps. - **Reports** — detailed list-and-summary reports anyone can filter and export. -- **Cubes** — the data layer underneath, ready for self-service slice-and-dice. +- **Cubes** — the semantic layer underneath. In this app that layer is a set of **datasets**: HotCRM declares no cube of its own, and the analytics service compiles each dataset into its cube internally (ADR-0021). ## What's included | Layer | What it does | | --- | --- | -| [**Dashboards**](/docs/analytics/dashboards) | 4 ready-made dashboards (CRM, Sales, Service, Executive) | -| [**Reports**](/docs/analytics/reports) | 10+ standard reports across leads, deals, cases, contracts | -| [**Cubes**](/docs/analytics/cubes) | 4 cubes (sales, pipeline, service, marketing) for self-service analysis | +| [**Dashboards**](/docs/analytics/dashboards) | **Five** ready-made dashboards — CRM Overview, Sales Performance, Sales Activity, Customer Service, Executive Overview | +| [**Reports**](/docs/analytics/reports) | **Ten** standard reports — four on deals, three on cases, one each on leads, accounts and churn | +| [**Cubes**](/docs/analytics/cubes) | **Nine** datasets in `src/datasets/`, the layer both of the above bind by name | + +Two counts on that table used to read differently, and the difference is worth knowing before you go looking. **There is no contract report** — and none can be built, because `src/datasets/` reads nine objects and `crm_contract` is not one of them, so nothing in analytics counts, sums or groups a contract. And **no cube is named** *Sales*, *Pipeline*, *Service* or *Marketing* — those four names exist nowhere in the app. [Cubes](/docs/analytics/cubes) says which datasets answer the first three and why the fourth has no data source at all. ## What you get out of the box @@ -33,32 +35,34 @@ The moment you have data in HotCRM, you have analytics. No setup required. The same sharing rules that govern records also govern dashboards and reports: -- A rep running *Pipeline by Stage* sees only their pipeline. +- A rep running **Opportunities by Stage** sees only their own deals. - A manager sees the rollup for their team. - An exec sees company-wide. This means **one dashboard** works for the whole org — what each person sees depends on who they are. +*Pipeline by Stage* is not a report you can run, though the name is real twice over: it is the open-pipeline funnel **tile** that CRM Overview, Sales Performance and Executive Overview share (`src/dashboards/shared-widgets.ts`), and it is the title of the bar chart on the **Opportunities by Stage** report. Search the report library for it and you find nothing — see [Reports](/docs/analytics/reports). + ## Where to find things -In the Enterprise CRM app, the **Analytics** group contains: +In the Enterprise CRM app the group is called **Insights**, and it holds five entries: + +- **CRM Overview** — the cross-functional dashboard. +- **Forecasts** — the forecast records themselves. +- **Pipeline Coverage**, **Lead Inflow**, **SLA Performance** — three reports pinned to the sidebar by name. -- **Dashboards** — the visual hub. -- **Reports** — the report library. -- **Cubes** — the data-modeling layer (admins). +There is no *Analytics* group, and nothing in the sidebar is called *Dashboards*, *Reports* or *Cubes*. The other dashboards are pinned to the group that owns them — **Sales Performance** under Sales, **Sales Activity** under Activity, **Service Overview** under Service — and the rest of the report library is reached from the reports screen rather than from the nav. [Quick tour](/docs/getting-started/quick-tour) lists the whole sidebar, group by group. ## AI-powered insights -The AI Copilot can read directly from cubes — ask questions like: +**On this app's side, nothing connects the Copilot to a dataset.** The six skills under `src/skills/` name no dataset, no cube and no measure. The one that answers data questions, **Live Data Access**, is wired to the platform's object tools and is instructed to re-read the object's schema and aggregate over records — so a Copilot answer is computed from the objects, not read off a compiled cube. -- *"What's driving the gap between commit and forecast?"* -- *"Why did East close 3 fewer deals than West last quarter?"* -- *"Show me which products correlate with longer sales cycles."* +That also sets what it can be asked. *"What's driving the gap between commit and forecast?"* has data behind it: **Commit**, **Quota**, **Closed** and **Pipeline** are all real figures on the forecast record. *"Why did East close 3 fewer deals than West last quarter?"* and *"Show me which products correlate with longer sales cycles"* do not — no region is stored on a deal, no dataset reads line items, and the one duration the opportunity carries (**Days in Current Stage**) is a formula evaluated after the query, so nothing can aggregate it. [Cubes](/docs/analytics/cubes) lists the gaps one by one. -See [Sales Skills](/docs/ai-copilot/sales-copilot) for more. +Whether the platform's own agent additionally reads the compiled cubes is a question about the runtime rather than about this app, so this page makes no claim about it in either direction; check it against your deployment. See [Sales Skills](/docs/ai-copilot/sales-copilot) for what the sales-side skills do. ## Start here -- [Dashboards](/docs/analytics/dashboards) — the four built-in dashboards. -- [Reports](/docs/analytics/reports) — the standard report library. -- [Cubes](/docs/analytics/cubes) — for analysts who want to build their own. +- [Dashboards](/docs/analytics/dashboards) — the five built-in dashboards. +- [Reports](/docs/analytics/reports) — the ten standard reports. +- [Cubes](/docs/analytics/cubes) — the nine datasets underneath, and what each can and cannot answer. diff --git a/content/docs/analytics/index.zh-Hans.mdx b/content/docs/analytics/index.zh-Hans.mdx index 673cdc00..d0b794d8 100644 --- a/content/docs/analytics/index.zh-Hans.mdx +++ b/content/docs/analytics/index.zh-Hans.mdx @@ -1,6 +1,6 @@ --- title: 分析 -description: 仪表盘、报表,以及为它们提供支持的多维数据集——HotCRM 如何将日常活动转化为洞察。 +description: 仪表盘、报表,以及它们底下的语义层——HotCRM 如何将日常活动转化为洞察。 --- # 分析 @@ -9,15 +9,17 @@ HotCRM 分析将你的 CRM 活动转化为**决策**。三个层次协同工作 - **仪表盘** — 面向高管、经理和销售代表的可视化一览视图。 - **报表** — 任何人都可以筛选和导出的详细列表与汇总报表。 -- **多维数据集** — 底层数据层,可用于自助式切片分析。 +- **多维数据集** — 底下的语义层。在本应用里,这一层是一组 **dataset**:HotCRM 自己不声明任何 cube,由分析服务在内部把每个 dataset 编译成它的 cube(ADR-0021)。 ## 包含内容 | 层次 | 作用 | | --- | --- | -| [**仪表盘**](/zh-Hans/docs/analytics/dashboards) | 4 个开箱即用的仪表盘(CRM、销售、服务、高管) | -| [**报表**](/zh-Hans/docs/analytics/reports) | 涵盖线索、交易、工单、合同的 10+ 个标准报表 | -| [**多维数据集**](/zh-Hans/docs/analytics/cubes) | 4 个多维数据集(销售、管道、服务、市场营销),用于自助分析 | +| [**仪表盘**](/zh-Hans/docs/analytics/dashboards) | **五个**开箱即用的仪表盘 —— CRM Overview、Sales Performance、Sales Activity、Customer Service、Executive Overview | +| [**报表**](/zh-Hans/docs/analytics/reports) | **十份**标准报表 —— 交易 4 份、工单 3 份,线索、客户、流失各 1 份 | +| [**多维数据集**](/zh-Hans/docs/analytics/cubes) | `src/datasets/` 下的**九个** dataset,也就是上面两层按名字绑定的那一层 | + +上表有两个数字过去写的是别的样子,去找东西之前值得先知道。**这里没有合同报表** —— 也建不出来:`src/datasets/` 只读九个对象,`crm_contract` 不在其中,所以分析里没有任何东西能对合同做计数、求和或分组。另外,**没有任何 cube 叫这四个名字** —— *Sales*、*Pipeline*、*Service*、*Marketing* 在应用里零命中。前三个由哪些 dataset 回答、第四个为什么连数据源都没有,见 [多维数据集](/zh-Hans/docs/analytics/cubes)。 ## 开箱即用的能力 @@ -33,32 +35,34 @@ HotCRM 分析将你的 CRM 活动转化为**决策**。三个层次协同工作 管理记录的共享规则同样管理仪表盘和报表: -- 运行 *Pipeline by Stage* 的销售代表只能看到自己的管道。 +- 运行 **Opportunities by Stage** 的销售代表只能看到自己的交易。 - 经理看到其团队的汇总。 - 高管看到全公司范围。 这意味着**一个仪表盘**适用于整个组织——每个人看到的内容取决于他们的身份。 +*Pipeline by Stage* 不是一张可以运行的报表,但这个名字有两个真身:它是 CRM Overview、Sales Performance 与 Executive Overview 共用的那块未结管道漏斗**磁贴**(`src/dashboards/shared-widgets.ts`),也是 **Opportunities by Stage** 报表上那张柱状图的标题。在报表库里按这个名字搜索,什么也搜不到 —— 见 [报表](/zh-Hans/docs/analytics/reports)。 + ## 在哪里可以找到 -在 Enterprise CRM 应用中,**分析**组包含: +在 Enterprise CRM 应用里,这个分组叫 **Insights**,下面有五个条目: + +- **CRM Overview** —— 跨职能的那块仪表盘。 +- **Forecasts** —— 预测记录本身。 +- **Pipeline Coverage**、**Lead Inflow**、**SLA Performance** —— 三张按名字钉在侧边栏上的报表。 -- **仪表盘** — 可视化中枢。 -- **报表** — 报表库。 -- **多维数据集** — 数据建模层(管理员)。 +没有 *Analytics* 分组,侧边栏上也没有任何条目叫 *Dashboards*、*Reports* 或 *Cubes*。其余仪表盘各自钉在负责它的分组下 —— **Sales Performance** 在 Sales、**Sales Activity** 在 Activity、**Service Overview** 在 Service —— 报表库里剩下的报表则从报表界面进入,而不是从导航进入。整条侧边栏逐组的列法,见 [快速导览](/zh-Hans/docs/getting-started/quick-tour)。 ## AI 驱动的洞察 -AI Copilot 可以直接从多维数据集读取数据——可以提出如下问题: +**在本应用这一侧,没有任何东西把 Copilot 连到某个 dataset 上。** `src/skills/` 下的六个技能,没有一个点名任何 dataset、cube 或度量。其中负责回答数据问题的那个 —— **Live Data Access** —— 接的是平台的对象工具,而它的指令要求先重新读一遍对象的 schema,再在记录上做聚合。所以 Copilot 的回答是从对象上算出来的,而不是从编译好的 cube 上读出来的。 -- *"是什么造成了承诺额与预测额之间的差距?"* -- *"为什么上季度东区比西区少成交了 3 笔交易?"* -- *"告诉我哪些产品与更长的销售周期相关。"* +这也决定了能问它什么。*"是什么造成了承诺额与预测额之间的差距?"* 背后是有数据的:**Commit**、**Quota**、**Closed**、**Pipeline** 都是预测记录上真实的数字。*"为什么上季度东区比西区少成交了 3 笔交易?"* 与 *"告诉我哪些产品与更长的销售周期相关。"* 则没有 —— 交易上不存区域,没有任何 dataset 读行项目,而商机唯一带着的那个时长(**Days in Current Stage**)是查询之后才算的公式,什么都聚合不了。这些缺口逐条列在 [多维数据集](/zh-Hans/docs/analytics/cubes)。 -更多内容请参阅 [销售技能](/zh-Hans/docs/ai-copilot/sales-copilot)。 +平台自带的 agent 是否另外也读编译后的 cube,是关于运行时而不是关于本应用的问题 —— 所以本页对它不做任何方向上的断言,请对着你自己的部署去核。销售侧的技能各自做什么,见 [销售技能](/zh-Hans/docs/ai-copilot/sales-copilot)。 ## 从这里开始 -- [仪表盘](/zh-Hans/docs/analytics/dashboards) — 四个内置仪表盘。 -- [报表](/zh-Hans/docs/analytics/reports) — 标准报表库。 -- [多维数据集](/zh-Hans/docs/analytics/cubes) — 适合想要自行构建的分析师。 +- [仪表盘](/zh-Hans/docs/analytics/dashboards) —— 五个内置仪表盘。 +- [报表](/zh-Hans/docs/analytics/reports) —— 十份标准报表。 +- [多维数据集](/zh-Hans/docs/analytics/cubes) —— 底下的九个 dataset,以及每个能回答什么、不能回答什么。 diff --git a/content/docs/analytics/index.zh-Hant.mdx b/content/docs/analytics/index.zh-Hant.mdx index adb82df9..7e0207e7 100644 --- a/content/docs/analytics/index.zh-Hant.mdx +++ b/content/docs/analytics/index.zh-Hant.mdx @@ -1,6 +1,6 @@ --- title: 分析 -description: 儀表板、報表,以及為它們提供支援的多維資料集——HotCRM 如何將日常活動轉化為洞察。 +description: 儀表板、報表,以及它們底下的語義層——HotCRM 如何將日常活動轉化為洞察。 --- # 分析 @@ -9,15 +9,17 @@ HotCRM 分析將你的 CRM 活動轉化為**決策**。三個層次協同運作 - **儀表板** — 面向高階主管、經理和業務代表的視覺化一覽檢視。 - **報表** — 任何人都可以篩選和匯出的詳細清單與彙總報表。 -- **多維資料集** — 底層資料層,可用於自助式切片分析。 +- **多維資料集** — 底下的語義層。在本應用裡,這一層是一組 **dataset**:HotCRM 自己不宣告任何 cube,由分析服務在內部把每個 dataset 編譯成它的 cube(ADR-0021)。 ## 包含內容 | 層次 | 作用 | | --- | --- | -| [**儀表板**](/zh-Hant/docs/analytics/dashboards) | 4 個開箱即用的儀表板(CRM、銷售、服務、高階主管) | -| [**報表**](/zh-Hant/docs/analytics/reports) | 涵蓋潛在客戶、交易、工單、合約的 10+ 個標準報表 | -| [**多維資料集**](/zh-Hant/docs/analytics/cubes) | 4 個多維資料集(銷售、管道、服務、行銷),用於自助分析 | +| [**儀表板**](/zh-Hant/docs/analytics/dashboards) | **五個**開箱即用的儀表板 —— CRM Overview、Sales Performance、Sales Activity、Customer Service、Executive Overview | +| [**報表**](/zh-Hant/docs/analytics/reports) | **十份**標準報表 —— 交易 4 份、工單 3 份,潛在客戶、客戶、流失各 1 份 | +| [**多維資料集**](/zh-Hant/docs/analytics/cubes) | `src/datasets/` 下的**九個** dataset,也就是上面兩層按名字繫結的那一層 | + +上表有兩個數字過去寫的是別的樣子,去找東西之前值得先知道。**這裡沒有合約報表** —— 也建不出來:`src/datasets/` 只讀九個物件,`crm_contract` 不在其中,所以分析裡沒有任何東西能對合約做計數、求和或分組。另外,**沒有任何 cube 叫這四個名字** —— *Sales*、*Pipeline*、*Service*、*Marketing* 在應用裡零命中。前三個由哪些 dataset 回答、第四個為什麼連資料來源都沒有,見 [多維資料集](/zh-Hant/docs/analytics/cubes)。 ## 開箱即用的能力 @@ -33,32 +35,34 @@ HotCRM 分析將你的 CRM 活動轉化為**決策**。三個層次協同運作 管理記錄的共享規則同樣管理儀表板和報表: -- 執行 *Pipeline by Stage* 的業務代表只能看到自己的管道。 +- 執行 **Opportunities by Stage** 的業務代表只能看到自己的交易。 - 經理看到其團隊的彙總。 - 高階主管看到全公司範圍。 這意味著**一個儀表板**適用於整個組織——每個人看到的內容取決於他們的身分。 +*Pipeline by Stage* 不是一張可以執行的報表,但這個名字有兩個真身:它是 CRM Overview、Sales Performance 與 Executive Overview 共用的那塊未結管道漏斗**磁貼**(`src/dashboards/shared-widgets.ts`),也是 **Opportunities by Stage** 報表上那張長條圖的標題。在報表庫裡按這個名字搜尋,什麼也搜不到 —— 見 [報表](/zh-Hant/docs/analytics/reports)。 + ## 在哪裡可以找到 -在 Enterprise CRM 應用中,**分析**群組包含: +在 Enterprise CRM 應用裡,這個群組叫 **Insights**,下面有五個條目: + +- **CRM Overview** —— 跨職能的那塊儀表板。 +- **Forecasts** —— 預測記錄本身。 +- **Pipeline Coverage**、**Lead Inflow**、**SLA Performance** —— 三張按名字釘在側邊欄上的報表。 -- **儀表板** — 視覺化中樞。 -- **報表** — 報表庫。 -- **多維資料集** — 資料建模層(管理員)。 +沒有 *Analytics* 群組,側邊欄上也沒有任何條目叫 *Dashboards*、*Reports* 或 *Cubes*。其餘儀表板各自釘在負責它的群組下 —— **Sales Performance** 在 Sales、**Sales Activity** 在 Activity、**Service Overview** 在 Service —— 報表庫裡剩下的報表則從報表介面進入,而不是從導覽進入。整條側邊欄逐組的列法,見 [快速導覽](/zh-Hant/docs/getting-started/quick-tour)。 ## AI 驅動的洞察 -AI Copilot 可以直接從多維資料集讀取資料——可以提出如下問題: +**在本應用這一側,沒有任何東西把 Copilot 連到某個 dataset 上。** `src/skills/` 下的六個技能,沒有一個點名任何 dataset、cube 或度量。其中負責回答資料問題的那個 —— **Live Data Access** —— 接的是平台的物件工具,而它的指令要求先重新讀一遍物件的 schema,再在記錄上做彙總。所以 Copilot 的回答是從物件上算出來的,而不是從編譯好的 cube 上讀出來的。 -- *"是什麼造成了承諾額與預測額之間的差距?"* -- *"為什麼上一季東區比西區少成交了 3 筆交易?"* -- *"告訴我哪些產品與更長的銷售週期相關。"* +這也決定了能問它什麼。*"是什麼造成了承諾額與預測額之間的差距?"* 背後是有資料的:**Commit**、**Quota**、**Closed**、**Pipeline** 都是預測記錄上真實的數字。*"為什麼上一季東區比西區少成交了 3 筆交易?"* 與 *"告訴我哪些產品與更長的銷售週期相關。"* 則沒有 —— 交易上不存區域,沒有任何 dataset 讀行項目,而商機唯一帶著的那個時長(**Days in Current Stage**)是查詢之後才算的公式,什麼都彙總不了。這些缺口逐條列在 [多維資料集](/zh-Hant/docs/analytics/cubes)。 -更多內容請參閱 [銷售技能](/zh-Hant/docs/ai-copilot/sales-copilot)。 +平台自帶的 agent 是否另外也讀編譯後的 cube,是關於執行時而不是關於本應用的問題 —— 所以本頁對它不做任何方向上的斷言,請對著你自己的部署去核。銷售側的技能各自做什麼,見 [銷售技能](/zh-Hant/docs/ai-copilot/sales-copilot)。 ## 從這裡開始 -- [儀表板](/zh-Hant/docs/analytics/dashboards) — 四個內建儀表板。 -- [報表](/zh-Hant/docs/analytics/reports) — 標準報表庫。 -- [多維資料集](/zh-Hant/docs/analytics/cubes) — 適合想要自行建構的分析師。 +- [儀表板](/zh-Hant/docs/analytics/dashboards) —— 五個內建儀表板。 +- [報表](/zh-Hant/docs/analytics/reports) —— 十份標準報表。 +- [多維資料集](/zh-Hant/docs/analytics/cubes) —— 底下的九個 dataset,以及每個能回答什麼、不能回答什麼。 diff --git a/content/docs/getting-started/for-developers.mdx b/content/docs/getting-started/for-developers.mdx index 9c147d36..c2fcea68 100644 --- a/content/docs/getting-started/for-developers.mdx +++ b/content/docs/getting-started/for-developers.mdx @@ -38,7 +38,7 @@ hotcrm/ │ ├── agents/ # AI agents │ ├── skills/ # AI skills │ ├── apps/, views/, pages/ -│ ├── dashboards/, reports/, cubes/ +│ ├── dashboards/, reports/, datasets/ │ ├── profiles/, sharing/ │ ├── translations/ │ └── data/ diff --git a/content/docs/getting-started/for-developers.zh-Hans.mdx b/content/docs/getting-started/for-developers.zh-Hans.mdx index f61e76c1..b88b2b77 100644 --- a/content/docs/getting-started/for-developers.zh-Hans.mdx +++ b/content/docs/getting-started/for-developers.zh-Hans.mdx @@ -38,7 +38,7 @@ hotcrm/ │ ├── agents/ # AI agents │ ├── skills/ # AI skills │ ├── apps/, views/, pages/ -│ ├── dashboards/, reports/, cubes/ +│ ├── dashboards/, reports/, datasets/ │ ├── profiles/, sharing/ │ ├── translations/ │ └── data/ diff --git a/content/docs/getting-started/for-developers.zh-Hant.mdx b/content/docs/getting-started/for-developers.zh-Hant.mdx index f34da63d..c09c8465 100644 --- a/content/docs/getting-started/for-developers.zh-Hant.mdx +++ b/content/docs/getting-started/for-developers.zh-Hant.mdx @@ -38,7 +38,7 @@ hotcrm/ │ ├── agents/ # AI agents │ ├── skills/ # AI skills │ ├── apps/, views/, pages/ -│ ├── dashboards/, reports/, cubes/ +│ ├── dashboards/, reports/, datasets/ │ ├── profiles/, sharing/ │ ├── translations/ │ └── data/ diff --git a/content/docs/getting-started/introduction.mdx b/content/docs/getting-started/introduction.mdx index 293032b7..bf845d7a 100644 --- a/content/docs/getting-started/introduction.mdx +++ b/content/docs/getting-started/introduction.mdx @@ -18,7 +18,7 @@ The platform spans the four classic CRM pillars, plus an AI layer that knows you | [**Marketing**](/docs/marketing/index) | Run campaigns, score engagement, measure ROI | | [**Revenue**](/docs/revenue/index) | Maintain products, govern contracts, route discount approvals | | [**AI Copilot**](/docs/ai-copilot/index) | An assistant that drafts emails, triages cases, summarises accounts | -| [**Analytics**](/docs/analytics/index) | Dashboards, reports, and self-service data cubes | +| [**Analytics**](/docs/analytics/index) | Dashboards, reports, and the datasets underneath them | Everything is **wired together** out of the box — leads convert into opportunities, opportunities link to quotes and contracts, contracts trigger renewals, support cases see the customer's full history. diff --git a/content/docs/getting-started/introduction.zh-Hans.mdx b/content/docs/getting-started/introduction.zh-Hans.mdx index 67b7e953..b3207219 100644 --- a/content/docs/getting-started/introduction.zh-Hans.mdx +++ b/content/docs/getting-started/introduction.zh-Hans.mdx @@ -18,7 +18,7 @@ description: HotCRM 是什么、面向谁,以及开箱即用包含哪些内容 | [**市场营销**](/zh-Hans/docs/marketing/index) | 开展营销活动、为互动评分、衡量 ROI | | [**收入**](/zh-Hans/docs/revenue/index) | 维护产品、治理合同、路由折扣审批 | | [**AI Copilot**](/zh-Hans/docs/ai-copilot/index) | 一个起草邮件、分流案例、汇总客户的助手 | -| [**分析**](/zh-Hans/docs/analytics/index) | 仪表盘、报表和自助式数据 cube | +| [**分析**](/zh-Hans/docs/analytics/index) | 仪表盘、报表,以及它们底下的 dataset | 一切开箱即用地**串联在一起** —— 线索转化为商机,商机关联到报价单和合同,合同触发续约,支持案例能看到客户的完整历史。 diff --git a/content/docs/getting-started/introduction.zh-Hant.mdx b/content/docs/getting-started/introduction.zh-Hant.mdx index 2e354493..557c7786 100644 --- a/content/docs/getting-started/introduction.zh-Hant.mdx +++ b/content/docs/getting-started/introduction.zh-Hant.mdx @@ -18,7 +18,7 @@ description: HotCRM 是什麼、面向誰,以及開箱即用包含哪些內容 | [**市場行銷**](/zh-Hant/docs/marketing/index) | 開展行銷活動、為互動評分、衡量 ROI | | [**收入**](/zh-Hant/docs/revenue/index) | 維護產品、治理合約、路由折扣審批 | | [**AI Copilot**](/zh-Hant/docs/ai-copilot/index) | 一個起草郵件、分流案例、彙總客戶的助手 | -| [**分析**](/zh-Hant/docs/analytics/index) | 儀表板、報表和自助式資料 cube | +| [**分析**](/zh-Hant/docs/analytics/index) | 儀表板、報表,以及它們底下的 dataset | 一切開箱即用地**串聯在一起** —— 線索轉化為商機,商機關聯到報價單和合約,合約觸發續約,支援案例能看到客戶的完整歷史。 diff --git a/content/docs/marketplace/fork-hotcrm.mdx b/content/docs/marketplace/fork-hotcrm.mdx index 542de885..000b2d9d 100644 --- a/content/docs/marketplace/fork-hotcrm.mdx +++ b/content/docs/marketplace/fork-hotcrm.mdx @@ -18,7 +18,7 @@ A working ObjectStack app needs ~15 conventions wired correctly: - Profile + role hierarchy + sharing rules - Approval processes - Hooks correctly lowered for the build pipeline -- Dashboards backed by analytics cubes +- Dashboards backed by analytics datasets - Documentation that's actually accurate Writing all of that from scratch is a week. Forking HotCRM and renaming it is an hour. diff --git a/content/docs/marketplace/fork-hotcrm.zh-Hans.mdx b/content/docs/marketplace/fork-hotcrm.zh-Hans.mdx index a51fcc29..e7af2fbc 100644 --- a/content/docs/marketplace/fork-hotcrm.zh-Hans.mdx +++ b/content/docs/marketplace/fork-hotcrm.zh-Hans.mdx @@ -18,7 +18,7 @@ HotCRM 是 ObjectStack 应用市场应用的**参考实现**。复刻它可以 - 配置文件 + 角色层级 + 共享规则 - 审批流程 - 为构建管线正确下沉的钩子 -- 由分析多维数据集支撑的仪表盘 +- 由分析 dataset 支撑的仪表盘 - 真正准确的文档 从零编写所有这些需要一周。复刻 HotCRM 并重命名只需一小时。 diff --git a/content/docs/marketplace/fork-hotcrm.zh-Hant.mdx b/content/docs/marketplace/fork-hotcrm.zh-Hant.mdx index 2e176f68..06953081 100644 --- a/content/docs/marketplace/fork-hotcrm.zh-Hant.mdx +++ b/content/docs/marketplace/fork-hotcrm.zh-Hant.mdx @@ -18,7 +18,7 @@ HotCRM 是 ObjectStack 應用市集應用程式的**參考實作**。複刻它 - 設定檔 + 角色階層 + 共用規則 - 簽核流程 - 為建置管線正確下沉的鉤子 -- 由分析多維度資料集支撐的儀表板 +- 由分析 dataset 支撐的儀表板 - 真正準確的文件 從零撰寫所有這些需要一週。複刻 HotCRM 並重新命名只需一小時。 diff --git a/content/docs/reference/faq.mdx b/content/docs/reference/faq.mdx index 3e43a264..1e56a38d 100644 --- a/content/docs/reference/faq.mdx +++ b/content/docs/reference/faq.mdx @@ -130,12 +130,12 @@ Sharing. Reports respect record visibility — you see only what you can see. A Check: - The tile's **filter** — too restrictive? -- The underlying **cube refresh** — overdue? +- The underlying **analytics refresh** — the compiled cube is refreshed by the analytics service, on no schedule HotCRM declares, so a stale tile is a question for your deployment. - Your **profile permissions** for the source object. ### I changed a field on a record — when will the dashboard update? -Most tiles use cubes with incremental refresh every few minutes. Force a refresh from the dashboard's **⟳ Refresh** button. +Each dashboard re-queries on the interval it declares: every **60 s** on Customer Service, every **180 s** on Sales Performance, every **300 s** on CRM Overview, Sales Activity and Executive Overview (`src/dashboards/`). How soon the analytics service re-compiles the cube underneath is a deployment question — this app configures no refresh schedule of its own. HotCRM's dashboard metadata declares no manual refresh control either, so any refresh button your console shows comes from the platform rather than from this app. ### Why can't I add a custom field to a report? diff --git a/content/docs/reference/faq.zh-Hans.mdx b/content/docs/reference/faq.zh-Hans.mdx index 9413d27d..9c0b8ef7 100644 --- a/content/docs/reference/faq.zh-Hans.mdx +++ b/content/docs/reference/faq.zh-Hans.mdx @@ -130,12 +130,12 @@ Copilot 遵守你的共享设置 —— 如果你看不到它,它也看不到 检查: - 磁贴的**筛选器** —— 是否过于严格? -- 底层的 **cube 刷新** —— 是否逾期? +- 底层的**分析刷新** —— 编译后的 cube 由分析服务刷新,HotCRM 没有声明任何刷新计划,所以磁贴不新鲜要去问你自己的部署。 - 你对源对象的**简档权限**。 ### 我更改了某条记录上的某个字段 —— 仪表盘何时更新? -大多数磁贴使用每隔几分钟增量刷新的 cube。从仪表盘的 **⟳ Refresh** 按钮强制刷新。 +每块仪表盘按自己声明的间隔重新查询:Customer Service 每 **60 秒**、Sales Performance 每 **180 秒**、CRM Overview / Sales Activity / Executive Overview 每 **300 秒**(`src/dashboards/`)。底下那个 cube 多久由分析服务重新编译一次,是部署侧的问题 —— 本应用自己不配置任何刷新计划。HotCRM 的仪表盘元数据也没有声明任何手动刷新控件,所以你的控制台上若有刷新按钮,它来自平台而不是本应用。 ### 为什么我无法将自定义字段添加到报表? diff --git a/content/docs/reference/faq.zh-Hant.mdx b/content/docs/reference/faq.zh-Hant.mdx index cd4602f3..8be28149 100644 --- a/content/docs/reference/faq.zh-Hant.mdx +++ b/content/docs/reference/faq.zh-Hant.mdx @@ -130,12 +130,12 @@ Copilot 遵守你的共享設定 —— 如果你看不到它,它也看不到 檢查: - 磁貼的**篩選器** —— 是否過於嚴格? -- 底層的 **cube 重新整理** —— 是否逾期? +- 底層的**分析重新整理** —— 編譯後的 cube 由分析服務重新整理,HotCRM 沒有宣告任何重新整理計畫,所以磁貼不新鮮要去問你自己的部署。 - 你對來源物件的**設定檔權限**。 ### 我變更了某筆記錄上的某個欄位 —— 儀表板何時更新? -大多數磁貼使用每隔幾分鐘增量重新整理的 cube。從儀表板的 **⟳ Refresh** 按鈕強制重新整理。 +每塊儀表板按自己宣告的間隔重新查詢:Customer Service 每 **60 秒**、Sales Performance 每 **180 秒**、CRM Overview / Sales Activity / Executive Overview 每 **300 秒**(`src/dashboards/`)。底下那個 cube 多久由分析服務重新編譯一次,是部署側的問題 —— 本應用自己不組態任何重新整理計畫。HotCRM 的儀表板中繼資料也沒有宣告任何手動重新整理控制項,所以你的主控台上若有重新整理按鈕,它來自平台而不是本應用。 ### 為什麼我無法將自訂欄位加入報表? diff --git a/content/docs/reference/performance-and-limits.mdx b/content/docs/reference/performance-and-limits.mdx index 968c911e..aac94b99 100644 --- a/content/docs/reference/performance-and-limits.mdx +++ b/content/docs/reference/performance-and-limits.mdx @@ -72,7 +72,7 @@ Rate-limit responses use HTTP **429** with `Retry-After` header. Always implemen Bulk-skill invocations (e.g., qualify 500 leads) count as 500 skill calls — be mindful of quotas. -## Reporting & cubes +## Reporting & analytics | Item | Limit | | --- | --- | @@ -80,7 +80,8 @@ Bulk-skill invocations (e.g., qualify 500 leads) count as 500 skill calls — be | Report rows exported | **1,000,000** | | Dashboards per user | Unlimited | | Widgets per dashboard | **50** | -| Cube refresh frequency | Every **5 min** (incremental) / nightly (full) | +| Dashboard refresh interval | **60 s** on Customer Service, **180 s** on Sales Performance, **300 s** on the other three — declared per dashboard in `src/dashboards/` | +| Cube refresh frequency | Owned by the analytics service. HotCRM declares no refresh schedule and no incremental-versus-full policy anywhere in `src/`, so there is no app-side figure to quote — check your deployment | | Concurrent report runs (per user) | **5** | For deeper analysis, [export to a data warehouse](/docs/guides/import-and-export#scheduled-export). diff --git a/content/docs/reference/performance-and-limits.zh-Hans.mdx b/content/docs/reference/performance-and-limits.zh-Hans.mdx index d4a791f9..55d583fb 100644 --- a/content/docs/reference/performance-and-limits.zh-Hans.mdx +++ b/content/docs/reference/performance-and-limits.zh-Hans.mdx @@ -72,7 +72,7 @@ description: 记录、API 速率限制、AI 配额、文件大小 —— HotCRM 批量技能调用(例如,审定 500 条线索)计为 500 次技能调用 —— 请留意配额。 -## 报表与 cube +## 报表与分析 | 项目 | 限制 | | --- | --- | @@ -80,7 +80,8 @@ description: 记录、API 速率限制、AI 配额、文件大小 —— HotCRM | 导出的报表行数 | **1,000,000** | | 每用户的仪表盘 | 无限制 | | 每个仪表盘的小部件 | **50** | -| cube 刷新频率 | 每 **5 分钟**(增量)/ 每晚(全量) | +| 仪表盘刷新间隔 | Customer Service **60 秒**、Sales Performance **180 秒**、其余三个 **300 秒** —— 由 `src/dashboards/` 逐个仪表盘声明 | +| cube 刷新频率 | 归分析服务管。HotCRM 在 `src/` 里没有声明任何刷新计划,也没有增量与全量刷新策略,因此应用侧给不出这个数字 —— 请对着你自己的部署去核 | | 并发报表运行(每用户) | **5** | 如需更深入的分析,请[导出到数据仓库](/zh-Hans/docs/guides/import-and-export)。 diff --git a/content/docs/reference/performance-and-limits.zh-Hant.mdx b/content/docs/reference/performance-and-limits.zh-Hant.mdx index caabc910..12040444 100644 --- a/content/docs/reference/performance-and-limits.zh-Hant.mdx +++ b/content/docs/reference/performance-and-limits.zh-Hant.mdx @@ -72,7 +72,7 @@ description: 記錄、API 速率限制、AI 配額、檔案大小 —— HotCRM 批量技能呼叫(例如,審定 500 條線索)計為 500 次技能呼叫 —— 請留意配額。 -## 報表與 cube +## 報表與分析 | 項目 | 限制 | | --- | --- | @@ -80,7 +80,8 @@ description: 記錄、API 速率限制、AI 配額、檔案大小 —— HotCRM | 匯出的報表列數 | **1,000,000** | | 每使用者的儀表板 | 無限制 | | 每個儀表板的小工具 | **50** | -| cube 重新整理頻率 | 每 **5 分鐘**(增量)/ 每晚(全量) | +| 儀表板重新整理間隔 | Customer Service **60 秒**、Sales Performance **180 秒**、其餘三個 **300 秒** —— 由 `src/dashboards/` 逐個儀表板宣告 | +| cube 重新整理頻率 | 歸分析服務管。HotCRM 在 `src/` 裡沒有宣告任何重新整理計畫,也沒有增量與全量重新整理策略,因此應用側給不出這個數字 —— 請對著你自己的部署去核 | | 並行報表執行(每使用者) | **5** | 如需更深入的分析,請[匯出到資料倉儲](/zh-Hant/docs/guides/import-and-export)。 diff --git a/content/docs/sales/pipeline-management.mdx b/content/docs/sales/pipeline-management.mdx index 950d3601..d623dfeb 100644 --- a/content/docs/sales/pipeline-management.mdx +++ b/content/docs/sales/pipeline-management.mdx @@ -90,7 +90,7 @@ See [Analytics › Reports](/docs/analytics/reports) for details. The Sales Manager / Director view of the forecast is built from: -- The **Opportunity** analytic cube (measure: sum of amount, dimension: stage) +- The **Opportunity Metrics** dataset (`opportunity_metrics`) — measure **Total Amount** (sum of amount), dimension **Stage** - The **Sales Dashboard** widgets (pipeline by stage, win rate, top deals) - The **Pipeline Coverage** report (quarter × stage matrix) diff --git a/content/docs/sales/pipeline-management.zh-Hans.mdx b/content/docs/sales/pipeline-management.zh-Hans.mdx index aceb5468..1fd88a2a 100644 --- a/content/docs/sales/pipeline-management.zh-Hans.mdx +++ b/content/docs/sales/pipeline-management.zh-Hans.mdx @@ -90,7 +90,7 @@ description: 销售管道的日常运作方式——推进交易、自动概率 销售经理 / 总监的预测视图由以下部分构建: -- **商机**分析立方体(度量:金额之和,维度:阶段) +- **Opportunity Metrics** dataset(`opportunity_metrics`)—— 度量 **Total Amount**(金额之和),维度 **Stage** - **销售仪表盘**小部件(按阶段的销售管道、赢单率、顶级交易) - **销售管道覆盖率**报告(季度 × 阶段矩阵) diff --git a/content/docs/sales/pipeline-management.zh-Hant.mdx b/content/docs/sales/pipeline-management.zh-Hant.mdx index 505354e2..788c732d 100644 --- a/content/docs/sales/pipeline-management.zh-Hant.mdx +++ b/content/docs/sales/pipeline-management.zh-Hant.mdx @@ -90,7 +90,7 @@ description: 銷售管道的日常運作方式——推進交易、自動機率 銷售經理 / 總監的預測視圖由以下部分構建: -- **商機**分析立方體(度量:金額之和,維度:階段) +- **Opportunity Metrics** dataset(`opportunity_metrics`)—— 度量 **Total Amount**(金額之和),維度 **Stage** - **銷售儀表板**小工具(按階段的銷售管道、贏單率、頂級交易) - **銷售管道覆蓋率**報告(季度 × 階段矩陣) diff --git a/content/docs/whats-new.mdx b/content/docs/whats-new.mdx index 42d1ad48..8c11fb2e 100644 --- a/content/docs/whats-new.mdx +++ b/content/docs/whats-new.mdx @@ -102,7 +102,7 @@ The biggest release yet — a foundational upgrade plus a major documentation ov ### Analytics -- **Four built-in cubes**: Sales, Pipeline, Service, Marketing — for self-service analysis. +- **Nine built-in datasets**: Opportunity, Case, Lead, Account, Forecast, Activity, Task, Product and Contact Metrics — the semantic layer every dashboard tile and report binds by name. HotCRM declares no cube of its own: the analytics service compiles each dataset into one internally (ADR-0021), so there are no *Sales* / *Pipeline* / *Service* / *Marketing* cubes to open. - **Subscription emails** for dashboards and reports — get the weekly digest in your inbox. ### Documentation diff --git a/content/docs/whats-new.zh-Hans.mdx b/content/docs/whats-new.zh-Hans.mdx index b4774b20..6fc268b3 100644 --- a/content/docs/whats-new.zh-Hans.mdx +++ b/content/docs/whats-new.zh-Hans.mdx @@ -84,7 +84,7 @@ HOTCRM_TOKEN=... ./scripts/wow1-live-schema.sh ### 分析 -- **四个内置多维数据集**:销售、管道、服务、营销 —— 用于自助分析。 +- **九个内置 dataset**:Opportunity、Case、Lead、Account、Forecast、Activity、Task、Product、Contact Metrics —— 每块仪表盘磁贴和每张报表都按名字绑定的那层语义层。HotCRM 自己不声明任何 cube:由分析服务在内部把每个 dataset 编译成 cube(ADR-0021),所以并没有 *Sales* / *Pipeline* / *Service* / *Marketing* 这几个 cube 可打开。 - 仪表盘和报告的**订阅邮件** —— 在收件箱里收到每周摘要。 ### 文档 diff --git a/content/docs/whats-new.zh-Hant.mdx b/content/docs/whats-new.zh-Hant.mdx index 3d1739b1..088e0b48 100644 --- a/content/docs/whats-new.zh-Hant.mdx +++ b/content/docs/whats-new.zh-Hant.mdx @@ -84,7 +84,7 @@ HOTCRM_TOKEN=... ./scripts/wow1-live-schema.sh ### 分析 -- **四個內建多維度資料集**:銷售、管線、服務、行銷 —— 用於自助分析。 +- **九個內建 dataset**:Opportunity、Case、Lead、Account、Forecast、Activity、Task、Product、Contact Metrics —— 每塊儀表板磁貼和每張報表都按名字繫結的那層語義層。HotCRM 自己不宣告任何 cube:由分析服務在內部把每個 dataset 編譯成 cube(ADR-0021),所以並沒有 *Sales* / *Pipeline* / *Service* / *Marketing* 這幾個 cube 可打開。 - 儀表板和報告的**訂閱電子郵件** —— 在收件匣裡收到每週摘要。 ### 文件 diff --git a/test/docs-analytics-vocabulary.test.ts b/test/docs-analytics-vocabulary.test.ts new file mode 100644 index 00000000..9629b2a2 --- /dev/null +++ b/test/docs-analytics-vocabulary.test.ts @@ -0,0 +1,428 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import { describe, it, expect } from 'vitest'; +import { existsSync, readdirSync, readFileSync } from 'node:fs'; +import { join, relative } from 'node:path'; +import { REPO_ROOT } from './helpers/repo-root'; +import { CrmApp } from '../src/apps/crm.app'; +import * as dashboards from '../src/dashboards/index'; +import * as datasets from '../src/datasets/index'; +import * as reports from '../src/reports/index'; +import { OpportunitiesByStageReport } from '../src/reports/opportunity.report'; +import { pipelineByStageFunnelWidget } from '../src/dashboards/shared-widgets'; + +/** + * The analytics landing page, and the cube vocabulary around it, pinned to + * source (#976, #977). + * + * `content/docs/analytics/index.mdx` is the first screen a reader of the + * analytics docs sees, and every count on it was a count of something else. + * PR #954 / #975 had already written `analytics/reports` and `analytics/cubes` + * to source; the landing page in front of them still promised *four cubes*, + * *four dashboards*, *10+ reports including contracts*, a *Pipeline by Stage* + * report, an *Analytics* navigation group and a Copilot reading cubes directly. + * Eight further pages carried the same retired vocabulary (#977), two of them + * concretely: a `src/cubes/` directory removed in #492, and a cube refresh + * cadence nothing in `src/` configures. + * + * Nothing caught any of it: `os validate` and `pnpm lint` walk authored + * metadata and never open `content/docs`, so — as with the service-index guard + * in `docs-service-index-analytics.test.ts` (#948) and the quick-tour one in + * `docs-quick-tour-navigation.test.ts` (#960) — the check has to live where the + * claim lives. + * + * ## What is derived, and what is authored + * + * Every number and product name below is READ FROM `src/`: dashboard labels off + * the dashboard barrel, report labels off the report barrel, the dataset count + * off the dataset barrel, the navigation group and its children off + * `CrmApp.navigation`, the refresh cadence off each dashboard's + * `refreshInterval`. Change the app and this file goes red at PR time, in all + * three locales — the failure mode the landing page had for months. + * + * The authored parts are the count WORDS (the pages spell them, and each is + * asserted against its derived figure in the last describe) and {@link PHANTOMS} + * — names the old page carried that resolve to nothing in the app. The + * typography convention #927 / PR #932 established carries the distinction, and + * the quick-tour guard leans on the same one: **bold** is reserved for names the + * app really has, *italic* for a name a reader arrives with that the product + * does not carry. A phantom must still be NAMED — say where the thing really + * lives, do not delete it silently — and must never be bolded. + * + * ## Why the nav rules are scoped to one section + * + * *Dashboards*, *Reports* and *Cubes* are phantoms only as NAVIGATION items: + * nothing in the sidebar carries those labels. They are perfectly real as the + * three docs pages this page links to, and the "What's included" table bolds + * them in exactly that sense. So the bold/italic rules run over the "Where to + * find things" block alone, the way `docs-quick-tour-navigation.test.ts` scopes + * its rules to the left-nav block, rather than over the whole page. + * + * ## Reverse verification (#976) + * + * Predicted direction: **red before this PR's content fix, green after**, and + * measured that way — on the pre-fix page every locale failed at least four + * rules: the dashboard count (4 against the barrel's 5), the report count + * (`10+` plus a contract claim against the barrel's 10 with no contract + * report), the nav block (which named an *Analytics* group and bolded + * *Dashboards* / *Reports* / *Cubes* as if they were sidebar items) and the + * Insights-children rule (that group was absent entirely). No rule here can + * pass by asserting nothing: each documented count is paired with the derived + * figure it came from, and both tree walks carry a vacuity guard. + */ + +type AnyRec = Record; + +const DOCS_ROOT = join(REPO_ROOT, 'content/docs'); + +/* ---------------------------------------------------------------- source */ + +const DASHBOARD_LABELS: string[] = Object.values(dashboards as Record).map( + (d) => d.label as string, +); +const REPORT_LABELS: string[] = Object.values(reports as Record).map( + (r) => r.label as string, +); +const DATASET_LIST = Object.values(datasets as Record); + +const NAV = ((CrmApp as AnyRec).navigation ?? []) as AnyRec[]; +const GROUPS = NAV.filter((n) => n.type === 'group'); +const INSIGHTS = GROUPS.find((g) => g.label === 'Insights') as AnyRec | undefined; +const INSIGHTS_CHILDREN: string[] = ((INSIGHTS?.children ?? []) as AnyRec[]).map( + (c) => c.label as string, +); + +/** Every navigation label in the app, at any depth. */ +const ALL_NAV_LABELS: string[] = (() => { + const walk = (nodes: AnyRec[]): AnyRec[] => + nodes.flatMap((n) => [n, ...walk((n.children ?? []) as AnyRec[])]); + return walk(NAV).map((n) => n.label as string); +})(); + +/** Distinct `refreshInterval` values declared across the dashboards, in seconds. */ +const REFRESH_INTERVALS: number[] = [ + ...new Set( + Object.values(dashboards as Record) + .map((d) => d.refreshInterval as number | undefined) + .filter((n): n is number => typeof n === 'number'), + ), +].sort((a, b) => a - b); + +/* ----------------------------------------------------------------- pages */ + +const PAGES = [ + { + file: 'content/docs/analytics/index.mdx', + five: 'Five', + ten: 'Ten', + nine: 'Nine', + noContract: 'There is no contract report', + navHeading: '## Where to find things', + }, + { + file: 'content/docs/analytics/index.zh-Hans.mdx', + five: '五个', + ten: '十份', + nine: '九个', + noContract: '这里没有合同报表', + navHeading: '## 在哪里可以找到', + }, + { + file: 'content/docs/analytics/index.zh-Hant.mdx', + five: '五個', + ten: '十份', + nine: '九個', + noContract: '這裡沒有合約報表', + navHeading: '## 在哪裡可以找到', + }, +] as const; + +/** Names the old nav section carried that resolve to no navigation label. */ +const PHANTOMS = ['Analytics', 'Dashboards', 'Reports', 'Cubes'] as const; + +/** The four cube names the page advertised. None exists anywhere in the app. */ +const RETIRED_CUBES = ['Sales', 'Pipeline', 'Service', 'Marketing'] as const; + +/** + * The CJK range as escapes rather than literal characters, so this file stays + * greppable in a repo whose tooling scans it as text — the same reason + * `docs-quick-tour-navigation.test.ts` writes it this way. + */ +const CJK = /[\u3400-\u9fff]/; + +const read = (file: string): string => readFileSync(join(REPO_ROOT, file), 'utf8'); + +/** The block from `heading` up to the next `## `. */ +const blockOf = (text: string, heading: string): string => { + const lines = text.split('\n'); + const from = lines.findIndex((l) => l.startsWith(heading)); + expect(from, `block start '${heading}' not found`).toBeGreaterThanOrEqual(0); + const rest = lines.slice(from); + const end = rest.findIndex((l, i) => i > 0 && l.startsWith('## ')); + return (end === -1 ? rest : rest.slice(0, end)).join('\n'); +}; + +const boldNames = (text: string): string[] => + [...text.matchAll(/\*\*([^*\n]+)\*\*/g)].map((m) => m[1].trim()).filter((s) => !CJK.test(s)); + +const italicNames = (text: string): string[] => + [...text.replace(/\*\*[^*\n]+\*\*/g, '').matchAll(/\*([^*\n]+)\*/g)].map((m) => m[1].trim()); + +describe('analytics/index states the counts and names the app really ships (#976)', () => { + describe.each(PAGES)('$file', ({ file, five, ten, nine, noContract, navHeading }) => { + it('names all five dashboards, and says five', () => { + const text = read(file); + expect(text, `${file}: the dashboard count is not stated as "${five}"`).toContain(five); + const missing = DASHBOARD_LABELS.filter((l) => !text.includes(l)); + expect( + missing, + `${file}: dashboard(s) the app ships but the page does not name: ${missing.join(', ')}. ` + + 'Sales Activity is the one the old "4 ready-made dashboards" row dropped.', + ).toEqual([]); + }); + + it('says ten reports, and says a contract report does not exist', () => { + const text = read(file); + expect(text, `${file}: the report count is not stated as "${ten}"`).toContain(ten); + expect(text, `${file}: "10+" is back — the library is exactly ten`).not.toMatch(/10\s*\+/); + expect( + text, + `${file}: the page must say a contract report does not exist — no dataset reads crm_contract`, + ).toContain(noContract); + }); + + it('says nine datasets and points at the directory they live in', () => { + const text = read(file); + expect(text, `${file}: the dataset count is not stated as "${nine}"`).toContain(nine); + expect(text, `${file}: the page does not name src/datasets/`).toContain('src/datasets/'); + }); + + it('names the Insights group and every child the app pins to it', () => { + const block = blockOf(read(file), navHeading); + expect(block, `${file}: the real navigation group is not named`).toContain('**Insights**'); + const missing = INSIGHTS_CHILDREN.filter((l) => !block.includes(`**${l}**`)); + expect( + missing, + `${file}: Insights child/children not named in bold: ${missing.join(', ')}`, + ).toEqual([]); + }); + + it('bolds only real navigation labels in the nav section', () => { + const unknown = boldNames(blockOf(read(file), navHeading)).filter( + (n) => !ALL_NAV_LABELS.includes(n), + ); + expect( + unknown, + `${file}: bolded name(s) in the navigation section that no nav item carries: ` + + `${unknown.join(', ')}. Bold is reserved for real names here — a name the sidebar does ` + + 'not carry goes in *italics*.', + ).toEqual([]); + }); + + it('still names every retired nav name, in italics, and never in bold', () => { + const block = blockOf(read(file), navHeading); + const italics = italicNames(block); + const bold = boldNames(block); + + const unnamed = PHANTOMS.filter((n) => !italics.includes(n)); + expect( + unnamed, + `${file}: a name the old section carried was dropped instead of re-pointed: ` + + `${unnamed.join(', ')}. Readers arrive with these names.`, + ).toEqual([]); + + const promoted = PHANTOMS.filter((n) => bold.includes(n)); + expect( + promoted, + `${file}: nav name(s) the app does not carry, written in bold: ${promoted.join(', ')}`, + ).toEqual([]); + }); + + it('names the real report behind the funnel everyone calls Pipeline by Stage', () => { + const text = read(file); + expect( + text, + `${file}: the report a rep actually runs is ${OpportunitiesByStageReport.label}`, + ).toContain(`**${OpportunitiesByStageReport.label}**`); + expect(italicNames(text), `${file}: the tile/chart title must be named as a non-report`) + .toContain('Pipeline by Stage'); + expect( + boldNames(text), + `${file}: Pipeline by Stage is bolded as if it were a report — it is a tile title and a ` + + 'chart title, and no report answers to it', + ).not.toContain('Pipeline by Stage'); + }); + + it('names the four retired cube names in italics', () => { + const italics = italicNames(read(file)); + const unnamed = RETIRED_CUBES.filter((n) => !italics.includes(n)); + expect( + unnamed, + `${file}: retired cube name(s) not named as retired: ${unnamed.join(', ')}`, + ).toEqual([]); + }); + + it('links only to pages that exist', () => { + const targets = [ + ...read(file).matchAll(/\]\((\/(?:zh-Han[st]\/)?docs\/[^)#\s]+)\)/g), + ].map((m) => m[1]); + expect( + targets.length, + `${file}: no internal doc links found — this rule would be vacuous`, + ).toBeGreaterThan(3); + const dangling = targets.filter((t) => { + const slug = t.replace(/^\/(?:zh-Han[st]\/)?docs\//, '').replace(/\/$/, ''); + const locale = t.startsWith('/zh-Hans/') + ? '.zh-Hans' + : t.startsWith('/zh-Hant/') + ? '.zh-Hant' + : ''; + return ( + !existsSync(join(DOCS_ROOT, `${slug}${locale}.mdx`)) && + !existsSync(join(DOCS_ROOT, slug, `index${locale}.mdx`)) + ); + }); + expect( + dangling, + `${file}: link(s) to a page that does not exist: ${dangling.join(', ')}`, + ).toEqual([]); + }); + }); +}); + +describe('the cube vocabulary retired with src/cubes/ is gone from the docs (#977)', () => { + const walk = (dir: string): string[] => + readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { + const full = join(dir, entry.name); + return entry.isDirectory() ? walk(full) : full.endsWith('.mdx') ? [full] : []; + }); + + const ALL_PAGES = walk(DOCS_ROOT); + + it('has docs pages to check', () => { + // A sweep over an empty tree passes by checking nothing — the same vacuity + // guard `source-hygiene-scan-surface.test.ts` and the conversion-rate check + // put in front of their own walks. + expect(ALL_PAGES.length).toBeGreaterThan(100); + }); + + it('no page draws a `cubes/` directory (#492 removed it)', () => { + const offenders = ALL_PAGES.filter((f) => /(?:^|[\s,`])(?:src\/)?cubes\//m.test(readFileSync(f, 'utf8'))) + .map((f) => relative(REPO_ROOT, f)); + expect( + offenders, + `page(s) drawing a directory that does not exist: ${offenders.join(', ')}. ` + + 'The semantic layer lives in src/datasets/ (ADR-0021) — a path in prose is still a promise.', + ).toEqual([]); + }); + + it('the pages that quote a refresh cadence quote the declared one', () => { + // The retired claim was "incremental refresh every 5 min / every few + // minutes", a figure nothing in `src/` configures. What IS declared is each + // dashboard's own `refreshInterval` — derived here rather than transcribed, + // so a change in source lands on the docs at PR time. + expect( + REFRESH_INTERVALS.length, + 'no dashboard declares a refreshInterval — this rule has gone vacuous', + ).toBeGreaterThan(1); + const carriers = [ + 'content/docs/reference/faq.mdx', + 'content/docs/reference/faq.zh-Hans.mdx', + 'content/docs/reference/faq.zh-Hant.mdx', + 'content/docs/reference/performance-and-limits.mdx', + 'content/docs/reference/performance-and-limits.zh-Hans.mdx', + 'content/docs/reference/performance-and-limits.zh-Hant.mdx', + ]; + const missing = carriers.flatMap((f) => { + const text = read(f); + return REFRESH_INTERVALS.filter((s) => !new RegExp(`\\b${s}\\b`).test(text)).map( + (s) => `${f}: does not state the declared ${s}s dashboard refresh interval`, + ); + }); + expect(missing, missing.join('\n')).toEqual([]); + }); + + it('the FAQ claims no manual refresh control the metadata does not declare', () => { + // U+27F3, written as an escape rather than pasted, for the same + // greppability reason as CJK above. + const REFRESH_GLYPH = /\u27f3/; + for (const f of [ + 'content/docs/reference/faq.mdx', + 'content/docs/reference/faq.zh-Hans.mdx', + 'content/docs/reference/faq.zh-Hant.mdx', + ]) { + expect( + REFRESH_GLYPH.test(read(f)), + `${f}: names a refresh button by its glyph. No dashboard in src/dashboards/ declares a ` + + 'manual refresh control — state what the app declares (refreshInterval) and leave the ' + + "console's own chrome to the platform.", + ).toBe(false); + } + }); + + it('the glossary keeps its definition of the cube concept', () => { + // #977 called this out explicitly: the concept is real and its entry is + // correct, so a vocabulary sweep must not take it out with the rest. + for (const f of [ + 'content/docs/reference/glossary.mdx', + 'content/docs/reference/glossary.zh-Hans.mdx', + 'content/docs/reference/glossary.zh-Hant.mdx', + ]) { + expect( + read(f), + `${f}: the Cube glossary entry was swept away with the retired usages`, + ).toMatch(/\*\*Cube\*\*/); + } + }); +}); + +describe('the source facts these pages now rest on (#976, #977)', () => { + it('the app ships five dashboards, one of them Sales Activity', () => { + expect(DASHBOARD_LABELS).toHaveLength(5); + expect(DASHBOARD_LABELS).toContain('Sales Activity'); + }); + + it('the report library is exactly ten, and none of them is a contract report', () => { + expect(REPORT_LABELS).toHaveLength(10); + expect(REPORT_LABELS.filter((l) => /contract/i.test(l))).toEqual([]); + }); + + it('nine datasets, and none of them reads crm_contract', () => { + expect(DATASET_LIST).toHaveLength(9); + expect(DATASET_LIST.map((d) => d.object as string)).not.toContain('crm_contract'); + }); + + it('the navigation group is Insights, and no group is called Analytics', () => { + const labels = GROUPS.map((g) => g.label as string); + expect(labels).toContain('Insights'); + expect(labels).not.toContain('Analytics'); + expect(INSIGHTS_CHILDREN).toEqual([ + 'CRM Overview', + 'Forecasts', + 'Pipeline Coverage', + 'Lead Inflow', + 'SLA Performance', + ]); + }); + + it('carries no navigation label matching any name the page calls a phantom', () => { + expect(PHANTOMS.filter((n) => ALL_NAV_LABELS.includes(n))).toEqual([]); + }); + + it('Pipeline by Stage is a tile title and a chart title, and no report answers to it', () => { + const title = pipelineByStageFunnelWidget({ x: 0, y: 0, w: 6, h: 4 }).title; + expect(title).toBe('Pipeline by Stage'); + expect(OpportunitiesByStageReport.chart?.title).toBe(title); + expect(REPORT_LABELS).not.toContain(title); + }); + + it('the sales-skills page the landing page links to exists', () => { + // #976 reported this link as dangling. It is not: #589 / PR #611 renamed + // the LINK TEXT (Sales Copilot -> Sales Skills) and the target page has + // been there throughout. Pinned so a future page move turns this red + // instead of quietly re-creating the link the issue expected to find. + for (const suffix of ['', '.zh-Hans', '.zh-Hant']) { + expect(existsSync(join(DOCS_ROOT, `ai-copilot/sales-copilot${suffix}.mdx`))).toBe(true); + } + }); +}); From 968338cedca8cd3558e731a4e09ea7408c7a3182 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 6 Aug 2026 14:43:25 +0000 Subject: [PATCH 2/2] docs(analytics): name the forecast figures by their record and dataset labels The landing page's Copilot section cited Commit / Quota / Closed / Pipeline as figures on the forecast record. Three of the four are: closed_amount is labelled **Closed Won** on crm_forecast and only becomes **Closed** as a measure on the Forecast Metrics dataset. Say both, so the name a reader sees on the record and the name they select in a report each appear where they belong. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa --- content/docs/analytics/index.mdx | 2 +- content/docs/analytics/index.zh-Hans.mdx | 2 +- content/docs/analytics/index.zh-Hant.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/analytics/index.mdx b/content/docs/analytics/index.mdx index e480171f..9fb53890 100644 --- a/content/docs/analytics/index.mdx +++ b/content/docs/analytics/index.mdx @@ -57,7 +57,7 @@ There is no *Analytics* group, and nothing in the sidebar is called *Dashboards* **On this app's side, nothing connects the Copilot to a dataset.** The six skills under `src/skills/` name no dataset, no cube and no measure. The one that answers data questions, **Live Data Access**, is wired to the platform's object tools and is instructed to re-read the object's schema and aggregate over records — so a Copilot answer is computed from the objects, not read off a compiled cube. -That also sets what it can be asked. *"What's driving the gap between commit and forecast?"* has data behind it: **Commit**, **Quota**, **Closed** and **Pipeline** are all real figures on the forecast record. *"Why did East close 3 fewer deals than West last quarter?"* and *"Show me which products correlate with longer sales cycles"* do not — no region is stored on a deal, no dataset reads line items, and the one duration the opportunity carries (**Days in Current Stage**) is a formula evaluated after the query, so nothing can aggregate it. [Cubes](/docs/analytics/cubes) lists the gaps one by one. +That also sets what it can be asked. *"What's driving the gap between commit and forecast?"* has data behind it: **Commit**, **Quota**, **Closed Won** and **Pipeline** are all real figures on the forecast record, published as **Commit**, **Quota**, **Closed** and **Pipeline** on the **Forecast Metrics** dataset. *"Why did East close 3 fewer deals than West last quarter?"* and *"Show me which products correlate with longer sales cycles"* do not — no region is stored on a deal, no dataset reads line items, and the one duration the opportunity carries (**Days in Current Stage**) is a formula evaluated after the query, so nothing can aggregate it. [Cubes](/docs/analytics/cubes) lists the gaps one by one. Whether the platform's own agent additionally reads the compiled cubes is a question about the runtime rather than about this app, so this page makes no claim about it in either direction; check it against your deployment. See [Sales Skills](/docs/ai-copilot/sales-copilot) for what the sales-side skills do. diff --git a/content/docs/analytics/index.zh-Hans.mdx b/content/docs/analytics/index.zh-Hans.mdx index d0b794d8..543c3029 100644 --- a/content/docs/analytics/index.zh-Hans.mdx +++ b/content/docs/analytics/index.zh-Hans.mdx @@ -57,7 +57,7 @@ HotCRM 分析将你的 CRM 活动转化为**决策**。三个层次协同工作 **在本应用这一侧,没有任何东西把 Copilot 连到某个 dataset 上。** `src/skills/` 下的六个技能,没有一个点名任何 dataset、cube 或度量。其中负责回答数据问题的那个 —— **Live Data Access** —— 接的是平台的对象工具,而它的指令要求先重新读一遍对象的 schema,再在记录上做聚合。所以 Copilot 的回答是从对象上算出来的,而不是从编译好的 cube 上读出来的。 -这也决定了能问它什么。*"是什么造成了承诺额与预测额之间的差距?"* 背后是有数据的:**Commit**、**Quota**、**Closed**、**Pipeline** 都是预测记录上真实的数字。*"为什么上季度东区比西区少成交了 3 笔交易?"* 与 *"告诉我哪些产品与更长的销售周期相关。"* 则没有 —— 交易上不存区域,没有任何 dataset 读行项目,而商机唯一带着的那个时长(**Days in Current Stage**)是查询之后才算的公式,什么都聚合不了。这些缺口逐条列在 [多维数据集](/zh-Hans/docs/analytics/cubes)。 +这也决定了能问它什么。*"是什么造成了承诺额与预测额之间的差距?"* 背后是有数据的:预测记录上真实带着 **Commit**、**Quota**、**Closed Won**、**Pipeline** 四个数字,在 **Forecast Metrics** dataset 上发布为 **Commit**、**Quota**、**Closed**、**Pipeline** 四个度量。*"为什么上季度东区比西区少成交了 3 笔交易?"* 与 *"告诉我哪些产品与更长的销售周期相关。"* 则没有 —— 交易上不存区域,没有任何 dataset 读行项目,而商机唯一带着的那个时长(**Days in Current Stage**)是查询之后才算的公式,什么都聚合不了。这些缺口逐条列在 [多维数据集](/zh-Hans/docs/analytics/cubes)。 平台自带的 agent 是否另外也读编译后的 cube,是关于运行时而不是关于本应用的问题 —— 所以本页对它不做任何方向上的断言,请对着你自己的部署去核。销售侧的技能各自做什么,见 [销售技能](/zh-Hans/docs/ai-copilot/sales-copilot)。 diff --git a/content/docs/analytics/index.zh-Hant.mdx b/content/docs/analytics/index.zh-Hant.mdx index 7e0207e7..9c0f2091 100644 --- a/content/docs/analytics/index.zh-Hant.mdx +++ b/content/docs/analytics/index.zh-Hant.mdx @@ -57,7 +57,7 @@ HotCRM 分析將你的 CRM 活動轉化為**決策**。三個層次協同運作 **在本應用這一側,沒有任何東西把 Copilot 連到某個 dataset 上。** `src/skills/` 下的六個技能,沒有一個點名任何 dataset、cube 或度量。其中負責回答資料問題的那個 —— **Live Data Access** —— 接的是平台的物件工具,而它的指令要求先重新讀一遍物件的 schema,再在記錄上做彙總。所以 Copilot 的回答是從物件上算出來的,而不是從編譯好的 cube 上讀出來的。 -這也決定了能問它什麼。*"是什麼造成了承諾額與預測額之間的差距?"* 背後是有資料的:**Commit**、**Quota**、**Closed**、**Pipeline** 都是預測記錄上真實的數字。*"為什麼上一季東區比西區少成交了 3 筆交易?"* 與 *"告訴我哪些產品與更長的銷售週期相關。"* 則沒有 —— 交易上不存區域,沒有任何 dataset 讀行項目,而商機唯一帶著的那個時長(**Days in Current Stage**)是查詢之後才算的公式,什麼都彙總不了。這些缺口逐條列在 [多維資料集](/zh-Hant/docs/analytics/cubes)。 +這也決定了能問它什麼。*"是什麼造成了承諾額與預測額之間的差距?"* 背後是有資料的:預測記錄上真實帶著 **Commit**、**Quota**、**Closed Won**、**Pipeline** 四個數字,在 **Forecast Metrics** dataset 上發布為 **Commit**、**Quota**、**Closed**、**Pipeline** 四個度量。*"為什麼上一季東區比西區少成交了 3 筆交易?"* 與 *"告訴我哪些產品與更長的銷售週期相關。"* 則沒有 —— 交易上不存區域,沒有任何 dataset 讀行項目,而商機唯一帶著的那個時長(**Days in Current Stage**)是查詢之後才算的公式,什麼都彙總不了。這些缺口逐條列在 [多維資料集](/zh-Hant/docs/analytics/cubes)。 平台自帶的 agent 是否另外也讀編譯後的 cube,是關於執行時而不是關於本應用的問題 —— 所以本頁對它不做任何方向上的斷言,請對著你自己的部署去核。銷售側的技能各自做什麼,見 [銷售技能](/zh-Hant/docs/ai-copilot/sales-copilot)。