From 341b0d86194f2e970ebaf57ff5877002ac468e05 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 14:34:21 +0000 Subject: [PATCH 1/2] feat(views): tint only the exception rows in the four lists that keep a tint Nine list views gave every value of a field a row colour, and each of those fields is already a column that draws its own coloured badge, so a page of colour marked no row. The four lists that keep a tint now colour only the row a user must act on: - Cases (All Cases, Unassigned - triage): is_sla_violated true -> red - Tasks: priority urgent -> red - Event attendees: response declined -> red - Accounts: is_active false -> slate Open Deals, Events, Knowledge Articles and Leads' High Priority carry no rowColor any more. The stage row-colour pins in view-references.test.ts guarded the retired Open Deals map and go with it; the generic "rowColor keys are real option values" guard stays and still reads the Task and Attendee keys. Claude-Session: https://claude.ai/code/session_01P8mZuzuyZAHSrgQKmv6ASv Co-authored-by: Claude --- src/sales/views/account.view.ts | 4 +- src/sales/views/event.view.ts | 5 -- src/sales/views/event_attendee.view.ts | 8 +-- src/sales/views/lead.view.ts | 11 +--- src/sales/views/opportunity.view.ts | 22 ------- src/sales/views/task.view.ts | 11 ++-- src/service/views/case.view.ts | 14 ++--- src/service/views/knowledge_article.view.ts | 4 -- test/view-references.test.ts | 64 +++------------------ 9 files changed, 26 insertions(+), 117 deletions(-) diff --git a/src/sales/views/account.view.ts b/src/sales/views/account.view.ts index 1b611e37..15b9fd70 100644 --- a/src/sales/views/account.view.ts +++ b/src/sales/views/account.view.ts @@ -25,7 +25,9 @@ export const AccountViews = defineView({ { field: 'health_score', width: 140 }, ], sort: [{ field: 'annual_revenue', order: 'desc' }], - rowColor: { field: 'is_active', colors: { true: 'green', false: 'slate' } }, + // Only the exception row is tinted: an inactive account greys out, an + // active one stays plain. + rowColor: { field: 'is_active', colors: { false: 'slate' } }, selection: { type: 'multiple' }, pagination: { pageSize: 50, pageSizeOptions: [25, 50, 100] }, exportOptions: { formats: ['csv', 'xlsx'] }, diff --git a/src/sales/views/event.view.ts b/src/sales/views/event.view.ts index 67d2e5e3..5f988daa 100644 --- a/src/sales/views/event.view.ts +++ b/src/sales/views/event.view.ts @@ -39,11 +39,6 @@ export const EventViews = defineView({ { field: 'owner_id', width: 150 }, ], sort: [{ field: 'start_datetime', order: 'desc' }], - rowColor: { - // Mirrors the option colors on crm_event.status. - field: 'status', - colors: { planned: 'blue', held: 'green', cancelled: 'slate', no_show: 'orange' }, - }, selection: { type: 'multiple' }, pagination: { pageSize: 50 }, appearance: { diff --git a/src/sales/views/event_attendee.view.ts b/src/sales/views/event_attendee.view.ts index a6dc429d..82acf0c8 100644 --- a/src/sales/views/event_attendee.view.ts +++ b/src/sales/views/event_attendee.view.ts @@ -67,11 +67,9 @@ export const EventAttendeeViews = defineView({ { field: 'is_organizer', width: 100, align: 'center' }, ], sort: [{ field: 'is_organizer', order: 'desc' }], - rowColor: { - // Mirrors the option colors on crm_event_attendee.response. - field: 'response', - colors: { accepted: 'green', declined: 'red', tentative: 'orange', no_response: 'slate' }, - }, + // Only the exception row is tinted: a declined invitation turns red. The + // response column already draws each value's badge. + rowColor: { field: 'response', colors: { declined: 'red' } }, pagination: { pageSize: 25 }, }, diff --git a/src/sales/views/lead.view.ts b/src/sales/views/lead.view.ts index 989120df..f3f6859f 100644 --- a/src/sales/views/lead.view.ts +++ b/src/sales/views/lead.view.ts @@ -441,8 +441,8 @@ export const LeadViews = defineView({ * rating >= 4, still New or Contacted. What differs is what they are FOR. * Hot Leads is the work order — sorted by next-follow-up (the SLA that * routing just stamped), with phone, email and owner on the row so it can - * be dialled top to bottom. High Priority is the scan list — score-tinted - * rows and lead source, no SLA column, no time ordering. A 5-star-only + * be dialled top to bottom. High Priority is the scan list — rating and + * lead source on the row, no SLA column, no time ordering. A 5-star-only * queue, if it is ever a real need, gets its own view under its own name * instead of squatting on "Hot". * @@ -483,13 +483,6 @@ export const LeadViews = defineView({ { field: 'rating', operator: 'greater_than_or_equal', value: 4 }, { field: 'status', operator: 'in', value: ['new', 'contacted'] }, ], - rowColor: { - field: 'rating', - colors: { - '5': 'green', - '4': 'orange', - }, - }, }, /** diff --git a/src/sales/views/opportunity.view.ts b/src/sales/views/opportunity.view.ts index 15661bbb..b80248ab 100644 --- a/src/sales/views/opportunity.view.ts +++ b/src/sales/views/opportunity.view.ts @@ -39,28 +39,6 @@ export const OpportunityViews = defineView({ { field: 'close_date', width: 140, sortable: true }, ], sort: [{ field: 'close_date', order: 'asc' }], - rowColor: { - field: 'stage', - colors: { - prospecting: 'slate', - qualification: 'sky', - // Teal sits between the cool qualification blue and the warm proposal - // amber, keeping the cool→warm funnel ramp readable. NOT the gold - // `#FFD700` this option carries in `_picklists.ts`: gold is one hue - // step from proposal's amber, so the two adjacent stages would tint - // rows the same. - // - // Values are colour NAMES, not hex: objectui's `useRowColor` resolves - // only a name from its own vocabulary (or a full `bg-*` class) and - // turns a hex into no colour at all — so until 17.6.0's lint said so, - // this map tinted nothing. - needs_analysis: 'teal', - proposal: 'amber', - negotiation: 'purple', - closed_won: 'green', - closed_lost: 'red', - }, - }, pagination: { pageSize: 25, pageSizeOptions: [25, 50, 100] }, selection: { type: 'multiple' }, showRecordCount: true, diff --git a/src/sales/views/task.view.ts b/src/sales/views/task.view.ts index 8c7a0caa..6233b9c4 100644 --- a/src/sales/views/task.view.ts +++ b/src/sales/views/task.view.ts @@ -66,13 +66,10 @@ export const TaskViews = defineView({ { field: 'is_completed', order: 'asc' }, { field: 'due_date', order: 'asc' }, ], - rowColor: { - // Task priority is low/normal/high/urgent — the previous critical/medium - // keys were Case values, so urgent and normal rows got no color at all. - // Colors mirror the option colors on crm_task.priority. - field: 'priority', - colors: { urgent: 'red', high: 'orange', normal: 'green', low: 'slate' }, - }, + // Only the exception row is tinted: an urgent task turns red. The priority + // column already draws each value's badge, so a colour per value marked + // every row and flagged none. + rowColor: { field: 'priority', colors: { urgent: 'red' } }, selection: { type: 'multiple' }, pagination: { pageSize: 50 }, appearance: { diff --git a/src/service/views/case.view.ts b/src/service/views/case.view.ts index 2978409b..507f9471 100644 --- a/src/service/views/case.view.ts +++ b/src/service/views/case.view.ts @@ -45,10 +45,10 @@ export const CaseViews = defineView({ { field: 'priority_rank', order: 'desc' }, { field: 'sla_due_date', order: 'asc' }, ], - rowColor: { - field: 'priority', - colors: { critical: 'red', high: 'orange', medium: 'yellow', low: 'slate' }, - }, + // Only the exception row is tinted: a case whose SLA is breached turns + // red and every other row stays plain. A colour per priority painted every + // row the priority column's own badge already marks, so it flagged none. + rowColor: { field: 'is_sla_violated', colors: { true: 'red' } }, selection: { type: 'multiple' }, pagination: { pageSize: 50 }, // The UI door for the `allowExport` grant four profiles already hold on @@ -238,10 +238,8 @@ export const CaseViews = defineView({ { field: 'priority_rank', order: 'desc' }, { field: 'sla_due_date', order: 'asc' }, ], - rowColor: { - field: 'priority', - colors: { critical: 'red', high: 'orange', medium: 'yellow', low: 'slate' }, - }, + // Same exception tint as the main queue: a breached case turns red. + rowColor: { field: 'is_sla_violated', colors: { true: 'red' } }, // The empty state carries the operational instruction, because "no rows" // here is ambiguous on its own: it means either "the round-robin placed // everything" (good) or "no case has arrived yet" (also fine) — and the diff --git a/src/service/views/knowledge_article.view.ts b/src/service/views/knowledge_article.view.ts index 34c8d465..30a4f8b4 100644 --- a/src/service/views/knowledge_article.view.ts +++ b/src/service/views/knowledge_article.view.ts @@ -42,10 +42,6 @@ export const KnowledgeArticleViews = defineView({ grouping: { fields: [{ field: 'category', order: 'asc' }] }, selection: { type: 'multiple' }, pagination: { pageSize: 50 }, - rowColor: { - field: 'status', - colors: { draft: 'slate', in_review: 'amber', published: 'emerald', archived: 'gray' }, - }, appearance: { showDescription: true, allowedVisualizations: ['grid', 'kanban'], diff --git a/test/view-references.test.ts b/test/view-references.test.ts index 2c74caf1..86bcf8f6 100644 --- a/test/view-references.test.ts +++ b/test/view-references.test.ts @@ -249,12 +249,12 @@ describe('row colors and kanban groups key off real option values', () => { * The two guards above answer "is every value written here a real option?" — * a subset check. Nothing asked the converse, "is every real option written * here?", and that is the half that broke (#759): `needs_analysis` is one of - * the seven canonical `OPPORTUNITY_STAGE_OPTIONS` and was absent from BOTH the - * detail page's stage path and the Open Deals `rowColor` map. Six of seven - * stages worked, so both surfaces looked fine on every deal that happened not - * to be in Needs Analysis — the deal that WAS lit up no step on the path and - * got no row tint, which reads to a user as corrupted data rather than as - * missing metadata. + * the seven canonical `OPPORTUNITY_STAGE_OPTIONS` and was absent from the + * detail page's stage path. Six of seven stages worked, so the path looked + * fine on every deal that happened not to be in Needs Analysis — the deal that + * WAS lit up no step, which reads to a user as corrupted data rather than as + * missing metadata. (Open Deals no longer tints rows by stage, #2107, so the + * path is the one enumeration left to guard.) * * The expectation is derived from `OPPORTUNITY_STAGE_OPTIONS` itself — the * single source `crm_opportunity.stage` and the `mass_update_stage` action are @@ -278,24 +278,11 @@ describe('every canonical opportunity stage reaches the UI that enumerates stage ]), ); - /** Every list `rowColor` keyed on `crm_opportunity.stage`, as [viewName, colors]. */ - const stageRowColors = (): [string, AnyRec][] => - views - .filter((v) => (v.list?.data?.object ?? v.object) === 'crm_opportunity') - .flatMap((v) => - ([v.list, ...Object.values(v.listViews ?? {})].filter(Boolean) as AnyRec[]) - .filter((list) => list.rowColor?.field === 'stage') - .map((list): [string, AnyRec] => [ - `view "${list.name ?? 'default'}"`, - list.rowColor?.colors ?? {}, - ]), - ); - it('the canonical stage list is the set the object validates against', () => { // Guards the guard twice over: an empty or renamed constant would make // every assertion below pass by comparing against nothing, and a `stage` - // field that stopped being built from this constant would leave the two - // sites below chasing a list the runtime no longer enforces. + // field that stopped being built from this constant would leave the path + // guard below chasing a list the runtime no longer enforces. expect(canonical.length, 'OPPORTUNITY_STAGE_OPTIONS is empty').toBeGreaterThan(0); const objDef = objects.find((o) => o.name === 'crm_opportunity'); const fieldValues = (objDef?.fields?.stage?.options ?? []).map((o: AnyRec) => String(o.value)); @@ -321,41 +308,6 @@ describe('every canonical opportunity stage reaches the UI that enumerates stage } expect(bad, `incomplete stage paths:\n ${bad.join('\n ')}`).toEqual([]); }); - - it('every stage has a row colour in the opportunity list views that tint by stage', () => { - const sites = stageRowColors(); - expect(sites.length, 'no rowColor keyed on crm_opportunity.stage was found').toBeGreaterThan(0); - - const bad: string[] = []; - for (const [site, colors] of sites) { - const keys = Object.keys(colors); - for (const stage of canonical) { - if (!keys.includes(stage)) bad.push(`${site}: no rowColor entry for stage "${stage}"`); - } - for (const key of keys) { - if (!canonical.includes(key)) bad.push(`${site}: rowColor key "${key}" is not a canonical stage`); - } - } - expect(bad, `incomplete stage row colours:\n ${bad.join('\n ')}`).toEqual([]); - }); - - it('stage colours stay distinguishable from one another', () => { - // The point of the map is that a rep can tell two stages apart at a - // glance; two stages sharing a hex make the tint say nothing. (#759 was - // nearly fixed by copying `needs_analysis`'s `#FFD700` out of - // _picklists.ts, one hue step from proposal's `#f59e0b`.) - const bad: string[] = []; - for (const [site, colors] of stageRowColors()) { - const seen = new Map(); - for (const key of Object.keys(colors)) { - const hex = String(colors[key]).toLowerCase(); - const owner = seen.get(hex); - if (owner) bad.push(`${site}: "${key}" and "${owner}" are both ${hex}`); - else seen.set(hex, key); - } - } - expect(bad, `stages sharing a row colour:\n ${bad.join('\n ')}`).toEqual([]); - }); }); /** From d9a5739c8bd962e6d7f206740986929bba7e8ba3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 14:42:51 +0000 Subject: [PATCH 2/2] docs: describe the exception-only row tints; note the two that do not render Every doc page that described a list's row tint, in all three locales, now says what the lists do: a breached case is the red row in All Cases (and stays red once closed late), an urgent task is the red row, and Open Deals and High Priority leads carry no tint. The priority hex list on the SLA page is gone. The All Accounts and Unassigned - triage tints are declared but paint nothing on 18.0.0-next.1: the console's list fetch selects only the columns, and neither list has a column for its tint's field. A comment beside each says so, and the changeset tells the release-notes reader. Claude-Session: https://claude.ai/code/session_01P8mZuzuyZAHSrgQKmv6ASv Co-authored-by: Claude --- .changeset/2107-exception-row-tints.md | 13 +++++++++++++ content/docs/sales/activities.mdx | 2 +- content/docs/sales/activities.zh-Hans.mdx | 2 +- content/docs/sales/activities.zh-Hant.mdx | 2 +- content/docs/sales/leads.mdx | 2 +- content/docs/sales/leads.zh-Hans.mdx | 2 +- content/docs/sales/leads.zh-Hant.mdx | 2 +- content/docs/sales/opportunities.mdx | 2 +- content/docs/sales/opportunities.zh-Hans.mdx | 2 +- content/docs/sales/opportunities.zh-Hant.mdx | 2 +- content/docs/service/cases.mdx | 4 ++-- content/docs/service/cases.zh-Hans.mdx | 4 ++-- content/docs/service/cases.zh-Hant.mdx | 4 ++-- content/docs/service/sla-and-escalation.mdx | 2 +- content/docs/service/sla-and-escalation.zh-Hans.mdx | 2 +- content/docs/service/sla-and-escalation.zh-Hant.mdx | 2 +- src/sales/views/account.view.ts | 4 +++- src/service/views/case.view.ts | 3 +++ 18 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 .changeset/2107-exception-row-tints.md diff --git a/.changeset/2107-exception-row-tints.md b/.changeset/2107-exception-row-tints.md new file mode 100644 index 00000000..5f738c6d --- /dev/null +++ b/.changeset/2107-exception-row-tints.md @@ -0,0 +1,13 @@ +--- +'hotcrm': patch +--- + +List rows are tinted only when they need attention. Nine list views used to give every row a colour, one per value of a field that already shows as a coloured badge in its own column, so a whole page of colour marked no row. Now: + +- **Cases** (*All Cases* and *Unassigned — triage*): a case flagged **SLA Violated** is red. Before, the tint followed priority and painted every case, breached or not. +- **Tasks**: an urgent task is red. +- **Event Attendees**: a declined invitation is red. +- **Accounts**: an inactive account is grey; active accounts are no longer green. +- **Open Deals**, **Events**, **Knowledge Articles** and the **High Priority** leads list are no longer tinted. + +The *Unassigned — triage* and *All Accounts* tints are not visible yet. The console fetches only a list's columns, and neither list has a column for the field its tint reads (**SLA Violated**, **Active**). They will show once the platform fetches that field. diff --git a/content/docs/sales/activities.mdx b/content/docs/sales/activities.mdx index 9e984aa0..4604d464 100644 --- a/content/docs/sales/activities.mdx +++ b/content/docs/sales/activities.mdx @@ -101,7 +101,7 @@ Tasks are the richest object in the app for ways to look at the same records: ei | View | What it shows | | --- | --- | -| **All Tasks** *(the landing tab)* | Every task. Unfinished ones first, then soonest due; the row carries a completion tick, status, priority, due date, owner and an overdue flag, and is tinted by priority — red for urgent, orange for high, green for normal, grey for low. 50 rows a page, tick several to act on them together. This is the tab the page opens on when you arrive from anywhere other than **My Tasks**. | +| **All Tasks** *(the landing tab)* | Every task. Unfinished ones first, then soonest due; the row carries a completion tick, status, priority, due date, owner and an overdue flag, and an urgent task is tinted red; no other priority tints its row. 50 rows a page, tick several to act on them together. This is the tab the page opens on when you arrive from anywhere other than **My Tasks**. | | **Task Board** | The kanban board, one column per status, cards showing subject, priority, due date and progress. Completed tasks are left out — this is the work still moving. Opening a card slides the task in from the side rather than taking you off the board. | | **Task Schedule** | Tasks placed on a calendar by **due date** and coloured by priority. It filters nothing, so finished tasks stay on the calendar where they happened. | | **Execution Plan** | The gantt view: each task is a bar from its **reminder date** to its **due date**, filled to its progress percentage. | diff --git a/content/docs/sales/activities.zh-Hans.mdx b/content/docs/sales/activities.zh-Hans.mdx index ba042379..4be18cdb 100644 --- a/content/docs/sales/activities.zh-Hans.mdx +++ b/content/docs/sales/activities.zh-Hans.mdx @@ -101,7 +101,7 @@ description: 任务——你仍然欠客户的事,挂在任意记录上。每 | 视图 | 展示什么 | | --- | --- | -| **全部任务**(默认打开) | 所有任务。未完成的在前,同组内按截止日期由近及远;行上有完成勾选框、状态、优先级、截止日期、负责人和逾期标记,并按优先级着色——紧急红色、高橙色、普通绿色、低灰色。每页 50 行,可勾选多行批量处理。从**我的任务**以外的任何地方进来,页面默认落在的就是这个标签页。 | +| **全部任务**(默认打开) | 所有任务。未完成的在前,同组内按截止日期由近及远;行上有完成勾选框、状态、优先级、截止日期、负责人和逾期标记,紧急任务整行标红,其他优先级的行不着色。每页 50 行,可勾选多行批量处理。从**我的任务**以外的任何地方进来,页面默认落在的就是这个标签页。 | | **任务看板** | 看板视图,一个状态一列,卡面是主题、优先级、截止日期和进度。已完成的任务不在板上——这里只放还在动的活。点开卡片是从侧边滑出的抽屉,不会把你带离看板。 | | **任务日程** | 任务按**截止日期**落在日历上,按优先级着色。它不做任何过滤,所以已完成的任务仍留在它发生的那一天。 | | **执行计划** | 甘特图:每个任务是一根从**提醒日期**到**截止日期**的横条,按进度百分比填充。 | diff --git a/content/docs/sales/activities.zh-Hant.mdx b/content/docs/sales/activities.zh-Hant.mdx index 25d12422..3c730174 100644 --- a/content/docs/sales/activities.zh-Hant.mdx +++ b/content/docs/sales/activities.zh-Hant.mdx @@ -103,7 +103,7 @@ description: 任務——你仍然欠客戶的事,掛在任意記錄上。每 | 檢視 | 展示什麼 | | --- | --- | -| **全部任務**(預設打開) | 所有任務。未完成的在前,同組內按截止日期由近及遠;列上有完成勾選框、狀態、優先級、截止日期、負責人和逾期標記,並按優先級著色——緊急紅色、高橙色、普通綠色、低灰色。每頁 50 列,可勾選多列批次處理。從**我的任務**以外的任何地方進來,頁面預設落在的就是這個標籤頁。 | +| **全部任務**(預設打開) | 所有任務。未完成的在前,同組內按截止日期由近及遠;列上有完成勾選框、狀態、優先級、截止日期、負責人和逾期標記,緊急任務整列標紅,其他優先級的列不著色。每頁 50 列,可勾選多列批次處理。從**我的任務**以外的任何地方進來,頁面預設落在的就是這個標籤頁。 | | **任務看板** | 看板視圖,一個狀態一欄,卡面是主題、優先級、截止日期和進度。已完成的任務不在板上——這裡只放還在動的活。點開卡片是從側邊滑出的抽屜,不會把你帶離看板。 | | **任務日程** | 任務按**截止日期**落在日曆上,按優先級著色。它不做任何過濾,所以已完成的任務仍留在它發生的那一天。 | | **執行計劃** | 甘特圖:每個任務是一根從**提醒日期**到**截止日期**的橫條,按進度百分比填充。 | diff --git a/content/docs/sales/leads.mdx b/content/docs/sales/leads.mdx index 5ad42811..70eb6daa 100644 --- a/content/docs/sales/leads.mdx +++ b/content/docs/sales/leads.mdx @@ -161,7 +161,7 @@ The Leads list curates no tab row, so the switcher offers every saved view there | **All Leads** *(the landing view)* | Every lead, newest first, with the name as the link into the record plus company, email, status, score, source and owner. 25 rows a page, export to CSV or XLSX, and **Add to Campaign** on the selection toolbar. On an empty org it says so — *No Leads Yet · Get started by creating your first lead*. | | **My Leads** | The leads you own, highest star rating first and newest within a rating. Also reachable from **My Work › My Leads** in the sidebar. | | **🔥 Hot Leads** | Leads still **New** or **Contacted** rated **4 stars or better** — exactly the leads new-lead routing calls hot, gives a next-day follow-up date and alerts their owner about ("assign within 24h"). Ordered by **Next Follow-up Date**, soonest first, with phone, email and owner on the row so it can be worked top to bottom. | -| **High Priority** | The same population — **4 stars and up**, still New or Contacted — laid out to scan rather than to work through: no follow-up date, but the lead source on the row and rows tinted by rating (green at 5, orange at 4). | +| **High Priority** | The same population — **4 stars and up**, still New or Contacted — laid out to scan rather than to work through: no follow-up date, but the lead source on the row. | | **Suspected Duplicates** | The review queue described above: leads whose **Duplicate Status** is *Suspected*, oldest suspicion first, with the duplicate-link fields on the row. Working it drains it — a lead set to *Confirmed* or cleared drops out. When there is nothing to review it says *No Suspected Duplicates · Nothing to review — every re-captured email has been checked.* | | **Lead Pipeline** | The kanban board, one column per status, each column summing **Annual Revenue**. Opening a card slides the lead in from the side rather than taking you off the board. | | **Lead Calendar** | Leads placed on a month calendar by the day they arrived, titled by company and coloured by status — the shape of your inflow. | diff --git a/content/docs/sales/leads.zh-Hans.mdx b/content/docs/sales/leads.zh-Hans.mdx index ead9e8a6..870146f8 100644 --- a/content/docs/sales/leads.zh-Hans.mdx +++ b/content/docs/sales/leads.zh-Hans.mdx @@ -149,7 +149,7 @@ Copilot 会推荐下一步行动(例如,安排通话、发送跟进、取消 | **全部线索**(默认打开) | 所有线索,最新的在前;姓名是进入记录的链接,同行还有公司、邮箱、状态、评分、来源和负责人。每页 25 行,可导出 CSV / XLSX,勾选后可批量**加入营销活动**。组织里还没有线索时它会直说——*还没有线索 · 创建第一条线索,从这里开始*。 | | **我的线索** | 你负责的线索,评分高的在前,同评分内最新的在前。侧边栏的**我的工作 › 我的线索**进的也是它。 | | **🔥 高热度线索** | 仍处于**新建**或**已联系**、评分**四星及以上**的线索——正是新线索路由判为热、给出次日跟进日期、并向负责人发出「24 小时内认领」告警的那一批。按**下次跟进日期**由近及远排序,行上带电话、邮箱和负责人,从上往下打就行。 | -| **高优先级** | 同一批线索(**四星及以上**,同样只看新建和已联系),只是摆成一张速览表而不是工作队列:不带跟进日期,行上给出线索来源,并按评分着色——五星绿色,四星橙色。 | +| **高优先级** | 同一批线索(**四星及以上**,同样只看新建和已联系),只是摆成一张速览表而不是工作队列:不带跟进日期,行上给出线索来源。 | | **疑似重复线索** | 上文说的复核队列:**重复状态**为*疑似重复*的线索,最早被怀疑的排在最前,重复关联字段就在行上。处理一条少一条——确认或清除之后它就掉出队列。没有待复核的时候它会写明:*没有疑似重复 · 无需复核——每个重复录入的邮箱都已检查过。* | | **线索流水线** | 看板视图,一个状态一列,每列汇总**年营收**。点开卡片是从侧边滑出的抽屉,不会把你带离看板。 | | **线索日历** | 线索按进入系统的日期落在月历上,标题是公司名,按状态着色——看的是流入的形状。 | diff --git a/content/docs/sales/leads.zh-Hant.mdx b/content/docs/sales/leads.zh-Hant.mdx index edd6573f..396167c6 100644 --- a/content/docs/sales/leads.zh-Hant.mdx +++ b/content/docs/sales/leads.zh-Hant.mdx @@ -151,7 +151,7 @@ Copilot 會推薦下一步行動(例如,安排通話、發送跟進、取消 | **全部線索**(預設打開) | 所有線索,最新的在前;姓名是進入記錄的連結,同列還有公司、郵箱、狀態、評分、來源和負責人。每頁 25 列,可匯出 CSV / XLSX,勾選後可批次**加入行銷活動**。組織裡還沒有線索時它會直說——*還沒有線索 · 建立第一條線索,從這裡開始*。 | | **我的線索** | 你負責的線索,評分高的在前,同評分內最新的在前。側邊欄的 **My Work › My Leads** 進的也是它。 | | **🔥 高熱度線索** | 仍處於**新建**或**已聯絡**、評分**四星及以上**的線索——正是新線索路由判為熱、給出次日跟進日期、並向負責人發出「24 小時內認領」告警的那一批。按**下次跟進日期**由近及遠排序,列上帶電話、郵箱和負責人,從上往下打就行。 | -| **高優先級** | 同一批線索(**四星及以上**,同樣只看新建和已聯絡),只是擺成一張速覽表而不是工作佇列:不帶跟進日期,列上給出線索來源,並按評分著色——五星綠色,四星橙色。 | +| **高優先級** | 同一批線索(**四星及以上**,同樣只看新建和已聯絡),只是擺成一張速覽表而不是工作佇列:不帶跟進日期,列上給出線索來源。 | | **疑似重複線索** | 上文說的複核佇列:**重複狀態**為*疑似重複*的線索,最早被懷疑的排在最前,重複關聯欄位就在列上。處理一條少一條——確認或清除之後它就掉出佇列。沒有待複核的時候它會寫明:*沒有疑似重複 · 無需複核——每個重複錄入的郵箱都已檢查過。* | | **線索流水線** | 看板視圖,一個狀態一欄,每欄彙總**年營收**。點開卡片是從側邊滑出的抽屜,不會把你帶離看板。 | | **線索日曆** | 線索按進入系統的日期落在月曆上,標題是公司名,按狀態著色——看的是流入的形狀。 | diff --git a/content/docs/sales/opportunities.mdx b/content/docs/sales/opportunities.mdx index 9320f22b..880c5e5e 100644 --- a/content/docs/sales/opportunities.mdx +++ b/content/docs/sales/opportunities.mdx @@ -144,7 +144,7 @@ Opportunities opens on **Open Deals**, and the switcher along the top of the lis | View | What it shows | | --- | --- | -| **Open Deals** *(the landing view)* | Every deal that is not Closed Won or Closed Lost, soonest close date first, with rows tinted by stage. **Amount** and **Expected Revenue** carry column totals. | +| **Open Deals** *(the landing view)* | Every deal that is not Closed Won or Closed Lost, soonest close date first. **Amount** and **Expected Revenue** carry column totals. | | **My Open Deals** | The same open-deal filter, narrowed to the deals you own. | | **Sales Pipeline** | The kanban board — one column per stage, open deals only, drag a card to move the deal. Also reachable from **Pipeline** in the sidebar. | | **All Opportunities** | The unfiltered book *including* closed business, latest close date first. Where win/loss reviews and audits start. | diff --git a/content/docs/sales/opportunities.zh-Hans.mdx b/content/docs/sales/opportunities.zh-Hans.mdx index 5dbd67cd..3b21de6d 100644 --- a/content/docs/sales/opportunities.zh-Hans.mdx +++ b/content/docs/sales/opportunities.zh-Hans.mdx @@ -144,7 +144,7 @@ description: 活跃的销售交易——销售管道的核心,包含 7 个阶 | 视图 | 展示什么 | | --- | --- | -| **进行中商机**(默认打开) | 所有未赢单、未丢单的交易,按预计成交日期由近及远排列,行按阶段着色。**金额**与**预期营收**两列带合计。 | +| **进行中商机**(默认打开) | 所有未赢单、未丢单的交易,按预计成交日期由近及远排列。**金额**与**预期营收**两列带合计。 | | **我的进行中商机** | 同样的"进行中"过滤条件,只保留你负责的交易。 | | **销售流水线** | 看板视图——每个阶段一列,只含进行中的交易,拖动卡片即可推进阶段。也可从侧边栏的**销售管道**直接进入。 | | **全部商机** | 不过滤的完整台账,*含*已结束的交易,按预计成交日期由远及近排列。赢单/丢单复盘和审计从这里开始。 | diff --git a/content/docs/sales/opportunities.zh-Hant.mdx b/content/docs/sales/opportunities.zh-Hant.mdx index 96f3da81..fdfd32ea 100644 --- a/content/docs/sales/opportunities.zh-Hant.mdx +++ b/content/docs/sales/opportunities.zh-Hant.mdx @@ -146,7 +146,7 @@ description: 活躍的銷售交易——銷售管道的核心,包含 7 個階 | 視圖 | 展示什麼 | | --- | --- | -| **進行中商機**(預設打開) | 所有未贏單、未丟單的交易,按預計成交日期由近及遠排列,列按階段著色。**金額**與**預期營收**兩欄帶合計。 | +| **進行中商機**(預設打開) | 所有未贏單、未丟單的交易,按預計成交日期由近及遠排列。**金額**與**預期營收**兩欄帶合計。 | | **我的進行中商機** | 同樣的「進行中」過濾條件,只保留你負責的交易。 | | **銷售流水線** | 看板視圖——每個階段一欄,只含進行中的交易,拖動卡片即可推進階段。也可從側邊欄的**銷售管道**直接進入。 | | **全部商機** | 不過濾的完整帳冊,*含*已結束的交易,按預計成交日期由遠及近排列。贏單/丟單複盤和稽核從這裡開始。 | diff --git a/content/docs/service/cases.mdx b/content/docs/service/cases.mdx index 92159c84..c14a1b37 100644 --- a/content/docs/service/cases.mdx +++ b/content/docs/service/cases.mdx @@ -173,7 +173,7 @@ Nothing else claims a case, and each exclusion is deliberate: | View | What it shows | | --- | --- | -| **All Cases** | Every case, sorted by priority then SLA due date, with a row tint keyed on **priority** | +| **All Cases** | Every case, sorted by priority then SLA due date; a case flagged **SLA Violated** is tinted red, every other row stays plain | | **Service Workflow** | Kanban of open cases, grouped by status | | **SLA Calendar** | Cases laid out on their **SLA Due Date** — every case carries one now, so the calendar shows the whole open backlog | | **Case Timeline** | Cases on a *created → closed* timeline, grouped by owner | @@ -237,7 +237,7 @@ If the Copilot decides the priority is *Critical*, it will also recommend immedi ## Tips for service managers -- ✅ There is no *Breached SLA* view to watch, and no red badge either — the only colour rule on a case list is *All Cases*' row tint, and it is keyed on **priority**, not on a breach. Work **Escalated Cases** daily instead, and read the **SLA Violations** tile on Service Overview for the count. +- ✅ There is no *Breached SLA* view to watch, and no red badge either — but a breach does show on *All Cases*: a case flagged **SLA Violated** is a red row, and it stays red once resolved or closed, because a late close records the breach. Work **Escalated Cases** daily, and read the **SLA Violations** tile on Service Overview for the count. - ✅ Use the kanban for daily standups — it is named **Service Workflow** and reaches the case list as the **Service Workflow** tab. - ✅ Coach agents whose **Resolution Time** trends up — but you will be reading it off the cases themselves. There is no agent leaderboard: `case_metrics`, the dataset behind every service report and dashboard widget, declares Status, Priority, Origin, Type, Created and Resolving Article as its only dimensions, so nothing in analytics can group cases by agent. diff --git a/content/docs/service/cases.zh-Hans.mdx b/content/docs/service/cases.zh-Hans.mdx index fc6b98f7..1e6b1a5b 100644 --- a/content/docs/service/cases.zh-Hans.mdx +++ b/content/docs/service/cases.zh-Hans.mdx @@ -173,7 +173,7 @@ description: 客户支持工单——主题、优先级、状态、解决方案 | 视图 | 展示什么 | | --- | --- | -| **全部工单** | 全部工单,先按优先级、再按 SLA 截止日期排序,行底色键在**优先级**上 | +| **全部工单** | 全部工单,先按优先级、再按 SLA 截止日期排序;已标记 **SLA Violated** 的工单整行标红,其余行不着色 | | **服务流转** | 未结工单的看板,按状态分列 | | **SLA 日历** | 按 **SLA 截止日期** 把工单铺在日历上——现在每张工单都带截止日期,所以它显示的是整个未结积压 | | **工单时间线** | 工单按「创建 → 关闭」铺在时间轴上,按负责人分组 | @@ -237,7 +237,7 @@ Activity 标签页是工单上发生过什么的唯一可信来源。它将四 ## 给服务经理的提示 -- ✅ 没有 *Breached SLA* 这个视图可看,也没有什么红色徽章——工单列表上唯一的颜色规则是 *全部工单* 的行底色,它键在**优先级**上,不是违约。每天改看 **已升级工单**,数量则看服务概览上的 **SLA Violations** 磁贴。 +- ✅ 没有 *Breached SLA* 这个视图可看,也没有什么红色徽章——但违约在 *全部工单* 上看得见:已标记 **SLA Violated** 的工单是一行红色,解决或关闭之后依然是红的,因为逾期关闭会记下这次违约。每天看 **已升级工单**,数量则看服务概览上的 **SLA Violations** 磁贴。 - ✅ 用看板开每日站会——它叫 **服务流转**,在工单列表的 **服务流转** 页签上。 - ✅ 辅导那些 **Resolution Time** 趋势上升的客服——但这个趋势只能从工单本身读出来。不存在什么客服排行榜:`case_metrics` 这个数据集(服务侧每一张报表和每一个仪表盘部件都建在它上面)只声明了 Status、Priority、Origin、Type、Created、Resolving Article 六个维度,所以分析层里没有任何东西能按客服分组。 diff --git a/content/docs/service/cases.zh-Hant.mdx b/content/docs/service/cases.zh-Hant.mdx index 94076755..e5d4d950 100644 --- a/content/docs/service/cases.zh-Hant.mdx +++ b/content/docs/service/cases.zh-Hant.mdx @@ -175,7 +175,7 @@ description: 客戶支援工單——主題、優先順序、狀態、解決方 | 檢視 | 展示什麼 | | --- | --- | -| **全部工單** | 全部工單,先按優先順序、再按 SLA 截止日期排序,列底色鍵在**優先順序**上 | +| **全部工單** | 全部工單,先按優先順序、再按 SLA 截止日期排序;已標記 **SLA Violated** 的工單整列標紅,其餘列不著色 | | **服務流轉** | 未結工單的看板,按狀態分欄 | | **SLA 日曆** | 按 **SLA 截止日期** 把工單鋪在行事曆上——現在每張工單都帶截止日期,所以它顯示的是整個未結積壓 | | **工單時間線** | 工單按「建立 → 關閉」鋪在時間軸上,按負責人分組 | @@ -239,7 +239,7 @@ Activity 標籤頁是工單上發生過什麼的唯一可信來源。它將四 ## 給服務經理的提示 -- ✅ 沒有 *Breached SLA* 這個檢視可看,也沒有什麼紅色徽章——工單列表上唯一的顏色規則是 *全部工單* 的列底色,它鍵在**優先順序**上,不是違約。每天改看 **已升級工單**,數量則看服務概覽上的 **SLA Violations** 磁貼。 +- ✅ 沒有 *Breached SLA* 這個檢視可看,也沒有什麼紅色徽章——但違約在 *全部工單* 上看得見:已標記 **SLA Violated** 的工單是一列紅色,解決或關閉之後依然是紅的,因為逾期關閉會記下這次違約。每天看 **已升級工單**,數量則看服務概覽上的 **SLA Violations** 磁貼。 - ✅ 用看板開每日站會——它叫 **服務流轉**,在工單列表的 **服務流轉** 頁籤上。 - ✅ 輔導那些 **Resolution Time** 趨勢上升的客服——但這個趨勢只能從工單本身讀出來。不存在什麼客服排行榜:`case_metrics` 這個資料集(服務側每一張報表和每一個儀表板部件都建在它上面)只宣告了 Status、Priority、Origin、Type、Created、Resolving Article 六個維度,所以分析層裡沒有任何東西能按客服分組。 diff --git a/content/docs/service/sla-and-escalation.mdx b/content/docs/service/sla-and-escalation.mdx index fed9b525..6845f8fb 100644 --- a/content/docs/service/sla-and-escalation.mdx +++ b/content/docs/service/sla-and-escalation.mdx @@ -28,7 +28,7 @@ The **Critical row is flat at four hours on purpose.** Before the matrix, every A priority the matrix has no row for gets no due date at all, rather than a guessed one. -What a breach looks like is the same at every priority now: the hourly monitor flags **SLA Violated**, escalates the case, and alerts the **case owner** by inbox + email. There is no red banner on the case detail page — this app has no banner mechanism — and no alert to a support manager: the notify node's recipient list is `{currentCase.owner_id}` alone. In a list view, the thing that reports a breach is the **SLA Violated** column, not the row colour: the only colour rule on a case list is the **row tint** in *All Cases*, and it is keyed on **priority** (High `#f97316`, Medium `#eab308`, Low `#94a3b8`), painting every case of that priority from the moment it is created, breach or no breach. +What a breach looks like is the same at every priority now: the hourly monitor flags **SLA Violated**, escalates the case, and alerts the **case owner** by inbox + email. There is no red banner on the case detail page — this app has no banner mechanism — and no alert to a support manager: the notify node's recipient list is `{currentCase.owner_id}` alone. In a list view, a breach shows twice in *All Cases*: in the **SLA Violated** column, and as a red row from the moment the case is flagged. No other value tints a case row, so a red row means a breach, at any priority. The matrix lives in the hook rather than on a Setup screen — see the *Tips for admins* section below. diff --git a/content/docs/service/sla-and-escalation.zh-Hans.mdx b/content/docs/service/sla-and-escalation.zh-Hans.mdx index 3d13a37a..e20c86e6 100644 --- a/content/docs/service/sla-and-escalation.zh-Hans.mdx +++ b/content/docs/service/sla-and-escalation.zh-Hans.mdx @@ -28,7 +28,7 @@ SLA(服务级别协议)系统为**每一张工单**打上一个**截止时 矩阵里没有对应行的优先级,不会被打上任何截止日期,而不是被猜一个。 -现在四个优先级上"违约"长得都一样:每小时一轮的监控把它标记为 **SLA 已违约**并升级,然后通过站内消息 + 邮件提醒**工单负责人**。工单详情页上没有红色横幅——本应用没有任何横幅机制——也不会向支持经理发出警报:该 notify 节点的收件人列表只有 `{currentCase.owner_id}` 一项。在列表视图里,真正报告违约的是 **SLA 已违约**这一列,而不是行底色:工单列表上唯一的颜色规则是 *全部工单* 视图的**行底色**,它键在**优先级**上(High `#f97316`、Medium `#eab308`、Low `#94a3b8`),从工单创建的那一刻起就一直是这个颜色,与违不违约无关。 +现在四个优先级上"违约"长得都一样:每小时一轮的监控把它标记为 **SLA 已违约**并升级,然后通过站内消息 + 邮件提醒**工单负责人**。工单详情页上没有红色横幅——本应用没有任何横幅机制——也不会向支持经理发出警报:该 notify 节点的收件人列表只有 `{currentCase.owner_id}` 一项。在列表视图里,违约在 *全部工单* 上出现两次:一次在 **SLA 已违约**这一列,一次是工单被标记那一刻起整行标红。没有别的取值会给工单行着色,所以红行就是违约,与优先级无关。 矩阵写在钩子里,不在任何设置页面上——参见下方的*给管理员的提示*部分。 diff --git a/content/docs/service/sla-and-escalation.zh-Hant.mdx b/content/docs/service/sla-and-escalation.zh-Hant.mdx index 5f3b4fe0..d209e96b 100644 --- a/content/docs/service/sla-and-escalation.zh-Hant.mdx +++ b/content/docs/service/sla-and-escalation.zh-Hant.mdx @@ -30,7 +30,7 @@ SLA(服務級別協議)系統為**每一張工單**打上一個**截止時 矩陣裡沒有對應列的優先順序,不會被打上任何截止日期,而不是被猜一個。 -現在四個優先順序上"違約"長得都一樣:每小時一輪的監控把它標記為 **SLA 已違約**並升級,然後透過站內訊息 + 郵件提醒**工單負責人**。工單詳情頁上沒有紅色橫幅——本應用沒有任何橫幅機制——也不會向支援經理發出警報:該 notify 節點的收件人列表只有 `{currentCase.owner_id}` 一項。在列表檢視裡,真正報告違約的是 **SLA 已違約**這一欄,而不是列底色:工單列表上唯一的顏色規則是 *All Cases* 檢視的**列底色**,它鍵在**優先順序**上(High `#f97316`、Medium `#eab308`、Low `#94a3b8`),從工單建立的那一刻起就一直是這個顏色,與違不違約無關。 +現在四個優先順序上"違約"長得都一樣:每小時一輪的監控把它標記為 **SLA 已違約**並升級,然後透過站內訊息 + 郵件提醒**工單負責人**。工單詳情頁上沒有紅色橫幅——本應用沒有任何橫幅機制——也不會向支援經理發出警報:該 notify 節點的收件人列表只有 `{currentCase.owner_id}` 一項。在列表檢視裡,違約在 *All Cases* 上出現兩次:一次在 **SLA 已違約**這一欄,一次是工單被標記那一刻起整列標紅。沒有別的取值會給工單列著色,所以紅列就是違約,與優先順序無關。 矩陣寫在鉤子裡,不在任何 Setup 頁面上——參見下方的*給管理員的提示*部分。 diff --git a/src/sales/views/account.view.ts b/src/sales/views/account.view.ts index 15b9fd70..08ad29e0 100644 --- a/src/sales/views/account.view.ts +++ b/src/sales/views/account.view.ts @@ -26,7 +26,9 @@ export const AccountViews = defineView({ ], sort: [{ field: 'annual_revenue', order: 'desc' }], // Only the exception row is tinted: an inactive account greys out, an - // active one stays plain. + // active one stays plain. Measured on 18.0.0-next.1 it paints nothing: the + // console fetches only the columns and `is_active` is not one. That is a + // platform gap, reported upstream from #2107; ⛔ no column added to dodge it. rowColor: { field: 'is_active', colors: { false: 'slate' } }, selection: { type: 'multiple' }, pagination: { pageSize: 50, pageSizeOptions: [25, 50, 100] }, diff --git a/src/service/views/case.view.ts b/src/service/views/case.view.ts index 507f9471..6dc82d98 100644 --- a/src/service/views/case.view.ts +++ b/src/service/views/case.view.ts @@ -239,6 +239,9 @@ export const CaseViews = defineView({ { field: 'sla_due_date', order: 'asc' }, ], // Same exception tint as the main queue: a breached case turns red. + // Measured on 18.0.0-next.1 it paints nothing here: the console fetches + // only the columns and `is_sla_violated` is not one. That is a platform + // gap, reported upstream from #2107; ⛔ no column added to dodge it. rowColor: { field: 'is_sla_violated', colors: { true: 'red' } }, // The empty state carries the operational instruction, because "no rows" // here is ambiguous on its own: it means either "the round-robin placed