1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-06-25 10:25:36 +02: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:
Jake Fink 2023-02-27 16:31:55 -05:00 committed by GitHub
parent ff89d86d40
commit c160827272
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 66 additions and 63 deletions

View File

@ -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,
},
},
],

View File

@ -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"

View File

@ -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)
);
}
}

View File

@ -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>

View File

@ -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)
);
}
}

View File

@ -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>

View File

@ -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))
);
}
}

View File

@ -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[]>;

View File

@ -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));