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=<every status in the stage, comma-separated>
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?tab=timely

Operator playbook — assigning roles​

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

Rolefinancialoperationalclinical-auth
OPERATIONS_ANALYSTyesyesyes
EMR_CLIENT_VIEWER—yesyes
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)