Lewati ke konten utama
Versi: v0.0.78

v0.0.65 — Filter coverage + license escape hatch

Released: 2026-05-12.

Surface silent filter skip + one-click wire-into-card

The toolbar date filter has always silently skipped cards whose SQL didn't reference the filter column — correct cross-dialect behavior, but invisible to users. Operators would set a date range, see some cards update and others not, and reasonably conclude "the filter is broken". v0.0.65 closes that gap with a two-layer fix.

Surface layer

The dashboard toolbar and per-card chrome now make filter coverage explicit:

ElementSurfaces whenWhat it shows
⚠ Pick a date column pillRange is set, no date column configuredReminder to wire a column to the toolbar before the filter has anywhere to apply.
covers N/M cards pillRange + column both setClick to flash uncovered cards on the canvas.
⊘ filter skipped badge (per card)This card's SQL doesn't reference the filter column and has no bound paramTells you which cards are sitting out.
Date-column suggestionsPer dashboardPulled from card SQL — the toolbar suggests columns it sees referenced.

Fix layer

In edit mode, the ⊘ filter skipped badge becomes a button. Clicking it opens a modal with a diff preview:

  • Apply rewrites the card's SQL to include the filter column and adds the corresponding dashboard parameter binding.
  • The modal previews via POST /api/dashboards/{id}/cards/{card_id}/wire-filter with dry_run=true, so you see the exact rewrite before committing.

Pattern-based AND-injection respects GROUP BY / ORDER BY / HAVING / LIMIT — the filter clause lands inside the WHERE, not after LIMIT. A _filter_column_in_sql guard skips wrapping when the column is already bound via parameter nearby, so wired cards don't double-filter.

Coverage rules are centralized in pages/dashboards/filterCoverage.ts — the toolbar pill, per-card badge, and backend mirror all read from one source of truth.

License page — org-switch escape hatch

Bug observed 2026-05-12 on test ECS: a user whose localStorage X-Org-Id pointed at an unlicensed org (test-fixture pollution — "Pytest Smile Org") was hard-stuck on /license with no way to recover beyond pasting a fresh license key. The org switcher didn't render on the License Required screen, and every navigation away got redirected back by the axios interceptor.

Fixed:

  • Backend/api/organizations is now on the license-gate allowlist. The endpoint returns only org_id / slug / display_name (no business data), so opening it past the gate doesn't expand the unlicensed surface area.
  • FrontendLicenseRequired fetches /api/organizations and, when the user belongs to other orgs, renders a switch to another organization block with one-click switch buttons. Picking one stamps selected_org_id in localStorage and hard-reloads to /.

This doesn't address the underlying test-pollution (orgs 2–13 on test ECS DB are leftover pytest/e2e fixtures) — that's a separate cleanup. It does make the dead-end recoverable on any tenant if it happens again.