mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-16 01:21:48 +01:00
[EC-859] update billing routes for owners of Managed orgs (#4611)
* [EC-859] update billing routes for owners of Managed orgs * [EC-859] fix observable in billing tab * [EC-859] update observable name * [EC-859] update reporting and settings observables * [EC-859] add startsWith to reporting observable * [EC-859] async pipe once in settings * [EC-859] create get$ in org service * [EC-859] transition remaining components * [EC-859] add as org to template * [EC-859] add shareReplay to observable to prevent multicasting - future proof get$ on org service * [AC-859] fix missed org
This commit is contained in:
parent
ff89d86d40
commit
c160827272
@ -2,6 +2,7 @@ import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { canAccessBillingTab } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
|
||||
import { WebPlatformUtilsService } from "../../core/web-platform-utils.service";
|
||||
import { PaymentMethodComponent } from "../../settings/payment-method.component";
|
||||
@ -30,15 +31,19 @@ const routes: Routes = [
|
||||
{
|
||||
path: "payment-method",
|
||||
component: PaymentMethodComponent,
|
||||
canActivate: [OrganizationPermissionsGuard],
|
||||
data: {
|
||||
titleId: "paymentMethod",
|
||||
organizationPermissions: (org: Organization) => org.canManageBilling,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "history",
|
||||
component: OrgBillingHistoryViewComponent,
|
||||
canActivate: [OrganizationPermissionsGuard],
|
||||
data: {
|
||||
titleId: "billingHistory",
|
||||
organizationPermissions: (org: Organization) => org.canManageBilling,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -8,7 +8,7 @@
|
||||
{{ "subscription" | i18n }}
|
||||
</a>
|
||||
<a
|
||||
*ngIf="showPaymentAndHistory"
|
||||
*ngIf="showPaymentAndHistory$ | async"
|
||||
routerLink="payment-method"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
@ -16,7 +16,7 @@
|
||||
{{ "paymentMethod" | i18n }}
|
||||
</a>
|
||||
<a
|
||||
*ngIf="showPaymentAndHistory"
|
||||
*ngIf="showPaymentAndHistory$ | async"
|
||||
routerLink="history"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
|
@ -1,14 +1,27 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { map, Observable, switchMap } from "rxjs";
|
||||
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-billing-tab",
|
||||
templateUrl: "organization-billing-tab.component.html",
|
||||
})
|
||||
export class OrganizationBillingTabComponent {
|
||||
showPaymentAndHistory: boolean;
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {
|
||||
this.showPaymentAndHistory = !this.platformUtilsService.isSelfHost();
|
||||
export class OrganizationBillingTabComponent implements OnInit {
|
||||
showPaymentAndHistory$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private organizationService: OrganizationService,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.showPaymentAndHistory$ = this.route.params.pipe(
|
||||
switchMap((params) => this.organizationService.get$(params.organizationId)),
|
||||
map((org) => !this.platformUtilsService.isSelfHost() && org.canManageBilling)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div class="container page-content">
|
||||
<div class="row">
|
||||
<div class="col-3" *ngIf="showLeftNav">
|
||||
<div class="card" *ngIf="organization">
|
||||
<div class="col-3" *ngIf="showLeftNav$ | async">
|
||||
<div class="card" *ngIf="organization$ | async as org">
|
||||
<div class="card-header">{{ "reporting" | i18n }}</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a
|
||||
routerLink="events"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization.canAccessEventLogs"
|
||||
*ngIf="org.canAccessEventLogs"
|
||||
>
|
||||
{{ "eventLogs" | i18n }}
|
||||
</a>
|
||||
@ -16,14 +16,14 @@
|
||||
routerLink="reports"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization.canAccessReports"
|
||||
*ngIf="org.canAccessReports"
|
||||
>
|
||||
{{ "reports" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9" [ngClass]="showLeftNav ? 'col-9' : 'col-12'">
|
||||
<div class="col-9" [ngClass]="(showLeftNav$ | async) ? 'col-9' : 'col-12'">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
||||
import { map, Observable, shareReplay, startWith, switchMap } from "rxjs";
|
||||
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
@ -9,29 +9,21 @@ import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
selector: "app-org-reporting",
|
||||
templateUrl: "reporting.component.html",
|
||||
})
|
||||
export class ReportingComponent implements OnInit, OnDestroy {
|
||||
organization: Organization;
|
||||
showLeftNav = true;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
export class ReportingComponent implements OnInit {
|
||||
organization$: Observable<Organization>;
|
||||
showLeftNav$: Observable<boolean>;
|
||||
|
||||
constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params
|
||||
.pipe(
|
||||
concatMap(async (params) => {
|
||||
this.organization = await this.organizationService.get(params.organizationId);
|
||||
this.showLeftNav =
|
||||
this.organization.canAccessEventLogs && this.organization.canAccessReports;
|
||||
}),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
this.organization$ = this.route.params.pipe(
|
||||
switchMap((params) => this.organizationService.get$(params.organizationId)),
|
||||
shareReplay({ refCount: true, bufferSize: 1 })
|
||||
);
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
this.showLeftNav$ = this.organization$.pipe(
|
||||
map((o) => o.canAccessEventLogs && o.canAccessReports),
|
||||
startWith(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,12 @@
|
||||
<div class="col-3">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ "settings" | i18n }}</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group list-group-flush" *ngIf="organization$ | async as org">
|
||||
<a
|
||||
routerLink="account"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.isOwner"
|
||||
*ngIf="org.isOwner"
|
||||
>
|
||||
{{ "organizationInfo" | i18n }}
|
||||
</a>
|
||||
@ -16,7 +16,7 @@
|
||||
routerLink="policies"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.canManagePolicies"
|
||||
*ngIf="org.canManagePolicies"
|
||||
>
|
||||
{{ "policies" | i18n }}
|
||||
</a>
|
||||
@ -24,7 +24,7 @@
|
||||
routerLink="two-factor"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.use2fa && organization?.isOwner"
|
||||
*ngIf="org.use2fa && org.isOwner"
|
||||
>
|
||||
{{ "twoStepLogin" | i18n }}
|
||||
</a>
|
||||
@ -32,7 +32,7 @@
|
||||
routerLink="tools/import"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.canAccessImportExport"
|
||||
*ngIf="org.canAccessImportExport"
|
||||
>
|
||||
{{ "importData" | i18n }}
|
||||
</a>
|
||||
@ -40,7 +40,7 @@
|
||||
routerLink="tools/export"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.canAccessImportExport"
|
||||
*ngIf="org.canAccessImportExport"
|
||||
>
|
||||
{{ "exportVault" | i18n }}
|
||||
</a>
|
||||
@ -48,7 +48,7 @@
|
||||
routerLink="domain-verification"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.canManageDomainVerification"
|
||||
*ngIf="org?.canManageDomainVerification"
|
||||
>
|
||||
{{ "domainVerification" | i18n }}
|
||||
</a>
|
||||
@ -56,7 +56,7 @@
|
||||
routerLink="sso"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.canManageSso"
|
||||
*ngIf="org.canManageSso"
|
||||
>
|
||||
{{ "singleSignOn" | i18n }}
|
||||
</a>
|
||||
@ -64,7 +64,7 @@
|
||||
routerLink="scim"
|
||||
class="list-group-item"
|
||||
routerLinkActive="active"
|
||||
*ngIf="organization?.canManageScim"
|
||||
*ngIf="org.canManageScim"
|
||||
>
|
||||
{{ "scim" | i18n }}
|
||||
</a>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject, switchMap, takeUntil } from "rxjs";
|
||||
import { Observable, switchMap } from "rxjs";
|
||||
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
@ -9,26 +9,14 @@ import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
selector: "app-org-settings",
|
||||
templateUrl: "settings.component.html",
|
||||
})
|
||||
export class SettingsComponent implements OnInit, OnDestroy {
|
||||
organization: Organization;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
export class SettingsComponent implements OnInit {
|
||||
organization$: Observable<Organization>;
|
||||
|
||||
constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params
|
||||
.pipe(
|
||||
switchMap(async (params) => await this.organizationService.get(params.organizationId)),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe((organization) => {
|
||||
this.organization = organization;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
this.organization$ = this.route.params.pipe(
|
||||
switchMap((params) => this.organizationService.get$(params.organizationId))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export function canAccessReportingTab(org: Organization): boolean {
|
||||
}
|
||||
|
||||
export function canAccessBillingTab(org: Organization): boolean {
|
||||
return org.canManageBilling;
|
||||
return org.isOwner;
|
||||
}
|
||||
|
||||
export function canAccessOrgAdmin(org: Organization): boolean {
|
||||
@ -63,6 +63,7 @@ export function isNotProviderUser(org: Organization): boolean {
|
||||
export abstract class OrganizationService {
|
||||
organizations$: Observable<Organization[]>;
|
||||
|
||||
get$: (id: string) => Observable<Organization | undefined>;
|
||||
get: (id: string) => Organization;
|
||||
getByIdentifier: (identifier: string) => Organization;
|
||||
getAll: (userId?: string) => Promise<Organization[]>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BehaviorSubject, concatMap } from "rxjs";
|
||||
import { BehaviorSubject, concatMap, map, Observable } from "rxjs";
|
||||
|
||||
import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "../../abstractions/organization/organization.service.abstraction";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
@ -26,6 +26,10 @@ export class OrganizationService implements InternalOrganizationServiceAbstracti
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
get$(id: string): Observable<Organization | undefined> {
|
||||
return this.organizations$.pipe(map((orgs) => orgs.find((o) => o.id === id)));
|
||||
}
|
||||
|
||||
async getAll(userId?: string): Promise<Organization[]> {
|
||||
const organizationsMap = await this.stateService.getOrganizations({ userId: userId });
|
||||
return Object.values(organizationsMap || {}).map((o) => new Organization(o));
|
||||
|
Loading…
Reference in New Issue
Block a user