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:
@@ -187,6 +187,18 @@
|
|||||||
"markAppAsCritical": {
|
"markAppAsCritical": {
|
||||||
"message": "Mark app as critical"
|
"message": "Mark app as critical"
|
||||||
},
|
},
|
||||||
|
"markAsCritical": {
|
||||||
|
"message": "Mark as critical"
|
||||||
|
},
|
||||||
|
"applicationsSelected": {
|
||||||
|
"message": "applications selected"
|
||||||
|
},
|
||||||
|
"selectApplication": {
|
||||||
|
"message": "Select application"
|
||||||
|
},
|
||||||
|
"unselectApplication": {
|
||||||
|
"message": "Unselect application"
|
||||||
|
},
|
||||||
"applicationsMarkedAsCriticalSuccess": {
|
"applicationsMarkedAsCriticalSuccess": {
|
||||||
"message": "Applications marked as critical"
|
"message": "Applications marked as critical"
|
||||||
},
|
},
|
||||||
@@ -298,6 +310,15 @@
|
|||||||
"reviewNow": {
|
"reviewNow": {
|
||||||
"message": "Review now"
|
"message": "Review now"
|
||||||
},
|
},
|
||||||
|
"prioritizeCriticalApplications": {
|
||||||
|
"message": "Prioritize critical applications"
|
||||||
|
},
|
||||||
|
"atRiskItems": {
|
||||||
|
"message": "At-risk items"
|
||||||
|
},
|
||||||
|
"markAsCriticalPlaceholder": {
|
||||||
|
"message": "Mark as critical functionality will be implemented in a future update"
|
||||||
|
},
|
||||||
"unmarkAsCritical": {
|
"unmarkAsCritical": {
|
||||||
"message": "Unmark as critical"
|
"message": "Unmark as critical"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
|||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { getById } from "@bitwarden/common/platform/misc";
|
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 { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||||
|
|
||||||
import { ActivityCardComponent } from "./activity-card.component";
|
import { ActivityCardComponent } from "./activity-card.component";
|
||||||
import { PasswordChangeMetricComponent } from "./activity-cards/password-change-metric.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 { ApplicationsLoadingComponent } from "./risk-insights-loading.component";
|
||||||
import { RiskInsightsTabType } from "./risk-insights.component";
|
import { RiskInsightsTabType } from "./risk-insights.component";
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ export class AllActivityComponent implements OnInit {
|
|||||||
totalCriticalAppsCount = 0;
|
totalCriticalAppsCount = 0;
|
||||||
totalCriticalAppsAtRiskCount = 0;
|
totalCriticalAppsAtRiskCount = 0;
|
||||||
newApplicationsCount = 0;
|
newApplicationsCount = 0;
|
||||||
|
newApplications: string[] = [];
|
||||||
passwordChangeMetricHasProgressBar = false;
|
passwordChangeMetricHasProgressBar = false;
|
||||||
|
|
||||||
destroyRef = inject(DestroyRef);
|
destroyRef = inject(DestroyRef);
|
||||||
@@ -57,6 +59,7 @@ export class AllActivityComponent implements OnInit {
|
|||||||
this.totalCriticalAppsAtRiskMemberCount = summary.totalCriticalAtRiskMemberCount;
|
this.totalCriticalAppsAtRiskMemberCount = summary.totalCriticalAtRiskMemberCount;
|
||||||
this.totalCriticalAppsCount = summary.totalCriticalApplicationCount;
|
this.totalCriticalAppsCount = summary.totalCriticalApplicationCount;
|
||||||
this.totalCriticalAppsAtRiskCount = summary.totalCriticalAtRiskApplicationCount;
|
this.totalCriticalAppsAtRiskCount = summary.totalCriticalAtRiskApplicationCount;
|
||||||
|
this.newApplications = summary.newApplications;
|
||||||
this.newApplicationsCount = summary.newApplications.length;
|
this.newApplicationsCount = summary.newApplications.length;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,6 +79,7 @@ export class AllActivityComponent implements OnInit {
|
|||||||
protected allActivitiesService: AllActivitiesService,
|
protected allActivitiesService: AllActivitiesService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
|
private dialogService: DialogService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get RiskInsightsTabType() {
|
get RiskInsightsTabType() {
|
||||||
@@ -89,14 +93,13 @@ export class AllActivityComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the review new applications button click.
|
* Handles the review new applications button click.
|
||||||
* Shows a toast notification as a placeholder until the dialog is implemented.
|
* Opens a dialog showing the list of new applications that can be marked as critical.
|
||||||
* TODO: Implement dialog for reviewing new applications (follow-up task)
|
|
||||||
*/
|
*/
|
||||||
onReviewNewApplications = () => {
|
onReviewNewApplications = async () => {
|
||||||
this.toastService.showToast({
|
const dialogRef = NewApplicationsDialogComponent.open(this.dialogService, {
|
||||||
variant: "info",
|
newApplications: this.newApplications,
|
||||||
title: this.i18nService.t("applicationsNeedingReview"),
|
|
||||||
message: this.i18nService.t("newApplicationsWithCount", this.newApplicationsCount.toString()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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