- {{ currentMessage() | i18n }}
+ {{ stepConfig[progressStep()].message | i18n }}
{{ "thisMightTakeFewMinutes" | i18n }}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/report-loading.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/report-loading.component.ts
new file mode 100644
index 00000000000..f3cb89dff55
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/report-loading.component.ts
@@ -0,0 +1,33 @@
+import { CommonModule } from "@angular/common";
+import { Component, input } from "@angular/core";
+
+import { JslibModule } from "@bitwarden/angular/jslib.module";
+import { ReportProgress } from "@bitwarden/bit-common/dirt/reports/risk-insights";
+import { ProgressModule } from "@bitwarden/components";
+
+// Map of progress step to display config
+const ProgressStepConfig = Object.freeze({
+ [ReportProgress.FetchingMembers]: { message: "fetchingMemberData", progress: 20 },
+ [ReportProgress.AnalyzingPasswords]: { message: "analyzingPasswordHealth", progress: 40 },
+ [ReportProgress.CalculatingRisks]: { message: "calculatingRiskScores", progress: 60 },
+ [ReportProgress.GeneratingReport]: { message: "generatingReportData", progress: 80 },
+ [ReportProgress.Saving]: { message: "savingReport", progress: 95 },
+ [ReportProgress.Complete]: { message: "compilingInsights", progress: 100 },
+} as const);
+
+// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
+// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
+@Component({
+ selector: "dirt-report-loading",
+ imports: [CommonModule, JslibModule, ProgressModule],
+ templateUrl: "./report-loading.component.html",
+})
+export class ReportLoadingComponent {
+ // Progress step input from parent component.
+ // Recommended: delay emissions to this input to ensure each step displays for a minimum time.
+ // Refer to risk-insights.component for implementation example.
+ readonly progressStep = input(ReportProgress.FetchingMembers);
+
+ // Expose config map to template for direct lookup
+ protected readonly stepConfig = ProgressStepConfig;
+}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-loading.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-loading.component.ts
deleted file mode 100644
index d4c97a6fd5c..00000000000
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-loading.component.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { Component, DestroyRef, inject, OnInit, signal } from "@angular/core";
-import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-
-import { JslibModule } from "@bitwarden/angular/jslib.module";
-import {
- ReportProgress,
- RiskInsightsDataService,
-} from "@bitwarden/bit-common/dirt/reports/risk-insights";
-import { ProgressModule } from "@bitwarden/components";
-
-const PROGRESS_STEPS = [
- { step: ReportProgress.FetchingMembers, message: "fetchingMemberData", progress: 20 },
- { step: ReportProgress.AnalyzingPasswords, message: "analyzingPasswordHealth", progress: 40 },
- { step: ReportProgress.CalculatingRisks, message: "calculatingRiskScores", progress: 60 },
- { step: ReportProgress.GeneratingReport, message: "generatingReportData", progress: 80 },
- { step: ReportProgress.Saving, message: "savingReport", progress: 95 },
- { step: ReportProgress.Complete, message: "compilingInsights", progress: 100 },
-] as const;
-
-type LoadingMessage = (typeof PROGRESS_STEPS)[number]["message"];
-
-// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
-// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
-@Component({
- selector: "dirt-risk-insights-loading",
- imports: [CommonModule, JslibModule, ProgressModule],
- templateUrl: "./risk-insights-loading.component.html",
-})
-export class ApplicationsLoadingComponent implements OnInit {
- private dataService = inject(RiskInsightsDataService);
- private destroyRef = inject(DestroyRef);
-
- readonly currentMessage = signal(PROGRESS_STEPS[0].message);
- readonly progress = signal(PROGRESS_STEPS[0].progress);
-
- ngOnInit(): void {
- // Subscribe to actual progress events from the orchestrator
- this.dataService.reportProgress$
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe((progressStep) => {
- if (progressStep === null) {
- // Reset to initial state
- this.currentMessage.set(PROGRESS_STEPS[0].message);
- this.progress.set(PROGRESS_STEPS[0].progress);
- return;
- }
-
- // Find the matching step configuration
- const stepConfig = PROGRESS_STEPS.find((config) => config.step === progressStep);
- if (stepConfig) {
- this.currentMessage.set(stepConfig.message);
- this.progress.set(stepConfig.progress);
- }
- });
- }
-}