mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-28 12:45:45 +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:
parent
091c8ccd46
commit
4e9db9057b
@ -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"
|
|
||||||
>
|
|
||||||
<div class="form-group col-7">
|
|
||||||
<label for="availableSponsorshipOrg">{{ "familiesSponsoringOrgSelect" | i18n }}</label>
|
|
||||||
<select
|
|
||||||
id="availableSponsorshipOrg"
|
id="availableSponsorshipOrg"
|
||||||
name="Available Sponsorship Organization"
|
name="Available Sponsorship Organization"
|
||||||
formControlName="selectedSponsorshipOrgId"
|
formControlName="selectedSponsorshipOrgId"
|
||||||
class="form-control"
|
|
||||||
required
|
|
||||||
>
|
>
|
||||||
<option disabled="true" value="">-- {{ "select" | i18n }} --</option>
|
<bit-option
|
||||||
<option *ngFor="let o of availableSponsorshipOrgs$ | async" [ngValue]="o.id">
|
[disabled]="true"
|
||||||
{{ o.name }}
|
[value]=""
|
||||||
</option>
|
[label]="'--' + ('select' | i18n) + '--'"
|
||||||
</select>
|
></bit-option>
|
||||||
|
<bit-option
|
||||||
|
*ngFor="let o of availableSponsorshipOrgs$ | async"
|
||||||
|
[value]="o.id"
|
||||||
|
[label]="o.name"
|
||||||
|
></bit-option>
|
||||||
|
</bit-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>
|
||||||
|
<bit-label>{{ "sponsoredFamiliesEmail" | i18n }}:</bit-label>
|
||||||
<input
|
<input
|
||||||
id="sponsorshipEmail"
|
bitInput
|
||||||
class="form-control"
|
|
||||||
inputmode="email"
|
inputmode="email"
|
||||||
formControlName="sponsorshipEmail"
|
formControlName="sponsorshipEmail"
|
||||||
name="sponsorshipEmail"
|
|
||||||
required
|
|
||||||
[attr.aria-invalid]="sponsorshipEmailControl.invalid"
|
[attr.aria-invalid]="sponsorshipEmailControl.invalid"
|
||||||
/>
|
/>
|
||||||
<small
|
</bit-form-field>
|
||||||
aria-errormessage="sponsorshipEmail"
|
|
||||||
*ngIf="sponsorshipEmailControl.errors?.notAllowedValue"
|
|
||||||
class="error-inline"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
<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>
|
||||||
<div class="form-group col-7">
|
<div class="tw-col-span-7">
|
||||||
<button class="btn btn-primary btn-submit mt-2" type="submit" [disabled]="form.loading">
|
<button bitButton bitFormButton buttonType="primary" type="submit">
|
||||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
{{ "redeem" | i18n }}
|
||||||
<span>{{ "redeem" | i18n }}</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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>{{ "recipient" | i18n }}</th>
|
<th bitCell>{{ "recipient" | i18n }}</th>
|
||||||
<th>{{ "sponsoringOrg" | i18n }}</th>
|
<th bitCell>{{ "sponsoringOrg" | i18n }}</th>
|
||||||
<th>{{ "status" | i18n }}</th>
|
<th bitCell>{{ "status" | i18n }}</th>
|
||||||
<th></th>
|
<th bitCell></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</ng-container>
|
||||||
<tbody>
|
<ng-template body alignContent="middle">
|
||||||
<ng-container *ngFor="let o of activeSponsorshipOrgs$ | async">
|
<ng-container *ngFor="let o of activeSponsorshipOrgs$ | async">
|
||||||
<tr
|
<tr
|
||||||
|
bitRow
|
||||||
sponsoring-org-row
|
sponsoring-org-row
|
||||||
[sponsoringOrg]="o"
|
[sponsoringOrg]="o"
|
||||||
[isSelfHosted]="isSelfHosted"
|
[isSelfHosted]="isSelfHosted"
|
||||||
(sponsorshipRemoved)="forceReload()"
|
(sponsorshipRemoved)="forceReload()"
|
||||||
></tr>
|
>
|
||||||
|
<hr />
|
||||||
|
</tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</tbody>
|
</ng-template>
|
||||||
</table>
|
</bit-table>
|
||||||
</div>
|
<hr />
|
||||||
<small>{{ "sponsoredFamiliesLeaveCopy" | i18n }}</small>
|
<p bitTypography="body2">{{ "sponsoredFamiliesLeaveCopy" | i18n }}</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</bit-container>
|
</bit-container>
|
||||||
|
@ -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"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user