1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

feat(extension-login-approvals): [Auth/PM-14939] devices list view for browser (#14620)

Creates a new `DeviceManagementComponent` that fetches devices and formats them before handing them off to a view component for display.

View components:

- `DeviceManagementTableComponent` - displays on medium to large screens
- `DeviceManagementItemGroupComponent` - displays on small screens

Feature flag: `PM14938_BrowserExtensionLoginApproval`
This commit is contained in:
Alec Rippberger
2025-07-17 12:43:49 -05:00
committed by GitHub
parent 250e46ee70
commit 00b6b0224e
28 changed files with 872 additions and 26 deletions

View File

@@ -20,7 +20,7 @@ import {
import { SharedModule } from "../../../shared";
import { VaultBannersService } from "../../../vault/individual-vault/vault-banners/services/vault-banners.service";
import { DeviceManagementComponent } from "./device-management.component";
import { DeviceManagementOldComponent } from "./device-management-old.component";
class MockResizeObserver {
observe = jest.fn();
@@ -35,8 +35,8 @@ interface Message {
notificationId?: string;
}
describe("DeviceManagementComponent", () => {
let fixture: ComponentFixture<DeviceManagementComponent>;
describe("DeviceManagementOldComponent", () => {
let fixture: ComponentFixture<DeviceManagementOldComponent>;
let messageSubject: Subject<Message>;
let mockDevices: DeviceView[];
let vaultBannersService: VaultBannersService;
@@ -66,7 +66,7 @@ describe("DeviceManagementComponent", () => {
SharedModule,
TableModule,
PopoverModule,
DeviceManagementComponent,
DeviceManagementOldComponent,
],
providers: [
{
@@ -130,7 +130,7 @@ describe("DeviceManagementComponent", () => {
],
}).compileComponents();
fixture = TestBed.createComponent(DeviceManagementComponent);
fixture = TestBed.createComponent(DeviceManagementOldComponent);
vaultBannersService = TestBed.inject(VaultBannersService);
});

View File

@@ -45,10 +45,10 @@ interface DeviceTableData {
*/
@Component({
selector: "app-device-management",
templateUrl: "./device-management.component.html",
templateUrl: "./device-management-old.component.html",
imports: [CommonModule, SharedModule, TableModule, PopoverModule],
})
export class DeviceManagementComponent {
export class DeviceManagementOldComponent {
protected dataSource = new TableDataSource<DeviceTableData>();
protected currentDevice: DeviceView | undefined;
protected loading = true;

View File

@@ -1,13 +1,15 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { DeviceManagementComponent } from "@bitwarden/angular/auth/device-management/device-management.component";
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ChangePasswordComponent } from "../change-password.component";
import { TwoFactorSetupComponent } from "../two-factor/two-factor-setup.component";
import { DeviceManagementComponent } from "./device-management.component";
import { DeviceManagementOldComponent } from "./device-management-old.component";
import { PasswordSettingsComponent } from "./password-settings/password-settings.component";
import { SecurityKeysComponent } from "./security-keys.component";
import { SecurityComponent } from "./security.component";
@@ -55,11 +57,15 @@ const routes: Routes = [
component: SecurityKeysComponent,
data: { titleId: "keys" },
},
{
path: "device-management",
component: DeviceManagementComponent,
data: { titleId: "devices" },
},
...featureFlaggedRoute({
defaultComponent: DeviceManagementOldComponent,
flaggedComponent: DeviceManagementComponent,
featureFlag: FeatureFlag.PM14938_BrowserExtensionLoginApproval,
routeOptions: {
path: "device-management",
data: { titleId: "devices" },
},
}),
],
},
];

View File

@@ -10,6 +10,8 @@ import {
OrganizationUserApiService,
CollectionService,
} from "@bitwarden/admin-console/common";
import { DefaultDeviceManagementComponentService } from "@bitwarden/angular/auth/device-management/default-device-management-component.service";
import { DeviceManagementComponentServiceAbstraction } from "@bitwarden/angular/auth/device-management/device-management-component.service.abstraction";
import { ChangePasswordService } from "@bitwarden/angular/auth/password-management/change-password";
import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction";
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
@@ -406,6 +408,11 @@ const safeProviders: SafeProvider[] = [
RouterService,
],
}),
safeProvider({
provide: DeviceManagementComponentServiceAbstraction,
useClass: DefaultDeviceManagementComponentService,
deps: [],
}),
];
@NgModule({

View File

@@ -3489,6 +3489,9 @@
"webVault": {
"message": "Web vault"
},
"webApp": {
"message": "Web app"
},
"cli": {
"message": "CLI"
},
@@ -3971,6 +3974,22 @@
"youDeniedALogInAttemptFromAnotherDevice": {
"message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
},
"loginRequestApprovedForEmailOnDevice": {
"message": "Login request approved for $EMAIL$ on $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
"example": "name@example.com"
},
"device": {
"content": "$2",
"example": "Web app - Chrome"
}
}
},
"youDeniedLoginAttemptFromAnotherDevice": {
"message": "You denied a login attempt from another device. If this was you, try to log in with the device again."
},
"loginRequestHasAlreadyExpired": {
"message": "Login request has already expired."
},
@@ -4115,6 +4134,9 @@
"reviewLoginRequest": {
"message": "Review login request"
},
"loginRequest": {
"message": "Login request"
},
"freeTrialEndPromptCount": {
"message": "Your free trial ends in $COUNT$ days.",
"placeholders": {