From e1b2a4978949029eb9fef0adf2ba353b1697d7fa Mon Sep 17 00:00:00 2001 From: ccremer Date: Thu, 13 Apr 2023 12:51:04 +0200 Subject: [PATCH] Fix redirect if empty list --- cypress/e2e/billingentities.cy.ts | 9 +++--- cypress/e2e/billingentity-form.cy.ts | 4 +-- cypress/e2e/organizations.cy.ts | 32 +++++++++++++++++++ .../billingentity/billing-entity.component.ts | 14 +++++++- .../first-time-login-dialog.component.ts | 4 +-- .../organization-form.component.ts | 11 ++++++- 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/cypress/e2e/billingentities.cy.ts b/cypress/e2e/billingentities.cy.ts index d4833f40..1e6b171d 100644 --- a/cypress/e2e/billingentities.cy.ts +++ b/cypress/e2e/billingentities.cy.ts @@ -25,12 +25,12 @@ describe('Test billing entity list', () => { cy.get(':nth-child(3) > .flex-row > .text-3xl').should('contain.text', 'be-2347'); }); - it('empty list', () => { + it('empty list should redirect to new', () => { setBillingEntities(cy); cy.visit('/billingentities'); - cy.get('#billingentities-title').should('contain.text', 'Billing'); - cy.get('#addButton').should('contain.text', 'Add new Billing'); - cy.get('#no-billingentity-message').should('contain.text', 'No billing entities available.'); + cy.get('#title').should('contain.text', 'New Billing'); + + cy.url().should('contain', 'billingentities/$new?edit=y'); }); it('request failed', () => { @@ -91,6 +91,7 @@ describe('no permissions', () => { cy.visit('/billingentities'); cy.get('h1').should('contain.text', 'Billing'); cy.get('addButton').should('not.exist'); + cy.get('#no-billingentity-message').should('contain.text', 'No billing entities available.'); }); it('no edit permission', () => { diff --git a/cypress/e2e/billingentity-form.cy.ts b/cypress/e2e/billingentity-form.cy.ts index 884508b8..d273c385 100644 --- a/cypress/e2e/billingentity-form.cy.ts +++ b/cypress/e2e/billingentity-form.cy.ts @@ -162,7 +162,7 @@ describe('Test billing entity form elements', () => { }); it('should cancel editing', () => { - setBillingEntities(cy); + setBillingEntities(cy, billingEntityNxt); // give at least 1 item to avoid redirect back to form. ['.p-button-secondary', 'a[appbacklink]'].forEach((cancelSelector) => { cy.visit('/billingentities/$new?edit=y'); @@ -171,7 +171,7 @@ describe('Test billing entity form elements', () => { cy.get(cancelSelector).click(); cy.get('app-billingentity-form').should('not.exist'); - cy.get('p-messages').should('contain.text', 'No billing entities available'); + cy.get('.text-3xl').should('have.length', 1); }); }); }); diff --git a/cypress/e2e/organizations.cy.ts b/cypress/e2e/organizations.cy.ts index 6b5b66d1..722f547f 100644 --- a/cypress/e2e/organizations.cy.ts +++ b/cypress/e2e/organizations.cy.ts @@ -10,6 +10,8 @@ import { OrganizationPermissions } from '../../src/app/types/organization'; import { BillingEntityPermissions } from '../../src/app/types/billing-entity'; import { billingEntityNxt, billingEntityVshn, setBillingEntities } from '../fixtures/billingentities'; import { OrganizationMembersPermissions } from '../../src/app/types/organization-members'; +import { ZonePermissions } from '../../src/app/types/zone'; +import { zoneCloudscale1 } from '../fixtures/zone'; describe('Test organization list', () => { beforeEach(() => { @@ -268,6 +270,36 @@ describe('Test organization add', () => { cy.get(':nth-child(3) > .flex-row [title="Edit members"]').should('exist'); }); + it('add organization for first-time', () => { + cy.setPermission( + { verb: 'list', ...OrganizationPermissions }, + { verb: 'create', ...OrganizationPermissions }, + { verb: 'list', ...BillingEntityPermissions }, + { verb: 'list', ...ZonePermissions }, + { verb: 'update', ...OrganizationPermissions, namespace: organizationVshn.metadata.name }, + { verb: 'list', ...OrganizationMembersPermissions, namespace: organizationVshn.metadata.name } + ); + + setOrganization(cy, organizationNxt); + setBillingEntities(cy, billingEntityNxt); + cy.intercept('GET', 'appuio-api/apis/appuio.io/v1/zones', { + body: { items: [zoneCloudscale1] }, + }); + + cy.intercept('POST', 'appuio-api/apis/organization.appuio.io/v1/organizations', { + body: organizationVshn, + }).as('add'); + cy.visit('/organizations/$new?firstTime=y'); + cy.get('#title').should('contain.text', 'New Organization'); + + cy.get('#id').type('nxt'); + cy.get('#selectedBillingEntity').click().contains('Engineering').click(); + cy.get('button[type=submit]').click(); + cy.wait('@add'); + cy.url().should('contain', '/zones'); + cy.get('#zones-title').should('contain.text', 'Zones'); + }); + it('add organization with invalid id', () => { cy.setPermission( { verb: 'list', ...OrganizationPermissions }, diff --git a/src/app/billingentity/billing-entity.component.ts b/src/app/billingentity/billing-entity.component.ts index 4000f0fa..8b9343b1 100644 --- a/src/app/billingentity/billing-entity.component.ts +++ b/src/app/billingentity/billing-entity.component.ts @@ -5,6 +5,7 @@ import { faAdd, faEdit, faInfo, faMagnifyingGlass, faUserGroup, faWarning } from import { combineLatestAll, forkJoin, from, map, Observable, of } from 'rxjs'; import { BillingEntityCollectionService } from '../store/billingentity-collection.service'; import { switchMap } from 'rxjs/operators'; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-billing-entity', @@ -21,7 +22,11 @@ export class BillingEntityComponent implements OnInit { payload$?: Observable; - constructor(public billingEntityService: BillingEntityCollectionService) {} + constructor( + public billingEntityService: BillingEntityCollectionService, + private router: Router, + private route: ActivatedRoute + ) {} ngOnInit(): void { this.payload$ = forkJoin([ @@ -30,6 +35,13 @@ export class BillingEntityComponent implements OnInit { ]).pipe( switchMap(([entities, canCreateBilling]) => { if (entities.length === 0) { + if (canCreateBilling) { + void this.router.navigate(['$new'], { + queryParams: { firstTime: undefined, edit: 'y' }, + queryParamsHandling: 'merge', + relativeTo: this.route, + }); + } // no billing entities return of({ canCreateBilling: canCreateBilling, diff --git a/src/app/first-time-login-dialog/first-time-login-dialog.component.ts b/src/app/first-time-login-dialog/first-time-login-dialog.component.ts index db1d1bf3..fea46813 100644 --- a/src/app/first-time-login-dialog/first-time-login-dialog.component.ts +++ b/src/app/first-time-login-dialog/first-time-login-dialog.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ActivatedRoute, ActivationStart, Router } from '@angular/router'; -import { faAdd, faCog, faDollarSign, faSitemap } from '@fortawesome/free-solid-svg-icons'; +import { faAdd, faCog, faSitemap } from '@fortawesome/free-solid-svg-icons'; import { FormControl } from '@angular/forms'; import { IdentityService } from '../core/identity.service'; import { filter, forkJoin, map, Observable, of, take } from 'rxjs'; @@ -24,9 +24,7 @@ export class FirstTimeLoginDialogComponent implements OnInit { faSitemap = faSitemap; faAdd = faAdd; faCoq = faCog; - faDollarSign = faDollarSign; hideFirstTimeLoginDialogControl = new FormControl(false); - nextAction?: 'join' | 'add'; constructor( private router: Router, diff --git a/src/app/organizations/organization-form/organization-form.component.ts b/src/app/organizations/organization-form/organization-form.component.ts index b6409875..82117a82 100644 --- a/src/app/organizations/organization-form/organization-form.component.ts +++ b/src/app/organizations/organization-form/organization-form.component.ts @@ -134,7 +134,16 @@ export class OrganizationFormComponent implements OnInit, OnDestroy { severity: 'success', summary: $localize`Successfully saved`, }); - void this.router.navigate([this.navigationService.previousLocation()], { relativeTo: this.activatedRoute }); + const firstTime = this.activatedRoute.snapshot.queryParamMap.get('firstTime') === 'y'; + if (firstTime) { + void this.router.navigate(['zones'], { + queryParams: { edit: undefined, firstTime: undefined }, + queryParamsHandling: 'merge', + }); + return; + } + const previous = this.navigationService.previousRoute('..'); + void this.router.navigate([previous.path], { relativeTo: this.activatedRoute, queryParams: previous.queryParams }); } private saveOrUpdateFailure(err: Error): void {