mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-05 05:17:40 +02:00
[PM-2411] Update billing-sync-api-key
to use Dialog (#6537)
* Update billing-sync-api-key dialog * Oscar's feedback
This commit is contained in:
parent
c3856ce821
commit
b1a92ba04b
@ -1,117 +1,75 @@
|
|||||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="billingSyncApiKeyTitle">
|
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
<bit-dialog>
|
||||||
<form
|
<h1 bitDialogTitle>
|
||||||
class="modal-content"
|
|
||||||
#form
|
|
||||||
(ngSubmit)="submit()"
|
|
||||||
[appApiAction]="formPromise"
|
|
||||||
ngNativeValidate
|
|
||||||
>
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title" id="billingSyncApiKeyTitle">
|
|
||||||
{{ (hasBillingToken ? "viewBillingSyncToken" : "generateBillingSyncToken") | i18n }}
|
{{ (hasBillingToken ? "viewBillingSyncToken" : "generateBillingSyncToken") | i18n }}
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<div bitDialogContent>
|
||||||
type="button"
|
<app-user-verification formControlName="verification" *ngIf="!clientSecret">
|
||||||
class="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
appA11yTitle="{{ 'close' | i18n }}"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<app-user-verification
|
|
||||||
[(ngModel)]="masterPassword"
|
|
||||||
ngDefaultControl
|
|
||||||
name="secret"
|
|
||||||
*ngIf="!clientSecret"
|
|
||||||
>
|
|
||||||
</app-user-verification>
|
</app-user-verification>
|
||||||
|
|
||||||
<ng-container *ngIf="clientSecret && showRotateScreen">
|
<ng-container *ngIf="clientSecret && showRotateScreen">
|
||||||
<p>{{ "rotateBillingSyncTokenTitle" | i18n }}</p>
|
<p>{{ "rotateBillingSyncTokenTitle" | i18n }}</p>
|
||||||
<app-callout type="warning">
|
<bit-callout type="warning">
|
||||||
{{ "rotateBillingSyncTokenWarning" | i18n }}
|
{{ "rotateBillingSyncTokenWarning" | i18n }}
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div *ngIf="clientSecret && !showRotateScreen">
|
<div *ngIf="clientSecret && !showRotateScreen">
|
||||||
<p>{{ "copyPasteBillingSync" | i18n }}</p>
|
<p>{{ "copyPasteBillingSync" | i18n }}</p>
|
||||||
<label for="clientSecret">{{ "billingSyncKey" | i18n }}</label>
|
<bit-form-field>
|
||||||
<div class="input-group">
|
<bit-label>{{ "billingSyncKey" | i18n }}</bit-label>
|
||||||
<input
|
<input
|
||||||
|
bitInput
|
||||||
id="clientSecret"
|
id="clientSecret"
|
||||||
class="form-control text-monospace"
|
|
||||||
type="text"
|
type="text"
|
||||||
[(ngModel)]="clientSecret"
|
[value]="clientSecret"
|
||||||
name="clientSecret"
|
class="tw-font-mono"
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
<div class="input-group-append">
|
|
||||||
<button
|
<button
|
||||||
|
bitIconButton="bwi-clone"
|
||||||
|
bitSuffix
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline-secondary"
|
[appCopyClick]="clientSecret"
|
||||||
(click)="copy()"
|
[appA11yTitle]="'copyValue' | i18n"
|
||||||
[appA11yTitle]="'copy' | i18n"
|
></button>
|
||||||
>
|
</bit-form-field>
|
||||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
<div class="tw-mt-2 tw-text-sm tw-text-muted" *ngIf="showLastSyncText">
|
||||||
</button>
|
<b class="tw-font-semibold">{{ "lastSync" | i18n }}:</b>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="small text-muted mt-2" *ngIf="showLastSyncText">
|
|
||||||
<b class="font-weight-semibold">{{ "lastSync" | i18n }}:</b>
|
|
||||||
{{ lastSyncDate | date : "medium" }}
|
{{ lastSyncDate | date : "medium" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="small text-danger mt-2" *ngIf="showAwaitingSyncText">
|
<div class="tw-mt-2 tw-text-sm tw-text-danger" *ngIf="showAwaitingSyncText">
|
||||||
<i class="bwi bwi-error"></i>
|
<i class="bwi bwi-error"></i>
|
||||||
{{
|
{{
|
||||||
(daysBetween === 1 ? "awaitingSyncSingular" : "awaitingSyncPlural")
|
(daysBetween === 1 ? "awaitingSyncSingular" : "awaitingSyncPlural") | i18n : daysBetween
|
||||||
| i18n : daysBetween
|
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<ng-container bitDialogFooter>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-primary btn-submit"
|
bitButton
|
||||||
[disabled]="form.loading"
|
bitFormButton
|
||||||
|
buttonType="primary"
|
||||||
*ngIf="!clientSecret || showRotateScreen"
|
*ngIf="!clientSecret || showRotateScreen"
|
||||||
>
|
>
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
*ngIf="form.loading"
|
|
||||||
></i>
|
|
||||||
<span>
|
|
||||||
{{ submitButtonText }}
|
{{ submitButtonText }}
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button bitButton bitDialogClose type="button" *ngIf="!showRotateScreen">
|
||||||
type="button"
|
|
||||||
class="btn btn-outline-secondary"
|
|
||||||
data-dismiss="modal"
|
|
||||||
*ngIf="!showRotateScreen"
|
|
||||||
>
|
|
||||||
{{ "close" | i18n }}
|
{{ "close" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button bitButton type="button" *ngIf="showRotateScreen" (click)="cancelRotate()">
|
||||||
type="button"
|
|
||||||
class="btn btn-outline-secondary"
|
|
||||||
*ngIf="showRotateScreen"
|
|
||||||
(click)="cancelRotate()"
|
|
||||||
>
|
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
bitButton
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline-secondary"
|
|
||||||
*ngIf="clientSecret && !showRotateScreen"
|
*ngIf="clientSecret && !showRotateScreen"
|
||||||
(click)="rotateToken()"
|
(click)="rotateToken()"
|
||||||
>
|
>
|
||||||
{{ "rotateToken" | i18n }}
|
{{ "rotateToken" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</ng-container>
|
||||||
|
</bit-dialog>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Component } from "@angular/core";
|
import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
|
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@ -8,8 +9,10 @@ import { OrganizationApiKeyRequest } from "@bitwarden/common/admin-console/model
|
|||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
|
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { Verification } from "@bitwarden/common/types/verification";
|
import { Verification } from "@bitwarden/common/types/verification";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
export interface BillingSyncApiModalData {
|
export interface BillingSyncApiModalData {
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
@ -20,41 +23,48 @@ export interface BillingSyncApiModalData {
|
|||||||
templateUrl: "billing-sync-api-key.component.html",
|
templateUrl: "billing-sync-api-key.component.html",
|
||||||
})
|
})
|
||||||
export class BillingSyncApiKeyComponent {
|
export class BillingSyncApiKeyComponent {
|
||||||
organizationId: string;
|
protected organizationId: string;
|
||||||
hasBillingToken: boolean;
|
protected hasBillingToken: boolean;
|
||||||
|
|
||||||
|
protected formGroup = new FormGroup({
|
||||||
|
verification: new FormControl<Verification>(null, Validators.required),
|
||||||
|
});
|
||||||
|
|
||||||
showRotateScreen: boolean;
|
showRotateScreen: boolean;
|
||||||
masterPassword: Verification;
|
|
||||||
formPromise: Promise<ApiKeyResponse>;
|
|
||||||
clientSecret?: string;
|
clientSecret?: string;
|
||||||
keyRevisionDate?: Date;
|
keyRevisionDate?: Date;
|
||||||
lastSyncDate?: Date = null;
|
lastSyncDate?: Date;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(DIALOG_DATA) protected data: BillingSyncApiModalData,
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
modalConfig: ModalConfig<BillingSyncApiModalData>
|
private logService: LogService
|
||||||
) {
|
) {
|
||||||
this.organizationId = modalConfig.data.organizationId;
|
this.organizationId = data.organizationId;
|
||||||
this.hasBillingToken = modalConfig.data.hasBillingToken;
|
this.hasBillingToken = data.hasBillingToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy() {
|
copy() {
|
||||||
this.platformUtilsService.copyToClipboard(this.clientSecret);
|
this.platformUtilsService.copyToClipboard(this.clientSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
submit = async () => {
|
||||||
if (this.showRotateScreen) {
|
try {
|
||||||
this.formPromise = this.userVerificationService
|
const request = this.userVerificationService
|
||||||
.buildRequest(this.masterPassword, OrganizationApiKeyRequest)
|
.buildRequest(this.formGroup.value.verification, OrganizationApiKeyRequest)
|
||||||
.then((request) => {
|
.then((request) => {
|
||||||
request.type = OrganizationApiKeyType.BillingSync;
|
request.type = OrganizationApiKeyType.BillingSync;
|
||||||
|
return request;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.showRotateScreen) {
|
||||||
|
const response = await request.then((request) => {
|
||||||
return this.organizationApiService.rotateApiKey(this.organizationId, request);
|
return this.organizationApiService.rotateApiKey(this.organizationId, request);
|
||||||
});
|
});
|
||||||
const response = await this.formPromise;
|
|
||||||
await this.load(response);
|
await this.load(response);
|
||||||
this.showRotateScreen = false;
|
this.showRotateScreen = false;
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
@ -63,16 +73,16 @@ export class BillingSyncApiKeyComponent {
|
|||||||
this.i18nService.t("billingSyncApiKeyRotated")
|
this.i18nService.t("billingSyncApiKeyRotated")
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.formPromise = this.userVerificationService
|
const response = await request.then((request) => {
|
||||||
.buildRequest(this.masterPassword, OrganizationApiKeyRequest)
|
|
||||||
.then((request) => {
|
|
||||||
request.type = OrganizationApiKeyType.BillingSync;
|
|
||||||
return this.organizationApiService.getOrCreateApiKey(this.organizationId, request);
|
return this.organizationApiService.getOrCreateApiKey(this.organizationId, request);
|
||||||
});
|
});
|
||||||
const response = await this.formPromise;
|
|
||||||
await this.load(response);
|
await this.load(response);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async load(response: ApiKeyResponse) {
|
async load(response: ApiKeyResponse) {
|
||||||
this.clientSecret = response.apiKey;
|
this.clientSecret = response.apiKey;
|
||||||
@ -116,4 +126,8 @@ export class BillingSyncApiKeyComponent {
|
|||||||
get daysBetween(): number {
|
get daysBetween(): number {
|
||||||
return this.dayDiff(this.keyRevisionDate, new Date());
|
return this.dayDiff(this.keyRevisionDate, new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static open(dialogService: DialogService, data: BillingSyncApiModalData) {
|
||||||
|
return dialogService.open(BillingSyncApiKeyComponent, { data });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { ModalConfig, ModalService } from "@bitwarden/angular/services/modal.service";
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
@ -18,10 +17,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import {
|
import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component";
|
||||||
BillingSyncApiKeyComponent,
|
|
||||||
BillingSyncApiModalData,
|
|
||||||
} from "./billing-sync-api-key.component";
|
|
||||||
import { SecretsManagerSubscriptionOptions } from "./sm-adjust-subscription.component";
|
import { SecretsManagerSubscriptionOptions } from "./sm-adjust-subscription.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -54,7 +50,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private modalService: ModalService,
|
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -330,22 +325,13 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
}
|
}
|
||||||
|
|
||||||
async manageBillingSync() {
|
async manageBillingSync() {
|
||||||
const modalConfig: ModalConfig<BillingSyncApiModalData> = {
|
const dialogRef = BillingSyncApiKeyComponent.open(this.dialogService, {
|
||||||
data: {
|
|
||||||
organizationId: this.organizationId,
|
organizationId: this.organizationId,
|
||||||
hasBillingToken: this.hasBillingSyncToken,
|
hasBillingToken: this.hasBillingSyncToken,
|
||||||
},
|
});
|
||||||
};
|
|
||||||
const modalRef = this.modalService.open(BillingSyncApiKeyComponent, modalConfig);
|
|
||||||
|
|
||||||
modalRef.onClosed
|
await firstValueFrom(dialogRef.closed);
|
||||||
.pipe(
|
|
||||||
concatMap(async () => {
|
|
||||||
this.load();
|
this.load();
|
||||||
}),
|
|
||||||
takeUntil(this.destroy$)
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDownloadLicense() {
|
closeDownloadLicense() {
|
||||||
|
Loading…
Reference in New Issue
Block a user