1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 01:03:35 +00:00

Vertical Vault Navigation (#6957)

* WIP admin console layout

* Update icons

* Migrate more things

* Migrate the last pages

* Move header to web

* Fix story not working

* Convert header component to standalone

* Migrate org layout to standalone

* Enable org switcher

* Add AC to product switcher

* Migrate provider portal to vertical nav

* Migrate PM

* Prettier fixes

* Change AC and PP to use secondary variant layout & update logos

* Remove full width setting

* Remove commented code

* Add header to report pages

* Add provider portal banner

* Fix banner for billing pages

* Move vault title to header

* Prevent scrollbar jumping

* Move send button to header

* Replace search input with bit-search

* Remove unused files and css

* Add banner

* Tweak storage option

* Fix duplicate nav item after merge

* Migrate banner state to state provider framework

* [AC-2078] Fix device approvals header

* [PM-5861] Hide AC from product switcher for users that do not have access

* [PM-5860] Fix Vault and Send page headers

* [AC-2075] Fix missing link on reporting nav group

* [AC-2079] Hide Payment Method and Billing History pages for self-hosted instances

* [AC-2090] Hide reports/event log nav items for users that do not have permission

* [AC-2092] Fix missing provider portal option in product switcher on page load

* Add null check for organization in org layout component

* [AC-2094] Fix missing page header for new client orgs page

* [AC-2093] Update New client button styling

* Fix failing test after merge

* [PM-2087] Use disk-local for web layout banner

* [PM-6041] Update banner copy to read "web app"

* [PM-6094] Update banner link to marketing URL

* [PM-6114] add CL container component to VVR pages (#7802)

* create bit-container component

* add container to all page components

* Fix linting errors after merge with main

* Fix product switcher stories

* Fix web-header stories

* mock org state properly in product switcher stories (#7956)

* refactor: move web layout migration banner logic into a service (#7958)

* make CL codeowner of web header files

* move migration banner logic to service; update stories

* [PM-5862] Ensure a sync has run before hiding navigation links

* Remove leftover banner global state

* Re-add dropped selfHosted ngIf

* Add rel noreferrer

* Remove comment

---------

Co-authored-by: Shane Melton <smelton@bitwarden.com>
Co-authored-by: Will Martin <contact@willmartian.com>
This commit is contained in:
Oscar Hinton
2024-02-23 18:22:45 +01:00
committed by GitHub
parent a31e3bf842
commit 38d8fbdb5a
128 changed files with 4228 additions and 4647 deletions

View File

@@ -1,13 +1,14 @@
<h1>
{{ "deviceApprovals" | i18n }}
<i
class="bwi bwi-spinner bwi-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
*ngIf="actionInProgress || loading"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</h1>
<app-header>
<ng-container slot="title-suffix" *ngIf="loading || actionInProgress">
<i
class="bwi bwi-spinner bwi-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</ng-container>
</app-header>
<p>
{{ "deviceApprovalsDesc" | i18n }}
</p>

View File

@@ -1,10 +1,8 @@
<div class="tw-flex tw-flex-row tw-justify-between">
<h1>{{ "domainVerification" | i18n }}</h1>
<app-header>
<button *ngIf="!loading" type="button" buttonType="primary" bitButton (click)="addDomain()">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i> {{ "newDomain" | i18n }}
</button>
</div>
</app-header>
<ng-container *ngIf="loading">
<i

View File

@@ -1,6 +1,4 @@
<div class="page-header">
<h1>{{ "scim" | i18n }}</h1>
</div>
<app-header></app-header>
<p>{{ "scimDescription" | i18n }}</p>

View File

@@ -6,7 +6,6 @@ import { canAccessSettingsTab } from "@bitwarden/common/admin-console/abstractio
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationPermissionsGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/org-permissions.guard";
import { OrganizationLayoutComponent } from "@bitwarden/web-vault/app/admin-console/organizations/layouts/organization-layout.component";
import { SettingsComponent } from "@bitwarden/web-vault/app/admin-console/organizations/settings/settings.component";
import { SsoComponent } from "../../auth/sso/sso.component";
@@ -22,7 +21,6 @@ const routes: Routes = [
children: [
{
path: "settings",
component: SettingsComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
organizationPermissions: canAccessSettingsTab,
@@ -33,6 +31,7 @@ const routes: Routes = [
component: DomainVerificationComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
titleId: "domainVerification",
organizationPermissions: (org: Organization) => org.canManageDomainVerification,
},
},
@@ -41,6 +40,7 @@ const routes: Routes = [
component: SsoComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
titleId: "singleSignOn",
organizationPermissions: (org: Organization) => org.canManageSso,
},
},
@@ -49,6 +49,7 @@ const routes: Routes = [
component: ScimComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
titleId: "scim",
organizationPermissions: (org: Organization) => org.canManageScim,
},
},

View File

@@ -1,6 +1,7 @@
import { NgModule } from "@angular/core";
import { NoItemsModule } from "@bitwarden/components";
import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared";
import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
import { SsoComponent } from "../../auth/sso/sso.component";
@@ -13,7 +14,13 @@ import { ScimComponent } from "./manage/scim.component";
import { OrganizationsRoutingModule } from "./organizations-routing.module";
@NgModule({
imports: [SharedModule, CoreOrganizationModule, OrganizationsRoutingModule, NoItemsModule],
imports: [
SharedModule,
CoreOrganizationModule,
OrganizationsRoutingModule,
NoItemsModule,
LooseComponentsModule,
],
declarations: [
SsoComponent,
ScimComponent,

View File

@@ -1,32 +1,19 @@
<div class="page-header d-flex">
<h1>{{ "clients" | i18n }}</h1>
<div class="ml-auto d-flex">
<div>
<label class="sr-only" for="search">{{ "search" | i18n }}</label>
<input
type="search"
class="form-control form-control-sm"
id="search"
placeholder="{{ 'search' | i18n }}"
[(ngModel)]="searchText"
/>
</div>
<a class="btn btn-sm btn-outline-primary ml-3" routerLink="create" *ngIf="manageOrganizations">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "newClientOrganization" | i18n }}
</a>
<button
type="button"
class="btn btn-sm btn-outline-primary ml-3"
(click)="addExistingOrganization()"
*ngIf="manageOrganizations && showAddExisting"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "addExistingOrganization" | i18n }}
</button>
</div>
</div>
<app-header>
<bit-search [placeholder]="'search' | i18n" [(ngModel)]="searchText"></bit-search>
<a bitButton routerLink="create" *ngIf="manageOrganizations" buttonType="primary">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "newClient" | i18n }}
</a>
<button
type="button"
bitButton
(click)="addExistingOrganization()"
*ngIf="manageOrganizations && showAddExisting"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "addExistingOrganization" | i18n }}
</button>
</app-header>
<ng-container *ngIf="loading">
<i

View File

@@ -1,5 +1,3 @@
<div class="page-header">
<h1>{{ "newClientOrganization" | i18n }}</h1>
</div>
<app-header [title]="'newClientOrganization' | i18n"></app-header>
<p>{{ "newClientOrganizationDesc" | i18n }}</p>
<app-organization-plans [providerId]="providerId"></app-organization-plans>

View File

@@ -1,58 +1,58 @@
<div class="page-header d-flex">
<h1>{{ "eventLogs" | i18n }}</h1>
<div class="ml-auto d-flex">
<div class="form-inline">
<label class="sr-only" for="start">{{ "startDate" | i18n }}</label>
<input
type="datetime-local"
class="form-control form-control-sm"
id="start"
placeholder="{{ 'startDate' | i18n }}"
[(ngModel)]="start"
placeholder="YYYY-MM-DDTHH:MM"
(change)="dirtyDates = true"
/>
<span class="mx-2">-</span>
<label class="sr-only" for="end">{{ "endDate" | i18n }}</label>
<input
type="datetime-local"
class="form-control form-control-sm"
id="end"
placeholder="{{ 'endDate' | i18n }}"
[(ngModel)]="end"
placeholder="YYYY-MM-DDTHH:MM"
(change)="dirtyDates = true"
/>
</div>
<form #refreshForm [appApiAction]="refreshPromise" class="d-inline">
<button
type="button"
class="btn btn-sm btn-outline-primary ml-3"
(click)="loadEvents(true)"
[disabled]="loaded && refreshForm.loading"
>
<i
class="bwi bwi-refresh bwi-fw"
aria-hidden="true"
[ngClass]="{ 'bwi-spin': loaded && refreshForm.loading }"
></i>
{{ "refresh" | i18n }}
</button>
</form>
<form #exportForm [appApiAction]="exportPromise" class="d-inline">
<button
type="button"
class="btn btn-sm btn-outline-primary btn-submit manual ml-3"
[ngClass]="{ loading: exportForm.loading }"
(click)="exportEvents()"
[disabled]="(loaded && exportForm.loading) || dirtyDates"
>
<i class="bwi bwi-spinner bwi-spin" aria-hidden="true"></i>
<span>{{ "export" | i18n }}</span>
</button>
</form>
<app-header></app-header>
<div class="ml-auto d-flex">
<div class="form-inline">
<label class="sr-only" for="start">{{ "startDate" | i18n }}</label>
<input
type="datetime-local"
class="form-control form-control-sm"
id="start"
placeholder="{{ 'startDate' | i18n }}"
[(ngModel)]="start"
placeholder="YYYY-MM-DDTHH:MM"
(change)="dirtyDates = true"
/>
<span class="mx-2">-</span>
<label class="sr-only" for="end">{{ "endDate" | i18n }}</label>
<input
type="datetime-local"
class="form-control form-control-sm"
id="end"
placeholder="{{ 'endDate' | i18n }}"
[(ngModel)]="end"
placeholder="YYYY-MM-DDTHH:MM"
(change)="dirtyDates = true"
/>
</div>
<form #refreshForm [appApiAction]="refreshPromise" class="d-inline">
<button
type="button"
class="btn btn-sm btn-outline-primary ml-3"
(click)="loadEvents(true)"
[disabled]="loaded && refreshForm.loading"
>
<i
class="bwi bwi-refresh bwi-fw"
aria-hidden="true"
[ngClass]="{ 'bwi-spin': loaded && refreshForm.loading }"
></i>
{{ "refresh" | i18n }}
</button>
</form>
<form #exportForm [appApiAction]="exportPromise" class="d-inline">
<button
type="button"
class="btn btn-sm btn-outline-primary btn-submit manual ml-3"
[ngClass]="{ loading: exportForm.loading }"
(click)="exportEvents()"
[disabled]="(loaded && exportForm.loading) || dirtyDates"
>
<i class="bwi bwi-spinner bwi-spin" aria-hidden="true"></i>
<span>{{ "export" | i18n }}</span>
</button>
</form>
</div>
<ng-container *ngIf="!loaded">
<i
class="bwi bwi-spinner bwi-spin text-muted"

View File

@@ -1,30 +0,0 @@
<div class="container page-content">
<div class="row">
<div class="col-3">
<div class="card" *ngIf="provider">
<div class="card-header">{{ "manage" | i18n }}</div>
<div class="list-group list-group-flush">
<a
routerLink="people"
class="list-group-item"
routerLinkActive="active"
*ngIf="provider.canManageUsers"
>
{{ "people" | i18n }}
</a>
<a
routerLink="events"
class="list-group-item"
routerLinkActive="active"
*ngIf="provider.canAccessEventLogs && accessEvents"
>
{{ "eventLogs" | i18n }}
</a>
</div>
</div>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
</div>
</div>

View File

@@ -1,82 +1,77 @@
<div class="page-header d-flex">
<h1>{{ "people" | i18n }}</h1>
<div class="ml-auto d-flex">
<bit-toggle-group
[selected]="status"
(selectedChange)="filter($event)"
[attr.aria-label]="'memberStatusFilter' | i18n"
<app-header>
<bit-search class="tw-grow" [(ngModel)]="searchText" [placeholder]="'search' | i18n"></bit-search>
<button type="button" bitButton buttonType="primary" (click)="invite()">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "inviteUsers" | i18n }}
</button>
</app-header>
<div class="ml-auto d-flex tw-mb-4">
<bit-toggle-group
[selected]="status"
(selectedChange)="filter($event)"
[attr.aria-label]="'memberStatusFilter' | i18n"
>
<bit-toggle [value]="null">
{{ "all" | i18n }}
<span bitBadge variant="info" *ngIf="allCount">{{ allCount }}</span>
</bit-toggle>
<bit-toggle [value]="userStatusType.Invited">
{{ "invited" | i18n }}
<span bitBadge variant="info" *ngIf="invitedCount">{{ invitedCount }}</span>
</bit-toggle>
<bit-toggle [value]="userStatusType.Accepted">
{{ "accepted" | i18n }}
<span bitBadge variant="warning" *ngIf="acceptedCount">{{ acceptedCount }}</span>
</bit-toggle>
</bit-toggle-group>
<div class="dropdown ml-3" appListDropdown>
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
id="bulkActionsButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
appA11yTitle="{{ 'options' | i18n }}"
>
<bit-toggle [value]="null">
{{ "all" | i18n }}
<span bitBadge variant="info" *ngIf="allCount">{{ allCount }}</span>
</bit-toggle>
<bit-toggle [value]="userStatusType.Invited">
{{ "invited" | i18n }}
<span bitBadge variant="info" *ngIf="invitedCount">{{ invitedCount }}</span>
</bit-toggle>
<bit-toggle [value]="userStatusType.Accepted">
{{ "accepted" | i18n }}
<span bitBadge variant="warning" *ngIf="acceptedCount">{{ acceptedCount }}</span>
</bit-toggle>
</bit-toggle-group>
<div class="ml-3">
<bit-search
class="tw-grow"
[(ngModel)]="searchText"
[placeholder]="'search' | i18n"
></bit-search>
</div>
<div class="dropdown ml-3" appListDropdown>
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
id="bulkActionsButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
appA11yTitle="{{ 'options' | i18n }}"
>
<i class="bwi bwi-cog" aria-hidden="true"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="bulkActionsButton">
<button type="button" class="dropdown-item" appStopClick (click)="bulkReinvite()">
<i class="bwi bwi-fw bwi-envelope" aria-hidden="true"></i>
{{ "reinviteSelected" | i18n }}
</button>
<button
type="button"
class="dropdown-item text-success"
appStopClick
(click)="bulkConfirm()"
*ngIf="showBulkConfirmUsers"
>
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
{{ "confirmSelected" | i18n }}
</button>
<button type="button" class="dropdown-item text-danger" appStopClick (click)="bulkRemove()">
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
{{ "remove" | i18n }}
</button>
<div class="dropdown-divider"></div>
<button type="button" class="dropdown-item" appStopClick (click)="selectAll(true)">
<i class="bwi bwi-fw bwi-check-square" aria-hidden="true"></i>
{{ "selectAll" | i18n }}
</button>
<button type="button" class="dropdown-item" appStopClick (click)="selectAll(false)">
<i class="bwi bwi-fw bwi-minus-square" aria-hidden="true"></i>
{{ "unselectAll" | i18n }}
</button>
</div>
</div>
<button type="button" class="btn btn-outline-primary ml-3" (click)="invite()">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "inviteUser" | i18n }}
<i class="bwi bwi-cog" aria-hidden="true"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="bulkActionsButton">
<button type="button" class="dropdown-item" appStopClick (click)="bulkReinvite()">
<i class="bwi bwi-fw bwi-envelope" aria-hidden="true"></i>
{{ "reinviteSelected" | i18n }}
</button>
<button
type="button"
class="dropdown-item text-success"
appStopClick
(click)="bulkConfirm()"
*ngIf="showBulkConfirmUsers"
>
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
{{ "confirmSelected" | i18n }}
</button>
<button type="button" class="dropdown-item text-danger" appStopClick (click)="bulkRemove()">
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
{{ "remove" | i18n }}
</button>
<div class="dropdown-divider"></div>
<button type="button" class="dropdown-item" appStopClick (click)="selectAll(true)">
<i class="bwi bwi-fw bwi-check-square" aria-hidden="true"></i>
{{ "selectAll" | i18n }}
</button>
<button type="button" class="dropdown-item" appStopClick (click)="selectAll(false)">
<i class="bwi bwi-fw bwi-minus-square" aria-hidden="true"></i>
{{ "unselectAll" | i18n }}
</button>
</div>
</div>
</div>
<ng-container *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin text-muted"

View File

@@ -1,45 +1,32 @@
<app-navbar></app-navbar>
<app-payment-method-banners *ngIf="false"></app-payment-method-banners>
<div class="org-nav" *ngIf="provider">
<div class="container d-flex">
<div class="d-flex flex-column">
<div class="my-auto d-flex align-items-center pl-1">
<bit-avatar [text]="provider.name" [id]="provider.id"></bit-avatar>
<div class="org-name ml-3">
<span>{{ provider.name }}</span>
<small class="text-muted">{{ "provider" | i18n }}</small>
</div>
<div class="ml-3 card border-danger text-danger bg-transparent" *ngIf="!provider.enabled">
<div class="card-body py-2">
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
{{ "providerIsDisabled" | i18n }}
</div>
</div>
</div>
<ul class="nav nav-tabs" *ngIf="showMenuBar">
<li class="nav-item">
<a class="nav-link" routerLink="clients" routerLinkActive="active">
<i class="bwi bwi-bank" aria-hidden="true"></i>
{{ "clients" | i18n }}
</a>
</li>
<li class="nav-item" *ngIf="showManageTab">
<a class="nav-link" [routerLink]="manageRoute" routerLinkActive="active">
<i class="bwi bwi-sliders" aria-hidden="true"></i>
{{ "manage" | i18n }}
</a>
</li>
<li class="nav-item" *ngIf="showSettingsTab">
<a class="nav-link" routerLink="settings" routerLinkActive="active">
<i class="bwi bwi-cogs" aria-hidden="true"></i>
{{ "settings" | i18n }}
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="container page-content">
<bit-layout variant="secondary">
<nav slot="sidebar" *ngIf="provider">
<a routerLink="." class="tw-m-5 tw-mt-7 tw-block">
<bit-icon [icon]="logo"></bit-icon>
</a>
<bit-nav-item icon="bwi-bank" [text]="'clients' | i18n" route="clients"></bit-nav-item>
<bit-nav-group icon="bwi-sliders" [text]="'manage' | i18n" route="manage" *ngIf="showManageTab">
<bit-nav-item
[text]="'people' | i18n"
route="manage/people"
*ngIf="provider.canManageUsers"
></bit-nav-item>
<bit-nav-item
[text]="'eventLogs' | i18n"
route="manage/events"
*ngIf="provider.useEvents"
></bit-nav-item>
</bit-nav-group>
<bit-nav-item
icon="bwi-cogs"
[text]="'settings' | i18n"
route="settings"
*ngIf="showSettingsTab"
></bit-nav-item>
</nav>
<app-payment-method-banners
*ngIf="false"
class="-tw-m-6 tw-flex tw-flex-col tw-pb-6"
></app-payment-method-banners>
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
</bit-layout>

View File

@@ -1,15 +1,32 @@
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { ActivatedRoute, RouterModule } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { IconModule, LayoutComponent, NavigationModule } from "@bitwarden/components";
import { ProviderPortalLogo } from "@bitwarden/web-vault/app/admin-console/icons/provider-portal-logo";
import { PaymentMethodBannersComponent } from "@bitwarden/web-vault/app/components/payment-method-banners/payment-method-banners.component";
@Component({
selector: "providers-layout",
templateUrl: "providers-layout.component.html",
standalone: true,
imports: [
CommonModule,
RouterModule,
JslibModule,
LayoutComponent,
IconModule,
NavigationModule,
PaymentMethodBannersComponent,
],
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class ProvidersLayoutComponent {
protected readonly logo = ProviderPortalLogo;
provider: Provider;
private providerId: string;

View File

@@ -5,17 +5,16 @@ import { AuthGuard } from "@bitwarden/angular/auth/guards";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { ProvidersComponent } from "@bitwarden/web-vault/app/admin-console/providers/providers.component";
import { FrontendLayoutComponent } from "@bitwarden/web-vault/app/layouts/frontend-layout.component";
import { UserLayoutComponent } from "@bitwarden/web-vault/app/layouts/user-layout.component";
import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component";
import { ProviderPermissionsGuard } from "./guards/provider-permissions.guard";
import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { EventsComponent } from "./manage/events.component";
import { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component";
import { ProvidersLayoutComponent } from "./providers-layout.component";
import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component";
import { SetupComponent } from "./setup/setup.component";
@@ -23,7 +22,15 @@ const routes: Routes = [
{
path: "",
canActivate: [AuthGuard],
component: ProvidersComponent,
component: UserLayoutComponent,
children: [
{
path: "",
canActivate: [AuthGuard],
component: ProvidersComponent,
data: { titleId: "providers" },
},
],
},
{
path: "",
@@ -59,7 +66,6 @@ const routes: Routes = [
{ path: "clients", component: ClientsComponent, data: { titleId: "clients" } },
{
path: "manage",
component: ManageComponent,
children: [
{
path: "",
@@ -88,7 +94,6 @@ const routes: Routes = [
},
{
path: "settings",
component: SettingsComponent,
children: [
{
path: "",

View File

@@ -16,14 +16,12 @@ import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component";
import { EventsComponent } from "./manage/events.component";
import { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component";
import { UserAddEditComponent } from "./manage/user-add-edit.component";
import { ProvidersLayoutComponent } from "./providers-layout.component";
import { ProvidersRoutingModule } from "./providers-routing.module";
import { WebProviderService } from "./services/web-provider.service";
import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component";
import { SetupComponent } from "./setup/setup.component";
@@ -37,6 +35,7 @@ import { SetupComponent } from "./setup/setup.component";
OrganizationPlansComponent,
PaymentMethodBannersComponent,
SearchModule,
ProvidersLayoutComponent,
],
declarations: [
AcceptProviderComponent,
@@ -47,10 +46,7 @@ import { SetupComponent } from "./setup/setup.component";
ClientsComponent,
CreateOrganizationComponent,
EventsComponent,
ManageComponent,
PeopleComponent,
ProvidersLayoutComponent,
SettingsComponent,
SetupComponent,
SetupProviderComponent,
UserAddEditComponent,

View File

@@ -1,6 +1,5 @@
<div class="page-header">
<h1>{{ "myProvider" | i18n }}</h1>
</div>
<app-header></app-header>
<div *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin text-muted"

View File

@@ -1,17 +0,0 @@
<div class="container page-content">
<div class="row">
<div class="col-3">
<div class="card">
<div class="card-header">{{ "settings" | i18n }}</div>
<div class="list-group list-group-flush">
<a routerLink="account" class="list-group-item" routerLinkActive="active">
{{ "myProvider" | i18n }}
</a>
</div>
</div>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
</div>
</div>

View File

@@ -1,4 +1,3 @@
<app-navbar></app-navbar>
<app-payment-method-banners *ngIf="false"></app-payment-method-banners>
<div class="container page-content">
<div class="page-header">
@@ -34,4 +33,3 @@
</div>
</form>
</div>
<app-footer></app-footer>

View File

@@ -1,6 +1,4 @@
<div class="page-header d-flex">
<h1>{{ "singleSignOn" | i18n }}</h1>
</div>
<app-header></app-header>
<ng-container *ngIf="loading">
<i

View File

@@ -1,14 +1,14 @@
import { NgModule } from "@angular/core";
import { LayoutComponent as BitLayoutComponent, NavigationModule } from "@bitwarden/components";
import { OrgSwitcherComponent } from "@bitwarden/web-vault/app/layouts/org-switcher/org-switcher.component";
import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
import { LayoutComponent } from "./layout.component";
import { NavigationComponent } from "./navigation.component";
import { OrgSwitcherComponent } from "./org-switcher.component";
@NgModule({
imports: [SharedModule, NavigationModule, BitLayoutComponent],
declarations: [LayoutComponent, NavigationComponent, OrgSwitcherComponent],
imports: [SharedModule, NavigationModule, BitLayoutComponent, OrgSwitcherComponent],
declarations: [LayoutComponent, NavigationComponent],
})
export class LayoutModule {}

View File

@@ -1,42 +0,0 @@
<bit-nav-group
*ngIf="activeOrganization$ | async as activeOrganization"
[text]="activeOrganization.name"
[ariaLabel]="['organization' | i18n, activeOrganization.name].join(' ')"
icon="bwi-business"
[route]="['../', activeOrganization.id]"
[(open)]="open"
[exactMatch]="true"
>
<i
slot="end"
*ngIf="!activeOrganization.enabled"
class="bwi bwi-exclamation-triangle tw-my-auto !text-alt-2"
[attr.aria-label]="'organizationIsDisabled' | i18n"
appA11yTitle="{{ 'organizationIsDisabled' | i18n }}"
></i>
<ng-container *ngIf="organizations$ | async as organizations">
<bit-nav-item
*ngFor="let org of organizations"
[text]="org.name"
[ariaLabel]="['organization' | i18n, org.name].join(' ')"
[route]="['../', org.id]"
(mainContentClicked)="toggle()"
[exactMatch]="true"
>
<i
slot="end"
*ngIf="org.enabled == false"
class="bwi bwi-exclamation-triangle !text-alt-2"
[attr.aria-label]="'organizationIsDisabled' | i18n"
appA11yTitle="{{ 'organizationIsDisabled' | i18n }}"
></i>
</bit-nav-item>
</ng-container>
<bit-nav-item
*ngIf="!hideNewButton"
icon="bwi-plus"
[text]="'newOrganization' | i18n"
route="/create-organization"
></bit-nav-item>
<bit-nav-divider></bit-nav-divider>
</bit-nav-group>

View File

@@ -1,59 +0,0 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, map, Observable } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import type { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
@Component({
selector: "org-switcher",
templateUrl: "org-switcher.component.html",
})
export class OrgSwitcherComponent {
protected organizations$: Observable<Organization[]> =
this.organizationService.organizations$.pipe(
map((orgs) =>
orgs.filter((org) => this.filter(org)).sort((a, b) => a.name.localeCompare(b.name)),
),
);
protected activeOrganization$: Observable<Organization> = combineLatest([
this.route.paramMap,
this.organizations$,
]).pipe(map(([params, orgs]) => orgs.find((org) => org.id === params.get("organizationId"))));
/**
* Filter function for displayed organizations in the `org-switcher`
* @example
* const smFilter = (org: Organization) => org.canAccessSecretsManager
* // <org-switcher [filter]="smFilter">
*/
@Input()
filter: (org: Organization) => boolean = () => true;
/**
* Is `true` if the expanded content is visible
*/
@Input()
open = false;
@Output()
openChange = new EventEmitter<boolean>();
/**
* Visibility of the New Organization button
* (Temporary; will be removed when ability to create organizations is added to SM.)
*/
@Input()
hideNewButton = false;
constructor(
private route: ActivatedRoute,
private organizationService: OrganizationService,
) {}
protected toggle(event?: MouseEvent) {
event?.stopPropagation();
this.open = !this.open;
this.openChange.emit(this.open);
}
}