1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 05:43:41 +00:00
Files
browser/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts
Will Martin a0429d7d09 [CL-622][CL-562][CL-621][CL-632] various drawer improvements (#14120)
- add focus trap to drawers
- add config to open existing dialogs as drawers
- make drawer take up fill width/height on smaller screens
2025-05-15 10:32:52 -04:00

165 lines
4.7 KiB
TypeScript

import { Component, Inject } from "@angular/core";
import {
AbstractControl,
FormBuilder,
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
ValidationErrors,
Validators,
} from "@angular/forms";
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PlanSponsorshipType } from "@bitwarden/common/billing/enums";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { OrgKey } from "@bitwarden/common/types/key";
import {
DialogRef,
ButtonModule,
DialogConfig,
DIALOG_DATA,
DialogModule,
DialogService,
FormFieldModule,
ToastService,
} from "@bitwarden/components";
interface RequestSponsorshipForm {
sponsorshipEmail: FormControl<string | null>;
sponsorshipNote: FormControl<string | null>;
}
interface AddSponsorshipDialogParams {
organizationId: string;
organizationKey: OrgKey;
}
@Component({
templateUrl: "add-sponsorship-dialog.component.html",
standalone: true,
imports: [
JslibModule,
ButtonModule,
DialogModule,
FormsModule,
ReactiveFormsModule,
FormFieldModule,
],
})
export class AddSponsorshipDialogComponent {
sponsorshipForm: FormGroup<RequestSponsorshipForm>;
loading = false;
organizationId: string;
organizationKey: OrgKey;
constructor(
private dialogRef: DialogRef,
private formBuilder: FormBuilder,
private i18nService: I18nService,
private organizationUserApiService: OrganizationUserApiService,
private toastService: ToastService,
private apiService: ApiService,
private encryptService: EncryptService,
@Inject(DIALOG_DATA) protected dialogParams: AddSponsorshipDialogParams,
) {
this.organizationId = this.dialogParams?.organizationId;
this.organizationKey = this.dialogParams.organizationKey;
this.sponsorshipForm = this.formBuilder.group<RequestSponsorshipForm>({
sponsorshipEmail: new FormControl<string | null>("", {
validators: [Validators.email, Validators.required],
asyncValidators: [this.isOrganizationMember.bind(this)],
updateOn: "change",
}),
sponsorshipNote: new FormControl<string | null>("", {
validators: [Validators.maxLength(1000)],
}),
});
}
static open(dialogService: DialogService, config: DialogConfig<AddSponsorshipDialogParams>) {
return dialogService.open(AddSponsorshipDialogComponent, {
...config,
data: config.data,
} as unknown as DialogConfig<unknown, DialogRef>);
}
protected async save() {
this.sponsorshipEmailControl.markAllAsTouched();
if (this.sponsorshipForm.invalid) {
return;
}
this.loading = true;
try {
const notes = this.sponsorshipForm.value.sponsorshipNote || "";
const email = this.sponsorshipForm.value.sponsorshipEmail || "";
const encryptedNotes = await this.encryptService.encryptString(notes, this.organizationKey);
const isAdminInitiated = true;
await this.apiService.postCreateSponsorship(this.organizationId, {
sponsoredEmail: email,
planSponsorshipType: PlanSponsorshipType.FamiliesForEnterprise,
friendlyName: email,
isAdminInitiated,
notes: encryptedNotes.encryptedString,
});
this.toastService.showToast({
variant: "success",
title: undefined,
message: this.i18nService.t("sponsorshipCreated"),
});
await this.resetForm();
} catch (e: any) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: e?.message || this.i18nService.t("unexpectedError"),
});
}
this.loading = false;
this.dialogRef.close();
}
private async resetForm() {
this.sponsorshipForm.reset();
}
get sponsorshipEmailControl() {
return this.sponsorshipForm.controls.sponsorshipEmail;
}
get sponsorshipNoteControl() {
return this.sponsorshipForm.controls.sponsorshipNote;
}
private async isOrganizationMember(control: AbstractControl): Promise<ValidationErrors | null> {
const value = control.value;
const users = await this.organizationUserApiService.getAllMiniUserDetails(this.organizationId);
const userExists = users.data.some(
(member) => member.email.toLowerCase() === value.toLowerCase(),
);
if (userExists) {
return {
isOrganizationMember: {
message: this.i18nService.t("organizationHasMemberMessage", value),
},
};
}
return null;
}
}