@genesislcap/blank-app-seed 3.27.3 → 3.28.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.
- package/.genx/configure.js +7 -2
- package/.genx/package.json +1 -1
- package/.genx/templates/angular/chart.hbs +15 -20
- package/.genx/templates/angular/component/component.hbs +4 -2
- package/.genx/templates/angular/route.template.hbs +9 -3
- package/.genx/templates/angular/tabsPanel.hbs +5 -0
- package/.genx/utils/formatRouteData.js +6 -3
- package/.genx/utils/registerPartials.js +10 -1
- package/.gitignore +4 -0
- package/CHANGELOG.md +7 -0
- package/client-tmp/angular/angular.json +2 -1
- package/client-tmp/angular/globals.d.ts +1 -0
- package/client-tmp/angular/package.json +26 -8
- package/client-tmp/angular/src/app/app.component.ts +45 -6
- package/client-tmp/angular/src/app/app.config.ts +0 -13
- package/client-tmp/angular/src/app/app.module.ts +17 -4
- package/client-tmp/angular/src/app/guards/auth.guard.ts +5 -12
- package/client-tmp/angular/src/app/guards/chained.guard.ts +33 -0
- package/client-tmp/angular/src/app/guards/connection.guard.ts +21 -0
- package/client-tmp/angular/src/app/guards/permissions.guard.ts +6 -8
- package/client-tmp/angular/src/app/layouts/base.layout.ts +1 -5
- package/client-tmp/angular/src/app/layouts/default/default.layout.css +1 -0
- package/client-tmp/angular/src/app/layouts/default/default.layout.html +5 -6
- package/client-tmp/angular/src/app/layouts/default/default.layout.ts +9 -4
- package/client-tmp/angular/src/app/services/route.service.ts +95 -0
- package/client-tmp/angular/src/app/share/foundation-login.ts +5 -3
- package/client-tmp/angular/src/app/share/genesis-components.ts +48 -21
- package/client-tmp/angular/src/app/store/index.ts +1 -0
- package/client-tmp/angular/src/app/store/store.ts +34 -0
- package/client-tmp/angular/src/declaration.d.ts +7 -1
- package/client-tmp/angular/src/environments/environment.prod.ts +1 -0
- package/client-tmp/angular/src/environments/environment.ts +2 -1
- package/client-tmp/angular/src/main.ts +14 -3
- package/client-tmp/angular/src/pbc/README.md +7 -0
- package/client-tmp/angular/src/pbc/container.ts +40 -0
- package/client-tmp/angular/src/pbc/elementsRenderer.ts +57 -0
- package/client-tmp/angular/src/pbc/utils.ts +121 -0
- package/client-tmp/angular/tsconfig.app.json +2 -1
- package/client-tmp/angular/tsconfig.spec.json +3 -2
- package/client-tmp/angular/webpack.shared.config.js +17 -4
- package/package.json +1 -1
- package/client-tmp/angular/src/app/app-routing.module.ts +0 -40
- package/client-tmp/angular/src/app/services/auth.service.ts +0 -22
- package/client-tmp/angular/src/app/types/menu.ts +0 -8
package/.genx/configure.js
CHANGED
|
@@ -21,8 +21,13 @@ module.exports = async (data, utils) => {
|
|
|
21
21
|
data.versions = versions;
|
|
22
22
|
|
|
23
23
|
registerPartials(utils, data.framework);
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const formatRouteDataForFramework = formatRouteData.bind(
|
|
25
|
+
this,
|
|
26
|
+
data.framework,
|
|
27
|
+
);
|
|
28
|
+
data.routes = data.routes
|
|
29
|
+
.filter(validateRoute)
|
|
30
|
+
.map(formatRouteDataForFramework);
|
|
26
31
|
|
|
27
32
|
const FDC3EventHandlersEnabled = data.routes.find(
|
|
28
33
|
(route) => route.FDC3EventHandlersEnabled,
|
package/.genx/package.json
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</rapid-g2plot-chart>
|
|
12
|
-
</ng-container>
|
|
1
|
+
<rapid-g2plot-chart
|
|
2
|
+
type="{{ config.type }}"
|
|
3
|
+
[config]="chartConfig"
|
|
4
|
+
[ngStyle]="hasUserPermission('{{config.permissions.viewRight}}') ? { width: '100%', height: '100%' } : { display: 'none' }"
|
|
5
|
+
>
|
|
6
|
+
<chart-datasource
|
|
7
|
+
resourceName="{{ config.resourceName }}"
|
|
8
|
+
server-fields="{{ config.xField }} {{ config.yField }}"
|
|
9
|
+
></chart-datasource>
|
|
10
|
+
</rapid-g2plot-chart>
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</app-error-message>
|
|
20
|
-
</ng-template>
|
|
21
|
-
</section>
|
|
12
|
+
<app-error-message
|
|
13
|
+
[ngStyle]="!hasUserPermission('{{config.permissions.viewRight}}') ? { width: '100%', height: '100%', display: 'flex', 'justify-content': 'center', 'align-items': 'center' } : { display: 'none' }"
|
|
14
|
+
elementType="h3"
|
|
15
|
+
message="You do not have access to view this component.">
|
|
16
|
+
</app-error-message>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Component, Input, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import { getUser } from '@genesislcap/foundation-user';
|
|
4
|
-
import { GridOptionsConfig } from "@genesislcap/rapid-grid-pro";
|
|
5
4
|
import { getViewUpdateRightComponent } from '../../../utils';
|
|
5
|
+
{{#if tile.config.gridOptions}}
|
|
6
|
+
import { GridOptionsConfig } from "@genesislcap/rapid-grid-pro";
|
|
7
|
+
{{/if}}
|
|
6
8
|
{{#if tile.config.createFormUiSchema}}
|
|
7
9
|
import { createFormSchema } from './{{kebabCase tile.title}}.create.form.schema';
|
|
8
10
|
{{/if}}
|
|
@@ -37,7 +39,7 @@ export class {{pascalCase tile.componentName}} {
|
|
|
37
39
|
columnDefs = columnDefs;{{/if}}{{#if tile.config.gridOptions}}
|
|
38
40
|
gridOptions = gridOptions as GridOptionsConfig;{{/if}}{{#if tile.config.reqrep}}
|
|
39
41
|
reqrep = { pollingInterval: 5000 };{{/if}}{{#if tile.config.type}}
|
|
40
|
-
chartConfig = { {{#ifEquals
|
|
42
|
+
chartConfig = { {{#ifEquals tile.config.type 'pie'}}
|
|
41
43
|
"radius": 0.75,
|
|
42
44
|
"angleField": "value",
|
|
43
45
|
"colorField": "groupBy",{{else}}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{{#if route.tiles}}
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<rapid-flex-layout>
|
|
3
|
+
<rapid-tabs>
|
|
4
|
+
{{#each route.tiles}}
|
|
5
|
+
<rapid-tab slot="tab">{{this.title}}</rapid-tab>
|
|
6
|
+
{{/each}}
|
|
7
|
+
|
|
8
|
+
{{> (lookup ./route 'layoutType') }}
|
|
9
|
+
</rapid-tabs>
|
|
10
|
+
</rapid-flex-layout>
|
|
5
11
|
{{else}}
|
|
6
12
|
<!-- insert template code here -->
|
|
7
13
|
Welcome to {{sentenceCase route.name}}
|
|
@@ -4,11 +4,14 @@ const {
|
|
|
4
4
|
} = require('./gridSerializers');
|
|
5
5
|
const formatJSONValue = require('./formatJSONValue');
|
|
6
6
|
const getLayoutType = require('./getLayoutType');
|
|
7
|
-
const { COMPONENT_TYPE } = require('../static');
|
|
7
|
+
const { COMPONENT_TYPE, FRAMEWORK_ANGULAR_ALIAS } = require('../static');
|
|
8
8
|
|
|
9
|
-
const formatRouteData = (route) => {
|
|
9
|
+
const formatRouteData = (framework, route) => {
|
|
10
10
|
const layoutKey = route?.layoutKey || `${route.name}_${Date.now()}`;
|
|
11
|
-
const layoutType =
|
|
11
|
+
const layoutType =
|
|
12
|
+
framework === FRAMEWORK_ANGULAR_ALIAS
|
|
13
|
+
? 'tabs-panel'
|
|
14
|
+
: route?.layoutType || getLayoutType(route);
|
|
12
15
|
|
|
13
16
|
const FDC3ClickCategory = 'fdc3';
|
|
14
17
|
const FDC3EventHandlersEnabled = !!route.tiles?.find(
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
const { resolve } = require('node:path');
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
DIR_TEMPLATE_BY_FRAMEWORK,
|
|
4
|
+
FRAMEWORK_ANGULAR_ALIAS,
|
|
5
|
+
} = require('../static');
|
|
3
6
|
|
|
4
7
|
const registerPartials = ({ registerPartial }, framework) => {
|
|
5
8
|
// It can be reverted after adding changes for angular
|
|
6
9
|
const sourceTemplateDir = `../${DIR_TEMPLATE_BY_FRAMEWORK[framework]}`;
|
|
7
10
|
|
|
11
|
+
if (framework === FRAMEWORK_ANGULAR_ALIAS) {
|
|
12
|
+
registerPartial(
|
|
13
|
+
'tabs-panel',
|
|
14
|
+
resolve(__dirname, `${sourceTemplateDir}/tabsPanel.hbs`),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
8
17
|
registerPartial(
|
|
9
18
|
'grid-layout',
|
|
10
19
|
resolve(__dirname, `${sourceTemplateDir}/gridLayout.hbs`),
|
package/.gitignore
CHANGED
|
@@ -26,6 +26,9 @@ pids
|
|
|
26
26
|
*.sln
|
|
27
27
|
*.sw?
|
|
28
28
|
.DS_Store
|
|
29
|
+
.idea/gradle.xml
|
|
30
|
+
.idea/misc.xml
|
|
31
|
+
.idea/vcs.xml
|
|
29
32
|
|
|
30
33
|
# Development
|
|
31
34
|
target/
|
|
@@ -36,6 +39,7 @@ build/
|
|
|
36
39
|
*.ignore
|
|
37
40
|
*~
|
|
38
41
|
.gradle
|
|
42
|
+
client/.angular
|
|
39
43
|
client/bootstrapDone
|
|
40
44
|
.husky/.gitignore
|
|
41
45
|
build-cache/
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.28.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v3.27.3...v3.28.0) (2024-07-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* pbc support in angular [FUI-2065](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2065) (#289) 7662cde
|
|
9
|
+
|
|
3
10
|
## [3.27.3](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v3.27.2...v3.27.3) (2024-07-29)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -77,20 +77,27 @@
|
|
|
77
77
|
"@angular/platform-browser": "^18.0.4",
|
|
78
78
|
"@angular/platform-browser-dynamic": "^18.0.4",
|
|
79
79
|
"@angular/router": "^18.0.4",
|
|
80
|
+
"@genesislcap/foundation-comms": "{{versions.UI}}",
|
|
81
|
+
"@genesislcap/foundation-entity-management": "{{versions.UI}}",
|
|
82
|
+
"@genesislcap/foundation-events": "{{versions.UI}}",
|
|
80
83
|
{{#if FDC3.includeDependencies}}
|
|
81
84
|
"@genesislcap/foundation-fdc3": "{{versions.UI}}",
|
|
82
85
|
{{/if}}
|
|
83
|
-
"@genesislcap/foundation-
|
|
84
|
-
"@genesislcap/foundation-login": "{{versions.UI}}",
|
|
85
|
-
"@genesislcap/foundation-entity-management": "{{versions.UI}}",
|
|
86
|
+
"@genesislcap/foundation-forms": "{{versions.UI}}",
|
|
86
87
|
"@genesislcap/foundation-header": "{{versions.UI}}",
|
|
88
|
+
"@genesislcap/foundation-layout": "{{versions.UI}}",
|
|
89
|
+
"@genesislcap/foundation-logger": "{{versions.UI}}",
|
|
90
|
+
"@genesislcap/foundation-login": "{{versions.UI}}",
|
|
91
|
+
"@genesislcap/foundation-shell": "{{versions.UI}}",
|
|
92
|
+
"@genesislcap/foundation-store": "{{versions.UI}}",
|
|
87
93
|
"@genesislcap/foundation-ui": "{{versions.UI}}",
|
|
88
94
|
"@genesislcap/foundation-utils": "{{versions.UI}}",
|
|
89
95
|
"@genesislcap/foundation-user": "{{versions.UI}}",
|
|
96
|
+
"@genesislcap/foundation-zero": "{{versions.UI}}",
|
|
97
|
+
"@genesislcap/g2plot-chart": "{{versions.UI}}",
|
|
90
98
|
"@genesislcap/rapid-design-system": "{{versions.UI}}",
|
|
91
99
|
"@genesislcap/rapid-grid-pro": "{{versions.UI}}",
|
|
92
|
-
"@genesislcap/
|
|
93
|
-
"@genesislcap/g2plot-chart": "{{versions.UI}}",
|
|
100
|
+
"@genesislcap/web-core": "{{versions.UI}}",
|
|
94
101
|
"numeral": "2.0.6",
|
|
95
102
|
"tslib": "^2.3.0",
|
|
96
103
|
"zone.js": "~0.14.3"
|
|
@@ -105,15 +112,26 @@
|
|
|
105
112
|
"@angular/platform-browser-dynamic": "^18.0.4",
|
|
106
113
|
"@angular/router": "^18.0.4",
|
|
107
114
|
"@genesislcap/foundation-comms": "{{versions.UI}}",
|
|
108
|
-
"@genesislcap/foundation-login": "{{versions.UI}}",
|
|
109
115
|
"@genesislcap/foundation-entity-management": "{{versions.UI}}",
|
|
116
|
+
"@genesislcap/foundation-events": "{{versions.UI}}",
|
|
117
|
+
{{#if FDC3.includeDependencies}}
|
|
118
|
+
"@genesislcap/foundation-fdc3": "{{versions.UI}}",
|
|
119
|
+
{{/if}}
|
|
120
|
+
"@genesislcap/foundation-forms": "{{versions.UI}}",
|
|
110
121
|
"@genesislcap/foundation-header": "{{versions.UI}}",
|
|
122
|
+
"@genesislcap/foundation-layout": "{{versions.UI}}",
|
|
123
|
+
"@genesislcap/foundation-logger": "{{versions.UI}}",
|
|
124
|
+
"@genesislcap/foundation-login": "{{versions.UI}}",
|
|
125
|
+
"@genesislcap/foundation-shell": "{{versions.UI}}",
|
|
126
|
+
"@genesislcap/foundation-store": "{{versions.UI}}",
|
|
111
127
|
"@genesislcap/foundation-ui": "{{versions.UI}}",
|
|
128
|
+
"@genesislcap/foundation-utils": "{{versions.UI}}",
|
|
112
129
|
"@genesislcap/foundation-user": "{{versions.UI}}",
|
|
130
|
+
"@genesislcap/foundation-zero": "{{versions.UI}}",
|
|
131
|
+
"@genesislcap/g2plot-chart": "{{versions.UI}}",
|
|
113
132
|
"@genesislcap/rapid-design-system": "{{versions.UI}}",
|
|
114
133
|
"@genesislcap/rapid-grid-pro": "{{versions.UI}}",
|
|
115
|
-
"@genesislcap/
|
|
116
|
-
"@genesislcap/g2plot-chart": "{{versions.UI}}",
|
|
134
|
+
"@genesislcap/web-core": "{{versions.UI}}",
|
|
117
135
|
"numeral": "2.0.6",
|
|
118
136
|
"tslib": "^2.3.0",
|
|
119
137
|
"zone.js": "~0.14.3"
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
import { Component,
|
|
1
|
+
import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit } from '@angular/core';
|
|
2
2
|
import { Router, NavigationEnd } from '@angular/router';
|
|
3
3
|
import getLayoutNameByRoute from './utils/getLayoutNameByRoute';
|
|
4
4
|
import type { LayoutComponentName } from './types/layout';
|
|
5
5
|
import { configureFoundationLogin } from './share/foundation-login';
|
|
6
|
+
import { registerComponents } from './share/genesis-components';
|
|
7
|
+
import { getStore } from './store';
|
|
8
|
+
import { customEventFactory, registerStylesTarget } from '../pbc/utils';
|
|
6
9
|
{{#if FDC3.channels.length}}
|
|
7
10
|
import { listenToChannel, onFDC3Ready } from './utils';
|
|
8
11
|
{{/if}}
|
|
9
12
|
|
|
10
|
-
// Genesis Components
|
|
11
|
-
import './share/genesis-components';
|
|
12
|
-
|
|
13
13
|
@Component({
|
|
14
14
|
selector: '{{rootElement}}',
|
|
15
15
|
templateUrl: './app.component.html',
|
|
16
16
|
styleUrl: './app.component.css',
|
|
17
17
|
})
|
|
18
|
-
export class AppComponent implements AfterViewInit {
|
|
18
|
+
export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
|
|
19
19
|
layoutName?: LayoutComponentName;
|
|
20
20
|
title = '{{capitalCase appName}}';
|
|
21
|
+
store = getStore();
|
|
21
22
|
|
|
22
23
|
constructor(
|
|
23
|
-
private
|
|
24
|
+
private el: ElementRef,
|
|
25
|
+
router: Router,
|
|
24
26
|
) {
|
|
25
27
|
configureFoundationLogin({ router });
|
|
26
28
|
|
|
@@ -32,6 +34,43 @@ export class AppComponent implements AfterViewInit {
|
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
ngOnInit() {
|
|
38
|
+
this.addEventListeners();
|
|
39
|
+
this.readyStore();
|
|
40
|
+
registerStylesTarget(this.el.nativeElement, 'main');
|
|
41
|
+
this.loadRemotes();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ngOnDestroy() {
|
|
45
|
+
this.removeEventListeners();
|
|
46
|
+
this.disconnectStore();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async loadRemotes() {
|
|
50
|
+
await registerComponents();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addEventListeners() {
|
|
54
|
+
this.el.nativeElement.addEventListener('store-connected', this.store.onConnected);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
removeEventListeners() {
|
|
58
|
+
this.el.nativeElement.removeEventListener('store-connected', this.store.onConnected);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
readyStore() {
|
|
62
|
+
this.dispatchCustomEvent('store-connected', this.el.nativeElement);
|
|
63
|
+
this.dispatchCustomEvent('store-ready', true);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
disconnectStore() {
|
|
67
|
+
this.dispatchCustomEvent('store-disconnected');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
dispatchCustomEvent(type: string, detail?: any) {
|
|
71
|
+
this.el.nativeElement.dispatchEvent(customEventFactory(type, detail));
|
|
72
|
+
}
|
|
73
|
+
|
|
35
74
|
ngAfterViewInit() {
|
|
36
75
|
{{#if FDC3.channels.length}}
|
|
37
76
|
onFDC3Ready(this.FDC3ReadyHandler);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { MainMenu } from './types/menu'
|
|
2
1
|
import type { LayoutComponentName } from './types/layout';
|
|
3
2
|
|
|
4
3
|
export const AUTH_PATH = 'login'
|
|
@@ -19,15 +18,3 @@ export const layoutComponentImportsByName = {
|
|
|
19
18
|
export const layoutNameByRouteMap: Map<string, LayoutComponentName> = new Map([
|
|
20
19
|
[`/${AUTH_PATH}`, layoutComponentName.blank],
|
|
21
20
|
]);
|
|
22
|
-
|
|
23
|
-
export const mainMenu: MainMenu = [
|
|
24
|
-
{{#each routes}}
|
|
25
|
-
{
|
|
26
|
-
index: {{@index}},
|
|
27
|
-
path: '{{kebabCase this.name}}',
|
|
28
|
-
title: '{{#if this.title}}{{this.title}}{{else}}{{this.name}}{{/if}}',
|
|
29
|
-
icon: '{{this.icon}}',
|
|
30
|
-
variant: 'solid'
|
|
31
|
-
}{{#unless @last}},{{/unless}}
|
|
32
|
-
{{/each}}
|
|
33
|
-
];
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
1
|
+
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, APP_INITIALIZER } from '@angular/core';
|
|
2
2
|
import { BrowserModule } from '@angular/platform-browser';
|
|
3
3
|
import { FormsModule } from '@angular/forms';
|
|
4
|
-
import {
|
|
4
|
+
import { Router, RouterModule } from '@angular/router';
|
|
5
|
+
import { PBCContainer } from '../pbc/container';
|
|
6
|
+
import { PBCElementsRenderer } from '../pbc/elementsRenderer';
|
|
5
7
|
import { AppComponent } from './app.component';
|
|
6
8
|
import { DefaultLayoutComponent } from './layouts/default/default.layout';
|
|
7
9
|
import { BlankLayoutComponent } from './layouts/blank/blank.layout';
|
|
8
10
|
import { LayoutLazyLoadDirective } from './directive/app-lazy-load.directive';
|
|
11
|
+
import { RouteService } from './services/route.service';
|
|
9
12
|
|
|
10
13
|
@NgModule({
|
|
11
14
|
declarations: [
|
|
@@ -15,11 +18,21 @@ import { LayoutLazyLoadDirective } from './directive/app-lazy-load.directive';
|
|
|
15
18
|
BlankLayoutComponent,
|
|
16
19
|
],
|
|
17
20
|
imports: [
|
|
18
|
-
AppRoutingModule,
|
|
19
21
|
BrowserModule,
|
|
20
22
|
FormsModule,
|
|
23
|
+
RouterModule.forRoot(RouteService.routes),
|
|
24
|
+
PBCContainer,
|
|
25
|
+
PBCElementsRenderer,
|
|
26
|
+
],
|
|
27
|
+
providers: [
|
|
28
|
+
RouteService,
|
|
29
|
+
{
|
|
30
|
+
provide: APP_INITIALIZER,
|
|
31
|
+
useFactory: (service: RouteService, router: Router) => () => router.resetConfig(service.allRoutes()),
|
|
32
|
+
deps: [RouteService, Router],
|
|
33
|
+
multi: true,
|
|
34
|
+
},
|
|
21
35
|
],
|
|
22
|
-
providers: [],
|
|
23
36
|
bootstrap: [AppComponent],
|
|
24
37
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
25
38
|
})
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
|
-
import { CanActivate, Router } from '@angular/router';
|
|
3
|
-
import { AuthService } from '../services/auth.service';
|
|
4
2
|
import { AUTH_PATH } from '../app.config';
|
|
3
|
+
import { PermissionsGuard } from './permissions.guard';
|
|
5
4
|
|
|
6
5
|
@Injectable({
|
|
7
6
|
providedIn: 'root',
|
|
8
7
|
})
|
|
9
|
-
export class AuthGuard
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
) {}
|
|
14
|
-
|
|
15
|
-
canActivate(): boolean {
|
|
16
|
-
const isUserAuthenticated = this.authService.isUserAuthenticated();
|
|
17
|
-
|
|
18
|
-
if (!isUserAuthenticated) {
|
|
8
|
+
export class AuthGuard extends PermissionsGuard {
|
|
9
|
+
override async canActivate(): Promise<boolean> {
|
|
10
|
+
if (!this.user.isAuthenticated) {
|
|
11
|
+
this.user.trackPath();
|
|
19
12
|
this.router.navigate([`/${AUTH_PATH}`]);
|
|
20
13
|
return false;
|
|
21
14
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
|
3
|
+
import { getUser } from '@genesislcap/foundation-user';
|
|
4
|
+
import { AuthGuard } from './auth.guard';
|
|
5
|
+
import { ConnectionGuard } from './connection.guard';
|
|
6
|
+
import { PermissionsGuard } from './permissions.guard';
|
|
7
|
+
|
|
8
|
+
@Injectable({
|
|
9
|
+
providedIn: 'root',
|
|
10
|
+
})
|
|
11
|
+
export class ChainedGuard implements CanActivate {
|
|
12
|
+
protected user = getUser();
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
protected router: Router,
|
|
16
|
+
protected connectionGuard: ConnectionGuard,
|
|
17
|
+
protected authGuard: AuthGuard,
|
|
18
|
+
protected permissionsGuard: PermissionsGuard
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
|
22
|
+
let result = await this.connectionGuard.canActivate();
|
|
23
|
+
if (!result) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
result = await this.authGuard.canActivate();
|
|
27
|
+
if (!result) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
result = await this.permissionsGuard.canActivate(route);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { CanActivate} from '@angular/router';
|
|
3
|
+
import { getConnect } from '@genesislcap/foundation-comms';
|
|
4
|
+
|
|
5
|
+
@Injectable({
|
|
6
|
+
providedIn: 'root'
|
|
7
|
+
})
|
|
8
|
+
export class ConnectionGuard implements CanActivate {
|
|
9
|
+
protected connect = getConnect();
|
|
10
|
+
|
|
11
|
+
async canActivate(): Promise<boolean> {
|
|
12
|
+
if (this.connect.isConnected) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const hostUrl = sessionStorage.getItem('hostUrl');
|
|
16
|
+
if (!hostUrl) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return this.connect.connect(hostUrl);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
2
|
import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router';
|
|
3
|
-
import {
|
|
3
|
+
import { getUser } from '@genesislcap/foundation-user';
|
|
4
4
|
import { NOT_PERMITTED_PATH } from '../app.config';
|
|
5
5
|
|
|
6
6
|
@Injectable({
|
|
7
7
|
providedIn: 'root',
|
|
8
8
|
})
|
|
9
9
|
export class PermissionsGuard implements CanActivate {
|
|
10
|
-
|
|
11
|
-
private authService: AuthService,
|
|
12
|
-
private router: Router,
|
|
13
|
-
) {}
|
|
10
|
+
protected user = getUser();
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
const isPermitted = await this.authService.hasUserPermission(route.data['permissionCode']);
|
|
12
|
+
constructor(protected router: Router) {}
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
|
15
|
+
const { permissionCode } = route.data;
|
|
16
|
+
if (permissionCode && !this.user.hasPermission(permissionCode)) {
|
|
19
17
|
await this.router.navigate([`/${NOT_PERMITTED_PATH}`]);
|
|
20
18
|
return false;
|
|
21
19
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { Router } from '@angular/router';
|
|
2
2
|
|
|
3
3
|
export default class BaseLayout {
|
|
4
|
-
router: Router
|
|
5
|
-
|
|
6
|
-
constructor(router: Router) {
|
|
7
|
-
this.router = router;
|
|
8
|
-
}
|
|
4
|
+
constructor(protected router: Router) {}
|
|
9
5
|
|
|
10
6
|
navigate(path: string) {
|
|
11
7
|
this.router.navigate([path]);
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
<rapid-design-system-provider #designSystemProvider>
|
|
2
|
+
<pbc-elements-renderer [target]="['layout-start']"></pbc-elements-renderer>
|
|
2
3
|
<foundation-header
|
|
3
4
|
#foundationHeader
|
|
4
5
|
show-luminance-toggle-button
|
|
5
6
|
show-misc-toggle-button
|
|
6
7
|
(luminance-icon-clicked)="onLuminanceToogle()"
|
|
7
8
|
[navigateTo]="navigateAngular"
|
|
9
|
+
[routeNavItems]="navItems"
|
|
8
10
|
>
|
|
9
|
-
<section class="routes-wrapper" slot="routes">
|
|
10
|
-
<rapid-button *ngFor="let menuLink of allRoutes" (click)="navigate(menuLink.path)" >
|
|
11
|
-
<rapid-icon name="\{{ menuLink.icon }}"></rapid-icon>
|
|
12
|
-
\{{ menuLink.title }}
|
|
13
|
-
</rapid-button>
|
|
14
|
-
</section>
|
|
15
11
|
</foundation-header>
|
|
16
12
|
<section class="content">
|
|
13
|
+
<pbc-elements-renderer [target]="['content-start']"></pbc-elements-renderer>
|
|
17
14
|
<router-outlet></router-outlet>
|
|
15
|
+
<pbc-elements-renderer [target]="['content', 'content-end']"></pbc-elements-renderer>
|
|
18
16
|
</section>
|
|
17
|
+
<pbc-elements-renderer [target]="['layout', 'layout-end']"></pbc-elements-renderer>
|
|
19
18
|
</rapid-design-system-provider>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
|
2
2
|
import { Router } from '@angular/router';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { configureDesignSystem, FoundationRouteNavItem } from '@genesislcap/foundation-ui';
|
|
4
|
+
import { baseLayerLuminance, StandardLuminance } from '@genesislcap/web-core';
|
|
5
5
|
import * as designTokens from '../../../styles/design-tokens.json';
|
|
6
|
+
import { RouteService } from '../../services/route.service';
|
|
6
7
|
import BaseLayout from '../base.layout';
|
|
7
|
-
import {
|
|
8
|
+
import { registerStylesTarget } from '../../../pbc/utils';
|
|
8
9
|
|
|
9
10
|
@Component({
|
|
10
11
|
selector: 'app-default-layout',
|
|
@@ -13,16 +14,20 @@ import { mainMenu } from '../../app.config';
|
|
|
13
14
|
})
|
|
14
15
|
export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit {
|
|
15
16
|
@ViewChild('designSystemProvider') designSystemProviderElement!: ElementRef;
|
|
16
|
-
|
|
17
|
+
navItems: FoundationRouteNavItem[] = [];
|
|
17
18
|
|
|
18
19
|
constructor(
|
|
20
|
+
private el: ElementRef,
|
|
19
21
|
router: Router,
|
|
22
|
+
routeService: RouteService,
|
|
20
23
|
) {
|
|
21
24
|
super(router);
|
|
25
|
+
this.navItems = routeService.getNavItems();
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
ngAfterViewInit() {
|
|
25
29
|
configureDesignSystem(this.designSystemProviderElement.nativeElement, designTokens);
|
|
30
|
+
registerStylesTarget(this.el.nativeElement, 'layout');
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
navigateAngular = (path: string) => {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Route, Routes } from '@angular/router';
|
|
3
|
+
import { getApp } from '@genesislcap/foundation-shell/app';
|
|
4
|
+
import { FoundationRoute, FoundationRouteNavItem, getNavItems } from '@genesislcap/foundation-ui';
|
|
5
|
+
import { PBCContainer } from '../../pbc/container';
|
|
6
|
+
import { AUTH_PATH, NOT_PERMITTED_PATH } from '../app.config';
|
|
7
|
+
import { ChainedGuard } from '../guards/chained.guard';
|
|
8
|
+
import { AuthLoginComponent } from '../pages/auth-login/auth-login.component';
|
|
9
|
+
import { NotPermittedComponent } from '../pages/not-permitted/not-permitted.component';
|
|
10
|
+
{{#each routes}}
|
|
11
|
+
import { {{pascalCase this.name}}Component } from '../pages/{{kebabCase this.name}}/{{kebabCase this.name}}.component';
|
|
12
|
+
{{/each}}
|
|
13
|
+
|
|
14
|
+
@Injectable({
|
|
15
|
+
providedIn: 'root'
|
|
16
|
+
})
|
|
17
|
+
export class RouteService {
|
|
18
|
+
static routes: Routes = [
|
|
19
|
+
{
|
|
20
|
+
path: '',
|
|
21
|
+
redirectTo: `${AUTH_PATH}`,
|
|
22
|
+
pathMatch: 'full',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
path: `${AUTH_PATH}`,
|
|
26
|
+
component: AuthLoginComponent,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: `${NOT_PERMITTED_PATH}`,
|
|
30
|
+
component: NotPermittedComponent,
|
|
31
|
+
},
|
|
32
|
+
{{#each routes}}
|
|
33
|
+
{
|
|
34
|
+
path: '{{kebabCase this.name}}',
|
|
35
|
+
canActivate: [ChainedGuard],
|
|
36
|
+
component: {{pascalCase this.name}}Component,
|
|
37
|
+
data: {
|
|
38
|
+
permissionCode: '{{this.permissions.viewRight}}',
|
|
39
|
+
navItems: [
|
|
40
|
+
{
|
|
41
|
+
navId: 'header',
|
|
42
|
+
title: '{{#if this.title}}{{this.title}}{{else}}{{this.name}}{{/if}}',
|
|
43
|
+
icon: {
|
|
44
|
+
name: '{{this.icon}}',
|
|
45
|
+
variant: 'solid',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{{/each}}
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @privateRemarks
|
|
56
|
+
* The shell has access to context, so it's possible for it to return a pre-mapped framework variant of routes.
|
|
57
|
+
* In this iteration we're doing it inline, given the angular version may move and here we know the shape we need.
|
|
58
|
+
*/
|
|
59
|
+
pbcRoutes(): Routes {
|
|
60
|
+
return getApp().routes.map((route) => {
|
|
61
|
+
return <Route>{
|
|
62
|
+
title: route.title,
|
|
63
|
+
path: route.path,
|
|
64
|
+
/**
|
|
65
|
+
* Ask about permissions.viewRight in PBC context, as we may need to apply a data.permissionCode here.
|
|
66
|
+
* Not sure if they are added to the filesystem prior to handlebars template processing across the files.
|
|
67
|
+
*/
|
|
68
|
+
canActivate: [ChainedGuard],
|
|
69
|
+
component: PBCContainer,
|
|
70
|
+
data: {
|
|
71
|
+
...route.settings,
|
|
72
|
+
pbcElement: route.element,
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
pbcElementTag: route.elementTag,
|
|
75
|
+
navItems: route.navItems
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
allRoutes(): Routes {
|
|
82
|
+
return [
|
|
83
|
+
...RouteService.routes,
|
|
84
|
+
...this.pbcRoutes(),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getNavItems(): FoundationRouteNavItem[] {
|
|
89
|
+
const allNavItems = this.allRoutes().flatMap(route => (<FoundationRoute>{
|
|
90
|
+
path: route.path as string,
|
|
91
|
+
navItems: route.data?.['navItems'],
|
|
92
|
+
}));
|
|
93
|
+
return getNavItems(allNavItems);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {configure, define} from '@genesislcap/foundation-login';
|
|
2
2
|
import type { Router } from '@angular/router';
|
|
3
|
+
import { getUser } from '@genesislcap/foundation-user';
|
|
4
|
+
import { css, DI } from '@genesislcap/web-core';
|
|
3
5
|
import { AUTH_PATH } from '../app.config';
|
|
4
|
-
import { css } from '@microsoft/fast-element';
|
|
5
|
-
import { DI } from '@microsoft/fast-foundation';
|
|
6
6
|
import logo from '../../assets/logo.svg';
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line
|
|
@@ -28,10 +28,12 @@ export const configureFoundationLogin = ({
|
|
|
28
28
|
router: Router;
|
|
29
29
|
}) => {
|
|
30
30
|
configure(DI.getOrCreateDOMContainer(), {
|
|
31
|
+
// autoConnect: true, // < Guard in place to ensure connection. Keeping the connect form in place for now.
|
|
32
|
+
autoAuth: true, // < Allow users to skip login
|
|
31
33
|
showConnectionIndicator: true,
|
|
32
34
|
hostPath: AUTH_PATH,
|
|
33
35
|
redirectHandler: () => {
|
|
34
|
-
router.navigate(['{{kebabCase routes.[0].name}}'])
|
|
36
|
+
router.navigate([getUser().lastPath() ?? '{{kebabCase routes.[0].name}}'])
|
|
35
37
|
},
|
|
36
38
|
...ssoSettings,
|
|
37
39
|
logo: css `
|
|
@@ -1,29 +1,56 @@
|
|
|
1
|
-
import { configure as configureHeader }from '@genesislcap/foundation-header/config';
|
|
2
|
-
import { foundationLayoutComponents } from '@genesislcap/foundation-layout';
|
|
3
1
|
import { EntityManagement } from '@genesislcap/foundation-entity-management';
|
|
2
|
+
import { Form } from '@genesislcap/foundation-forms';
|
|
3
|
+
import { foundationLayoutComponents } from '@genesislcap/foundation-layout';
|
|
4
|
+
import { getApp } from '@genesislcap/foundation-shell/app';
|
|
5
|
+
import * as zeroDesignSystem from '@genesislcap/foundation-zero';
|
|
4
6
|
import { g2plotChartsComponents } from '@genesislcap/g2plot-chart';
|
|
5
7
|
import * as rapidDesignSystem from '@genesislcap/rapid-design-system';
|
|
6
8
|
import { rapidGridComponents } from '@genesislcap/rapid-grid-pro';
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Ensure tree shaking doesn't remove these.
|
|
12
|
+
*/
|
|
8
13
|
EntityManagement;
|
|
14
|
+
Form;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* registerComponents.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export async function registerComponents() {
|
|
21
|
+
const { configure: configureHeader } = await import('@genesislcap/foundation-header/config');
|
|
22
|
+
/**
|
|
23
|
+
* Register any PBC components with the design system
|
|
24
|
+
*/
|
|
25
|
+
getApp().registerComponents({
|
|
26
|
+
designSystem: rapidDesignSystem,
|
|
27
|
+
});
|
|
9
28
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
rapidDesignSystem
|
|
30
|
+
.provideDesignSystem()
|
|
31
|
+
.register(
|
|
32
|
+
rapidDesignSystem.baseComponents,
|
|
33
|
+
rapidGridComponents,
|
|
34
|
+
g2plotChartsComponents,
|
|
35
|
+
foundationLayoutComponents,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
configureHeader({
|
|
39
|
+
templateOptions: {
|
|
40
|
+
provider: 'template',
|
|
41
|
+
icon: 'rapid-icon',
|
|
42
|
+
button: 'rapid-button',
|
|
43
|
+
connectionIndicator: 'rapid-connection-indicator',
|
|
44
|
+
select: 'rapid-select',
|
|
45
|
+
option: 'rapid-option',
|
|
46
|
+
flyout: 'rapid-flyout',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
21
49
|
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
);
|
|
50
|
+
/**
|
|
51
|
+
* May be still required while we transition all PBCs to rapid. Remove when complete.
|
|
52
|
+
*/
|
|
53
|
+
zeroDesignSystem
|
|
54
|
+
.provideDesignSystem()
|
|
55
|
+
.register(zeroDesignSystem.baseComponents, g2plotChartsComponents, foundationLayoutComponents);
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './store';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CustomEventMap } from '@genesislcap/foundation-events';
|
|
2
|
+
import { getApp } from '@genesislcap/foundation-shell/app';
|
|
3
|
+
import {
|
|
4
|
+
AbstractStoreRoot,
|
|
5
|
+
registerStore,
|
|
6
|
+
StoreRoot,
|
|
7
|
+
StoreRootEventDetailMap,
|
|
8
|
+
} from '@genesislcap/foundation-store';
|
|
9
|
+
import { DI } from '@genesislcap/web-core';
|
|
10
|
+
|
|
11
|
+
export interface Store extends StoreRoot {}
|
|
12
|
+
|
|
13
|
+
export type StoreEventDetailMap = StoreRootEventDetailMap & {};
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementEventMap extends CustomEventMap<StoreEventDetailMap> {}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class DefaultStore extends AbstractStoreRoot<Store, StoreEventDetailMap> implements Store {
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register the store root
|
|
25
|
+
*/
|
|
26
|
+
getApp().registerStoreRoot(this);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const Store = registerStore(DefaultStore, 'Store');
|
|
31
|
+
|
|
32
|
+
export function getStore(): Store {
|
|
33
|
+
return DI.getOrCreateDOMContainer().get(Store) as Store;
|
|
34
|
+
}
|
|
@@ -2,4 +2,10 @@ declare module "*.svg" {
|
|
|
2
2
|
const content: any;
|
|
3
3
|
export default content;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Temp to unblock testing until we update the types pointer in all PBC packages
|
|
8
|
+
*/
|
|
9
|
+
declare module '@genesislcap/pbc-auth-ui';
|
|
10
|
+
declare module '@genesislcap/pbc-documents-ui';
|
|
11
|
+
declare module '@genesislcap/pbc-reconciliation-ui';
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
2
|
+
import { createLogger } from '@genesislcap/foundation-logger';
|
|
2
3
|
import { AppModule } from './app/app.module';
|
|
4
|
+
import { registerPBCs } from './pbc/utils';
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
const logger = createLogger('main');
|
|
7
|
+
|
|
8
|
+
function bootstrapApp() {
|
|
9
|
+
platformBrowserDynamic()
|
|
10
|
+
.bootstrapModule(AppModule)
|
|
11
|
+
.catch((err) => logger.error(err));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
registerPBCs()
|
|
15
|
+
.then(hasAssets => logger.debug(hasAssets ? 'PBCs registered' : 'No PBCs detected'))
|
|
16
|
+
.catch((err) => logger.error(err))
|
|
17
|
+
.finally(bootstrapApp)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, OnInit, ViewChild } from '@angular/core';
|
|
2
|
+
import { ActivatedRoute } from '@angular/router';
|
|
3
|
+
import { deriveElementTag } from './utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* An app level container for the lazy loading of the web component based PBC routes in angular router.
|
|
7
|
+
*/
|
|
8
|
+
@Component({
|
|
9
|
+
standalone: true,
|
|
10
|
+
selector: 'pbc-container',
|
|
11
|
+
template: `<div #container class="container"></div>`, // todo get rid of the extra div
|
|
12
|
+
styles: [`
|
|
13
|
+
.container {
|
|
14
|
+
width: 100%;
|
|
15
|
+
height: 100%;
|
|
16
|
+
}
|
|
17
|
+
`],
|
|
18
|
+
schemas: [
|
|
19
|
+
CUSTOM_ELEMENTS_SCHEMA
|
|
20
|
+
],
|
|
21
|
+
})
|
|
22
|
+
export class PBCContainer implements OnInit {
|
|
23
|
+
@ViewChild('container', { static: true }) container!: ElementRef;
|
|
24
|
+
|
|
25
|
+
constructor(private route: ActivatedRoute) {}
|
|
26
|
+
|
|
27
|
+
async ngOnInit() {
|
|
28
|
+
const { pbcElement, pbcElementTag } = this.route.snapshot.data ?? {};
|
|
29
|
+
if (!pbcElement) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Account for PBC elements being a mix of constructors and lazy loading functions
|
|
34
|
+
*/
|
|
35
|
+
const element = pbcElement.define ? pbcElement : await pbcElement();
|
|
36
|
+
const tagName = pbcElementTag || deriveElementTag(element.name);
|
|
37
|
+
const customElement = document.createElement(tagName);
|
|
38
|
+
this.container.nativeElement.appendChild(customElement);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
|
|
3
|
+
import { AppElementPredicate, AppTargetId } from '@genesislcap/foundation-shell/app';
|
|
4
|
+
import { createLogger } from '@genesislcap/foundation-logger';
|
|
5
|
+
import type { ViewTemplate } from '@genesislcap/web-core';
|
|
6
|
+
import { customEventFactory, getTargetElements } from './utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* An app level component for renderering target PBC elements to an angular template.
|
|
10
|
+
*/
|
|
11
|
+
@Component({
|
|
12
|
+
standalone: true,
|
|
13
|
+
selector: 'pbc-elements-renderer',
|
|
14
|
+
template: `<div #container class="container"></div>`, // < can we get rid of the extra div?
|
|
15
|
+
styles: [`
|
|
16
|
+
.container {}
|
|
17
|
+
`],
|
|
18
|
+
schemas: [
|
|
19
|
+
CUSTOM_ELEMENTS_SCHEMA
|
|
20
|
+
],
|
|
21
|
+
imports: [
|
|
22
|
+
CommonModule,
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
export class PBCElementsRenderer implements OnChanges, OnInit {
|
|
26
|
+
@ViewChild('container', { static: true }) container!: ElementRef;
|
|
27
|
+
@Input() target: AppTargetId = [];
|
|
28
|
+
@Input() predicate: AppElementPredicate = () => true;
|
|
29
|
+
templates: ViewTemplate[] = [];
|
|
30
|
+
logger = createLogger('pbc-elements-renderer');
|
|
31
|
+
|
|
32
|
+
async ngOnInit() {
|
|
33
|
+
this.setTemplates();
|
|
34
|
+
this.renderTemplates();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$emit(type: string, detail?: any) {
|
|
38
|
+
this.container.nativeElement.dispatchEvent(customEventFactory(type, detail));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
42
|
+
if (changes['target']) {
|
|
43
|
+
this.logger.debug('target changed, rendering qualifying elements');
|
|
44
|
+
this.setTemplates();
|
|
45
|
+
this.renderTemplates();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setTemplates() {
|
|
50
|
+
this.templates = getTargetElements(this.target, this.predicate);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
renderTemplates() {
|
|
54
|
+
this.container.nativeElement.replaceChildren();
|
|
55
|
+
this.templates.forEach((template) => template.render(this, this.container.nativeElement));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createLogger } from '@genesislcap/foundation-logger';
|
|
2
|
+
import {
|
|
3
|
+
AppContext,
|
|
4
|
+
AppElement,
|
|
5
|
+
AppElementPredicate,
|
|
6
|
+
AppStyle,
|
|
7
|
+
AppStylePredicate,
|
|
8
|
+
AppTargetId,
|
|
9
|
+
assetPredicate,
|
|
10
|
+
getApp,
|
|
11
|
+
registrationPredicate,
|
|
12
|
+
targetIdPredicate
|
|
13
|
+
} from '@genesislcap/foundation-shell/app';
|
|
14
|
+
import { importPBCAssets } from '@genesislcap/foundation-shell/pbc';
|
|
15
|
+
import { toElementStyles } from '@genesislcap/foundation-utils';
|
|
16
|
+
import { ComposableStyles } from '@genesislcap/web-core';
|
|
17
|
+
|
|
18
|
+
const logger = createLogger('pbc-utils');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Some of this logic already exists in shell, but exists as part of a logic chain that assumes web components.
|
|
22
|
+
* After testing, we can backport the changes required.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export async function registerPBCs(): Promise<boolean> {
|
|
29
|
+
const app = getApp();
|
|
30
|
+
const pbcAssets = await importPBCAssets();
|
|
31
|
+
app.registerAssets(pbcAssets);
|
|
32
|
+
return app.hasAssets();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @privateRemarks
|
|
37
|
+
* Shared across elements and styles.
|
|
38
|
+
*/
|
|
39
|
+
const assetFilter = (
|
|
40
|
+
asset: AppElement | AppStyle,
|
|
41
|
+
targetId: AppTargetId,
|
|
42
|
+
predicate: AppElementPredicate | AppStylePredicate = () => true,
|
|
43
|
+
context: AppContext,
|
|
44
|
+
) => targetIdPredicate(asset, targetId) &&
|
|
45
|
+
assetPredicate(asset, context) &&
|
|
46
|
+
registrationPredicate(
|
|
47
|
+
asset,
|
|
48
|
+
predicate,
|
|
49
|
+
context,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export function getTargetStyles(
|
|
56
|
+
targetId: AppTargetId,
|
|
57
|
+
predicate: AppStylePredicate = () => true
|
|
58
|
+
) {
|
|
59
|
+
const app = getApp();
|
|
60
|
+
return app.styles
|
|
61
|
+
.filter(asset => assetFilter(asset, targetId, predicate, app.config.context!))
|
|
62
|
+
.map((token) => token.styles)
|
|
63
|
+
.flat();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export function getTargetElements(
|
|
71
|
+
targetId: AppTargetId,
|
|
72
|
+
predicate: AppElementPredicate = () => true
|
|
73
|
+
) {
|
|
74
|
+
const app = getApp();
|
|
75
|
+
return app.elements
|
|
76
|
+
.filter(asset => assetFilter(asset, targetId, predicate, app.config.context!))
|
|
77
|
+
.map((token) => token.elements);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
export function registerStylesTarget(nativeElement: HTMLElement, targetId: AppTargetId) {
|
|
84
|
+
const styles = getTargetStyles(targetId);
|
|
85
|
+
if (!styles || styles.length === 0) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
applyDynamicStyles(nativeElement, styles);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
export function applyDynamicStyles(nativeElement: HTMLElement, style: ComposableStyles | ComposableStyles[]) {
|
|
95
|
+
const elementStyles = toElementStyles(style);
|
|
96
|
+
const styleTarget = nativeElement.shadowRoot ? nativeElement.shadowRoot : document;
|
|
97
|
+
elementStyles.addStylesTo(styleTarget);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @public
|
|
102
|
+
*/
|
|
103
|
+
export function customEventFactory(type: string, detail?: any) {
|
|
104
|
+
return new CustomEvent(type, {
|
|
105
|
+
bubbles: true,
|
|
106
|
+
cancelable: true,
|
|
107
|
+
composed: true,
|
|
108
|
+
detail,
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @privateRemarks
|
|
114
|
+
* May need to add an elementTag to routes, ie. elementTag: 'notifications-dashboard'. May remove when complete.
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
117
|
+
export function deriveElementTag(name: string): string {
|
|
118
|
+
const tagName = name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
119
|
+
logger.debug(`Guessing pbc element tag is '${tagName}' based on '${name}'. This may be incorrect, please set pbcElementTag in route data.`);
|
|
120
|
+
return tagName;
|
|
121
|
+
}
|
|
@@ -32,11 +32,24 @@ module.exports = {
|
|
|
32
32
|
},
|
|
33
33
|
resolve: {
|
|
34
34
|
alias: {
|
|
35
|
-
'
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
35
|
+
'pbc': path.resolve(
|
|
36
|
+
__dirname,
|
|
37
|
+
'src/pbc',
|
|
38
|
+
)
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
|
+
/**
|
|
42
|
+
* Ensure webpack handles module loading strictly to allow module federation fallbacks.
|
|
43
|
+
*/
|
|
44
|
+
output: {
|
|
45
|
+
strictModuleErrorHandling: true,
|
|
46
|
+
strictModuleExceptionHandling: true,
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Mark the 'foundationZero/ZeroDesignSystem' module federated remote as external.
|
|
50
|
+
*/
|
|
51
|
+
externals: {
|
|
52
|
+
'foundationZero/ZeroDesignSystem': 'foundationZero/ZeroDesignSystem',
|
|
53
|
+
},
|
|
41
54
|
};
|
|
42
55
|
|
package/package.json
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { NgModule } from '@angular/core';
|
|
2
|
-
import { RouterModule, Routes } from '@angular/router';
|
|
3
|
-
import { AuthGuard } from './guards/auth.guard';
|
|
4
|
-
import { PermissionsGuard } from './guards/permissions.guard';
|
|
5
|
-
import { AuthLoginComponent } from './pages/auth-login/auth-login.component';
|
|
6
|
-
import { NotPermittedComponent } from './pages/not-permitted/not-permitted.component';
|
|
7
|
-
{{#each routes}}
|
|
8
|
-
import { {{pascalCase this.name}}Component } from './pages/{{kebabCase this.name}}/{{kebabCase this.name}}.component';
|
|
9
|
-
{{/each}}
|
|
10
|
-
import { AUTH_PATH, NOT_PERMITTED_PATH } from './app.config';
|
|
11
|
-
|
|
12
|
-
export const routes: Routes = [
|
|
13
|
-
{
|
|
14
|
-
path: '',
|
|
15
|
-
redirectTo: `${AUTH_PATH}`,
|
|
16
|
-
pathMatch: 'full',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
path: `${AUTH_PATH}`,
|
|
20
|
-
component: AuthLoginComponent,
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
path: `${NOT_PERMITTED_PATH}`,
|
|
24
|
-
component: NotPermittedComponent,
|
|
25
|
-
},
|
|
26
|
-
{{#each routes}}
|
|
27
|
-
{
|
|
28
|
-
path: '{{kebabCase this.name}}',
|
|
29
|
-
canActivate: [AuthGuard{{#if this.permissions.viewRight}}, PermissionsGuard{{/if}}],
|
|
30
|
-
component: {{pascalCase this.name}}Component,
|
|
31
|
-
data: { permissionCode: '{{this.permissions.viewRight}}' },
|
|
32
|
-
},
|
|
33
|
-
{{/each}}
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
@NgModule({
|
|
37
|
-
imports: [RouterModule.forRoot(routes)],
|
|
38
|
-
exports: [RouterModule],
|
|
39
|
-
})
|
|
40
|
-
export class AppRoutingModule {}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@angular/core';
|
|
2
|
-
import { Auth } from '@genesislcap/foundation-comms';
|
|
3
|
-
import { DI } from "@microsoft/fast-foundation";
|
|
4
|
-
|
|
5
|
-
@Injectable({
|
|
6
|
-
providedIn: 'root',
|
|
7
|
-
})
|
|
8
|
-
export class AuthService {
|
|
9
|
-
auth: Auth;
|
|
10
|
-
|
|
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);
|
|
21
|
-
}
|
|
22
|
-
}
|