Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[AC-1911] Clients: Create components to manage client organization se…
…at allocation (#8505) * implementing the clients changes * resolve pr comments on message.json * moved the method to billing-api.service * move the request and response files to billing folder * remove the adding existing orgs * resolve the routing issue * resolving the pr comments * code owner changes * fix the assignedseat * resolve the warning message * resolve the error on update * passing the right id * resolve the unassign value * removed unused logservice * Adding the loader on submit button
- Loading branch information
1 parent
b9771c1
commit 9956f02
Showing
16 changed files
with
575 additions
and
9 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
.../src/app/billing/providers/clients/manage-client-organization-subscription.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<bit-dialog dialogSize="large" [loading]="loading"> | ||
<span bitDialogTitle> | ||
{{ "manageSeats" | i18n }} | ||
<small class="tw-text-muted" *ngIf="clientName">{{ clientName }}</small> | ||
</span> | ||
<div bitDialogContent> | ||
<p> | ||
{{ "manageSeatsDescription" | i18n }} | ||
</p> | ||
<bit-form-field> | ||
<bit-label> | ||
{{ "assignedSeats" | i18n }} | ||
</bit-label> | ||
<input | ||
id="assignedSeats" | ||
type="number" | ||
appAutoFocus | ||
bitInput | ||
required | ||
[(ngModel)]="assignedSeats" | ||
/> | ||
</bit-form-field> | ||
<ng-container *ngIf="remainingOpenSeats > 0"> | ||
<p> | ||
<small class="tw-text-muted">{{ unassignedSeats }}</small> | ||
<small class="tw-text-muted">{{ "unassignedSeatsDescription" | i18n }}</small> | ||
</p> | ||
<p> | ||
<small class="tw-text-muted">{{ AdditionalSeatPurchased }}</small> | ||
<small class="tw-text-muted">{{ "purchaseSeatDescription" | i18n }}</small> | ||
</p> | ||
</ng-container> | ||
</div> | ||
<ng-container bitDialogFooter> | ||
<button | ||
type="submit" | ||
bitButton | ||
buttonType="primary" | ||
bitFormButton | ||
(click)="updateSubscription(assignedSeats)" | ||
> | ||
<i class="bwi bwi-refresh bwi-fw" aria-hidden="true"></i> | ||
{{ "save" | i18n }} | ||
</button> | ||
<button bitButton type="button" buttonType="secondary" bitDialogClose> | ||
{{ "cancel" | i18n }} | ||
</button> | ||
</ng-container> | ||
</bit-dialog> |
115 changes: 115 additions & 0 deletions
115
...eb/src/app/billing/providers/clients/manage-client-organization-subscription.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; | ||
import { Component, Inject, OnInit } from "@angular/core"; | ||
|
||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response"; | ||
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction"; | ||
import { ProviderSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/provider-subscription-update.request"; | ||
import { Plans } from "@bitwarden/common/billing/models/response/provider-subscription-response"; | ||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; | ||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; | ||
import { DialogService } from "@bitwarden/components"; | ||
|
||
type ManageClientOrganizationDialogParams = { | ||
organization: ProviderOrganizationOrganizationDetailsResponse; | ||
}; | ||
|
||
@Component({ | ||
templateUrl: "manage-client-organization-subscription.component.html", | ||
}) | ||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil | ||
export class ManageClientOrganizationSubscriptionComponent implements OnInit { | ||
loading = true; | ||
providerOrganizationId: string; | ||
providerId: string; | ||
|
||
clientName: string; | ||
assignedSeats: number; | ||
unassignedSeats: number; | ||
planName: string; | ||
AdditionalSeatPurchased: number; | ||
remainingOpenSeats: number; | ||
|
||
constructor( | ||
public dialogRef: DialogRef, | ||
@Inject(DIALOG_DATA) protected data: ManageClientOrganizationDialogParams, | ||
private billingApiService: BillingApiService, | ||
private i18nService: I18nService, | ||
private platformUtilsService: PlatformUtilsService, | ||
) { | ||
this.providerOrganizationId = data.organization.id; | ||
this.providerId = data.organization.providerId; | ||
this.clientName = data.organization.organizationName; | ||
this.assignedSeats = data.organization.seats; | ||
this.planName = data.organization.plan; | ||
} | ||
|
||
async ngOnInit() { | ||
try { | ||
const response = await this.billingApiService.getProviderClientSubscriptions(this.providerId); | ||
this.AdditionalSeatPurchased = this.getPurchasedSeatsByPlan(this.planName, response.plans); | ||
const seatMinimum = this.getProviderSeatMinimumByPlan(this.planName, response.plans); | ||
const assignedByPlan = this.getAssignedByPlan(this.planName, response.plans); | ||
this.remainingOpenSeats = seatMinimum - assignedByPlan; | ||
this.unassignedSeats = Math.abs(this.remainingOpenSeats); | ||
} catch (error) { | ||
this.remainingOpenSeats = 0; | ||
this.AdditionalSeatPurchased = 0; | ||
} | ||
this.loading = false; | ||
} | ||
|
||
async updateSubscription(assignedSeats: number) { | ||
this.loading = true; | ||
if (!assignedSeats) { | ||
this.platformUtilsService.showToast( | ||
"error", | ||
null, | ||
this.i18nService.t("assignedSeatCannotUpdate"), | ||
); | ||
return; | ||
} | ||
|
||
const request = new ProviderSubscriptionUpdateRequest(); | ||
request.assignedSeats = assignedSeats; | ||
|
||
await this.billingApiService.putProviderClientSubscriptions( | ||
this.providerId, | ||
this.providerOrganizationId, | ||
request, | ||
); | ||
this.platformUtilsService.showToast("success", null, this.i18nService.t("subscriptionUpdated")); | ||
this.loading = false; | ||
this.dialogRef.close(); | ||
} | ||
|
||
getPurchasedSeatsByPlan(planName: string, plans: Plans[]): number { | ||
const plan = plans.find((plan) => plan.planName === planName); | ||
if (plan) { | ||
return plan.purchasedSeats; | ||
} else { | ||
return 0; | ||
} | ||
} | ||
|
||
getAssignedByPlan(planName: string, plans: Plans[]): number { | ||
const plan = plans.find((plan) => plan.planName === planName); | ||
if (plan) { | ||
return plan.assignedSeats; | ||
} else { | ||
return 0; | ||
} | ||
} | ||
|
||
getProviderSeatMinimumByPlan(planName: string, plans: Plans[]) { | ||
const plan = plans.find((plan) => plan.planName === planName); | ||
if (plan) { | ||
return plan.seatMinimum; | ||
} else { | ||
return 0; | ||
} | ||
} | ||
|
||
static open(dialogService: DialogService, data: ManageClientOrganizationDialogParams) { | ||
return dialogService.open(ManageClientOrganizationSubscriptionComponent, { data }); | ||
} | ||
} |
Oops, something went wrong.