1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-02 13:23:29 +01:00

[AC-1418] Add initial SM adjustment form

This commit is contained in:
Shane Melton 2023-06-21 15:11:08 -07:00
parent c46d522ac8
commit 55eaff06d6
No known key found for this signature in database
3 changed files with 233 additions and 3 deletions

View File

@ -1 +1,92 @@
<p>Secrets Manager Adjust Subscription Component</p>
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-form-field class="tw-w-1/2">
<bit-label>{{ "subscriptionSeats" | i18n }}</bit-label>
<input bitInput id="smSeatCount" formControlName="seatCount" type="number" step="1" />
<bit-hint>
<strong>{{ "total" | i18n }}:</strong>
{{ formGroup.value.seatCount }} &times; {{ options.seatPrice | currency }} =
{{ seatTotal | currency }} / {{ options.interval | i18n }}
</bit-hint>
</bit-form-field>
<bit-form-control>
<bit-label>{{ "limitSubscription" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox id="limitSmSeats" formControlName="limitSeats" />
<bit-hint>
{{ "limitSmSubscriptionDesc" | i18n }}
</bit-hint>
</bit-form-control>
<bit-form-field class="tw-w-1/2" *ngIf="formGroup.value.limitSeats">
<bit-label>{{ "maxSeatLimit" | i18n }}</bit-label>
<input
bitInput
id="smSeatLimit"
formControlName="seatLimit"
type="number"
step="1"
[min]="formGroup.value.seatCount"
/>
<bit-hint>
<strong>{{ "total" | i18n }}:</strong>
{{ formGroup.value.seatLimit || 0 }} &times; {{ options.seatPrice | currency }} =
{{ maxSeatTotal | currency }} / {{ options.interval | i18n }}
</bit-hint>
</bit-form-field>
<bit-form-field class="tw-w-1/2">
<bit-label>{{ "additionalServiceAccounts" | i18n }}</bit-label>
<input
bitInput
id="additionalServiceAccountCount"
formControlName="serviceAccountCount"
type="number"
step="1"
min="0"
/>
<bit-hint>
<div>
{{
"additionalServiceAccountsDesc"
| i18n : options.baseServiceAccountCount : (monthlyServiceAccountPrice | currency)
}}
</div>
<div>
<strong>{{ "total" | i18n }}:</strong>
{{ formGroup.value.serviceAccountCount || 0 }} &times;
{{ options.additionalServiceAccountPrice | currency }} =
{{ serviceAccountTotal | currency }} / {{ options.interval | i18n }}
</div>
</bit-hint>
</bit-form-field>
<bit-form-control>
<bit-label>{{ "limitServiceAccounts" | i18n }}</bit-label>
<input
type="checkbox"
bitCheckbox
id="limitServiceAccounts"
formControlName="limitServiceAccounts"
/>
<bit-hint>
{{ "limitServiceAccountsDesc" | i18n }}
</bit-hint>
</bit-form-control>
<bit-form-field class="tw-w-1/2" *ngIf="formGroup.value.limitServiceAccounts">
<bit-label>{{ "serviceAccountLimit" | i18n }}</bit-label>
<input
bitInput
id="additionalServiceAccountLimit"
formControlName="serviceAccountLimit"
type="number"
step="1"
[min]="formGroup.value.serviceAccountCount"
/>
<bit-hint>
<strong>{{ "total" | i18n }}:</strong>
{{ formGroup.value.serviceAccountLimit || 0 }} &times;
{{ options.additionalServiceAccountPrice | currency }} =
{{ maxServiceAccountTotal | currency }} / {{ options.interval | i18n }}
</bit-hint>
</bit-form-field>
<button type="submit" bitButton buttonType="primary" bitFormButton>
{{ "save" | i18n }}
</button>
<bit-error-summary [formGroup]="formGroup" class="tw-mt-2"></bit-error-summary>
</form>

View File

@ -1,7 +1,118 @@
import { Component } from "@angular/core";
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { Subject, takeUntil } from "rxjs";
export interface SecretsManagerSubscriptionOptions {
interval: "year" | "month";
seatCount: number;
seatLimit: number;
seatPrice: number;
/**
* The number of service accounts that are included in the base subscription.
*/
baseServiceAccountCount: number;
additionalServiceAccountCount: number;
additionalServiceAccountLimit: number;
additionalServiceAccountPrice: number;
}
@Component({
selector: "app-sm-adjust-subscription",
templateUrl: "sm-adjust-subscription.component.html",
})
export class SecretsManagerAdjustSubscriptionComponent {}
export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDestroy {
@Input() organizationId: string;
@Input() options: SecretsManagerSubscriptionOptions;
@Output() onAdjusted = new EventEmitter();
private destroy$ = new Subject<void>();
formGroup = this.formBuilder.group({
seatCount: [0, [Validators.required, Validators.min(0)]],
limitSeats: [false],
seatLimit: [null as number | null],
serviceAccountCount: [0, [Validators.required, Validators.min(0)]],
limitServiceAccounts: [false],
serviceAccountLimit: [null as number | null],
});
get monthlyServiceAccountPrice(): number {
return this.options.interval == "month"
? this.options.additionalServiceAccountPrice
: Math.round((this.options.additionalServiceAccountPrice / 12 + Number.EPSILON) * 100) / 100;
}
get serviceAccountTotal(): number {
return Math.abs(
this.formGroup.value.serviceAccountCount * this.options.additionalServiceAccountPrice
);
}
get seatTotal(): number {
return Math.abs(this.formGroup.value.seatCount * this.options.seatPrice);
}
get maxServiceAccountTotal(): number {
return Math.abs(
(this.formGroup.value.serviceAccountLimit ?? 0) * this.options.additionalServiceAccountPrice
);
}
get maxSeatTotal(): number {
return Math.abs((this.formGroup.value.seatLimit ?? 0) * this.options.seatPrice);
}
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
const seatLimitControl = this.formGroup.controls.seatLimit;
const serviceAccountLimitControl = this.formGroup.controls.serviceAccountLimit;
if (value.limitSeats) {
seatLimitControl.setValidators([Validators.required, Validators.min(value.seatCount)]);
seatLimitControl.enable({ emitEvent: false });
} else {
seatLimitControl.disable({ emitEvent: false });
}
if (value.limitServiceAccounts) {
serviceAccountLimitControl.setValidators([
Validators.required,
Validators.min(value.serviceAccountCount),
]);
serviceAccountLimitControl.enable({ emitEvent: false });
} else {
serviceAccountLimitControl.disable({ emitEvent: false });
}
});
this.formGroup.patchValue({
seatCount: this.options.seatCount,
seatLimit: this.options.seatLimit,
serviceAccountCount: this.options.additionalServiceAccountCount,
serviceAccountLimit: this.options.additionalServiceAccountLimit,
limitSeats: this.options.seatLimit != null,
limitServiceAccounts: this.options.additionalServiceAccountLimit != null,
});
}
submit = async () => {
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
return;
}
// TODO: Make the request to update the subscription
this.onAdjusted.emit();
};
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

View File

@ -3309,6 +3309,9 @@
"limitSubscriptionDesc": {
"message": "Set a seat limit for your subscription. Once this limit is reached, you will not be able to invite new members."
},
"limitSmSubscriptionDesc": {
"message": "Set a seat limit for your Secrets Manger subscription. Once this limit is reached, you will not be able to invite new members."
},
"maxSeatLimit": {
"message": "Seat Limit (optional)",
"description": "Upper limit of seats to allow through autoscaling"
@ -6911,5 +6914,30 @@
},
"freeOrganization": {
"message": "Free Organization"
},
"additionalServiceAccounts": {
"message": "Additional Service Accounts"
},
"limitServiceAccounts": {
"message": "Limit service accounts (optional)"
},
"limitServiceAccountsDesc": {
"message": "Set a limit for your service accounts. Once this limit is reached, you will not be able to create new service accounts."
},
"serviceAccountLimit": {
"message": "Service account limit (optional)"
},
"additionalServiceAccountsDesc": {
"message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.",
"placeholders": {
"count": {
"content": "$1",
"example": "50"
},
"cost": {
"content": "$2",
"example": "$0.50"
}
}
}
}