1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[AC-2860] Revise unassigned and purchased seat warning for CB (#10077)

* Rework create-client-dialog seat warning

* Rework manage-client-subscription-dialog seat warning

* Fix create client purchased seats label

* Fix manage client subscription purchased seats label logic

* Another manage subscription purchased seats fix
This commit is contained in:
Alex Morask
2024-07-17 10:12:40 -04:00
committed by GitHub
parent 83d141c914
commit e27d698d4b
5 changed files with 59 additions and 17 deletions

View File

@@ -8564,5 +8564,8 @@
"example": "Organization name" "example": "Organization name"
} }
} }
},
"purchasedSeatsRemoved": {
"message": "purchased seats removed"
} }
} }

View File

@@ -58,13 +58,16 @@
</bit-label> </bit-label>
<input type="number" bitInput formControlName="seats" min="1" /> <input type="number" bitInput formControlName="seats" min="1" />
<bit-hint <bit-hint
class="tw-text-muted tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1 tw-grid-rows-2" class="tw-text-muted tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1"
*ngIf="openSeats > 0" [ngClass]="{
'tw-grid-rows-1': additionalSeatsPurchased <= 0,
'tw-grid-rows-2': additionalSeatsPurchased > 0
}"
> >
<span class="tw-col-span-1" <span class="tw-col-span-1"
>{{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }}</span >{{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }}</span
> >
<span class="tw-col-span-1" <span class="tw-col-span-1" *ngIf="additionalSeatsPurchased > 0"
>{{ additionalSeatsPurchased }} >{{ additionalSeatsPurchased }}
{{ "purchaseSeatDescription" | i18n | lowercase }}</span {{ "purchaseSeatDescription" | i18n | lowercase }}</span
> >

View File

@@ -162,18 +162,16 @@ export class CreateClientDialogComponent implements OnInit {
this.dialogRef.close(this.ResultType.Submitted); this.dialogRef.close(this.ResultType.Submitted);
}; };
protected get openSeats(): number { protected get unassignedSeats(): number {
const selectedProviderPlan = this.getSelectedProviderPlan(); const selectedProviderPlan = this.getSelectedProviderPlan();
if (selectedProviderPlan === null) { if (selectedProviderPlan === null) {
return 0; return 0;
} }
return selectedProviderPlan.seatMinimum - selectedProviderPlan.assignedSeats; const openSeats = selectedProviderPlan.seatMinimum - selectedProviderPlan.assignedSeats;
}
protected get unassignedSeats(): number { const unassignedSeats = openSeats - this.formGroup.value.seats;
const unassignedSeats = this.openSeats - this.formGroup.value.seats;
return unassignedSeats > 0 ? unassignedSeats : 0; return unassignedSeats > 0 ? unassignedSeats : 0;
} }
@@ -185,11 +183,16 @@ export class CreateClientDialogComponent implements OnInit {
return 0; return 0;
} }
const selectedSeats = this.formGroup.value.seats ?? 0; if (selectedProviderPlan.purchasedSeats > 0) {
return this.formGroup.value.seats;
}
const purchased = selectedSeats - this.openSeats; const additionalSeatsPurchased =
this.formGroup.value.seats +
selectedProviderPlan.assignedSeats -
selectedProviderPlan.seatMinimum;
return purchased > 0 ? purchased : 0; return additionalSeatsPurchased > 0 ? additionalSeatsPurchased : 0;
} }
private getSelectedProviderPlan(): ProviderPlanResponse { private getSelectedProviderPlan(): ProviderPlanResponse {

View File

@@ -16,21 +16,27 @@
formControlName="assignedSeats" formControlName="assignedSeats"
[min]="dialogParams.organization.occupiedSeats" [min]="dialogParams.organization.occupiedSeats"
/> />
<bit-hint class="tw-text-muted" *ngIf="openSeats > 0 || isServiceUserWithPurchasedSeats"> <bit-hint class="tw-text-muted" *ngIf="!isServiceUserWithPurchasedSeats">
<div <div
*ngIf="!this.isServiceUserWithPurchasedSeats"
class="tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1" class="tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1"
[ngClass]="{ 'tw-grid-rows-2': this.isProviderAdmin }" [ngClass]="{
'tw-grid-rows-1': additionalSeatsPurchased === 0,
'tw-grid-rows-2': purchasingSeats || sellingSeats
}"
> >
<span class="tw-col-span-1"> <span class="tw-col-span-1">
{{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }} {{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }}
</span> </span>
<span *ngIf="this.isProviderAdmin" class="tw-col-span-1" <span *ngIf="purchasingSeats" class="tw-col-span-1"
>{{ additionalSeatsPurchased }} >{{ additionalSeatsPurchased }}
{{ "purchaseSeatDescription" | i18n | lowercase }}</span {{ "purchaseSeatDescription" | i18n | lowercase }}</span
> >
<span *ngIf="sellingSeats" class="tw-col-span-1"
>{{ purchasedSeatsRemoved }} {{ "purchasedSeatsRemoved" | i18n | lowercase }}</span
>
</div> </div>
</bit-hint> </bit-hint>
<bit-hint *ngIf="isServiceUserWithPurchasedSeats"></bit-hint>
</bit-form-field> </bit-form-field>
</div> </div>
<ng-container bitDialogFooter> <ng-container bitDialogFooter>

View File

@@ -36,7 +36,10 @@ export const openManageClientSubscriptionDialog = (
export class ManageClientSubscriptionDialogComponent implements OnInit { export class ManageClientSubscriptionDialogComponent implements OnInit {
protected loading = true; protected loading = true;
protected providerPlan: ProviderPlanResponse; protected providerPlan: ProviderPlanResponse;
protected assignedSeats: number;
protected openSeats: number; protected openSeats: number;
protected purchasedSeats: number;
protected seatMinimum: number;
protected readonly ResultType = ManageClientSubscriptionDialogResultType; protected readonly ResultType = ManageClientSubscriptionDialogResultType;
protected formGroup = new FormGroup({ protected formGroup = new FormGroup({
@@ -63,7 +66,10 @@ export class ManageClientSubscriptionDialogComponent implements OnInit {
(plan) => plan.planName === this.dialogParams.organization.plan, (plan) => plan.planName === this.dialogParams.organization.plan,
); );
this.assignedSeats = this.providerPlan.assignedSeats;
this.openSeats = this.providerPlan.seatMinimum - this.providerPlan.assignedSeats; this.openSeats = this.providerPlan.seatMinimum - this.providerPlan.assignedSeats;
this.purchasedSeats = this.providerPlan.purchasedSeats;
this.seatMinimum = this.providerPlan.seatMinimum;
this.formGroup.controls.assignedSeats.addValidators( this.formGroup.controls.assignedSeats.addValidators(
this.isServiceUserWithPurchasedSeats this.isServiceUserWithPurchasedSeats
@@ -165,9 +171,22 @@ export class ManageClientSubscriptionDialogComponent implements OnInit {
const seatDifference = const seatDifference =
this.formGroup.value.assignedSeats - this.dialogParams.organization.seats; this.formGroup.value.assignedSeats - this.dialogParams.organization.seats;
const purchasedSeats = seatDifference - this.openSeats; if (this.purchasedSeats > 0) {
return seatDifference;
}
return purchasedSeats > 0 ? purchasedSeats : 0; return seatDifference - this.openSeats;
}
get purchasedSeatsRemoved(): number {
const seatDifference =
this.dialogParams.organization.seats - this.formGroup.value.assignedSeats;
if (this.purchasedSeats >= seatDifference) {
return seatDifference;
}
return this.purchasedSeats;
} }
get isProviderAdmin(): boolean { get isProviderAdmin(): boolean {
@@ -177,4 +196,12 @@ export class ManageClientSubscriptionDialogComponent implements OnInit {
get isServiceUserWithPurchasedSeats(): boolean { get isServiceUserWithPurchasedSeats(): boolean {
return !this.isProviderAdmin && this.providerPlan && this.providerPlan.purchasedSeats > 0; return !this.isProviderAdmin && this.providerPlan && this.providerPlan.purchasedSeats > 0;
} }
get purchasingSeats(): boolean {
return this.additionalSeatsPurchased > 0;
}
get sellingSeats(): boolean {
return this.purchasedSeats > 0 && this.additionalSeatsPurchased < 0;
}
} }