|
2 | 2 | @inject HttpClient Http |
3 | 3 | @inject IConfiguration Configuration |
4 | 4 | @inject ISnackbar Snackbar |
| 5 | +@inject ITenantContextService TenantContextService |
| 6 | +@inject IOperatingModeService OperatingModeService |
5 | 7 |
|
6 | 8 | <PageTitle>Settings - Cloud Health Office</PageTitle> |
7 | 9 |
|
|
153 | 155 | </MudAlert> |
154 | 156 | </MudPaper> |
155 | 157 | </MudTabPanel> |
| 158 | + |
| 159 | + <!-- Operating Mode Tab --> |
| 160 | + <MudTabPanel Text="Operating Mode" Icon="@Icons.Material.Filled.Tune"> |
| 161 | + <MudPaper Class="pa-4 mt-4"> |
| 162 | + <MudText Typo="Typo.h5" GutterBottom="true">Operating Mode Configuration</MudText> |
| 163 | + <MudText Typo="Typo.body2" Color="Color.Secondary" Class="mb-4"> |
| 164 | + Controls how Cloud Health Office engines interact with your existing core admin system |
| 165 | + </MudText> |
| 166 | + |
| 167 | + @if (isLoadingOperatingMode) |
| 168 | + { |
| 169 | + <MudProgressCircular Indeterminate="true" /> |
| 170 | + } |
| 171 | + else if (operatingModeConfig != null) |
| 172 | + { |
| 173 | + @if (isUsingDefaults) |
| 174 | + { |
| 175 | + <MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Class="mb-4" Icon="@Icons.Material.Filled.Info"> |
| 176 | + Operating mode configuration loaded from defaults. Contact support or your account team to customize engine modes for your tenant. |
| 177 | + </MudAlert> |
| 178 | + } |
| 179 | + |
| 180 | + <MudSimpleTable Hover="true" Striped="true" Dense="false" Class="mb-4" Elevation="0" Style="border: 1px solid var(--mud-palette-table-lines);"> |
| 181 | + <thead> |
| 182 | + <tr> |
| 183 | + <th>Engine</th> |
| 184 | + <th>Current Mode</th> |
| 185 | + <th>Description</th> |
| 186 | + </tr> |
| 187 | + </thead> |
| 188 | + <tbody> |
| 189 | + @foreach (var engine in operatingModeConfig.Engines) |
| 190 | + { |
| 191 | + <tr> |
| 192 | + <td><strong>@FormatEngineName(engine.Key)</strong></td> |
| 193 | + <td> |
| 194 | + @if (engine.Value.Equals("augment", StringComparison.OrdinalIgnoreCase)) |
| 195 | + { |
| 196 | + <MudChip Color="Color.Warning" Size="Size.Small">Augment</MudChip> |
| 197 | + } |
| 198 | + else |
| 199 | + { |
| 200 | + <MudChip Color="Color.Success" Size="Size.Small">Replace</MudChip> |
| 201 | + } |
| 202 | + </td> |
| 203 | + <td><MudText Typo="Typo.body2">@GetEngineDescription(engine.Key, engine.Value)</MudText></td> |
| 204 | + </tr> |
| 205 | + } |
| 206 | + </tbody> |
| 207 | + </MudSimpleTable> |
| 208 | + |
| 209 | + <MudText Typo="Typo.subtitle2" Class="mt-4 mb-2">Mode Definitions</MudText> |
| 210 | + |
| 211 | + <MudGrid> |
| 212 | + <MudItem xs="12" md="6"> |
| 213 | + <MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Icon="@Icons.Material.Filled.CompareArrows" NoIcon="false"> |
| 214 | + <strong>Augment</strong> — Cloud Health Office runs alongside your existing system. Both results are computed and compared, but your legacy system remains authoritative. |
| 215 | + </MudAlert> |
| 216 | + </MudItem> |
| 217 | + <MudItem xs="12" md="6"> |
| 218 | + <MudAlert Severity="Severity.Success" Variant="Variant.Outlined" Icon="@Icons.Material.Filled.CheckCircle" NoIcon="false"> |
| 219 | + <strong>Replace</strong> — Cloud Health Office is the authoritative system for this function. Your legacy system is no longer used for this engine. |
| 220 | + </MudAlert> |
| 221 | + </MudItem> |
| 222 | + </MudGrid> |
| 223 | + |
| 224 | + <MudText Typo="Typo.caption" Color="Color.Secondary" Class="mt-4"> |
| 225 | + @if (operatingModeConfig.UpdatedAt.HasValue) |
| 226 | + { |
| 227 | + <span>Last updated: @operatingModeConfig.UpdatedAt.Value.ToString("MMM dd, yyyy 'at' h:mm tt") UTC. </span> |
| 228 | + } |
| 229 | + Operating mode changes are managed by your account team. |
| 230 | + </MudText> |
| 231 | + } |
| 232 | + </MudPaper> |
| 233 | + </MudTabPanel> |
156 | 234 | </MudTabs> |
157 | 235 | </MudContainer> |
158 | 236 |
|
159 | 237 | @code { |
160 | 238 | private SubscriptionInfo? subscription; |
161 | 239 | private bool isLoadingSubscription = true; |
| 240 | + private OperatingModeConfiguration? operatingModeConfig; |
| 241 | + private bool isLoadingOperatingMode = true; |
| 242 | + private bool isUsingDefaults; |
162 | 243 |
|
163 | 244 | protected override async Task OnInitializedAsync() |
164 | 245 | { |
165 | | - await LoadSubscriptionInfo(); |
| 246 | + await Task.WhenAll(LoadSubscriptionInfo(), LoadOperatingModeAsync()); |
166 | 247 | } |
167 | 248 |
|
168 | 249 | private async Task LoadSubscriptionInfo() |
|
243 | 324 | return Task.CompletedTask; |
244 | 325 | } |
245 | 326 |
|
| 327 | + private async Task LoadOperatingModeAsync() |
| 328 | + { |
| 329 | + isLoadingOperatingMode = true; |
| 330 | + try |
| 331 | + { |
| 332 | + var tenantId = await TenantContextService.GetTenantIdAsync(); |
| 333 | + if (string.IsNullOrEmpty(tenantId)) |
| 334 | + { |
| 335 | + isUsingDefaults = true; |
| 336 | + operatingModeConfig = new OperatingModeConfiguration |
| 337 | + { |
| 338 | + TenantId = "unknown", |
| 339 | + Engines = new Dictionary<string, string>(OperatingModeConfiguration.DefaultEngines, StringComparer.OrdinalIgnoreCase), |
| 340 | + UpdatedAt = null |
| 341 | + }; |
| 342 | + return; |
| 343 | + } |
| 344 | + |
| 345 | + operatingModeConfig = await OperatingModeService.GetOperatingModeAsync(tenantId); |
| 346 | + |
| 347 | + // The service sets UpdatedAt = null when returning defaults (service unreachable). |
| 348 | + // A real API response will always have a non-null UpdatedAt. |
| 349 | + isUsingDefaults = !operatingModeConfig.UpdatedAt.HasValue; |
| 350 | + } |
| 351 | + catch (Exception ex) |
| 352 | + { |
| 353 | + Console.WriteLine($"Failed to load operating mode: {ex.Message}"); |
| 354 | + isUsingDefaults = true; |
| 355 | + } |
| 356 | + finally |
| 357 | + { |
| 358 | + isLoadingOperatingMode = false; |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + private static string FormatEngineName(string key) => key switch |
| 363 | + { |
| 364 | + "benefitCalculation" => "Benefit Calculation", |
| 365 | + "rateResolution" => "Rate Resolution", |
| 366 | + "ncciEdits" => "NCCI Edits", |
| 367 | + "eligibilityVerification" => "Eligibility Verification", |
| 368 | + "claimsAdjudication" => "Claims Adjudication", |
| 369 | + _ => System.Text.RegularExpressions.Regex.Replace(key, "([a-z])([A-Z])", "$1 $2") |
| 370 | + }; |
| 371 | + |
| 372 | + private static string GetEngineDescription(string key, string mode) |
| 373 | + { |
| 374 | + var isAugment = mode.Equals("augment", StringComparison.OrdinalIgnoreCase); |
| 375 | + return key switch |
| 376 | + { |
| 377 | + "benefitCalculation" => isAugment |
| 378 | + ? "Benefit calculations run in parallel with your legacy system for comparison." |
| 379 | + : "Cloud Health Office is the sole engine computing member benefit amounts.", |
| 380 | + "rateResolution" => isAugment |
| 381 | + ? "Rate lookups are performed by both systems; legacy rates are authoritative." |
| 382 | + : "Cloud Health Office resolves all provider and procedure rates directly.", |
| 383 | + "ncciEdits" => isAugment |
| 384 | + ? "NCCI compliance edits are applied by both systems; legacy edits take precedence." |
| 385 | + : "Cloud Health Office applies all NCCI bundling and compliance edits.", |
| 386 | + "eligibilityVerification" => isAugment |
| 387 | + ? "Eligibility checks run through both systems; your existing system is the source of truth." |
| 388 | + : "Cloud Health Office handles all member eligibility verification.", |
| 389 | + "claimsAdjudication" => isAugment |
| 390 | + ? "Claims are adjudicated by both engines; your legacy system's decisions are authoritative." |
| 391 | + : "Cloud Health Office is the authoritative claims adjudication engine.", |
| 392 | + _ => isAugment |
| 393 | + ? "Running alongside your existing system in comparison mode." |
| 394 | + : "Cloud Health Office is the authoritative system for this function." |
| 395 | + }; |
| 396 | + } |
| 397 | + |
246 | 398 | private class SubscriptionInfo |
247 | 399 | { |
248 | 400 | public string Tier { get; set; } = ""; |
|
0 commit comments