Skip to content

Commit f3f4899

Browse files
os-zhuangclaude
andauthored
fix(dashboards): opt widgets out of global filters their object lacks (2.2.1) (#458)
2.2.0 fixed only the built-in `dateRange` picker, but ObjectStack 15 (framework#2501) injects EVERY dashboard filter — dateRange AND every globalFilters[] entry — into each widget's analytics query. Widgets on objects without the filtered column still crashed with `SqliteError: no such column: <field>` and rendered as error cards. - Executive `Lead Source` filter: crm_account has no `lead_source`, so total_accounts / new_accounts_by_month / accounts_by_industry threw `no such column: lead_source`. Add `lead_source: false` to their filterBindings (contact/lead keep filtering — they have the field). - CRM `Owner` filter: crm_product has no `owner`, so top_products would crash the same way. Add `owner: false` to its filterBindings. - Audited the full filter×object matrix: Sales (all opportunity_metrics) and Service (created_date/owner/priority all on crm_case) need nothing. Browser-verified by actually SELECTING the filter values (Lead Source = Web): account widgets stay populated, contact/lead filter correctly, zero analytics errors in the server log. verify (validate/build/test 17/17) green. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4e6b91c commit f3f4899

5 files changed

Lines changed: 27 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to HotCRM are documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); HotCRM follows [Semantic Versioning](https://semver.org/).
44

5+
## [2.2.1] — 2026-07-20
6+
7+
Follow-up patch to 2.2.0. The dashboard-filter fix in 2.2.0 only covered the built-in `dateRange` picker; the **`globalFilters[]`** have the same propagation behaviour (ObjectStack 15 / framework#2501 injects every dashboard filter into each widget's query) and were still crashing widgets on objects that lack the filtered field. Browser-verified by actually selecting the filter values, not just loading the dashboards.
8+
9+
### Fixed
10+
11+
- **Selecting the Executive dashboard's `Lead Source` filter crashed every account widget.** The `lead_source` global filter was injected into each widget's query; `crm_account` has no `lead_source` column, so `total_accounts`, `new_accounts_by_month`, and `accounts_by_industry` failed with `SqliteError: no such column: lead_source`. Added `lead_source: false` to those three widgets' [`filterBindings`](src/dashboards/executive.dashboard.ts) (they keep `dateRange: false` too). `crm_contact` and `crm_lead` *do* have `lead_source`, so `total_contacts` / `open_leads` correctly keep filtering. Verified in the browser: selecting `Lead Source = Web` now returns filtered/empty results with zero analytics errors.
12+
- **CRM dashboard's `Owner` filter would crash `top_products`.** `crm_product` has no `owner` column, so the `owner` global filter would fail the product-category widget the same way. Added `owner: false` to its [`filterBindings`](src/dashboards/crm.dashboard.ts) (alongside the existing `dateRange: false`).
13+
- Full field-vs-filter matrix audited across all four dashboards: Sales (all `opportunity_metrics`, which has `owner`/`type`/`close_date`) and Service (`created_date`/`owner`/`priority` all on `crm_case`) needed no change.
14+
515
## [2.2.0] — 2026-07-20
616

717
Platform upgrade to ObjectStack **16.0.0-rc.1** — the 16 release-candidate line (from 14.7, skipping the entire 15.x line). Manifest `specVersion` now declares `^16.0.0-rc.1` (was `^14.0.0`); app version `2.2.0`. ObjectStack 16 finishes the ADR-0049 "enforce-or-remove" sweep (dead metadata props now fail loudly instead of parsing inert), converges the hook/action org identifier on `organizationId`, flips `.strict()` on dashboard-widget / view-form / page schemas, and — most consequentially for this app — makes the `ai` capability a **fail-fast hard requirement** resolved to the closed `@objectstack/service-ai` package. Built, validated, type-checked, unit-tested (17/17), and browser-verified against `@objectstack/* 16.0.0-rc.1` (boot → 38 plugins loaded → 17 flows / 15 trigger-bound → `/_console/` login renders at HTTP 200 with no boot or console errors).

objectstack.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default defineStack({
3131
manifest: {
3232
id: 'app.objectstack.hotcrm',
3333
namespace: 'crm',
34-
version: '2.2.0',
34+
version: '2.2.1',
3535
type: 'app',
3636
name: 'HotCRM',
3737
description: 'AI-Native CRM for the ObjectStack marketplace — Accounts, Contacts, Leads, Opportunities, Cases, Knowledge, Forecasts, Campaigns, Contracts.',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hotcrm",
3-
"version": "2.2.0",
3+
"version": "2.2.1",
44
"description": "HotCRM - Production CRM built on ObjectStack",
55
"private": true,
66
"main": "./objectstack.config.ts",

src/dashboards/crm.dashboard.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,12 @@ export const CrmOverviewDashboard: Dashboard = {
181181
description: 'Total list-price revenue by product category',
182182
type: 'bar',
183183
colorVariant: 'blue',
184-
// crm_product has no `close_date`; opt out of the dashboard date-range
185-
// picker (ObjectStack 15 framework#2501 wired dashboard filters into every
186-
// widget query, so an unbound widget here fails with `no such column`).
187-
filterBindings: { dateRange: false },
184+
// crm_product has neither `close_date` nor `owner`; opt out of both
185+
// dashboard filters bound to those fields. ObjectStack 15 (framework#2501)
186+
// injects every dashboard filter (dateRange + globalFilters) into each
187+
// widget's query, so a widget on an object lacking a filter field fails
188+
// with `no such column`.
189+
filterBindings: { dateRange: false, owner: false },
188190
dataset: 'product_metrics', dimensions: ['category'], values: ['list_price_sum'],
189191
layout: { x: 6, y: 6, w: 6, h: 4 },
190192
chartConfig: {

src/dashboards/executive.dashboard.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,13 @@ export const ExecutiveDashboard: Dashboard = {
8888
actionUrl: '/objects/account',
8989
actionType: 'url',
9090
actionIcon: 'ArrowUpRight',
91-
// crm_account has no `close_date`; opt out of the dashboard date-range
92-
// picker (bound to close_date) — ObjectStack 15 (framework#2501) wired
93-
// dashboard filters into every widget's query, so an unbound widget on an
94-
// object lacking the date field would fail with `no such column`.
95-
filterBindings: { dateRange: false },
91+
// crm_account has neither `close_date` nor `lead_source`; opt this widget
92+
// out of both dashboard filters bound to those fields. ObjectStack 15
93+
// (framework#2501) injects every dashboard filter (dateRange + globalFilters)
94+
// into each widget's query, so a widget on an object lacking a filter field
95+
// fails with `no such column`. crm_account DOES have `owner`, so the owner
96+
// filter is left to apply.
97+
filterBindings: { dateRange: false, lead_source: false },
9698
dataset: 'account_metrics', values: ['account_count'],
9799
layout: { x: 3, y: 0, w: 3, h: 2 },
98100
options: {
@@ -203,7 +205,7 @@ export const ExecutiveDashboard: Dashboard = {
203205
type: 'bar',
204206
filter: { created_at: { $gte: '{6_months_ago}' } },
205207
colorVariant: 'purple',
206-
filterBindings: { dateRange: false }, // crm_account has no close_date; this widget scopes itself by created_at
208+
filterBindings: { dateRange: false, lead_source: false }, // crm_account has no close_date/lead_source; scopes itself by created_at
207209
dataset: 'account_metrics', dimensions: ['created_at'], values: ['account_count'],
208210
layout: { x: 6, y: 6, w: 6, h: 4 },
209211
chartConfig: {
@@ -230,7 +232,7 @@ export const ExecutiveDashboard: Dashboard = {
230232
description: 'Total annual revenue and account count per industry',
231233
type: 'table',
232234
colorVariant: 'default',
233-
filterBindings: { dateRange: false }, // crm_account has no close_date — opt out of the date picker
235+
filterBindings: { dateRange: false, lead_source: false }, // crm_account has no close_date/lead_source — opt out of both
234236
dataset: 'account_metrics', dimensions: ['industry'], values: ['annual_revenue_sum', 'account_count'],
235237
layout: { x: 0, y: 10, w: 12, h: 4 },
236238
options: {

0 commit comments

Comments
 (0)