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:
parent
c46d522ac8
commit
55eaff06d6
@ -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 }} × {{ 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 }} × {{ 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 }} ×
|
||||||
|
{{ 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 }} ×
|
||||||
|
{{ 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>
|
||||||
|
@ -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({
|
@Component({
|
||||||
selector: "app-sm-adjust-subscription",
|
selector: "app-sm-adjust-subscription",
|
||||||
templateUrl: "sm-adjust-subscription.component.html",
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3309,6 +3309,9 @@
|
|||||||
"limitSubscriptionDesc": {
|
"limitSubscriptionDesc": {
|
||||||
"message": "Set a seat limit for your subscription. Once this limit is reached, you will not be able to invite new members."
|
"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": {
|
"maxSeatLimit": {
|
||||||
"message": "Seat Limit (optional)",
|
"message": "Seat Limit (optional)",
|
||||||
"description": "Upper limit of seats to allow through autoscaling"
|
"description": "Upper limit of seats to allow through autoscaling"
|
||||||
@ -6911,5 +6914,30 @@
|
|||||||
},
|
},
|
||||||
"freeOrganization": {
|
"freeOrganization": {
|
||||||
"message": "Free Organization"
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user