refactor: change client selection in sign-up wizard to be more intuitive
Add radio buttons to make it clear which client is selected and apply css fixes for a better table layout (width, scrollable)
Closes:
MODIFY
src/app/app.module.ts
src/app/app.module.ts
@@ -66,6 +66,7 @@ import { NotificationsComponent } from './components/notifications/notifications
import { ApiMarkdownDescriptionComponent } from './components/api-markdown-description/api-markdown-description.component';
import { ApiStatusTagComponent } from './components/api-status-tag/api-status-tag.component';
import { ApiPublicEndpointComponent } from './components/api-public-endpoint/api-public-endpoint.component';
+import { MatRadioModule } from '@angular/material/radio';
export function initializeApp(
configService: ConfigService,
@@ -154,7 +155,8 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
}),
ReactiveFormsModule,
NgxChartsModule,
- MatBadgeModule
+ MatBadgeModule,
+ MatRadioModule
],
providers: [
{

MODIFY
src/app/components/marketplace-client-app/marketplace-client-app.component.html
src/app/components/marketplace-client-app/marketplace-client-app.component.html
~ See the License for the specific language governing permissions and
~ imitations under the License.
-->
-
-<div class="container">
+<div id="container-grid">
+ <div id="form-container">
<form>
<mat-form-field *ngIf="organizations.length > 1" class="mr-20">
<mat-label>{{ 'COMMON.ORGANIZATION' | translate }}</mat-label>
</button>
</div>
-<p class="primary">{{ 'CLIENTS.SELECT' | translate }}</p>
-<table mat-table [dataSource]="dataSource" class="mat-elevation-z2 mt-20 mb-20">
+ <p class="primary mb-0">{{ 'CLIENTS.SELECT' | translate }}</p>
+ <div data-simplebar id="table-container">
+ <table
+ mat-table
+ [dataSource]="dataSource"
+ class="mat-elevation-z2 mt-20 mb-20">
+ <!-- Checkbox Column -->
+ <ng-container matColumnDef="select">
+ <th mat-header-cell *matHeaderCellDef></th>
+ <td mat-cell *matCellDef="let element">
+ <mat-radio-button
+ color="primary"
+ (click)="$event.stopPropagation()"
+ (change)="$event ? this.selectClient(element) : null"
+ [checked]="selection.isSelected(element)">
+ </mat-radio-button>
+ </td>
+ </ng-container>
+
<!-- Org Name Column - only displayed if user has more than 1 -->
<ng-container matColumnDef="org-name">
<th mat-header-cell *matHeaderCellDef>
{{ 'COMMON.ORGANIZATION' | translate }}
</th>
<td mat-cell *matCellDef="let element">
- <span [class.primary]="clickedRows.has(element)">{{
+ <span [class.is-selected]="selection.isSelected(element)">{{
element.organizationName
}}</span>
</td>
{{ 'COMMON.CLIENT' | translate }}
</th>
<td mat-cell *matCellDef="let element">
- <span [class.primary]="clickedRows.has(element)">{{ element.name }}</span>
+ <span [class.is-selected]="selection.isSelected(element)">{{
+ element.name
+ }}</span>
</td>
</ng-container>
- <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+ <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr
mat-row
*matRowDef="let row; columns: displayedColumns"
- (click)="clickClient(row)"></tr>
+ (click)="selectClient(row)"></tr>
<tr class="mat-row" *matNoDataRow>
- <td class="mat-cell" colspan="2">
+ <td class="mat-cell" [colSpan]="displayedColumns.length">
{{ 'COMMON.NO_DATA_FOUND' | translate }}
</td>
</tr>
</table>
+ </div>
+</div>

MODIFY
src/app/components/marketplace-client-app/marketplace-client-app.component.scss
src/app/components/marketplace-client-app/marketplace-client-app.component.scss
*/
.is-selected {
- background: whitesmoke;
font-weight: bold;
+ color: var(--primary);
}
-.container {
+#form-container {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 20px;
}
+#container-grid {
+ display: grid;
+ justify-content: start;
+}
+
table {
- min-width: 30%;
+ width: 100%;
+}
+
+#table-container {
+ max-height: 35vh;
+ overflow: auto;
+}
+
+.mat-row .mat-cell {
+ cursor: pointer;
+}
+
+.mat-row:hover .mat-cell {
+ background: var(--dark-accent-20);
+}
+
+.mat-column-select {
+ overflow: initial;
+ width: 10%;
+ padding-right: 24px;
+}
+
+.mat-header-cell {
+ font-size: 16px;
}

MODIFY
src/app/components/marketplace-client-app/marketplace-client-app.component.ts
src/app/components/marketplace-client-app/marketplace-client-app.component.ts
@@ -28,6 +28,7 @@ import { switchMap } from 'rxjs/operators';
import { forkJoin } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { OrganizationService } from '../../services/org/organization.service';
+import { SelectionModel } from '@angular/cdk/collections';
@Component({
selector: 'app-marketplace-client-app',
@@ -35,15 +36,15 @@ import { OrganizationService } from '../../services/org/organization.service';
styleUrls: ['./marketplace-client-app.component.scss']
})
export class MarketplaceClientAppComponent implements OnInit {
- displayedColumns: string[] = ['name'];
+ displayedColumns: string[] = ['select', 'name'];
dataSource = new MatTableDataSource<IClientSummary>([]);
- clickedRows = new Set<IClientSummary>();
+ selection = new SelectionModel<IClientSummary>(false);
clientName = '';
organizationId = '';
organizations: IOrganizationSummary[] = [];
clients: IClientSummary[] = [];
- @Output() selectedClients = new EventEmitter<Set<IClientSummary>>();
+ @Output() selectedClient = new EventEmitter<IClientSummary>();
constructor(
private backend: BackendService,
@@ -56,17 +57,20 @@ export class MarketplaceClientAppComponent implements OnInit {
this.createOrgAndLoadClients();
}
- public clickClient(client: IClientSummary): void {
+ /**
+ * This method toggles between a selected/unselected client and
+ * emits the value to the parent component.
+ * @param client - the client that should be selected
+ */
+ public selectClient(client: IClientSummary): void {
+ if (this.selection.isSelected(client)) {
+ this.clearSelection();
+ } else {
this.clientName = client.name;
this.organizationId = client.organizationId;
- this.selectClient(client);
+ this.selection.select(client);
+ this.selectedClient.emit(client);
}
-
- private selectClient(client: IClientSummary): void {
- // always clear, because at the moment we only allow one client to be selected
- this.clickedRows.clear();
- this.clickedRows.add(client);
- this.selectedClients.emit(this.clickedRows);
}
/**
@@ -96,15 +100,10 @@ export class MarketplaceClientAppComponent implements OnInit {
}
public applyFilter(event: Event): void {
- this.clickedRows.clear();
+ this.clearSelection();
// https://material.angular.io/components/table/examples
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
-
- // if only one client is left we select it
- if (this.dataSource.filteredData.length === 1) {
- this.selectClient(this.dataSource.filteredData[0]);
- }
}
/**
@@ -145,12 +144,12 @@ export class MarketplaceClientAppComponent implements OnInit {
this.clients = results[1];
this.organizations = results[0];
if (this.organizations.length > 1) {
- this.displayedColumns = ['org-name', 'name'];
+ this.displayedColumns = ['select', 'org-name', 'name'];
}
this.dataSource = new MatTableDataSource(results[1]);
if (this.clients.length === 1) {
// if we only have one client we can select it automatically
- this.clickClient(this.clients[0]);
+ this.selectClient(this.clients[0]);
}
}
@@ -185,4 +184,13 @@ export class MarketplaceClientAppComponent implements OnInit {
? this.organizations[0].id
: this.organizationId;
}
+
+ /**
+ * Clears the selected client.
+ * This must be emitted to the parent component.
+ */
+ private clearSelection(): void {
+ this.selection.clear();
+ this.selectedClient.emit(undefined);
+ }
}

MODIFY
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.html
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.html
</mat-card-header>
<mat-card-content class="mat-card-header-text">
<app-marketplace-client-app
- (selectedClients)="
+ (selectedClient)="
checkApplications($event)
"></app-marketplace-client-app>
</mat-card-content>
<mat-divider></mat-divider>
<mat-card-actions class="right">
<button
- id="next-step-btn"
mat-raised-button
color="primary"
(click)="nextAfterClientSelect(stepper)">
{{ 'COMMON.BACK' | translate }}
</button>
<button
- id="next-step-btn"
mat-raised-button
matStepperNext
color="primary"

MODIFY
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.scss
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.scss
* imitations under the License.
*/
-.right {
- display: flex;
- justify-content: flex-end;
+@media only screen and (max-width: 1279px) {
+ mat-stepper {
+ width: 100%;
+ }
}
+@media only screen and (min-width: 1280px) {
mat-stepper {
- // TODO: Media Query for small screens
- width: 62vw;
+ width: 80%;
+ }
+}
+
+@media only screen and (min-width: 1920px) {
+ mat-stepper {
+ width: 60%;
+ }
+}
+
+.right {
+ display: flex;
+ justify-content: flex-end;
}
#center {

MODIFY
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.ts
src/app/components/marketplace-signup-stepper/marketplace-signup-stepper.component.ts
@@ -46,7 +46,7 @@ export class MarketplaceSignupStepperComponent implements OnInit {
// stepper completed checks, must be true to go to next step
agreedTermsAndPrivacy: boolean | undefined;
- selectedClients = new Set<IClientSummary>();
+ selectedClient: IClientSummary | undefined;
termsEnabled: boolean;
newContractDetails: ISignUpInfo;
contract: IContractExt;
@@ -83,8 +83,8 @@ export class MarketplaceSignupStepperComponent implements OnInit {
});
}
- checkApplications($event: Set<IClientSummary>): void {
- this.selectedClients = $event;
+ checkApplications($event: IClientSummary): void {
+ this.selectedClient = $event;
}
checkTerms($event: boolean): void {
@@ -92,16 +92,19 @@ export class MarketplaceSignupStepperComponent implements OnInit {
}
nextAfterClientSelect(stepper: MatStepper): void {
- if (this.selectedClients.size == 0) {
+ if (!this.selectedClient) {
this.printUserError('WIZARD.CLIENT_ERROR');
} else {
- this.checkIfContractAlreadyExists(stepper);
+ this.checkIfContractAlreadyExists(stepper, this.selectedClient);
}
}
- private checkIfContractAlreadyExists(stepper: MatStepper) {
- const client: IClientSummary = this.selectedClients.values().next()
- .value as IClientSummary;
+ private checkIfContractAlreadyExists(
+ stepper: MatStepper,
+ client: IClientSummary
+ ) {
+ console.info(`Using client ${client.organizationId}/${client.name}`);
+
this.backend
.getContracts(client.organizationId, client.id, '1.0')
.subscribe({
@@ -147,8 +150,8 @@ export class MarketplaceSignupStepperComponent implements OnInit {
}
createContractAndRegisterClient(stepper: MatStepper): void {
- const client: IClientSummary = this.selectedClients.values().next()
- .value as IClientSummary;
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const client: IClientSummary = this.selectedClient!;
const contract: INewContract = {
apiOrgId: this.newContractDetails.apiVersion.api.organization.id,

MODIFY
src/app/services/api/api.service.ts
src/app/services/api/api.service.ts
@@ -19,7 +19,6 @@ import { BackendService } from '../backend/backend.service';
import { Observable, of, throwError } from 'rxjs';
import { catchError, retry, switchMap } from 'rxjs/operators';
import {
- IApiSummary,
IApiVersion,
IApiVersionEndpointSummary,
IApiVersionSummary,

MODIFY
src/app/services/backend/backend.service.ts
src/app/services/backend/backend.service.ts
@@ -22,7 +22,6 @@ import {
IAction,
IApi,
IApiPlanSummary,
- IApiSummary,
IApiVersion,
IApiVersionEndpointSummary,
IApiVersionSummary,

MODIFY
src/assets/i18n/en.json
src/assets/i18n/en.json
"SECOND_STEP_LABEL": "Agreement",
"THIRD_STEP_LABEL": "Summary",
"FORTH_STEP_LABEL": "Confirmation",
- "FIRST_STEP_TITLE": "Select or create an new client",
+ "FIRST_STEP_TITLE": "Select or create a new client",
"SECOND_STEP_TITLE": "Terms and Conditions",
"THIRD_STEP_TITLE": "Summary",
"FORTH_STEP_TITLE": "Confirmation",

MODIFY
src/styles.scss
src/styles.scss
@@ -139,7 +139,7 @@ $default-font: "Open Sans", sans-serif;
}
/** Paddings and Margins **/
-$sizes: 5, 8, 10, 15, 20, 25, 30, 35, 40;
+$sizes: 0, 5, 8, 10, 15, 20, 25, 30, 35, 40;
@each $size in $sizes {
// Margins

refactor: change client selection in sign-up wizard to be more intuitive

Add radio buttons to make it clear which client is selected and apply css fixes for a better table layout (width, scrollable)

Closes: #93