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:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "scim" | i18n }}</h1>
|
||||
</div>
|
||||
<app-header></app-header>
|
||||
|
||||
<p>{{ "scimDescription" | i18n }}</p>
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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: "",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<div class="page-header d-flex">
|
||||
<h1>{{ "singleSignOn" | i18n }}</h1>
|
||||
</div>
|
||||
<app-header></app-header>
|
||||
|
||||
<ng-container *ngIf="loading">
|
||||
<i
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user