mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-26203] new apps dialog (#16696)
This commit is contained in:
@@ -13,11 +13,12 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { getById } from "@bitwarden/common/platform/misc";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { ToastService, DialogService } from "@bitwarden/components";
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||
|
||||
import { ActivityCardComponent } from "./activity-card.component";
|
||||
import { PasswordChangeMetricComponent } from "./activity-cards/password-change-metric.component";
|
||||
import { NewApplicationsDialogComponent } from "./new-applications-dialog.component";
|
||||
import { ApplicationsLoadingComponent } from "./risk-insights-loading.component";
|
||||
import { RiskInsightsTabType } from "./risk-insights.component";
|
||||
|
||||
@@ -37,6 +38,7 @@ export class AllActivityComponent implements OnInit {
|
||||
totalCriticalAppsCount = 0;
|
||||
totalCriticalAppsAtRiskCount = 0;
|
||||
newApplicationsCount = 0;
|
||||
newApplications: string[] = [];
|
||||
passwordChangeMetricHasProgressBar = false;
|
||||
|
||||
destroyRef = inject(DestroyRef);
|
||||
@@ -57,6 +59,7 @@ export class AllActivityComponent implements OnInit {
|
||||
this.totalCriticalAppsAtRiskMemberCount = summary.totalCriticalAtRiskMemberCount;
|
||||
this.totalCriticalAppsCount = summary.totalCriticalApplicationCount;
|
||||
this.totalCriticalAppsAtRiskCount = summary.totalCriticalAtRiskApplicationCount;
|
||||
this.newApplications = summary.newApplications;
|
||||
this.newApplicationsCount = summary.newApplications.length;
|
||||
});
|
||||
|
||||
@@ -76,6 +79,7 @@ export class AllActivityComponent implements OnInit {
|
||||
protected allActivitiesService: AllActivitiesService,
|
||||
private toastService: ToastService,
|
||||
private i18nService: I18nService,
|
||||
private dialogService: DialogService,
|
||||
) {}
|
||||
|
||||
get RiskInsightsTabType() {
|
||||
@@ -89,14 +93,13 @@ export class AllActivityComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* Handles the review new applications button click.
|
||||
* Shows a toast notification as a placeholder until the dialog is implemented.
|
||||
* TODO: Implement dialog for reviewing new applications (follow-up task)
|
||||
* Opens a dialog showing the list of new applications that can be marked as critical.
|
||||
*/
|
||||
onReviewNewApplications = () => {
|
||||
this.toastService.showToast({
|
||||
variant: "info",
|
||||
title: this.i18nService.t("applicationsNeedingReview"),
|
||||
message: this.i18nService.t("newApplicationsWithCount", this.newApplicationsCount.toString()),
|
||||
onReviewNewApplications = async () => {
|
||||
const dialogRef = NewApplicationsDialogComponent.open(this.dialogService, {
|
||||
newApplications: this.newApplications,
|
||||
});
|
||||
|
||||
await firstValueFrom(dialogRef.closed);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<bit-dialog [dialogSize]="'default'">
|
||||
<span bitDialogTitle>{{ "prioritizeCriticalApplications" | i18n }}</span>
|
||||
<div bitDialogContent>
|
||||
<div class="tw-overflow-x-auto">
|
||||
<table class="tw-w-full tw-border-collapse">
|
||||
<thead>
|
||||
<tr class="tw-border-b tw-border-secondary-300">
|
||||
<th bitTypography="body2" class="tw-text-left tw-py-3 tw-px-2 tw-w-12"></th>
|
||||
<th bitTypography="body2" class="tw-text-left tw-py-3 tw-px-2 tw-font-semibold">
|
||||
{{ "application" | i18n }}
|
||||
</th>
|
||||
<th bitTypography="body2" class="tw-text-right tw-py-3 tw-px-2 tw-font-semibold">
|
||||
{{ "atRiskItems" | i18n }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (app of newApplications; track app) {
|
||||
<tr class="tw-border-b tw-border-secondary-300 hover:tw-bg-background-alt">
|
||||
<td class="tw-py-3 tw-px-2">
|
||||
<button
|
||||
type="button"
|
||||
class="tw-bg-transparent tw-border-0 tw-p-0 tw-cursor-pointer"
|
||||
(click)="toggleSelection(app)"
|
||||
[attr.aria-label]="
|
||||
isSelected(app) ? ('unselectApplication' | i18n) : ('selectApplication' | i18n)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="bwi tw-text-muted"
|
||||
[ngClass]="isSelected(app) ? 'bwi-star-f' : 'bwi-star'"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</td>
|
||||
<td bitTypography="body1" class="tw-py-3 tw-px-2">
|
||||
<div class="tw-flex tw-items-center tw-gap-2">
|
||||
<i class="bwi bwi-globe tw-text-muted" aria-hidden="true"></i>
|
||||
<span>{{ app }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td bitTypography="body1" class="tw-py-3 tw-px-2 tw-text-right tw-text-muted">—</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container bitDialogFooter>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
size="small"
|
||||
buttonType="primary"
|
||||
(click)="onMarkAsCritical()"
|
||||
[attr.aria-label]="'markAsCritical' | i18n"
|
||||
>
|
||||
{{ "markAsCritical" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
size="small"
|
||||
buttonType="secondary"
|
||||
[bitDialogClose]
|
||||
[attr.aria-label]="'cancel' | i18n"
|
||||
>
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
@@ -0,0 +1,86 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, inject } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import {
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
DialogService,
|
||||
ToastService,
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
export interface NewApplicationsDialogData {
|
||||
newApplications: string[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: "./new-applications-dialog.component.html",
|
||||
imports: [CommonModule, ButtonModule, DialogModule, TypographyModule, I18nPipe],
|
||||
})
|
||||
export class NewApplicationsDialogComponent {
|
||||
protected newApplications: string[] = [];
|
||||
protected selectedApplications: Set<string> = new Set<string>();
|
||||
|
||||
private toastService = inject(ToastService);
|
||||
private i18nService = inject(I18nService);
|
||||
|
||||
/**
|
||||
* Opens the new applications dialog
|
||||
* @param dialogService The dialog service instance
|
||||
* @param data Dialog data containing the list of new applications
|
||||
* @returns Dialog reference
|
||||
*/
|
||||
static open(dialogService: DialogService, data: NewApplicationsDialogData) {
|
||||
const ref = dialogService.open<boolean, NewApplicationsDialogData>(
|
||||
NewApplicationsDialogComponent,
|
||||
{
|
||||
data,
|
||||
},
|
||||
);
|
||||
|
||||
// Set the component's data after opening
|
||||
const instance = ref.componentInstance as NewApplicationsDialogComponent;
|
||||
if (instance) {
|
||||
instance.newApplications = data.newApplications;
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the selection state of an application.
|
||||
* @param applicationName The application to toggle
|
||||
*/
|
||||
toggleSelection = (applicationName: string) => {
|
||||
if (this.selectedApplications.has(applicationName)) {
|
||||
this.selectedApplications.delete(applicationName);
|
||||
} else {
|
||||
this.selectedApplications.add(applicationName);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if an application is currently selected.
|
||||
* @param applicationName The application to check
|
||||
* @returns True if selected, false otherwise
|
||||
*/
|
||||
isSelected = (applicationName: string): boolean => {
|
||||
return this.selectedApplications.has(applicationName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Placeholder handler for mark as critical functionality.
|
||||
* Shows a toast notification with count of selected applications.
|
||||
* TODO: Implement actual mark as critical functionality (PM-26203 follow-up)
|
||||
*/
|
||||
onMarkAsCritical = () => {
|
||||
const selectedCount = this.selectedApplications.size;
|
||||
this.toastService.showToast({
|
||||
variant: "info",
|
||||
title: this.i18nService.t("markAsCritical"),
|
||||
message: `${selectedCount} ${this.i18nService.t("applicationsSelected")}`,
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user