Skip to main content

Dashboard drill-through and per-widget RBAC

Outcome

Every dashboard widget click lands on the right pre-filtered list. Restricted widgets show a clear "locked" placeholder instead of disappearing for users without permission.

Prerequisites

  • reports.read.{financial|operational|clinical-auth} scopes assigned to roles per intent.
  • Familiarity with the tenant RBAC matrix.

What this fixed

Two long-standing inconsistencies surfaced as RM-15 widgets accumulated session-by-session:

ProblemFix
Drill targets were each widget's responsibility, with duplicated monthToDateRange helpers and one widget (ArAgingChart) sending every click to a generic /receivables URL that ignored the bucket.Centralized helpers in apps/rcm-app/src/components/dashboard/drillthrough.ts.
RBAC gating hid whole rows via {canSeeFinancial && <Row/>}, so an operational viewer saw a collapsed dashboard with no signal that financial widgets exist behind a permission wall. KPI cards were gated as a single block on reports.read.financial, even the three operational KPIs.<WidgetGate> wrapper + split KPI gates.

<WidgetGate> wrapper

Replaces the inline {canSee* && …} pattern. When a viewer lacks the required scope, the widget slot renders a dashed-border placeholder with a lock icon, the widget title, and a hint listing the required permission (e.g. reports.read.financial), instead of disappearing entirely.

KPI gates

ScopeKPIs
reports.read.financialTotal Submitted, Total AR Balance, Avg Days in AR
reports.read.operationalClaims Pending, Denial Rate, Clean Claim Rate

Drill mappings

Widget clickDrill target
AR Aging bucket/claims?agingBucket=…&serviceFrom=…&serviceTo=…&openOnly=1
Denial Category bar/denials?tab=denials&denialCategory=…
Denial Trend dot/denials?tab=denials&denialCategory=…&dateFrom=…&dateTo=…
Revenue month bar/claims?tab=all&serviceFrom=…&serviceTo=…
Claim Funnel stage/claims?tab=all&status=…
Claims Action Queue row/claims?tab=all&status=<row.status>
Auth Alerts → "View all"/authorizations?status=APPROVED
Auto-Correction → "View analytics"/denials/analytics
Timely Filing → "View All Alerts"/claims?sort=filingDeadlineDate&order=asc

Operator playbook — assigning roles

Out-of-the-box scope bundles (tenant migration 101):

Rolefinancialoperationalclinical-auth
OPERATIONS_ANALYST
EMR_CLIENT_VIEWER
CONFIG_MANAGER

If a tenant needs a custom mix (e.g. CFO who only sees financial), provision a new role in /admin/rbac (see RBAC matrix editor) and grant the desired subset of reports.read.{financial,operational,clinical-auth}.

Verifying RBAC visibility

  1. Sign in as EMR_CLIENT_VIEWER (or any user with no reports.read.financial).

  2. /dashboard should render every operational + clinical-auth widget interactively, while financial slots show the locked placeholder ("You don't have permission to view this widget. Required: reports.read.financial.") in the same layout position — no row collapse.

  3. Sign in as OPERATIONS_ANALYST and confirm every drill target above lands on the expected pre-filtered list.

Break-glass

This is purely client-side. If a widget's drill target is wrong, edit the helper in drillthrough.ts and redeploy the rcm-app bundle — no DB or backend route touch is required.

Validation

CheckExpected
Widget clickLands on the correct filtered list
Locked placeholderShows for missing scopes (no row collapse)
Operational KPIs visible to operational-only roleYes

Cross-references

Next

8.1 — PHI key rotation (CLI)