From 66513a114e1b3e19d199c0a05c0bc17590227db9 Mon Sep 17 00:00:00 2001 From: Pavlo Tkach <3469726+ptkach@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:53:45 -0400 Subject: [PATCH] Add OT&E UI to the new console (#2536) --- console-webapp/src/app/app-routing.module.ts | 12 ++- console-webapp/src/app/app.module.ts | 6 +- .../src/app/ote/newOte.component.html | 36 +++++++++ .../src/app/ote/newOte.component.scss | 7 ++ .../src/app/ote/newOte.component.ts | 81 +++++++++++++++++++ .../src/app/ote/oteStatus.component.html | 31 +++++++ .../src/app/ote/oteStatus.component.scss | 28 +++++++ .../src/app/ote/oteStatus.component.ts | 62 ++++++++++++++ .../src/app/registrar/registrar.service.ts | 16 ++++ .../registrar/registrarDetails.component.html | 8 ++ .../registrar/registrarDetails.component.ts | 12 ++- .../registrar/registrarsTable.component.html | 10 +++ .../registrar/registrarsTable.component.scss | 4 + .../registrar/registrarsTable.component.ts | 10 +++ .../userLevelVisiblity.directive.ts | 3 +- .../app/shared/services/backend.service.ts | 18 +++++ 16 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 console-webapp/src/app/ote/newOte.component.html create mode 100644 console-webapp/src/app/ote/newOte.component.scss create mode 100644 console-webapp/src/app/ote/newOte.component.ts create mode 100644 console-webapp/src/app/ote/oteStatus.component.html create mode 100644 console-webapp/src/app/ote/oteStatus.component.scss create mode 100644 console-webapp/src/app/ote/oteStatus.component.ts diff --git a/console-webapp/src/app/app-routing.module.ts b/console-webapp/src/app/app-routing.module.ts index d14bc2cd5..8e2c6e509 100644 --- a/console-webapp/src/app/app-routing.module.ts +++ b/console-webapp/src/app/app-routing.module.ts @@ -17,6 +17,9 @@ import { Route, RouterModule } from '@angular/router'; import { BillingInfoComponent } from './billingInfo/billingInfo.component'; import { DomainListComponent } from './domains/domainList.component'; import { HomeComponent } from './home/home.component'; +import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component'; +import { NewOteComponent } from './ote/newOte.component'; +import { OteStatusComponent } from './ote/oteStatus.component'; import { RegistrarDetailsComponent } from './registrar/registrarDetails.component'; import { RegistrarComponent } from './registrar/registrarsTable.component'; import { ResourcesComponent } from './resources/resources.component'; @@ -26,7 +29,6 @@ import { SettingsComponent } from './settings/settings.component'; import UsersComponent from './settings/users/users.component'; import WhoisComponent from './settings/whois/whois.component'; import { SupportComponent } from './support/support.component'; -import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component'; export interface RouteWithIcon extends Route { iconName?: string; @@ -38,6 +40,14 @@ export const routes: RouteWithIcon[] = [ path: RegistryLockVerifyComponent.PATH, component: RegistryLockVerifyComponent, }, + { + path: NewOteComponent.PATH, + component: NewOteComponent, + }, + { + path: OteStatusComponent.PATH, + component: OteStatusComponent, + }, { path: 'registrars', component: RegistrarComponent }, { path: 'home', diff --git a/console-webapp/src/app/app.module.ts b/console-webapp/src/app/app.module.ts index 8f41f6217..11cf6dc8a 100644 --- a/console-webapp/src/app/app.module.ts +++ b/console-webapp/src/app/app.module.ts @@ -30,7 +30,10 @@ import { DomainListComponent } from './domains/domainList.component'; import { RegistryLockComponent } from './domains/registryLock.component'; import { HeaderComponent } from './header/header.component'; import { HomeComponent } from './home/home.component'; +import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component'; import { NavigationComponent } from './navigation/navigation.component'; +import { NewOteComponent } from './ote/newOte.component'; +import { OteStatusComponent } from './ote/oteStatus.component'; import NewRegistrarComponent from './registrar/newRegistrar.component'; import { RegistrarDetailsComponent } from './registrar/registrarDetails.component'; import { RegistrarSelectorComponent } from './registrar/registrarSelector.component'; @@ -54,7 +57,6 @@ import { UserDataService } from './shared/services/userData.service'; import { SnackBarModule } from './snackbar.module'; import { SupportComponent } from './support/support.component'; import { TldsComponent } from './tlds/tlds.component'; -import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component'; @NgModule({ declarations: [ @@ -66,6 +68,8 @@ import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component HeaderComponent, HomeComponent, LocationBackDirective, + NewOteComponent, + OteStatusComponent, UserLevelVisibility, NavigationComponent, NewRegistrarComponent, diff --git a/console-webapp/src/app/ote/newOte.component.html b/console-webapp/src/app/ote/newOte.component.html new file mode 100644 index 000000000..b98a08c47 --- /dev/null +++ b/console-webapp/src/app/ote/newOte.component.html @@ -0,0 +1,36 @@ +

Generate OT&E Accounts

+
+ @if (oteCreateResponseFormatted()) { +

Generated Successfully

+ + + Epp Credentials + Copy and paste this into an email to the registrars + + +

{{ oteCreateResponseFormatted() }}

+
+
+ } @else { +
+

+ + Base Registrar Id: + + +

+

+ + Contact Email: + + Will be granted web-console access to the OTE registrars. + +

+ +
+ } +
diff --git a/console-webapp/src/app/ote/newOte.component.scss b/console-webapp/src/app/ote/newOte.component.scss new file mode 100644 index 000000000..02e1bf260 --- /dev/null +++ b/console-webapp/src/app/ote/newOte.component.scss @@ -0,0 +1,7 @@ +.console-app__new-ote { + max-width: 720px; + mat-card-content { + white-space: break-spaces; + padding: 20px; + } +} diff --git a/console-webapp/src/app/ote/newOte.component.ts b/console-webapp/src/app/ote/newOte.component.ts new file mode 100644 index 000000000..7cff6c925 --- /dev/null +++ b/console-webapp/src/app/ote/newOte.component.ts @@ -0,0 +1,81 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, computed, signal } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { RegistrarService } from '../registrar/registrar.service'; + +export interface OteCreateResponse extends Map { + password: string; +} + +@Component({ + selector: 'app-ote', + templateUrl: './newOte.component.html', + styleUrls: ['./newOte.component.scss'], +}) +export class NewOteComponent { + public static PATH = 'new-ote'; + + oteCreateResponse = signal(undefined); + + readonly oteCreateResponseFormatted = computed(() => { + const oteCreateResponse = this.oteCreateResponse(); + if (oteCreateResponse) { + const { password } = oteCreateResponse; + return Object.entries(oteCreateResponse) + .filter((entry) => entry[0] !== 'password') + .map( + ([login, tld]) => + `Login: ${login}\t\tPassword: ${password}\t\tTLD: ${tld}` + ) + .join('\n'); + } + return undefined; + }); + + createOte = new FormGroup({ + registrarId: new FormControl('', [Validators.required]), + registrarEmail: new FormControl('', [Validators.required]), + }); + + constructor( + protected registrarService: RegistrarService, + private _snackBar: MatSnackBar + ) {} + + onSubmit() { + if (this.createOte.valid) { + const { registrarId, registrarEmail } = this.createOte.value; + this.registrarService + .generateOte( + { + registrarId, + registrarEmail, + }, + registrarId || '' + ) + .subscribe({ + next: (oteCreateResponse: OteCreateResponse) => { + this.oteCreateResponse.set(oteCreateResponse); + }, + error: (err: HttpErrorResponse) => { + this._snackBar.open(err.error || err.message); + }, + }); + } + } +} diff --git a/console-webapp/src/app/ote/oteStatus.component.html b/console-webapp/src/app/ote/oteStatus.component.html new file mode 100644 index 000000000..23da40e2e --- /dev/null +++ b/console-webapp/src/app/ote/oteStatus.component.html @@ -0,0 +1,31 @@ + +

OT&E Status Check

+ @if(isOte()) { +

+ Status: + {{ oteStatusUnfinished().length ? "Unfinished" : "Completed" }} +

+
+ @if(oteStatusCompleted().length) { +
+

Completed

+
+ check_box{{ entry.description }} +
+
+ } @if(oteStatusUnfinished().length) { +
+

Unfinished

+
+ check_box_outline_blank{{ entry.description }} +
+
+ } +
+ } @else { +

+ Registrar {{ registrarService.registrar()?.registrarId }} is not an OT&E + registrar +

+ } +
diff --git a/console-webapp/src/app/ote/oteStatus.component.scss b/console-webapp/src/app/ote/oteStatus.component.scss new file mode 100644 index 000000000..f03e35c3e --- /dev/null +++ b/console-webapp/src/app/ote/oteStatus.component.scss @@ -0,0 +1,28 @@ +.console-app__ote-status { + max-width: 730px; + display: flex; + flex-wrap: wrap; + &_completed, + &_unfinished { + border: 1px solid #ddd; + padding: 20px; + border-radius: 10px; + margin: 0 20px 30px 0; + div { + display: flex; + min-width: 300px; + align-items: flex-start; + max-width: 300px; + margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 1px solid #ddd; + &:last-child { + border: none; + } + } + + mat-icon { + min-width: 30px; + } + } +} diff --git a/console-webapp/src/app/ote/oteStatus.component.ts b/console-webapp/src/app/ote/oteStatus.component.ts new file mode 100644 index 000000000..e9adfc9c6 --- /dev/null +++ b/console-webapp/src/app/ote/oteStatus.component.ts @@ -0,0 +1,62 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, computed, signal } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { RegistrarService } from '../registrar/registrar.service'; + +export interface OteStatusResponse { + description: string; + requirement: number; + timesPerformed: number; + completed: boolean; +} + +@Component({ + selector: 'app-ote-status', + templateUrl: './oteStatus.component.html', + styleUrls: ['./oteStatus.component.scss'], +}) +export class OteStatusComponent { + public static PATH = 'ote-status'; + + oteStatusResponse = signal([]); + + oteStatusCompleted = computed(() => + this.oteStatusResponse().filter((v) => v.completed) + ); + oteStatusUnfinished = computed(() => + this.oteStatusResponse().filter((v) => !v.completed) + ); + isOte = computed( + () => this.registrarService.registrar()?.type?.toLowerCase() === 'ote' + ); + + constructor( + protected registrarService: RegistrarService, + private _snackBar: MatSnackBar + ) { + this.registrarService + .oteStatus(this.registrarService.registrarId()) + .subscribe({ + next: (oteStatusResponse: OteStatusResponse[]) => { + this.oteStatusResponse.set(oteStatusResponse); + }, + error: (err: HttpErrorResponse) => { + this._snackBar.open(err.error || err.message); + }, + }); + } +} diff --git a/console-webapp/src/app/registrar/registrar.service.ts b/console-webapp/src/app/registrar/registrar.service.ts index a24131cfb..a4c333df4 100644 --- a/console-webapp/src/app/registrar/registrar.service.ts +++ b/console-webapp/src/app/registrar/registrar.service.ts @@ -17,6 +17,8 @@ import { Observable, switchMap, tap } from 'rxjs'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; +import { OteCreateResponse } from '../ote/newOte.component'; +import { OteStatusResponse } from '../ote/oteStatus.component'; import { BackendService } from '../shared/services/backend.service'; import { GlobalLoader, @@ -69,6 +71,7 @@ export interface Registrar registrarId: string; registrarName: string; registryLockAllowed?: boolean; + type?: string; } @Injectable({ @@ -149,4 +152,17 @@ export class RegistrarService implements GlobalLoader { loadingTimeout() { this._snackBar.open('Timeout loading registrars'); } + + generateOte( + oteForm: Object, + registrarId: string + ): Observable { + return this.backend + .generateOte(oteForm, registrarId) + .pipe(tap((_) => this.loadRegistrars())); + } + + oteStatus(registrarId: string): Observable { + return this.backend.getOteStatus(registrarId); + } } diff --git a/console-webapp/src/app/registrar/registrarDetails.component.html b/console-webapp/src/app/registrar/registrarDetails.component.html index de6c78f47..7379d7e69 100644 --- a/console-webapp/src/app/registrar/registrarDetails.component.html +++ b/console-webapp/src/app/registrar/registrarDetails.component.html @@ -8,6 +8,14 @@
@if(!inEdit && !registrarNotFound) { + +