[SM-1168] Update access policy selector to disable on submit (#8519)
* Add loading and disabled on all inputs * Add proper spinner and form disable on submit
This commit is contained in:
parent
20de053770
commit
61d079cc34
|
@ -1,4 +1,4 @@
|
||||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
<form [formGroup]="formGroup" [bitSubmit]="submit" *ngIf="!loading; else spinner">
|
||||||
<div class="tw-w-2/5">
|
<div class="tw-w-2/5">
|
||||||
<p class="tw-mt-8" *ngIf="!loading">
|
<p class="tw-mt-8" *ngIf="!loading">
|
||||||
{{ "projectPeopleDescription" | i18n }}
|
{{ "projectPeopleDescription" | i18n }}
|
||||||
|
@ -19,3 +19,9 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<ng-template #spinner>
|
||||||
|
<div class="tw-items-center tw-justify-center tw-pt-64 tw-text-center">
|
||||||
|
<i class="bwi bwi-spinner bwi-spin bwi-3x"></i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from "@angular/forms";
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { combineLatest, Subject, switchMap, takeUntil, catchError, EMPTY } from "rxjs";
|
import { combineLatest, Subject, switchMap, takeUntil, catchError } from "rxjs";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
@ -37,11 +37,9 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
return convertToAccessPolicyItemViews(policies);
|
return convertToAccessPolicyItemViews(policies);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
catchError(() => {
|
catchError(async () => {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.router.navigate(["/sm", this.organizationId, "projects"]);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
return [];
|
||||||
this.router.navigate(["/sm", this.organizationId, "projects"]);
|
|
||||||
return EMPTY;
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -99,17 +97,20 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
if (this.formGroup.invalid) {
|
if (this.formGroup.invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const formValues = this.formGroup.value.accessPolicies;
|
||||||
|
this.formGroup.disable();
|
||||||
|
|
||||||
const showAccessRemovalWarning =
|
const showAccessRemovalWarning =
|
||||||
await this.accessPolicySelectorService.showAccessRemovalWarning(
|
await this.accessPolicySelectorService.showAccessRemovalWarning(
|
||||||
this.organizationId,
|
this.organizationId,
|
||||||
this.formGroup.value.accessPolicies,
|
formValues,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (showAccessRemovalWarning) {
|
if (showAccessRemovalWarning) {
|
||||||
const confirmed = await this.showWarning();
|
const confirmed = await this.showWarning();
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
this.setSelected(this.currentAccessPolicies);
|
this.setSelected(this.currentAccessPolicies);
|
||||||
|
this.formGroup.enable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
try {
|
try {
|
||||||
const projectPeopleView = convertToProjectPeopleAccessPoliciesView(
|
const projectPeopleView = convertToProjectPeopleAccessPoliciesView(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
this.formGroup.value.accessPolicies,
|
formValues,
|
||||||
);
|
);
|
||||||
const peoplePoliciesViews = await this.accessPolicyService.putProjectPeopleAccessPolicies(
|
const peoplePoliciesViews = await this.accessPolicyService.putProjectPeopleAccessPolicies(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
|
@ -126,9 +127,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
this.currentAccessPolicies = convertToAccessPolicyItemViews(peoplePoliciesViews);
|
this.currentAccessPolicies = convertToAccessPolicyItemViews(peoplePoliciesViews);
|
||||||
|
|
||||||
if (showAccessRemovalWarning) {
|
if (showAccessRemovalWarning) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.router.navigate(["sm", this.organizationId, "projects"]);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["sm", this.organizationId, "projects"]);
|
|
||||||
}
|
}
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"success",
|
"success",
|
||||||
|
@ -139,6 +138,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
this.validationService.showError(e);
|
this.validationService.showError(e);
|
||||||
this.setSelected(this.currentAccessPolicies);
|
this.setSelected(this.currentAccessPolicies);
|
||||||
}
|
}
|
||||||
|
this.formGroup.enable();
|
||||||
};
|
};
|
||||||
|
|
||||||
private setSelected(policiesToSelect: ApItemViewType[]) {
|
private setSelected(policiesToSelect: ApItemViewType[]) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
<form [formGroup]="formGroup" [bitSubmit]="submit" *ngIf="!loading; else spinner">
|
||||||
<div class="tw-w-2/5">
|
<div class="tw-w-2/5">
|
||||||
<p class="tw-mt-8" *ngIf="!loading">
|
<p class="tw-mt-8" *ngIf="!loading">
|
||||||
{{ "machineAccountPeopleDescription" | i18n }}
|
{{ "machineAccountPeopleDescription" | i18n }}
|
||||||
|
@ -20,3 +20,9 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<ng-template #spinner>
|
||||||
|
<div class="tw-items-center tw-justify-center tw-pt-64 tw-text-center">
|
||||||
|
<i class="bwi bwi-spinner bwi-spin bwi-3x"></i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from "@angular/forms";
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { catchError, combineLatest, EMPTY, Subject, switchMap, takeUntil } from "rxjs";
|
import { combineLatest, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
@ -40,12 +40,6 @@ export class ServiceAccountPeopleComponent implements OnInit, OnDestroy {
|
||||||
return convertToAccessPolicyItemViews(policies);
|
return convertToAccessPolicyItemViews(policies);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
catchError(() => {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["/sm", this.organizationId, "machine-accounts"]);
|
|
||||||
return EMPTY;
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private potentialGrantees$ = combineLatest([this.route.params]).pipe(
|
private potentialGrantees$ = combineLatest([this.route.params]).pipe(
|
||||||
|
@ -101,29 +95,32 @@ export class ServiceAccountPeopleComponent implements OnInit, OnDestroy {
|
||||||
if (this.isFormInvalid()) {
|
if (this.isFormInvalid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const formValues = this.formGroup.value.accessPolicies;
|
||||||
|
this.formGroup.disable();
|
||||||
|
|
||||||
const showAccessRemovalWarning =
|
const showAccessRemovalWarning =
|
||||||
await this.accessPolicySelectorService.showAccessRemovalWarning(
|
await this.accessPolicySelectorService.showAccessRemovalWarning(
|
||||||
this.organizationId,
|
this.organizationId,
|
||||||
this.formGroup.value.accessPolicies,
|
formValues,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
await this.handleAccessRemovalWarning(showAccessRemovalWarning, this.currentAccessPolicies)
|
await this.handleAccessRemovalWarning(showAccessRemovalWarning, this.currentAccessPolicies)
|
||||||
) {
|
) {
|
||||||
|
this.formGroup.enable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const peoplePoliciesViews = await this.updateServiceAccountPeopleAccessPolicies(
|
const peoplePoliciesViews = await this.updateServiceAccountPeopleAccessPolicies(
|
||||||
this.serviceAccountId,
|
this.serviceAccountId,
|
||||||
this.formGroup.value.accessPolicies,
|
formValues,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.handleAccessTokenAvailableWarning(
|
await this.handleAccessTokenAvailableWarning(
|
||||||
showAccessRemovalWarning,
|
showAccessRemovalWarning,
|
||||||
this.currentAccessPolicies,
|
this.currentAccessPolicies,
|
||||||
this.formGroup.value.accessPolicies,
|
formValues,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.currentAccessPolicies = convertToAccessPolicyItemViews(peoplePoliciesViews);
|
this.currentAccessPolicies = convertToAccessPolicyItemViews(peoplePoliciesViews);
|
||||||
|
@ -137,6 +134,7 @@ export class ServiceAccountPeopleComponent implements OnInit, OnDestroy {
|
||||||
this.validationService.showError(e);
|
this.validationService.showError(e);
|
||||||
this.setSelected(this.currentAccessPolicies);
|
this.setSelected(this.currentAccessPolicies);
|
||||||
}
|
}
|
||||||
|
this.formGroup.enable();
|
||||||
};
|
};
|
||||||
|
|
||||||
private setSelected(policiesToSelect: ApItemViewType[]) {
|
private setSelected(policiesToSelect: ApItemViewType[]) {
|
||||||
|
@ -198,9 +196,7 @@ export class ServiceAccountPeopleComponent implements OnInit, OnDestroy {
|
||||||
selectedPolicies: ApItemValueType[],
|
selectedPolicies: ApItemValueType[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (showAccessRemovalWarning) {
|
if (showAccessRemovalWarning) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
await this.router.navigate(["sm", this.organizationId, "machine-accounts"]);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["sm", this.organizationId, "machine-accounts"]);
|
|
||||||
} else if (
|
} else if (
|
||||||
this.accessPolicySelectorService.isAccessRemoval(currentAccessPolicies, selectedPolicies)
|
this.accessPolicySelectorService.isAccessRemoval(currentAccessPolicies, selectedPolicies)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
bitIconButton="bwi-close"
|
bitIconButton="bwi-close"
|
||||||
buttonType="main"
|
buttonType="main"
|
||||||
size="default"
|
size="default"
|
||||||
|
[disabled]="disabled"
|
||||||
[attr.title]="'remove' | i18n"
|
[attr.title]="'remove' | i18n"
|
||||||
[attr.aria-label]="'remove' | i18n"
|
[attr.aria-label]="'remove' | i18n"
|
||||||
(click)="selectionList.deselectItem(item.id); handleBlur()"
|
(click)="selectionList.deselectItem(item.id); handleBlur()"
|
||||||
|
@ -84,7 +85,14 @@
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
<div class="tw-ml-3 tw-mt-7 tw-shrink-0">
|
<div class="tw-ml-3 tw-mt-7 tw-shrink-0">
|
||||||
<button type="button" bitButton buttonType="secondary" (click)="addButton()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[loading]="loading"
|
||||||
|
[disabled]="disabled"
|
||||||
|
(click)="addButton()"
|
||||||
|
>
|
||||||
{{ "add" | i18n }}
|
{{ "add" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue