diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 64efaae9..36526e76 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -37,6 +37,7 @@ import { OrganizationCollectionService } from './store/organization-collection.s import { KubernetesDataServiceFactory } from './store/kubernetes-data.service'; import { KubernetesCollectionServiceFactory } from './store/kubernetes-collection.service'; import { SelfSubjectAccessReviewCollectionService } from './store/ssar-collection.service'; +import { NavigationService } from './shared/navigation.service'; @NgModule({ declarations: [ @@ -72,7 +73,8 @@ import { SelfSubjectAccessReviewCollectionService } from './store/ssar-collectio SelfSubjectAccessReviewCollectionService, { provide: APP_INITIALIZER, - deps: [AppConfigService, OAuthService], + // start the NavigationService early to catch route events. + deps: [AppConfigService, OAuthService, NavigationService], useFactory: initializeAppFactory, multi: true, }, diff --git a/src/app/billingentity/billingentity-members/billingentity-members.component.html b/src/app/billingentity/billingentity-members/billingentity-members.component.html index c27be7b0..71b80cc2 100644 --- a/src/app/billingentity/billingentity-members/billingentity-members.component.html +++ b/src/app/billingentity/billingentity-members/billingentity-members.component.html @@ -6,7 +6,7 @@ {{ payload.billingEntity.metadata.name }} Members - + diff --git a/src/app/billingentity/billingentity-members/billingentity-members.component.ts b/src/app/billingentity/billingentity-members/billingentity-members.component.ts index 5f8078d2..02015dda 100644 --- a/src/app/billingentity/billingentity-members/billingentity-members.component.ts +++ b/src/app/billingentity/billingentity-members/billingentity-members.component.ts @@ -15,6 +15,7 @@ import { defaultIfNotFound } from '../../store/kubernetes-collection.service'; import { ClusterRoleCollectionService } from '../../store/cluster-role-collection.service'; import { ClusterRole } from '../../types/clusterRole'; import { KubeObject } from '../../types/entity'; +import { NavigationService } from '../../shared/navigation.service'; interface Payload { billingEntity: BillingEntity; @@ -54,6 +55,7 @@ export class BillingentityMembersComponent implements OnInit, OnDestroy { constructor( private route: ActivatedRoute, private router: Router, + private navigationService: NavigationService, private billingService: BillingEntityCollectionService, private roleService: ClusterRoleCollectionService, public rolebindingService: ClusterRolebindingCollectionService, @@ -234,7 +236,7 @@ export class BillingentityMembersComponent implements OnInit, OnDestroy { severity: 'success', summary: $localize`Successfully saved`, }); - void this.router.navigate(['../..'], { relativeTo: this.route }); + void this.router.navigate([this.navigationService.previousLocation()], { relativeTo: this.route }); }, error: (error) => { this.messageService.add({ diff --git a/src/app/billingentity/billingentity-view/billingentity-view.component.html b/src/app/billingentity/billingentity-view/billingentity-view.component.html index 87cf6a4b..8770e9a8 100644 --- a/src/app/billingentity/billingentity-view/billingentity-view.component.html +++ b/src/app/billingentity/billingentity-view/billingentity-view.component.html @@ -4,7 +4,7 @@
{{ billingEntity.metadata.name }}
- +
diff --git a/src/app/organizations/organization-edit/organization-edit.component.html b/src/app/organizations/organization-edit/organization-edit.component.html index b352cb8b..a604bdc0 100644 --- a/src/app/organizations/organization-edit/organization-edit.component.html +++ b/src/app/organizations/organization-edit/organization-edit.component.html @@ -8,7 +8,7 @@ {{ payload.organization.metadata.name }}
- + diff --git a/src/app/organizations/organization-form/organization-form.component.ts b/src/app/organizations/organization-form/organization-form.component.ts index 5a32b51d..511a5aed 100644 --- a/src/app/organizations/organization-form/organization-form.component.ts +++ b/src/app/organizations/organization-form/organization-form.component.ts @@ -8,6 +8,7 @@ import { MessageService } from 'primeng/api'; import { OrganizationNameService } from '../organization-name.service'; import { OrganizationCollectionService } from '../../store/organization-collection.service'; import { BillingEntity } from '../../types/billing-entity'; +import { NavigationService } from '../../shared/navigation.service'; @Component({ selector: 'app-organization-form', @@ -40,7 +41,8 @@ export class OrganizationFormComponent implements OnInit, OnDestroy { private activatedRoute: ActivatedRoute, private messageService: MessageService, private organizationNameService: OrganizationNameService, - public organizationCollectionService: OrganizationCollectionService + public organizationCollectionService: OrganizationCollectionService, + private navigationService: NavigationService ) {} ngOnInit(): void { @@ -114,12 +116,11 @@ export class OrganizationFormComponent implements OnInit, OnDestroy { severity: 'success', summary: $localize`Successfully saved`, }); - void this.router.navigate(['..'], { relativeTo: this.activatedRoute }); + void this.router.navigate([this.navigationService.previousLocation()], { relativeTo: this.activatedRoute }); } private saveOrUpdateFailure(err: Error): void { let detail = ''; - console.debug('error!', err); if ('message' in err) { detail = err.message; } @@ -132,6 +133,7 @@ export class OrganizationFormComponent implements OnInit, OnDestroy { this.messageService.add({ severity: 'error', summary: $localize`Error`, + sticky: true, detail, }); } diff --git a/src/app/organizations/organization-members-edit/organization-members-edit.component.html b/src/app/organizations/organization-members-edit/organization-members-edit.component.html index 568d040b..0a7dae3f 100644 --- a/src/app/organizations/organization-members-edit/organization-members-edit.component.html +++ b/src/app/organizations/organization-members-edit/organization-members-edit.component.html @@ -6,7 +6,7 @@ {{ payload.members.metadata.namespace }} Members - + diff --git a/src/app/organizations/organization-members-edit/organization-members-edit.component.ts b/src/app/organizations/organization-members-edit/organization-members-edit.component.ts index 3753109f..92718e9a 100644 --- a/src/app/organizations/organization-members-edit/organization-members-edit.component.ts +++ b/src/app/organizations/organization-members-edit/organization-members-edit.component.ts @@ -8,6 +8,7 @@ import { MessageService } from 'primeng/api'; import { RoleBinding } from 'src/app/types/role-binding'; import { OrganizationMembersCollectionService } from '../../store/organizationmembers-collection.service'; import { RolebindingCollectionService } from '../../store/rolebinding-collection.service'; +import { NavigationService } from '../../shared/navigation.service'; interface Payload { members: OrganizationMembers; @@ -47,7 +48,8 @@ export class OrganizationMembersEditComponent implements OnInit { private messageService: MessageService, private router: Router, private membersService: OrganizationMembersCollectionService, - private rolebindingService: RolebindingCollectionService + private rolebindingService: RolebindingCollectionService, + private navigationService: NavigationService ) {} get userRefs(): FormArray | undefined { @@ -165,7 +167,7 @@ export class OrganizationMembersEditComponent implements OnInit { severity: 'success', summary: $localize`Successfully saved`, }); - void this.router.navigate(['../..'], { relativeTo: this.activatedRoute }); + void this.router.navigate([this.navigationService.previousLocation()], { relativeTo: this.activatedRoute }); }, error: (error) => { this.messageService.add({ diff --git a/src/app/shared/back-link.directive.ts b/src/app/shared/back-link.directive.ts new file mode 100644 index 00000000..b85afe17 --- /dev/null +++ b/src/app/shared/back-link.directive.ts @@ -0,0 +1,23 @@ +import { Directive, HostListener, Input } from '@angular/core'; +import { NavigationService } from './navigation.service'; +import { ActivatedRoute, Router } from '@angular/router'; + +/** + * This directive adds a `click` event listener to navigate back in history. + * It accepts an input that is used as the default path in case there is no history (e.g. opened link in a new tab). + */ +@Directive({ + selector: '[appBackLink]', +}) +export class BackLinkDirective { + constructor(private navigation: NavigationService, private router: Router, private activatedRoute: ActivatedRoute) {} + + @Input() + appBackLink?: string; + + @HostListener('click') + onClick(): void { + const route = this.navigation.previousLocation(this.appBackLink); + void this.router.navigate([route], { relativeTo: this.activatedRoute }); + } +} diff --git a/src/app/shared/navigation.service.ts b/src/app/shared/navigation.service.ts new file mode 100644 index 00000000..f3230244 --- /dev/null +++ b/src/app/shared/navigation.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; + +/** + * Inspired by https://nils-mehlhorn.de/posts/angular-navigate-back-previous-page/ + * Modified to be used with a Router working with default and relative paths in case the history is empty. + */ +@Injectable({ providedIn: 'root' }) +export class NavigationService { + private history: string[] = []; + + constructor(private router: Router) { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd) { + this.history.push(event.urlAfterRedirects); + this.history.splice(0, this.history.length - 5); // only keep the latest few, no need for more. + } + }); + } + + /** + * Gets the previous URI location in the history. + * @param defaultPath if the history is empty, return this path as fallback value + * @returns the URI, or '/' if no default was given. + */ + previousLocation(defaultPath?: string): string { + void this.history.pop(); // remove "current" location + if (this.history.length > 0) { + const previousLocation = this.history.pop(); + return previousLocation ?? defaultPath ?? '/'; + } else { + return defaultPath ?? '/'; + } + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9f9fe14b..d56cb6f9 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -20,9 +20,10 @@ import { MessageModule } from 'primeng/message'; import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { MultiSelectModule } from 'primeng/multiselect'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; +import { BackLinkDirective } from './back-link.directive'; @NgModule({ - declarations: [], + declarations: [BackLinkDirective], imports: [], exports: [ CommonModule, @@ -47,6 +48,7 @@ import { ProgressSpinnerModule } from 'primeng/progressspinner'; ConfirmDialogModule, MultiSelectModule, ProgressSpinnerModule, + BackLinkDirective, ], }) export class SharedModule {} diff --git a/src/app/teams/team-edit/team-edit.component.html b/src/app/teams/team-edit/team-edit.component.html index 6473c453..0c1f1ead 100644 --- a/src/app/teams/team-edit/team-edit.component.html +++ b/src/app/teams/team-edit/team-edit.component.html @@ -9,7 +9,7 @@ {{ team.metadata.name }} - + diff --git a/src/app/teams/team-edit/team-edit.component.ts b/src/app/teams/team-edit/team-edit.component.ts index dd7d036a..067f685f 100644 --- a/src/app/teams/team-edit/team-edit.component.ts +++ b/src/app/teams/team-edit/team-edit.component.ts @@ -6,6 +6,7 @@ import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@ang import { MessageService } from 'primeng/api'; import { Observable, of, take, tap } from 'rxjs'; import { TeamCollectionService } from '../../store/team-collection.service'; +import { NavigationService } from '../../shared/navigation.service'; @Component({ selector: 'app-team-edit', @@ -26,7 +27,8 @@ export class TeamEditComponent implements OnInit { private router: Router, private activatedRoute: ActivatedRoute, private messageService: MessageService, - public teamService: TeamCollectionService + public teamService: TeamCollectionService, + private navigationService: NavigationService ) {} get userRefs(): FormArray { @@ -71,7 +73,7 @@ export class TeamEditComponent implements OnInit { severity: 'success', summary: $localize`Successfully saved`, }); - void this.router.navigate(['../..'], { relativeTo: this.activatedRoute }); + void this.router.navigate([this.navigationService.previousLocation()], { relativeTo: this.activatedRoute }); }, error: (error) => { let detail = ''; diff --git a/src/app/zones/zone/zone-detail.component.html b/src/app/zones/zone/zone-detail.component.html index cddafe00..d08c3ff3 100644 --- a/src/app/zones/zone/zone-detail.component.html +++ b/src/app/zones/zone/zone-detail.component.html @@ -41,7 +41,7 @@