1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 05:00:10 +00:00

minor refactors

This commit is contained in:
Tom
2026-01-20 15:55:29 -05:00
parent 03fdae924d
commit ab75ea8659
8 changed files with 120 additions and 80 deletions

View File

@@ -108,42 +108,7 @@
<!-- Health Status (combined cell) -->
<td bitCell colspan="2" class="tw-text-center">
<div class="tw-flex tw-justify-center tw-gap-2">
<!-- Weak -->
@if (cipher.weakPassword === true) {
<span bitBadge variant="warning" title="{{ 'weak' | i18n }}">W</span>
}
<!-- Reused -->
@if (cipher.reusedPassword === true) {
<span bitBadge variant="warning" title="{{ 'reused' | i18n }}">R</span>
}
<!-- Exposed -->
@if (cipher.exposedPassword === true) {
<span
bitBadge
variant="danger"
[title]="cipher.exposedCount + ' ' + ('timesExposed' | i18n)"
>E</span
>
}
<!-- Healthy -->
@if (
cipher.status === RiskInsightsItemStatus.Healthy &&
!cipher.weakPassword &&
!cipher.reusedPassword &&
!cipher.exposedPassword
) {
<i class="bwi bwi-check tw-text-success-600" aria-hidden="true"></i>
}
<!-- Loading -->
@if (cipher.status === null) {
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
aria-hidden="true"
title="{{ 'loading' | i18n }}"
></i>
}
</div>
<app-cipher-health-badges [cipher]="cipher"></app-cipher-health-badges>
</td>
<!-- Member Count -->
@@ -179,6 +144,15 @@
}
</tbody>
</table>
} @else if (processingPhase() === ProcessingPhase.Error) {
<!-- Error State -->
<div class="tw-text-center tw-py-8 tw-text-danger-600">
<i class="bwi bwi-error tw-text-4xl tw-mb-4" aria-hidden="true"></i>
<p>{{ "errorOccurred" | i18n }}</p>
@if (error()) {
<p class="tw-text-sm tw-text-muted tw-mt-2">{{ error() }}</p>
}
</div>
} @else if (processingPhase() === ProcessingPhase.Idle) {
<!-- Initial State -->
<div class="tw-text-center tw-py-8 tw-text-muted">

View File

@@ -20,6 +20,8 @@ import {
import { BadgeModule, TableDataSource, TableModule } from "@bitwarden/components";
/* eslint-enable no-restricted-imports */
import { CipherHealthBadgesComponent } from "../shared/cipher-health-badges.component";
/**
* Applications tab component for the Risk Insights Prototype.
*
@@ -33,7 +35,7 @@ import { BadgeModule, TableDataSource, TableModule } from "@bitwarden/components
selector: "app-risk-insights-prototype-applications",
templateUrl: "./risk-insights-prototype-applications.component.html",
standalone: true,
imports: [CommonModule, JslibModule, TableModule, BadgeModule],
imports: [CommonModule, JslibModule, TableModule, BadgeModule, CipherHealthBadgesComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RiskInsightsPrototypeApplicationsComponent {
@@ -53,6 +55,7 @@ export class RiskInsightsPrototypeApplicationsComponent {
// Processing state
readonly processingPhase = this.orchestrator.processingPhase;
readonly error = this.orchestrator.error;
// Results
readonly applications = this.orchestrator.applications;

View File

@@ -116,6 +116,15 @@
</td>
</ng-template>
</bit-table-scroll>
} @else if (processingPhase() === ProcessingPhase.Error) {
<!-- Error State -->
<div class="tw-text-center tw-py-8 tw-text-danger-600">
<i class="bwi bwi-error tw-text-4xl tw-mb-4" aria-hidden="true"></i>
<p>{{ "errorOccurred" | i18n }}</p>
@if (error()) {
<p class="tw-text-sm tw-text-muted tw-mt-2">{{ error() }}</p>
}
</div>
} @else if (processingPhase() === ProcessingPhase.Idle) {
<!-- Initial State -->
<div class="tw-text-center tw-py-8 tw-text-muted">

View File

@@ -42,6 +42,7 @@ export class RiskInsightsPrototypeItemsComponent {
// Processing state
readonly processingPhase = this.orchestrator.processingPhase;
readonly error = this.orchestrator.error;
// Results
readonly items = this.orchestrator.items;

View File

@@ -105,42 +105,7 @@
<!-- Health Status (combined cell) -->
<td bitCell class="tw-text-center">
<div class="tw-flex tw-justify-center tw-gap-2">
<!-- Weak -->
@if (cipher.weakPassword === true) {
<span bitBadge variant="warning" title="{{ 'weak' | i18n }}">W</span>
}
<!-- Reused -->
@if (cipher.reusedPassword === true) {
<span bitBadge variant="warning" title="{{ 'reused' | i18n }}">R</span>
}
<!-- Exposed -->
@if (cipher.exposedPassword === true) {
<span
bitBadge
variant="danger"
[title]="cipher.exposedCount + ' ' + ('timesExposed' | i18n)"
>E</span
>
}
<!-- Healthy -->
@if (
cipher.status === RiskInsightsItemStatus.Healthy &&
!cipher.weakPassword &&
!cipher.reusedPassword &&
!cipher.exposedPassword
) {
<i class="bwi bwi-check tw-text-success-600" aria-hidden="true"></i>
}
<!-- Loading -->
@if (cipher.status === null) {
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
aria-hidden="true"
title="{{ 'loading' | i18n }}"
></i>
}
</div>
<app-cipher-health-badges [cipher]="cipher"></app-cipher-health-badges>
</td>
<!-- Status -->
@@ -163,6 +128,15 @@
}
</tbody>
</table>
} @else if (processingPhase() === ProcessingPhase.Error) {
<!-- Error State -->
<div class="tw-text-center tw-py-8 tw-text-danger-600">
<i class="bwi bwi-error tw-text-4xl tw-mb-4" aria-hidden="true"></i>
<p>{{ "errorOccurred" | i18n }}</p>
@if (error()) {
<p class="tw-text-sm tw-text-muted tw-mt-2">{{ error() }}</p>
}
</div>
} @else if (processingPhase() === ProcessingPhase.Idle) {
<!-- Initial State -->
<div class="tw-text-center tw-py-8 tw-text-muted">

View File

@@ -20,6 +20,8 @@ import {
import { BadgeModule, TableDataSource, TableModule } from "@bitwarden/components";
/* eslint-enable no-restricted-imports */
import { CipherHealthBadgesComponent } from "../shared/cipher-health-badges.component";
/**
* Members tab component for the Risk Insights Prototype.
*
@@ -33,7 +35,7 @@ import { BadgeModule, TableDataSource, TableModule } from "@bitwarden/components
selector: "app-risk-insights-prototype-members",
templateUrl: "./risk-insights-prototype-members.component.html",
standalone: true,
imports: [CommonModule, JslibModule, TableModule, BadgeModule],
imports: [CommonModule, JslibModule, TableModule, BadgeModule, CipherHealthBadgesComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RiskInsightsPrototypeMembersComponent {
@@ -49,6 +51,7 @@ export class RiskInsightsPrototypeMembersComponent {
// Processing state
readonly processingPhase = this.orchestrator.processingPhase;
readonly memberProgress = this.orchestrator.memberProgress;
readonly error = this.orchestrator.error;
// Results
readonly members = this.orchestrator.members;
@@ -77,9 +80,6 @@ export class RiskInsightsPrototypeMembersComponent {
return new Map(items.map((item) => [item.cipherId, item]));
});
/** Whether we've requested member data to be built (lazy loading trigger) */
private hasRequestedMemberData = false;
// ============================================================================
// Lifecycle
// ============================================================================
@@ -92,12 +92,14 @@ export class RiskInsightsPrototypeMembersComponent {
});
// Effect to trigger lazy loading of member aggregations when phase is ready
// Check actual state (members array length) instead of a local flag to handle
// component re-creation and state resets correctly
effect(() => {
const phase = this.processingPhase();
const isReady = phase === ProcessingPhase.Complete || phase === ProcessingPhase.RunningHibp;
const membersEmpty = this.members().length === 0;
if (isReady && !this.hasRequestedMemberData) {
this.hasRequestedMemberData = true;
if (isReady && membersEmpty) {
this.orchestrator.ensureMemberAggregationsBuilt();
}
});

View File

@@ -0,0 +1,74 @@
/* eslint-disable no-restricted-imports -- Prototype feature using licensed services */
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
RiskInsightsItem,
RiskInsightsItemStatus,
} from "@bitwarden/common/dirt/reports/risk-insights";
import { BadgeModule } from "@bitwarden/components";
/* eslint-enable no-restricted-imports */
/**
* Shared component for displaying cipher health badges (Weak/Reused/Exposed indicators).
*
* Used in the Applications and Members tabs' expanded row views to display
* consistent health status badges for individual ciphers.
*
* Displays:
* - W badge (warning) for weak passwords
* - R badge (warning) for reused passwords
* - E badge (danger) for exposed passwords
* - Check icon for healthy items
* - Spinner for items still loading
*/
@Component({
selector: "app-cipher-health-badges",
standalone: true,
imports: [CommonModule, JslibModule, BadgeModule],
template: `
<div class="tw-flex tw-justify-center tw-gap-2">
@if (cipher().weakPassword === true) {
<span bitBadge variant="warning" title="{{ 'weak' | i18n }}">W</span>
}
@if (cipher().reusedPassword === true) {
<span bitBadge variant="warning" title="{{ 'reused' | i18n }}">R</span>
}
@if (cipher().exposedPassword === true) {
<span
bitBadge
variant="danger"
[title]="cipher().exposedCount + ' ' + ('timesExposed' | i18n)"
>E</span
>
}
@if (isHealthy()) {
<i class="bwi bwi-check tw-text-success-600" aria-hidden="true"></i>
}
@if (cipher().status === null) {
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
aria-hidden="true"
title="{{ 'loading' | i18n }}"
></i>
}
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CipherHealthBadgesComponent {
/** The cipher item to display health badges for */
readonly cipher = input.required<RiskInsightsItem>();
/** Computed signal to determine if the cipher is healthy with no issues */
protected readonly isHealthy = computed(() => {
const c = this.cipher();
return (
c.status === RiskInsightsItemStatus.Healthy &&
!c.weakPassword &&
!c.reusedPassword &&
!c.exposedPassword
);
});
}

View File

@@ -4,6 +4,7 @@ import { from, Observable, of } from "rxjs";
import { catchError, switchMap, tap, last, map } from "rxjs/operators";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -48,6 +49,7 @@ export class RiskInsightsPrototypeOrchestrationService {
private readonly cipherAccessMappingService = inject(CipherAccessMappingService);
private readonly passwordHealthService = inject(PasswordHealthService);
private readonly riskInsightsService = inject(RiskInsightsPrototypeService);
private readonly logService = inject(LogService);
private readonly destroyRef = inject(DestroyRef);
// ============================================================================
@@ -466,7 +468,8 @@ export class RiskInsightsPrototypeOrchestrationService {
}),
last(),
map((): void => undefined),
catchError(() => {
catchError((err: unknown) => {
this.logService.error("[RiskInsightsPrototype] Error loading member counts:", err);
return of(undefined);
}),
);