Skip to content

Commit 12dcd47

Browse files
ftr: error page for unauthorized users or page not found (#18)
* ftr: error page for unauthorized users or page not found TRACEFOSS-859
1 parent 618f53b commit 12dcd47

18 files changed

+410
-57
lines changed

src/app/modules/core/core.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import { AboutModule } from '@page/about/about.module';
3030
import { AdminModule } from '@page/admin/admin.module';
3131
import { DashboardModule } from '@page/dashboard/dashboard.module';
3232
import { OtherPartsModule } from '@page/other-parts/other-parts.module';
33-
import { PageNotFoundModule } from '@page/page-not-found/page-not-found.module';
3433
import { PartsModule } from '@page/parts/parts.module';
3534
import { ToastService } from '@shared/components/toasts/toast.service';
3635
import { I18NextModule } from 'angular-i18next';
@@ -45,6 +44,7 @@ import { MockedKeycloakService } from './auth/mocked-keycloak.service';
4544
import { CoreRoutingModule } from './core.routing';
4645
import { I18N_PROVIDERS } from './i18n/global-i18n.providers';
4746
import { UserService } from './user/user.service';
47+
import { ErrorPageModule } from '@page/error-page/error-page.module';
4848

4949
@NgModule({
5050
declarations: [AppComponent],
@@ -55,7 +55,7 @@ import { UserService } from './user/user.service';
5555
HttpClientModule,
5656
KeycloakAngularModule,
5757
LayoutModule,
58-
PageNotFoundModule,
58+
ErrorPageModule,
5959
AboutModule,
6060
DashboardModule,
6161
PartsModule,

src/app/modules/core/core.routing.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import { NgModule } from '@angular/core';
2323
import { RouterModule, Routes } from '@angular/router';
2424
import { LayoutComponent } from '@layout/layout/layout.component';
25-
import { PageNotFoundComponent } from '@page/page-not-found/presentation/page-not-found.component';
2625
import { AuthGuard } from './auth/auth.guard';
26+
import { ErrorPageType } from '@page/error-page/model/error-page.model';
2727

2828
export /** @type {*} */
2929
const routes: Routes = [
@@ -35,8 +35,15 @@ const routes: Routes = [
3535
loadChildren: () => import('./layout/layout.module').then(m => m.LayoutModule),
3636
},
3737
{
38+
// if page not found we use ErrorPageModule + LayoutComponent to easy navigation
3839
path: '**',
39-
component: PageNotFoundComponent,
40+
component: LayoutComponent,
41+
data: {
42+
errorPage: {
43+
type: ErrorPageType.pageNotFound,
44+
},
45+
},
46+
loadChildren: () => import('../page/error-page/error-page.module').then(m => m.ErrorPageModule),
4047
},
4148
];
4249

src/app/modules/core/layout/layout.routing.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,50 @@ import { DASHBOARD_BASE_ROUTE } from '@page/dashboard/dashboard-route';
2828
import { INVESTIGATION_BASE_ROUTE } from '@page/investigations/investigations-external-route';
2929
import { OTHER_PARTS_BASE_ROUTE } from '@page/other-parts/other-parts-route';
3030
import { PARTS_BASE_ROUTE } from '@page/parts/parts-route';
31+
import { ErrorPageType } from '@page/error-page/model/error-page.model';
3132

3233
export /** @type {*} */
34+
// every page (except error pages) require at least "user" role
35+
// (to be able to detect unauthorized user and redirect to error page)
3336
const routes: Routes = [
3437
{
3538
path: '',
3639
pathMatch: 'full',
3740
redirectTo: 'dashboard',
3841
},
42+
{
43+
path: 'no-permissions',
44+
loadChildren: () => import('@page/error-page/error-page.module').then(m => m.ErrorPageModule),
45+
data: {
46+
errorPage: {
47+
type: ErrorPageType.noPermissions,
48+
},
49+
},
50+
},
3951
{
4052
path: DASHBOARD_BASE_ROUTE,
4153
loadChildren: () => import('../../page/dashboard/dashboard.module').then(m => m.DashboardModule),
4254
data: {
4355
breadcrumb: 'home',
56+
roles: ['user'],
4457
},
58+
canActivate: [RoleGuard],
4559
},
4660
{
4761
path: PARTS_BASE_ROUTE,
4862
loadChildren: () => import('../../page/parts/parts.module').then(m => m.PartsModule),
4963
data: {
5064
breadcrumb: 'parts',
65+
roles: ['user'],
5166
},
67+
canActivate: [RoleGuard],
5268
},
5369
{
5470
path: OTHER_PARTS_BASE_ROUTE,
5571
loadChildren: () => import('@page/other-parts/other-parts.module').then(m => m.OtherPartsModule),
5672
data: {
5773
breadcrumb: 'otherParts',
74+
roles: ['user'],
5875
},
5976
canActivate: [RoleGuard],
6077
},
@@ -63,6 +80,7 @@ const routes: Routes = [
6380
loadChildren: () => import('../../page/investigations/investigations.module').then(m => m.InvestigationsModule),
6481
data: {
6582
breadcrumb: 'investigations',
83+
roles: ['user'],
6684
},
6785
canActivate: [RoleGuard],
6886
},
@@ -71,7 +89,9 @@ const routes: Routes = [
7189
loadChildren: () => import('../../page/about/about.module').then(m => m.AboutModule),
7290
data: {
7391
breadcrumb: 'about',
92+
roles: ['user'],
7493
},
94+
canActivate: [RoleGuard],
7595
},
7696
{
7797
path: ADMIN_BASE_ROUTE,

src/app/modules/core/user/role.guard.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ export class RoleGuard implements CanActivate, CanActivateChild, CanDeactivate<u
6565
return true;
6666
}
6767

68-
void this.router.navigate(['']);
68+
// we use skipLocationChange = true, to don't lose context
69+
void this.router.navigate(['no-permissions'], { skipLocationChange: true });
6970
return false;
7071
}
7172
}

src/app/modules/core/user/role.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export class RoleService {
5656
return this.hasAccess('supervisor');
5757
}
5858

59+
public isAtLeastUser(): boolean {
60+
return this.hasAccess('user');
61+
}
62+
5963
private getParentsRolesFor(lookupRoles: Role[]): Role[] {
6064
const parentRoles = ROLES_RELATIONS.filter(({ child }) => lookupRoles.includes(child));
6165

src/app/modules/page/about/presentation/about.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@
2323
<app-header [title]="'pageAbout.title' | i18n"></app-header>
2424
<section
2525
class="mt-4 regular-text"
26-
[innerHTML]="'pageAbout.content' | i18n : { interpolation: { escapeValue: false } }"
26+
[innerHTML]="'pageAbout.content' | i18n: { interpolation: { escapeValue: false } }"
2727
></section>
2828
</div>

src/app/modules/page/page-not-found/page-not-found.module.ts renamed to src/app/modules/page/error-page/error-page.module.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@
1919
* SPDX-License-Identifier: Apache-2.0
2020
********************************************************************************/
2121

22-
import { CommonModule } from '@angular/common';
2322
import { NgModule } from '@angular/core';
24-
import { SharedModule } from '@shared/shared.module';
23+
import { getI18nPageProvider } from '@core/i18n';
24+
import { CommonModule } from '@angular/common';
25+
26+
import { ErrorPageRoutingModule } from './error-page.routing';
27+
import { ErrorPageComponent } from './presentation/error-page.component';
2528
import { TemplateModule } from '@shared/template.module';
26-
import { PageNotFoundComponent } from './presentation/page-not-found.component';
29+
import { SharedModule } from '@shared/shared.module';
2730

2831
@NgModule({
29-
declarations: [PageNotFoundComponent],
30-
imports: [CommonModule, TemplateModule, SharedModule],
32+
declarations: [ErrorPageComponent],
33+
imports: [CommonModule, TemplateModule, SharedModule, ErrorPageRoutingModule],
34+
providers: [...getI18nPageProvider('page.error-page')],
3135
})
32-
export class PageNotFoundModule {}
36+
export class ErrorPageModule {}

src/app/modules/page/page-not-found/presentation/page-not-found.component.spec.ts renamed to src/app/modules/page/error-page/error-page.routing.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,23 @@
1919
* SPDX-License-Identifier: Apache-2.0
2020
********************************************************************************/
2121

22-
import { PageNotFoundModule } from '@page/page-not-found/page-not-found.module';
23-
import { PageNotFoundComponent } from '@page/page-not-found/presentation/page-not-found.component';
24-
import { screen } from '@testing-library/angular';
25-
import { renderComponent } from '@tests/test-render.utils';
22+
import { NgModule } from '@angular/core';
23+
import { RouterModule, Routes } from '@angular/router';
24+
import { I18NEXT_NAMESPACE_RESOLVER } from 'angular-i18next';
25+
import { ErrorPageComponent } from '@page/error-page/presentation/error-page.component';
2626

27-
describe('PagNotFound', () => {
28-
const renderMap = () =>
29-
renderComponent(PageNotFoundComponent, {
30-
imports: [PageNotFoundModule],
31-
});
27+
const routes: Routes = [
28+
{
29+
path: '',
30+
pathMatch: 'full',
31+
component: ErrorPageComponent,
32+
data: { i18nextNamespaces: ['page.error-page'] },
33+
resolve: { i18next: I18NEXT_NAMESPACE_RESOLVER },
34+
},
35+
];
3236

33-
it('should render page not found component', async () => {
34-
await renderMap();
35-
36-
expect(screen.getByText('404')).toBeInTheDocument();
37-
// expect(screen.getByText('The page you requested could not be found')).toBeInTheDocument();
38-
});
39-
});
37+
@NgModule({
38+
imports: [RouterModule.forChild(routes)],
39+
exports: [RouterModule],
40+
})
41+
export class ErrorPageRoutingModule {}

src/app/modules/page/page-not-found/presentation/page-not-found.component.ts renamed to src/app/modules/page/error-page/model/error-page.model.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,8 @@
1919
* SPDX-License-Identifier: Apache-2.0
2020
********************************************************************************/
2121

22-
import { Component } from '@angular/core';
22+
export enum ErrorPageType {
23+
pageNotFound = 'pageNotFound',
2324

24-
@Component({
25-
selector: 'app-page-not-found',
26-
templateUrl: './page-not-found.component.html',
27-
styleUrls: ['./page-not-found.component.scss'],
28-
})
29-
export class PageNotFoundComponent {}
25+
noPermissions = 'noPermissions',
26+
}

src/app/modules/page/page-not-found/presentation/page-not-found.component.html renamed to src/app/modules/page/error-page/presentation/error-page.component.html

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,23 @@
1919
SPDX-License-Identifier: Apache-2.0
2020
-->
2121

22-
<div class="page-not-found-container">
23-
<section class="page-not-found-content">
24-
<h1 class="text-9xl mb-4">404</h1>
25-
<h3 class="mb-8">{{ 'errorPage.pageNotFound.message' | i18n }}</h3>
26-
<a href="#">
27-
<app-button [variant]="'raised'" [color]="'primary'">{{ 'actions.back' | i18n }}</app-button>
22+
<div class="error-page-container">
23+
<div class="">
24+
<img src="/assets/images/error_page.svg" alt="Error page image" />
25+
</div>
26+
27+
<div class="ml-2">
28+
<h1 class="text-4xl mb-4">{{ title | i18n }}</h1>
29+
<h3 class="text-xl mb-8">
30+
{{ message | i18n }}
31+
</h3>
32+
33+
<a *ngIf="actionLabel != ''" href="{{ actionUrl }}">
34+
<app-button [variant]="'raised'" [color]="'primary'">{{ actionLabel | i18n }}</app-button>
35+
</a>
36+
37+
<a *ngIf="showSignOutButton" (click)="this.logOut()">
38+
<app-button [variant]="'raised'" [color]="'primary'">{{ 'layout.nav.signOut' | i18n }}</app-button>
2839
</a>
29-
</section>
40+
</div>
3041
</div>

0 commit comments

Comments
 (0)