Admin UI (front-end)

Produces: None
Consumes: None

Overview

The Admin UI is an Angular-based front-end application that provides a comprehensive interface for managing the Link platform, configuring tenants, and monitoring report generation and submission workflows.

System-wide Functionality

  • User Management: Allows administrators to manage user accounts, including creation, editing, and deletion. Currently, the system supports a single "admin" role for platform-wide access.
  • Measure Management: Upload and manage FHIR-based measure definitions. Administrators can upload measure bundles in JSON format and explore related artifacts.
  • Validation Profile (IG) Management: Supports the management of FHIR Implementation Guides (IGs). Administrators can upload IGs as .tgz packages and view their terminology dependencies and package details.
  • Terminology Management: Allows for viewing and filtering terminology resources, such as CodeSystems and ValueSets, that are utilized by the platform's terminology service.
  • Integration Testing Support: Offers tools to initiate and track integration tests, enabling administrators to validate service connectivity and workflow correctness within the environment.

Tenant Configuration

  • Basic Configuration: Administrators can define tenant-specific settings, including Facility IDs, names, and timezones.
  • Census Scheduling and Acquisition Methods: Supports configuring how patient census data is acquired, including defining schedules and enabling/disabling automated census retrieval.
  • Measure Association & Scheduling: Enables associating specific measures with a tenant and configuring automated reporting schedules (Daily, Weekly, or Monthly cadences).
  • Data Sources & Acquisition:
    • FHIR Query Config: Configuration of FHIR-based data acquisition parameters.
    • FHIR List Config: Management of patient list-based acquisition settings.
    • Query Plans: Definition and management of query plans used to retrieve clinical data from data sources.
  • Normalization Operations: Configuration of transformation rules and operations applied to data during the normalization phase.

Tenant & Report Viewing

  • Tenant Dashboard: Provides a high-level view of a tenant's configuration and a list of all reports generated for that tenant.
  • Report Details:
    • Report Summary: Visual representation of report status using donut charts for Measure IP counts, Reporting Status, and Submission Status.
    • Patient List: A detailed table of all patients included in a report, showing individual reporting and submission statuses.
    • Pre-qualification (Validation): Detailed access to validation results, categorized by issue type (e.g., missing profiles or terminology warnings), allowing for quick identification of data quality issues.
  • Report Downloading: Enables download of generated report packages for manual delivery, archival, or verification once a report has been submitted.

Data Acquisition Log Analysis

  • Log Access: Integration with data acquisition logs allows administrators to analyze acquisition statistics and troubleshoot data retrieval issues directly from the report view.
  • Analysis Tools: Provides insights into the success and failure rates of data acquisition steps for a given report.

Location Mapping

The UI is embedded in the existing facility pages rather than new top-level routes: configuration lives with the facility's Data Acquisition settings (Facility Edit), and observed mappings live with the facility's operational data (Facility View).

It gives administrators answers to three operational questions:

  1. How does Link decide which locations belong to my facility? → the Reporting Organization configuration (per facility, with an Active flag).
  2. Which locations has Link actually seen, and did they map to my organization? → the Locations tab.
  3. Why was this patient's encounter excluded from my report? → the Encounters tab, with drill-down to the specific location mapping that produced the outcome.
UI surface Purpose Requirement
Facility Edit → Data Acquisition → Reporting Organization tab + dialog Define and activate the location-matching rule LNK-4238
Facility View → Locations tab Inspect acquired location mappings and their org/active status LNK-4239
Facility View → Encounters tab Inspect encounter mappings and each encounter's org-resolution outcome LNK-4239
Location Details dialog Drill into a single location mapping from an encounter row —
Normalization → Copy Location Alias to Type Iteratively Normalize Location.alias (incl. partOf parents) into Location.type Cerner alias→type normalization

Reporting Organization Configuration

Located on Facility Edit → Data Acquisition Configuration → Reporting Organization tab. The tab is disabled until the facility has an EHR vendor, because the reliable "this location belongs to my organization" signal differs per vendor (see How to Match a Reporting Organization to a Location Resource above): Epic encodes it in Location.identifier on the EAF/Hospital node; Cerner facilities are identified via Location.managingOrganization or a Location.type code (783) optionally combined with Location.alias.

The tab renders the current configuration read-only, with Add (no config yet), Edit, and Delete (confirmation-gated) actions opening a dialog containing:

  • Facility Id (read-only), Description (optional), Active (slide toggle, default on).
  • Method (vendor-specific): Epic — Identifier (default) or Custom FHIRPath; Cerner — Managing Organization (default), Location Type, or Custom FHIRPath.
  • Matches — one or more OR-combined match cards ("a location is included if it matches any of the following"), supporting organizations represented by multiple identifiers.

Each builder method generates a FHIRPath fragment, previewed live in a read-only Generated FHIRPath box:

  • Epic Identifier → Location.identifier.exists(system = '<system>' and value = '<code>')
  • Cerner Managing Organization → Location.managingOrganization.reference = 'Organization/<id>'
  • Cerner Location Type → Location.type.coding.exists(code = '<code>') plus optional and Location.alias = '<alias>'
  • Custom FHIRPath → free-form boolean expression for sites that fit neither vendor pattern.

Guardrails:

  • Server-side FHIRPath validation (Custom mode): expressions are validated via MeasureEval's POST /measureeval/fhir-path/$validate (debounced); errors block saving, warnings are advisory.
  • Activation gating: the Active toggle cannot be saved as on unless every frequency query plan (Discharge, Daily, Weekly, Monthly, Adhoc) includes both an Encounter and a Location query among its initial queries. A non-compliant plan blocks saving with a message naming the plan. Rationale: with resolution active, an Encounter whose Locations were never queried could never map to the organization, silently excluding every patient. The backend enforces the same rule; the form check fails fast with an actionable message.
  • Vendor mismatch detection: a stored rule that reverse-parses to the other vendor's method drops the form into Custom FHIRPath mode with a warning; nothing is auto-deleted. Legacy where(...) expressions are recognized alongside the current exists(...) form.
  • Duplicate prevention: create-vs-update is keyed on the stored configId, and the backend enforces a single active configuration per facility.

The configuration persists as a single condition row holding the combined expression ({ fhirPath, priority: 1 }).

Locations tab

Facility View → Locations: a read-only, paged (5/10/20), sortable view of the Location Tree Table — every Location queried for the facility with the outcome of its organization check.

Columns: Mapping ID (copy-to-clipboard), Location ID, Location Name, Location Alias, Part Of Value / Part Of ID (the Location.partOf parent the resolver walked), Is Org Location (Yes/No), Is Active (Yes/No), Created, Modified.

Filters: partial-match text on Location ID / Name / Alias / Part Of Value; tri-state Org Location (Any/Yes/No); Show inactive locations checkbox — the default view is active mappings only. Typical use: after go-live, filter Is Org Location = No and confirm those locations genuinely belong to other facilities in the network.

Encounters tab

Facility View → Encounters: the patient-facing side — for each acquired Encounter, did it resolve to the reporting organization? This is the screen for "why is patient X missing from the report?"

Columns: Mapping ID (copy), Encounter ID, Patient ID, Location ID, Mapped To Org (Yes/No), Created, Modified. Filters: Encounter ID, Patient ID, tri-state Mapped To Org.

Two diagnostic behaviors:

  • Location ID is a drill-down: each encounter location renders as a link opening the Location Details dialog for the exact mapping row the resolver used — the chain encounter → its locations → the mapping decision is walkable in place.
  • Unmapped encounters explain themselves: when Mapped To Org is No, a tooltip states the probable cause, distinguishing: no locations on the encounter; locations with no matching active organization mapping; or locations checked and found to belong to another organization.

The Location Details dialog shows the full mapping record; when the mapping is inactive it warns: "This location mapping is inactive. Encounters referencing it will not map to the organization."

Normalization operation

Copy Location Alias to Type Iteratively (Facility Edit → Normalization Configuration) copies Location.alias values into Location.type as CodeableConcepts, and does the same for every parent up the partOf hierarchy, bounded by Max Iterations (default 15). This implements the Cerner alias→type normalization that the Location Type matching method depends on. Scoped per facility or per vendor; options include Resource Types, Split On Comma, and Enabled.

Implemented API surface

The UI consumes the following routes via the BFF gateway. These supersede the routes previously listed in the API Interface table above (/location-org-configs, /location-mappings); the earlier table should be treated as superseded by the as-built routes:

Purpose Method Route
Get facility's org configs GET /data/location-config/facility/{facilityId}
Create org config POST /data/location-config/facility/{facilityId}
Update org config PUT /data/location-config/{configId}
Delete org config DELETE /data/location-config/{configId}
Search location mappings GET /data/location-mappings/facility/{facilityId}/search
Get one location mapping GET /data/location-mappings/{id}
Search encounter mappings GET /data/encounter-mappings/facilities/{facilityId}/search
Validate FHIRPath POST /measureeval/fhir-path/$validate
Query plans (activation gate) GET /data/{facilityId}/QueryPlan?type={type}

Search endpoints take 1-based `pageNumber`, `pageSize`, per-column filters (`LocationId`, `LocationName`, `LocationAlias`, `PartOfValue`, `IsOrgLocation`, `IsActive`; `EncounterId`, `PatientId`, `MappedToOrg`), `sortBy`, and `sortOrder` (0 = ascending, 1 = descending). The proposed `/location-mappings/hierarchies` endpoint has no UI consumer yet.

Flag semantics

Three separately named booleans participate in org resolution:

  • Configuration isActive — is location resolution enforced for this facility at all? Gated by query-plan compliance.
  • Location mapping isActive — does this individual mapping row count? Inactive rows are ignored by the resolver, hidden by default on the Locations tab, and flagged in the details dialog.
  • Encounter mappedToOrg — the per-encounter resolution outcome shown on the Encounters tab.

Generating Adhoc Reports

  • On-demand Generation: Allows administrators to manually trigger report generation for a specific facility, reporting period, and set of measures.
  • Patient Scoping: Supports restricting adhoc reports to a specific cohort by manually entering patient IDs or uploading a CSV file of patient identifiers.
  • Submission Control: Includes an option to bypass automatic submission, enabling administrators to generate and review reports before they are sent to external entities.

Volumes

Volume Mount Path Sub-path
Azure Storage Account /usr/share/nginx/html/assets/app.config.local.json app.config.local.json

Configuration

An app.config.local.json file is used to configure the Admin UI service at deployment time, overriding the defaults in app.config.json.

{
  "baseApiUrl": "<ADMIN-BFF-BASE-URL>/api",
  "authRequired": true,
  "oauth2": {
    "enabled": true,
    "issuer": "...",
    "clientId": "...",
    "scope": "openid profile email",
    "responseType": "code"
  }
}

This configuration file can be customized at deployment time and mounted in the /assets folder, or it can be overridden by ENV variables in a docker deployment.

Authentication

The Admin UI supports authentication using:

  • OAuth2 via an identity provider (e.g., Azure AD, Keycloak), enabling secure login with centralized identity and access management.
  • Token Generation via AdminBFF /api/login, which provides session tokens for downstream API access when deployed in a simplified or standalone context.

Environment Variables

ENV variable JSON Property Description Default Value Required
LINK_BASE_API_URL baseApiUrl The base URL for the Admin BFF API. http://localhost Yes
LINK_AUTH_REQUIRED authRequired Indicates whether authentication is required for the Admin UI. true Yes
LINK_OAUTH2_ENABLED oauth2.enabled Indicates whether OAuth2 authentication is enabled. false Yes
LINK_OAUTH2_ISSUER oauth2.issuer The issuer URL for the OAuth2 provider. null If oauth2 enabled
LINK_OAUTH2_CLIENT_ID oauth2.clientId The client ID for the OAuth2 application. null If oauth2 enabled
LINK_OAUTH2_SCOPE oauth2.scope The scope for the OAuth2 application. openid profile email If oauth2 enabled
LINK_OAUTH2_RESPONSE_TYPE oauth2.responseType The response type for the OAuth2 application. code If oauth2 enabled
GRAFANA_URL grafanaUrl URL for the Grafana UI. http://localhost:3000 Yes
KAFKA_URL kafkaUrl URL for the Kafka UI. http://localhost:9095 If Kafka UI is available

Local Development with Proxy Configuration

To facilitate local development and testing, the Admin UI can be configured to proxy API requests to a local or remote instance of the Admin BFF service. This is achieved by setting up a proxy configuration file.

{
    "/api": {
      "target": "http://localhost:5218",
      "secure": false,
      "changeOrigin": true,
      "logLevel": "debug"
    }
}

To use the proxy configuration, start the Angular development server with the following command:

ng serve --proxy-config proxy.conf.json --configuration=development

Relationships

flowchart LR
nAdminUI_93011C6["Service: Admin UI (front-end)"]