@genesislcap/blank-app-seed 3.21.0 → 3.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.genx/package.json +1 -1
  2. package/.genx/templates/angular/chart.hbs +21 -9
  3. package/.genx/templates/angular/entityManager.hbs +57 -45
  4. package/.genx/templates/angular/form.hbs +21 -8
  5. package/.genx/templates/angular/grid.hbs +30 -18
  6. package/.genx/templates/angular/route.hbs +8 -5
  7. package/.genx/templates/web-components/entityManager.hbs +1 -1
  8. package/CHANGELOG.md +7 -0
  9. package/client-tmp/angular/angular.json +21 -12
  10. package/client-tmp/angular/package.json +45 -20
  11. package/client-tmp/angular/src/app/app-routing.module.ts +10 -3
  12. package/client-tmp/angular/src/app/app.component.spec.ts +2 -8
  13. package/client-tmp/angular/src/app/app.component.ts +0 -1
  14. package/client-tmp/angular/src/app/app.config.ts +1 -0
  15. package/client-tmp/angular/src/app/components/error-message/error-message.component.html +15 -0
  16. package/client-tmp/angular/src/app/components/error-message/error-message.component.spec.ts +74 -0
  17. package/client-tmp/angular/src/app/components/error-message/error-message.component.ts +15 -0
  18. package/client-tmp/angular/src/app/guards/auth.guard.ts +3 -3
  19. package/client-tmp/angular/src/app/guards/permissions.guard.ts +24 -0
  20. package/client-tmp/angular/src/app/layouts/blank/blank.layout.html +0 -1
  21. package/client-tmp/angular/src/app/layouts/blank/blank.layout.spec.ts +1 -1
  22. package/client-tmp/angular/src/app/layouts/default/default.layout.html +0 -1
  23. package/client-tmp/angular/src/app/layouts/default/default.layout.spec.ts +30 -15
  24. package/client-tmp/angular/src/app/pages/auth-login/auth-login.component.spec.ts +6 -6
  25. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.html +4 -0
  26. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.scss +12 -0
  27. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.spec.ts +32 -0
  28. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.ts +13 -0
  29. package/client-tmp/angular/src/app/services/auth.service.ts +12 -4
  30. package/client-tmp/angular/src/app/share/foundation-login.ts +3 -0
  31. package/client-tmp/angular/src/app/utils/index.ts +1 -0
  32. package/client-tmp/angular/src/app/utils/permissions.ts +7 -0
  33. package/client-tmp/angular/src/styles/_mixins.scss +8 -0
  34. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed-config",
3
3
  "description": "Genesis Blank App Seed Configuration",
4
- "version": "3.21.0",
4
+ "version": "3.22.0",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "lint": "eslint .",
@@ -1,9 +1,21 @@
1
- <rapid-g2plot-chart
2
- type="{{ config.type }}"
3
- [config]="tile{{ config.index }}.chartConfig"
4
- >
5
- <chart-datasource
6
- resourceName="{{ config.resourceName }}"
7
- server-fields="{{ config.xField }} {{ config.yField }}"
8
- ></chart-datasource>
9
- </rapid-g2plot-chart>
1
+ <section style="height: 100%; width: 100%;">
2
+ <ng-container *ngIf="hasUserPermission('{{config.permissions.viewRight}}'); else notPermitted{{ config.index }}">
3
+ <rapid-g2plot-chart
4
+ type="{{ config.type }}"
5
+ [config]="tile{{ config.index }}.chartConfig"
6
+ >
7
+ <chart-datasource
8
+ resourceName="{{ config.resourceName }}"
9
+ server-fields="{{ config.xField }} {{ config.yField }}"
10
+ ></chart-datasource>
11
+ </rapid-g2plot-chart>
12
+ </ng-container>
13
+
14
+ <ng-template #notPermitted{{ config.index }}>
15
+ <app-error-message
16
+ style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;"
17
+ elementType="h3"
18
+ message="You do not have access to view this component.">
19
+ </app-error-message>
20
+ </ng-template>
21
+ </section>
@@ -1,45 +1,57 @@
1
- <entity-management
2
- design-system-prefix="rapid"
3
- enable-row-flashing
4
- enable-cell-flashing
5
- {{#if config.title}}
6
- title="{{ config.title }}"
7
- {{/if}}
8
- resourceName="{{ config.resourceName }}"
9
- {{#if config.createEvent}}
10
- createEvent="{{ config.createEvent }}"
11
- {{#if config.createFormUiSchema}}
12
- [createFormUiSchema]="tile{{ config.index }}.createFormUiSchema"
13
- {{/if}}
14
- {{/if}}
15
- {{#if config.updateEvent}}
16
- updateEvent="{{ config.updateEvent }}"
17
- {{#if config.updateFormUiSchema}}
18
- [updateFormUiSchema]="tile{{ config.index }}.updateFormUiSchema"
19
- {{/if}}
20
- {{/if}}
21
- {{#if config.deleteEvent}}
22
- deleteEvent="{{ config.deleteEvent }}"
23
- {{/if}}
24
- {{#if config.gridOptions}}
25
- [gridOptions]="{{ config.gridOptions }}"
26
- {{/if}}
27
- {{#if config.snapshot}}
28
- [datasourceConfig]="{ isSnapshot: {{ config.snapshot }} }"
29
- {{/if}}
30
- {{#if config.reqrep}}
31
- [datasourceConfig]="tile{{ config.index }}.reqrep"
32
- {{/if}}
33
- {{#if config.columns}}
34
- [columns]="tile{{ config.index }}.columns"
35
- {{/if}}
36
- {{#if config.modalPosition}}
37
- modal-position="{{ config.modalPosition }}"
38
- {{/if}}
39
- {{#if config.sizeColumnsToFit}}
40
- size-columns-to-fit
41
- {{/if}}
42
- {{#if config.enableSearchBar}}
43
- enable-search-bar
44
- {{/if}}
45
- ></entity-management>
1
+ <section style="height: 100%; width: 100%;">
2
+ <ng-container *ngIf="hasUserPermission('{{config.permissions.viewRight}}'); else notPermitted{{ config.index }}">
3
+ <entity-management
4
+ design-system-prefix="rapid"
5
+ enable-row-flashing
6
+ enable-cell-flashing
7
+ {{#if config.title}}
8
+ title="{{ config.title }}"
9
+ {{/if}}
10
+ resourceName="{{ config.resourceName }}"
11
+ {{#if config.createEvent}}
12
+ [createEvent]="hasUserPermission('{{config.permissions.updateRight}}') ? '{{ config.createEvent }}' : undefined"
13
+ {{#if config.createFormUiSchema}}
14
+ [createFormUiSchema]="tile{{ config.index }}.createFormUiSchema"
15
+ {{/if}}
16
+ {{/if}}
17
+ {{#if config.updateEvent}}
18
+ [updateEvent]="hasUserPermission('{{config.permissions.updateRight}}') ? '{{ config.updateEvent }}' : undefined"
19
+ {{#if config.updateFormUiSchema}}
20
+ [updateFormUiSchema]="tile{{ config.index }}.updateFormUiSchema"
21
+ {{/if}}
22
+ {{/if}}
23
+ {{#if config.deleteEvent}}
24
+ [deleteEvent]="hasUserPermission('{{config.permissions.updateRight}}') ? '{{ config.deleteEvent }}' : undefined"
25
+ {{/if}}
26
+ {{#if config.gridOptions}}
27
+ [gridOptions]="{{ config.gridOptions }}"
28
+ {{/if}}
29
+ {{#if config.snapshot}}
30
+ [datasourceConfig]="{ isSnapshot: {{ config.snapshot }} }"
31
+ {{/if}}
32
+ {{#if config.reqrep}}
33
+ [datasourceConfig]="tile{{ config.index }}.reqrep"
34
+ {{/if}}
35
+ {{#if config.columns}}
36
+ [columns]="tile{{ config.index }}.columns"
37
+ {{/if}}
38
+ {{#if config.modalPosition}}
39
+ modal-position="{{ config.modalPosition }}"
40
+ {{/if}}
41
+ {{#if config.sizeColumnsToFit}}
42
+ size-columns-to-fit
43
+ {{/if}}
44
+ {{#if config.enableSearchBar}}
45
+ enable-search-bar
46
+ {{/if}}
47
+ ></entity-management>
48
+ </ng-container>
49
+
50
+ <ng-template #notPermitted{{ config.index }}>
51
+ <app-error-message
52
+ style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;"
53
+ elementType="h3"
54
+ message="You do not have access to view this component.">
55
+ </app-error-message>
56
+ </ng-template>
57
+ </section>
@@ -1,8 +1,21 @@
1
- <foundation-form
2
- design-system-prefix="rapid"
3
- resourceName="{{config.resourceName}}"
4
- {{#if config.uischema}}
5
- [uischema]="tile{{ config.index }}.uischema"
6
- {{/if}}
7
- >
8
- </foundation-form>
1
+ <section style="height: 100%; width: 100%;">
2
+ <ng-container *ngIf="hasUserPermission('{{config.permissions.updateRight}}'); else notPermitted{{ config.index }}">
3
+ <foundation-form
4
+ design-system-prefix="rapid"
5
+ resourceName="{{config.resourceName}}"
6
+ {{#if config.uischema}}
7
+ [uischema]="tile{{ config.index }}.uischema"
8
+ {{/if}}
9
+ >
10
+ </foundation-form>
11
+ </ng-container>
12
+
13
+ <ng-template #notPermitted{{ config.index }}>
14
+ <app-error-message
15
+ style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;"
16
+ elementType="h3"
17
+ message="You do not have access to view this component.">
18
+ </app-error-message>
19
+ </ng-template>
20
+ </section>
21
+
@@ -1,18 +1,30 @@
1
- <rapid-grid-pro
2
- enable-row-flashing
3
- enable-cell-flashing
4
- >
5
- <grid-pro-genesis-datasource
6
- resource-name="{{config.resourceName}}"
7
- {{#if config.snapshot}}
8
- isSnapshot="{{config.snapshot}}"
9
- {{/if}}
10
- {{#if config.reqrep}}
11
- [datasourceConfig]="tile{{ config.index }}.reqrep"
12
- {{/if}}
13
- {{#if config.gridOptions}}
14
- [deferredGridOptions]="tile{{ config.index }}.gridOptions"
15
- {{/if}}
16
- >
17
- </grid-pro-genesis-datasource>
18
- </rapid-grid-pro>
1
+ <section style="height: 100%; width: 100%;">
2
+ <ng-container *ngIf="hasUserPermission('{{config.permissions.viewRight}}'); else notPermitted{{ config.index }}">
3
+ <rapid-grid-pro
4
+ enable-row-flashing
5
+ enable-cell-flashing
6
+ >
7
+ <grid-pro-genesis-datasource
8
+ resource-name="{{config.resourceName}}"
9
+ {{#if config.snapshot}}
10
+ isSnapshot="{{config.snapshot}}"
11
+ {{/if}}
12
+ {{#if config.reqrep}}
13
+ [datasourceConfig]="tile{{ config.index }}.reqrep"
14
+ {{/if}}
15
+ {{#if config.gridOptions}}
16
+ [deferredGridOptions]="tile{{ config.index }}.gridOptions"
17
+ {{/if}}
18
+ >
19
+ </grid-pro-genesis-datasource>
20
+ </rapid-grid-pro>
21
+ </ng-container>
22
+
23
+ <ng-template #notPermitted{{ config.index }}>
24
+ <app-error-message
25
+ style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;"
26
+ elementType="h3"
27
+ message="You do not have access to view this component.">
28
+ </app-error-message>
29
+ </ng-template>
30
+ </section>
@@ -1,13 +1,14 @@
1
- import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
2
- import {CommonModule} from '@angular/common';
3
- {{#if route.tiles}}
4
- import { getDateFormatter, getNumberFormatter } from '../../utils';
5
- {{/if}}
1
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { User, getUser } from '@genesislcap/foundation-user';
4
+ import { getDateFormatter, getNumberFormatter, getViewUpdateRightComponent } from '../../utils';
5
+ import { ErrorMessageComponent } from '../../components/error-message/error-message.component';
6
6
 
7
7
  @Component({
8
8
  selector: 'app-{{pascalCase route.name}}',
9
9
  standalone: true,
10
10
  imports: [
11
+ ErrorMessageComponent,
11
12
  CommonModule,
12
13
  ],
13
14
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -15,6 +16,8 @@ import { getDateFormatter, getNumberFormatter } from '../../utils';
15
16
  styleUrls: ['./{{kebabCase route.name}}.component.css'],
16
17
  })
17
18
  export class {{pascalCase route.name}}Component {
19
+ hasUserPermission = (permissionCode: string) => getViewUpdateRightComponent(getUser(), permissionCode);
20
+
18
21
  {{#each route.tiles}}
19
22
  tile{{this.config.index}} = { {{#if this.config.createFormUiSchema}}
20
23
  "createFormUiSchema": {{{ this.config.createFormUiSchema }}},{{/if}}{{#if this.config.updateFormUiSchema}}
@@ -48,6 +48,6 @@ ${whenElse(
48
48
  ></entity-management>
49
49
  `,
50
50
  html`
51
- <not-permitted-component></not-permitted-component>
51
+ <not-permitted-component></not-permitted-component>
52
52
  `,
53
53
  )}
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.22.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v3.21.0...v3.22.0) (2024-07-15)
4
+
5
+
6
+ ### Features
7
+
8
+ * angular handle route, component view/update permissions - [FUI-2066](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2066) (#273) 8c73b16
9
+
3
10
  ## [3.21.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v3.20.5...v3.21.0) (2024-07-09)
4
11
 
5
12
 
@@ -13,12 +13,6 @@
13
13
  "build": {
14
14
  "builder": "@angular-builders/custom-webpack:browser",
15
15
  "options": {
16
- "customWebpackConfig": {
17
- "path": "./custom-webpack.config.js",
18
- "mergeStrategies": {
19
- "externals": "replace"
20
- }
21
- },
22
16
  "outputPath": "dist/{{pkgName}}",
23
17
  "index": "src/index.html",
24
18
  "main": "src/main.ts",
@@ -39,7 +33,9 @@
39
33
  "size-sensor",
40
34
  "pdfast",
41
35
  "fmin",
42
- "@json-schema-tools/dereferencer"
36
+ "@json-schema-tools/dereferencer",
37
+ "numeral",
38
+ "numeral/locales"
43
39
  ]
44
40
  },
45
41
  "configurations": {
@@ -107,13 +103,26 @@
107
103
  },
108
104
  "test": {
109
105
  "builder": "@angular-builders/custom-webpack:karma",
110
- "options": {
111
- "customWebpackConfig": {
112
- "path": "./custom-webpack.config.js",
113
- "mergeStrategies": {
114
- "externals": "replace"
106
+ "configurations": {
107
+ "production": {
108
+ "customWebpackConfig": {
109
+ "path": "./webpack.prod.config.js",
110
+ "mergeStrategies": {
111
+ "externals": "replace"
112
+ }
115
113
  }
116
114
  },
115
+ "development": {
116
+ "customWebpackConfig": {
117
+ "path": "./webpack.dev.config.js",
118
+ "mergeStrategies": {
119
+ "externals": "replace"
120
+ }
121
+ }
122
+ }
123
+ },
124
+ "defaultConfiguration": "development",
125
+ "options": {
117
126
  "polyfills": ["zone.js", "zone.js/testing"],
118
127
  "tsConfig": "tsconfig.spec.json",
119
128
  "assets": ["src/favicon.ico", "src/assets"],
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "{{pkgName}}",
3
+ "description": "",
3
4
  "version": "{{applicationVersionWeb}}",
5
+ "private": true,
6
+ "license": "UNLICENSED",
4
7
  "scripts": {
5
8
  "ng": "ng",
6
9
  "bootstrap": "npm install --no-fund --no-audit",
@@ -9,7 +12,25 @@
9
12
  "watch": "ng build --watch --configuration development",
10
13
  "test": "ng test"
11
14
  },
12
- "private": true,
15
+ "devDependencies": {
16
+ "@angular-builders/custom-webpack": "^18.0.0",
17
+ "@angular-devkit/build-angular": "^18.0.4",
18
+ "@angular/cli": "^18.0.4",
19
+ "@angular/compiler-cli": "^18.0.4",
20
+ "@genesislcap/build-kit": "{{versions.UI}}",
21
+ "@types/jasmine": "~5.1.0",
22
+ "@types/numeral": "^2.0.5",
23
+ "file-loader": "^6.2.0",
24
+ "jasmine-core": "~5.1.0",
25
+ "karma": "~6.4.0",
26
+ "karma-chrome-launcher": "~3.2.0",
27
+ "karma-coverage": "~2.2.0",
28
+ "karma-jasmine": "~5.1.0",
29
+ "karma-jasmine-html-reporter": "~2.1.0",
30
+ "svg-url-loader": "^8.0.0",
31
+ "typescript": "~5.4.5",
32
+ "ts-node": "10.9.2"
33
+ },
13
34
  "dependencies": {
14
35
  "@angular/animations": "^18.0.4",
15
36
  "@angular/common": "^18.0.4",
@@ -24,32 +45,36 @@
24
45
  "@genesislcap/foundation-entity-management": "{{versions.UI}}",
25
46
  "@genesislcap/foundation-header": "{{versions.UI}}",
26
47
  "@genesislcap/foundation-ui": "{{versions.UI}}",
48
+ "@genesislcap/foundation-user": "{{versions.UI}}",
27
49
  "@genesislcap/rapid-design-system": "{{versions.UI}}",
28
50
  "@genesislcap/rapid-grid-pro": "{{versions.UI}}",
29
51
  "@genesislcap/foundation-layout": "{{versions.UI}}",
30
52
  "@genesislcap/g2plot-chart": "{{versions.UI}}",
31
53
  "numeral": "2.0.6",
32
- "rxjs": "~7.8.0",
33
54
  "tslib": "^2.3.0",
34
55
  "zone.js": "~0.14.3"
35
56
  },
36
- "devDependencies": {
37
- "@angular-builders/custom-webpack": "^18.0.0",
38
- "@angular-devkit/build-angular": "^18.0.4",
39
- "@angular/cli": "^18.0.4",
40
- "@angular/compiler-cli": "^18.0.4",
41
- "@genesislcap/build-kit": "{{versions.UI}}",
42
- "@types/jasmine": "~5.1.0",
43
- "@types/numeral": "^2.0.5",
44
- "file-loader": "^6.2.0",
45
- "jasmine-core": "~5.1.0",
46
- "karma": "~6.4.0",
47
- "karma-chrome-launcher": "~3.2.0",
48
- "karma-coverage": "~2.2.0",
49
- "karma-jasmine": "~5.1.0",
50
- "karma-jasmine-html-reporter": "~2.1.0",
51
- "svg-url-loader": "^8.0.0",
52
- "typescript": "~5.4.5",
53
- "ts-node": "10.9.2"
57
+ "overrides": {
58
+ "@angular/animations": "^18.0.4",
59
+ "@angular/common": "^18.0.4",
60
+ "@angular/compiler": "^18.0.4",
61
+ "@angular/core": "^18.0.4",
62
+ "@angular/forms": "^18.0.4",
63
+ "@angular/platform-browser": "^18.0.4",
64
+ "@angular/platform-browser-dynamic": "^18.0.4",
65
+ "@angular/router": "^18.0.4",
66
+ "@genesislcap/foundation-comms": "{{versions.UI}}",
67
+ "@genesislcap/foundation-login": "{{versions.UI}}",
68
+ "@genesislcap/foundation-entity-management": "{{versions.UI}}",
69
+ "@genesislcap/foundation-header": "{{versions.UI}}",
70
+ "@genesislcap/foundation-ui": "{{versions.UI}}",
71
+ "@genesislcap/foundation-user": "{{versions.UI}}",
72
+ "@genesislcap/rapid-design-system": "{{versions.UI}}",
73
+ "@genesislcap/rapid-grid-pro": "{{versions.UI}}",
74
+ "@genesislcap/foundation-layout": "{{versions.UI}}",
75
+ "@genesislcap/g2plot-chart": "{{versions.UI}}",
76
+ "numeral": "2.0.6",
77
+ "tslib": "^2.3.0",
78
+ "zone.js": "~0.14.3"
54
79
  }
55
80
  }
@@ -1,11 +1,13 @@
1
1
  import { NgModule } from '@angular/core';
2
2
  import { RouterModule, Routes } from '@angular/router';
3
3
  import { AuthGuard } from './guards/auth.guard';
4
+ import { PermissionsGuard } from './guards/permissions.guard';
4
5
  import { AuthLoginComponent } from './pages/auth-login/auth-login.component';
6
+ import { NotPermittedComponent } from './pages/not-permitted/not-permitted.component';
5
7
  {{#each routes}}
6
8
  import { {{pascalCase this.name}}Component } from './pages/{{kebabCase this.name}}/{{kebabCase this.name}}.component';
7
9
  {{/each}}
8
- import { AUTH_PATH } from './app.config';
10
+ import { AUTH_PATH, NOT_PERMITTED_PATH } from './app.config';
9
11
 
10
12
  export const routes: Routes = [
11
13
  {
@@ -17,11 +19,16 @@ export const routes: Routes = [
17
19
  path: `${AUTH_PATH}`,
18
20
  component: AuthLoginComponent,
19
21
  },
22
+ {
23
+ path: `${NOT_PERMITTED_PATH}`,
24
+ component: NotPermittedComponent,
25
+ },
20
26
  {{#each routes}}
21
27
  {
22
28
  path: '{{kebabCase this.name}}',
23
- canActivate: [AuthGuard],
24
- component: {{pascalCase this.name}}Component ,
29
+ canActivate: [AuthGuard{{#if this.permissions.viewRight}}, PermissionsGuard{{/if}}],
30
+ component: {{pascalCase this.name}}Component,
31
+ data: { permissionCode: '{{this.permissions.viewRight}}' },
25
32
  },
26
33
  {{/each}}
27
34
  ];
@@ -4,7 +4,7 @@ import { AppComponent } from './app.component';
4
4
  describe('AppComponent', () => {
5
5
  beforeEach(async () => {
6
6
  await TestBed.configureTestingModule({
7
- imports: [AppComponent],
7
+ declarations: [AppComponent],
8
8
  }).compileComponents();
9
9
  });
10
10
 
@@ -19,11 +19,5 @@ describe('AppComponent', () => {
19
19
  const app = fixture.componentInstance;
20
20
  expect(app.title).toEqual('{{capitalCase appName}}');
21
21
  });
22
-
23
- it('should render title', () => {
24
- const fixture = TestBed.createComponent(AppComponent);
25
- fixture.detectChanges();
26
- const compiled = fixture.nativeElement as HTMLElement;
27
- expect(compiled.querySelector('h1')?.textContent).toContain('Hello, {{capitalCase appName}}');
28
- });
29
22
  });
23
+
@@ -20,7 +20,6 @@ export class AppComponent {
20
20
  private router: Router,
21
21
  ) {
22
22
  configureFoundationLogin({ router });
23
-
24
23
 
25
24
  // Set layout componet based on route
26
25
  router.events.subscribe((event: any) => {
@@ -2,6 +2,7 @@ import type { MainMenu } from './types/menu'
2
2
  import type { LayoutComponentName } from './types/layout';
3
3
 
4
4
  export const AUTH_PATH = 'login'
5
+ export const NOT_PERMITTED_PATH = 'not-permitted'
5
6
 
6
7
  export const layoutComponentName = {
7
8
  default: 'DefaultLayoutComponent',
@@ -0,0 +1,15 @@
1
+ <section class="error-message-wrapper">
2
+ <div class="error-message" style="color: var(--neutral-foreground-rest); background-color: var(--neutral-layer-4); border-color: var(--error-color); border-radius: 7px; border-style: solid; border-width: 4px; padding: 5px; margin: 15px; text-align: center; width: fit-content;">
3
+ <ng-container [ngSwitch]="elementType">
4
+ <h1 *ngSwitchCase="'h1'">\{{ message }}</h1>
5
+ <h2 *ngSwitchCase="'h2'">\{{ message }}</h2>
6
+ <h3 *ngSwitchCase="'h3'">\{{ message }}</h3>
7
+ <h4 *ngSwitchCase="'h4'">\{{ message }}</h4>
8
+ <h5 *ngSwitchCase="'h5'">\{{ message }}</h5>
9
+ <h6 *ngSwitchCase="'h6'">\{{ message }}</h6>
10
+ <p *ngSwitchCase="'p'">\{{ message }}</p>
11
+ <span *ngSwitchCase="'span'">\{{ message }}</span>
12
+ <div *ngSwitchDefault>\{{ message }}</div>
13
+ </ng-container>
14
+ </div>
15
+ </section>
@@ -0,0 +1,74 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { ErrorMessageComponent } from './error-message.component';
3
+
4
+ describe('ErrorMessageComponent', () => {
5
+ let component: ErrorMessageComponent;
6
+ let fixture: ComponentFixture<ErrorMessageComponent>;
7
+
8
+ beforeEach(async () => {
9
+ await TestBed.configureTestingModule({
10
+ imports: [ErrorMessageComponent],
11
+ }).compileComponents();
12
+
13
+ fixture = TestBed.createComponent(ErrorMessageComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+
22
+ it('should render a message inside an h1 element', () => {
23
+ component.elementType = 'h1';
24
+ component.message = 'Error: Something went wrong!';
25
+ fixture.detectChanges();
26
+
27
+ const compiled = fixture.nativeElement;
28
+ const element = compiled.querySelector('h1');
29
+ expect(element).toBeTruthy();
30
+ expect(element.textContent).toContain('Error: Something went wrong!');
31
+ });
32
+
33
+ it('should render a message inside an h3 element', () => {
34
+ component.elementType = 'h3';
35
+ component.message = 'Warning: Check your inputs.';
36
+ fixture.detectChanges();
37
+
38
+ const compiled = fixture.nativeElement;
39
+ const element = compiled.querySelector('h3');
40
+ expect(element).toBeTruthy();
41
+ expect(element.textContent).toContain('Warning: Check your inputs.');
42
+ });
43
+
44
+ it('should render a message inside a p element', () => {
45
+ component.elementType = 'p';
46
+ component.message = 'Info: Your operation was successful.';
47
+ fixture.detectChanges();
48
+
49
+ const compiled = fixture.nativeElement;
50
+ const element = compiled.querySelector('p');
51
+ expect(element).toBeTruthy();
52
+ expect(element.textContent).toContain('Info: Your operation was successful.');
53
+ });
54
+
55
+ it('should default to div element if no elementType is provided', () => {
56
+ component.elementType = '';
57
+ component.message = 'Default to div element.';
58
+ fixture.detectChanges();
59
+
60
+ const compiled = fixture.nativeElement;
61
+ const element = compiled.querySelector('div');
62
+ expect(element.textContent).toContain('Default to div element.');
63
+ });
64
+
65
+ it('should apply error-message class to the rendered element', () => {
66
+ component.elementType = 'h2';
67
+ component.message = 'Testing class application.';
68
+ fixture.detectChanges();
69
+
70
+ const compiled = fixture.nativeElement;
71
+ const element = compiled.querySelector('.error-message');
72
+ expect(element).toBeTruthy();
73
+ });
74
+ });
@@ -0,0 +1,15 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'app-error-message',
6
+ templateUrl: './error-message.component.html',
7
+ standalone: true,
8
+ imports: [
9
+ CommonModule,
10
+ ],
11
+ })
12
+ export class ErrorMessageComponent {
13
+ @Input() elementType: string = 'div';
14
+ @Input() message: string = '';
15
+ }
@@ -12,11 +12,11 @@ export class AuthGuard implements CanActivate {
12
12
  private router: Router,
13
13
  ) {}
14
14
 
15
- async canActivate(): Promise<boolean> {
16
- const isUserAuthenticated = await this.authService.isUserAuthenticated();
15
+ canActivate(): boolean {
16
+ const isUserAuthenticated = this.authService.isUserAuthenticated();
17
17
 
18
18
  if (!isUserAuthenticated) {
19
- await this.router.navigate([`/${AUTH_PATH}`]);
19
+ this.router.navigate([`/${AUTH_PATH}`]);
20
20
  return false;
21
21
  }
22
22
  return true;
@@ -0,0 +1,24 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router';
3
+ import { AuthService } from '../services/auth.service';
4
+ import { NOT_PERMITTED_PATH } from '../app.config';
5
+
6
+ @Injectable({
7
+ providedIn: 'root',
8
+ })
9
+ export class PermissionsGuard implements CanActivate {
10
+ constructor(
11
+ private authService: AuthService,
12
+ private router: Router,
13
+ ) {}
14
+
15
+ async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
16
+ const isPermitted = await this.authService.hasUserPermission(route.data['permissionCode']);
17
+
18
+ if (!isPermitted) {
19
+ await this.router.navigate([`/${NOT_PERMITTED_PATH}`]);
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ }
@@ -1,6 +1,5 @@
1
1
  <rapid-design-system-provider #designSystemProvider>
2
2
  <section class="content">
3
3
  <router-outlet></router-outlet>
4
- <app-messages></app-messages>
5
4
  </section>
6
5
  </rapid-design-system-provider>
@@ -10,7 +10,7 @@ describe('BlankLayoutComponent', () => {
10
10
  beforeEach(() => {
11
11
  TestBed.configureTestingModule({
12
12
  declarations: [BlankLayoutComponent],
13
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
13
+ schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
14
14
  });
15
15
  fixture = TestBed.createComponent(BlankLayoutComponent);
16
16
  component = fixture.componentInstance;
@@ -15,6 +15,5 @@
15
15
  </foundation-header>
16
16
  <section class="content">
17
17
  <router-outlet></router-outlet>
18
- <app-messages></app-messages>
19
18
  </section>
20
19
  </rapid-design-system-provider>
@@ -1,34 +1,49 @@
1
- import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
3
- import { provideMockStore } from '@ngrx/store/testing';
4
- import * as StateChangerSelector from '../../store/state-changer/state-changer.selectors';
5
-
2
+ import { Router } from '@angular/router';
6
3
  import { DefaultLayoutComponent } from './default.layout';
4
+ import { ElementRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
5
+
6
+ class MockRouter {
7
+ navigate = jasmine.createSpy('navigate');
8
+ }
9
+
10
+ class MockElementRef implements ElementRef {
11
+ nativeElement = {};
12
+ }
7
13
 
8
14
  describe('DefaultLayoutComponent', () => {
9
15
  let component: DefaultLayoutComponent;
10
16
  let fixture: ComponentFixture<DefaultLayoutComponent>;
17
+ let router: MockRouter;
18
+ let elementRef: MockElementRef;
11
19
 
12
- beforeEach(() => {
13
- TestBed.configureTestingModule({
20
+ beforeEach(async () => {
21
+ await TestBed.configureTestingModule({
14
22
  declarations: [DefaultLayoutComponent],
15
23
  providers: [
16
- provideMockStore({
17
- initialState: {},
18
- selectors: [
19
- { selector: StateChangerSelector.getCriteria, value: 'initial-criteria' },
20
- { selector: StateChangerSelector.getResourceName, value: 'initial-resource-name' },
21
- ],
22
- }),
24
+ { provide: Router, useClass: MockRouter },
25
+ { provide: ElementRef, useClass: MockElementRef }
23
26
  ],
24
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
25
- });
27
+ schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
28
+ }).compileComponents();
29
+ });
30
+
31
+ beforeEach(() => {
26
32
  fixture = TestBed.createComponent(DefaultLayoutComponent);
27
33
  component = fixture.componentInstance;
34
+ router = TestBed.inject(Router) as unknown as MockRouter;
35
+ elementRef = TestBed.inject(ElementRef) as unknown as MockElementRef;
36
+ component.designSystemProviderElement = elementRef;
28
37
  fixture.detectChanges();
29
38
  });
30
39
 
31
40
  it('should create', () => {
32
41
  expect(component).toBeTruthy();
33
42
  });
43
+
44
+ it('should navigate to a path', () => {
45
+ const path = 'some/path';
46
+ component.navigateAngular(path);
47
+ expect(router.navigate).toHaveBeenCalledWith([path]);
48
+ });
34
49
  });
@@ -1,17 +1,17 @@
1
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
2
2
 
3
- import { AuthMockComponent } from './auth-mock.component';
3
+ import { AuthLoginComponent } from './auth-login.component';
4
4
 
5
- describe('AuthMockComponent', () => {
6
- let component: AuthMockComponent;
7
- let fixture: ComponentFixture<AuthMockComponent>;
5
+ describe('AuthLoginComponent', () => {
6
+ let component: AuthLoginComponent;
7
+ let fixture: ComponentFixture<AuthLoginComponent>;
8
8
 
9
9
  beforeEach(async () => {
10
10
  await TestBed.configureTestingModule({
11
- imports: [AuthMockComponent],
11
+ imports: [AuthLoginComponent],
12
12
  }).compileComponents();
13
13
 
14
- fixture = TestBed.createComponent(AuthMockComponent);
14
+ fixture = TestBed.createComponent(AuthLoginComponent);
15
15
  component = fixture.componentInstance;
16
16
  fixture.detectChanges();
17
17
  });
@@ -0,0 +1,4 @@
1
+ <app-error-message
2
+ elementType="h1"
3
+ message="You do not have permission to access this part of the application, please contact your administrator.">
4
+ </app-error-message>
@@ -0,0 +1,12 @@
1
+ @import '../../../styles/mixins';
2
+
3
+ :host {
4
+ @include screen('flex');
5
+ align-items: center;
6
+ justify-content: center;
7
+ }
8
+
9
+ h1 {
10
+ text-align: center;
11
+ color: var(--neutral-foreground-rest);
12
+ }
@@ -0,0 +1,32 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { NotPermittedComponent } from './not-permitted.component';
3
+
4
+ const MESSAGE_NOT_PERMITTED = 'You do not have permission to access this part of the application, please contact your administrator.';
5
+
6
+ describe('NotPermittedComponent', () => {
7
+ let component: NotPermittedComponent;
8
+ let fixture: ComponentFixture<NotPermittedComponent>;
9
+
10
+ beforeEach(async () => {
11
+ await TestBed.configureTestingModule({
12
+ imports: [ NotPermittedComponent ]
13
+ })
14
+ .compileComponents();
15
+ });
16
+
17
+ beforeEach(() => {
18
+ fixture = TestBed.createComponent(NotPermittedComponent);
19
+ component = fixture.componentInstance;
20
+ fixture.detectChanges();
21
+ });
22
+
23
+ it('should create', () => {
24
+ expect(component).toBeTruthy();
25
+ });
26
+
27
+ it('should display the correct error message', () => {
28
+ const compiled = fixture.nativeElement;
29
+ const errorMessageElement = compiled.querySelector('app-error-message h1');
30
+ expect(errorMessageElement.textContent).toBe(MESSAGE_NOT_PERMITTED);
31
+ });
32
+ });
@@ -0,0 +1,13 @@
1
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
+ import { ErrorMessageComponent } from '../../components/error-message/error-message.component';
3
+
4
+ @Component({
5
+ selector: 'app-not-permitted',
6
+ standalone: true,
7
+ imports: [ ErrorMessageComponent ],
8
+ templateUrl: './not-permitted.component.html',
9
+ styleUrls: ['./not-permitted.component.scss'],
10
+ schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
11
+ })
12
+ export class NotPermittedComponent {
13
+ }
@@ -1,14 +1,22 @@
1
1
  import { Injectable } from '@angular/core';
2
2
  import { Auth } from '@genesislcap/foundation-comms';
3
- import {DI} from "@microsoft/fast-foundation";
3
+ import { DI } from "@microsoft/fast-foundation";
4
4
 
5
5
  @Injectable({
6
6
  providedIn: 'root',
7
7
  })
8
8
  export class AuthService {
9
+ auth: Auth;
9
10
 
10
- async isUserAuthenticated(): Promise<boolean> {
11
- const auth: Auth = DI.getOrCreateDOMContainer().get(Auth);
12
- return auth.isLoggedIn
11
+ constructor() {
12
+ this.auth = DI.getOrCreateDOMContainer().get(Auth);
13
+ }
14
+
15
+ isUserAuthenticated(): boolean {
16
+ return this.auth.isLoggedIn;
17
+ }
18
+
19
+ hasUserPermission(permissionCode: string): boolean {
20
+ return this.auth.currentUser.hasPermission(permissionCode);
13
21
  }
14
22
  }
@@ -3,6 +3,9 @@ import type { Router } from '@angular/router';
3
3
  import { AUTH_PATH } from '../app.config';
4
4
  import { DI } from '@microsoft/fast-foundation';
5
5
 
6
+ // eslint-disable-next-line
7
+ declare var GENX_ENABLE_SSO: boolean;
8
+
6
9
  const ssoSettings =
7
10
  typeof GENX_ENABLE_SSO !== 'undefined' && GENX_ENABLE_SSO === true
8
11
  ? {
@@ -1,2 +1,3 @@
1
1
  export * from './formatters';
2
2
  export * from './getLayoutNameByRoute';
3
+ export * from './permissions';
@@ -0,0 +1,7 @@
1
+ import { User } from '@genesislcap/foundation-user';
2
+
3
+ export const getViewUpdateRightComponent = (
4
+ user: User,
5
+ right: string,
6
+ event: string | boolean = true,
7
+ ) => (!right || user.hasPermission(right) ? event : '');
@@ -0,0 +1,8 @@
1
+ @mixin screen($display: 'block') {
2
+ contain: content;
3
+ display: #{$display};
4
+ height: 100%;
5
+ width: 100%;
6
+ overflow-y: auto;
7
+ }
8
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed",
3
3
  "description": "Genesis Blank App Seed",
4
- "version": "3.21.0",
4
+ "version": "3.22.0",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "release": "semantic-release"