1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-24 12:06:15 +01:00

AC-2401 Migrate sponsored families component (#8874)

* AC-2401 Migrate sponsored families component

* AC-2401 Removed unused method
This commit is contained in:
KiruthigaManivannan 2024-05-24 19:00:06 +05:30 committed by GitHub
parent 091c8ccd46
commit 4e9db9057b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 111 additions and 90 deletions

View File

@ -3,103 +3,86 @@
<bit-container> <bit-container>
<ng-container *ngIf="loading"> <ng-container *ngIf="loading">
<i class="bwi bwi-spinner bwi-spin text-muted" title="{{ 'loading' | i18n }}"></i> <i class="bwi bwi-spinner bwi-spin text-muted" title="{{ 'loading' | i18n }}"></i>
<span class="sr-only">{{ "loading" | i18n }}</span> <span class="tw-sr-only">{{ "loading" | i18n }}</span>
</ng-container> </ng-container>
<ng-container *ngIf="!loading"> <ng-container *ngIf="!loading">
<p> <p bitTypography="body1">
{{ "sponsoredFamiliesEligible" | i18n }} {{ "sponsoredFamiliesEligible" | i18n }}
</p> </p>
<div> <div bitTypography="body1">
{{ "sponsoredFamiliesInclude" | i18n }}: {{ "sponsoredFamiliesInclude" | i18n }}:
<ul class="inset-list"> <ul class="tw-list-outside">
<li>{{ "sponsoredFamiliesPremiumAccess" | i18n }}</li> <li>{{ "sponsoredFamiliesPremiumAccess" | i18n }}</li>
<li>{{ "sponsoredFamiliesSharedCollections" | i18n }}</li> <li>{{ "sponsoredFamiliesSharedCollections" | i18n }}</li>
</ul> </ul>
</div> </div>
<form <form [formGroup]="sponsorshipForm" [bitSubmit]="submit" *ngIf="anyOrgsAvailable$ | async">
#form <div class="tw-grid tw-grid-cols-12 tw-gap-4">
(ngSubmit)="submit()" <div class="tw-col-span-7">
[appApiAction]="formPromise" <bit-form-field>
[formGroup]="sponsorshipForm" <bit-label>{{ "familiesSponsoringOrgSelect" | i18n }}</bit-label>
ngNativeValidate <bit-select
*ngIf="anyOrgsAvailable$ | async" id="availableSponsorshipOrg"
> name="Available Sponsorship Organization"
<div class="form-group col-7"> formControlName="selectedSponsorshipOrgId"
<label for="availableSponsorshipOrg">{{ "familiesSponsoringOrgSelect" | i18n }}</label> >
<select <bit-option
id="availableSponsorshipOrg" [disabled]="true"
name="Available Sponsorship Organization" [value]=""
formControlName="selectedSponsorshipOrgId" [label]="'--' + ('select' | i18n) + '--'"
class="form-control" ></bit-option>
required <bit-option
> *ngFor="let o of availableSponsorshipOrgs$ | async"
<option disabled="true" value="">-- {{ "select" | i18n }} --</option> [value]="o.id"
<option *ngFor="let o of availableSponsorshipOrgs$ | async" [ngValue]="o.id"> [label]="o.name"
{{ o.name }} ></bit-option>
</option> </bit-select>
</select> </bit-form-field>
</div> </div>
<div class="form-group col-7"> <div class="tw-col-span-7">
<label for="sponsorshipEmail">{{ "sponsoredFamiliesEmail" | i18n }}:</label> <bit-form-field>
<input <bit-label>{{ "sponsoredFamiliesEmail" | i18n }}:</bit-label>
id="sponsorshipEmail" <input
class="form-control" bitInput
inputmode="email" inputmode="email"
formControlName="sponsorshipEmail" formControlName="sponsorshipEmail"
name="sponsorshipEmail" [attr.aria-invalid]="sponsorshipEmailControl.invalid"
required />
[attr.aria-invalid]="sponsorshipEmailControl.invalid" </bit-form-field>
/> </div>
<small <div class="tw-col-span-7">
aria-errormessage="sponsorshipEmail" <button bitButton bitFormButton buttonType="primary" type="submit">
*ngIf="sponsorshipEmailControl.errors?.notAllowedValue" {{ "redeem" | i18n }}
class="error-inline" </button>
role="alert" </div>
>
<i class="bwi bwi-error" aria-hidden="true"></i>
{{ "cannotSponsorSelf" | i18n }}
</small>
<small
aria-errormessage="sponsorshipEmail"
*ngIf="sponsorshipEmailControl.errors?.email"
class="error-inline"
role="alert"
>
<i class="bwi bwi-error" aria-hidden="true"></i>
{{ "invalidEmail" | i18n }}
</small>
</div>
<div class="form-group col-7">
<button class="btn btn-primary btn-submit mt-2" type="submit" [disabled]="form.loading">
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "redeem" | i18n }}</span>
</button>
</div> </div>
</form> </form>
<ng-container *ngIf="anyActiveSponsorships$ | async"> <ng-container *ngIf="anyActiveSponsorships$ | async">
<div class="border-bottom"> <bit-table>
<table class="table table-hover table-list"> <ng-container header>
<thead> <tr>
<tr> <th bitCell>{{ "recipient" | i18n }}</th>
<th>{{ "recipient" | i18n }}</th> <th bitCell>{{ "sponsoringOrg" | i18n }}</th>
<th>{{ "sponsoringOrg" | i18n }}</th> <th bitCell>{{ "status" | i18n }}</th>
<th>{{ "status" | i18n }}</th> <th bitCell></th>
<th></th> </tr>
</ng-container>
<ng-template body alignContent="middle">
<ng-container *ngFor="let o of activeSponsorshipOrgs$ | async">
<tr
bitRow
sponsoring-org-row
[sponsoringOrg]="o"
[isSelfHosted]="isSelfHosted"
(sponsorshipRemoved)="forceReload()"
>
<hr />
</tr> </tr>
</thead> </ng-container>
<tbody> </ng-template>
<ng-container *ngFor="let o of activeSponsorshipOrgs$ | async"> </bit-table>
<tr <hr />
sponsoring-org-row <p bitTypography="body2">{{ "sponsoredFamiliesLeaveCopy" | i18n }}</p>
[sponsoringOrg]="o"
[isSelfHosted]="isSelfHosted"
(sponsorshipRemoved)="forceReload()"
></tr>
</ng-container>
</tbody>
</table>
</div>
<small>{{ "sponsoredFamiliesLeaveCopy" | i18n }}</small>
</ng-container> </ng-container>
</ng-container> </ng-container>
</bit-container> </bit-container>

View File

@ -1,8 +1,15 @@
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; import {
FormBuilder,
FormControl,
FormGroup,
Validators,
AbstractControl,
AsyncValidatorFn,
ValidationErrors,
} from "@angular/forms";
import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
import { notAllowedValueAsync } from "@bitwarden/angular/admin-console/validators/not-allowed-value-async.validator";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
@ -50,9 +57,9 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
validators: [Validators.required], validators: [Validators.required],
}), }),
sponsorshipEmail: new FormControl("", { sponsorshipEmail: new FormControl("", {
validators: [Validators.email], validators: [Validators.email, Validators.required],
asyncValidators: [ asyncValidators: [
notAllowedValueAsync( this.notAllowedValueAsync(
() => firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))), () => firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))),
true, true,
), ),
@ -84,6 +91,15 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
this.anyActiveSponsorships$ = this.activeSponsorshipOrgs$.pipe(map((orgs) => orgs.length > 0)); this.anyActiveSponsorships$ = this.activeSponsorshipOrgs$.pipe(map((orgs) => orgs.length > 0));
this.loading = false; this.loading = false;
this.sponsorshipForm
.get("sponsorshipEmail")
.valueChanges.pipe(takeUntil(this._destroy))
.subscribe((val) => {
if (this.sponsorshipEmailControl.hasError("email")) {
this.sponsorshipEmailControl.setErrors([{ message: this.i18nService.t("invalidEmail") }]);
}
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -91,7 +107,7 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
this._destroy.complete(); this._destroy.complete();
} }
async submit() { submit = async () => {
this.formPromise = this.apiService.postCreateSponsorship( this.formPromise = this.apiService.postCreateSponsorship(
this.sponsorshipForm.value.selectedSponsorshipOrgId, this.sponsorshipForm.value.selectedSponsorshipOrgId,
{ {
@ -108,7 +124,7 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
this.resetForm(); this.resetForm();
await this.forceReload(); await this.forceReload();
} };
async forceReload() { async forceReload() {
this.loading = true; this.loading = true;
@ -127,4 +143,26 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
get isSelfHosted(): boolean { get isSelfHosted(): boolean {
return this.platformUtilsService.isSelfHost(); return this.platformUtilsService.isSelfHost();
} }
notAllowedValueAsync(
valueGetter: () => Promise<string>,
caseInsensitive = false,
): AsyncValidatorFn {
return async (control: AbstractControl): Promise<ValidationErrors | null> => {
let notAllowedValue = await valueGetter();
let controlValue = control.value;
if (caseInsensitive) {
notAllowedValue = notAllowedValue.toLowerCase();
controlValue = controlValue.toLowerCase();
}
if (controlValue === notAllowedValue) {
return {
errors: {
message: this.i18nService.t("cannotSponsorSelf"),
},
};
}
};
}
} }