Admin UI (front-end)
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
.tgzpackages 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:
- How does Link decide which locations belong to my facility? → the Reporting Organization configuration (per facility, with an Active flag).
- Which locations has Link actually seen, and did they map to my organization? → the Locations tab.
- 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 optionaland 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 currentexists(...)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)"]