@genesislcap/blank-app-seed 5.10.4-prerelease.1 → 5.11.0-prerelease.2

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 (29) hide show
  1. package/.genx/package.json +1 -1
  2. package/.genx/templates/react/component/component.hbs +16 -39
  3. package/.genx/templates/react/entityManager.hbs +11 -5
  4. package/.genx/templates/react/grid.hbs +7 -0
  5. package/.genx/templates/react/gridLayout.hbs +36 -30
  6. package/.genx/templates/react/horizontalLayout.hbs +11 -7
  7. package/.genx/templates/react/route.hbs +35 -13
  8. package/.genx/templates/react/route.styles.hbs +3 -2
  9. package/.genx/templates/react/store.hbs +0 -8
  10. package/.genx/templates/react/tabsLayout.hbs +17 -7
  11. package/.genx/utils/generateStore.js +3 -9
  12. package/.genx/versions.json +1 -1
  13. package/.github/workflows/slack.yml +1 -1
  14. package/CHANGELOG.md +28 -0
  15. package/client-tmp/react/package.json +2 -0
  16. package/client-tmp/react/src/components/routes/ProtectedRoute.tsx +4 -2
  17. package/client-tmp/react/src/config.ts +0 -2
  18. package/client-tmp/react/src/custom-elements.d.ts +0 -3
  19. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +44 -32
  20. package/client-tmp/react/src/main.tsx +2 -0
  21. package/client-tmp/react/src/share/foundation-login.ts +9 -5
  22. package/client-tmp/react/src/share/genesis-components.ts +0 -2
  23. package/client-tmp/react/src/styles/flexlayout-theme.css +102 -0
  24. package/client-tmp/react/src/utils/layout.ts +3 -4
  25. package/package.json +1 -1
  26. package/.genx/.prettierrc +0 -5
  27. package/client-tmp/react/src/environments/environment.prod.ts +0 -8
  28. package/client-tmp/react/src/environments/environment.ts +0 -9
  29. package/client-tmp/react/src/utils/goldenLayout.helper.ts +0 -59
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/blank-app-seed-config",
3
3
  "description": "Genesis Blank App Seed Configuration",
4
- "version": "5.10.4-prerelease.1",
4
+ "version": "5.11.0-prerelease.2",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "lint": "eslint .",
@@ -1,4 +1,3 @@
1
- import { useEffect } from 'react';
2
1
  {{#ifEquals tile.type 'entity-manager'}}
3
2
  import { EntityManagement } from '@genesislcap/foundation-entity-management/react';
4
3
  {{/ifEquals}}
@@ -55,8 +54,9 @@ import { actions } from '../../../store/store';
55
54
  import { useSelector } from 'react-redux';
56
55
  import { selectors } from '../../../store/store';
57
56
  {{/if}}
58
- import { layoutComponentsMap, LayoutComponentNames } from '../../../store/store';
59
- import { getElementByTagFromComponent } from '../../../utils/goldenLayout.helper';
57
+ {{#ifAny tile.config.snapshot tile.config.reqrep tile.config.eventing.listener}}
58
+ import { DatasourceConfiguration } from '@genesislcap/foundation-entity-management';
59
+ {{/ifAny}}
60
60
  import './{{pascalCase tile.title}}Component.css';
61
61
 
62
62
  {{#ifAny tile.metadata.comment tile.metadata.todo}}
@@ -76,7 +76,11 @@ export const {{pascalCase tile.componentName}}: React.FC = () => {
76
76
  {{/if}}
77
77
  {{#if tile.config.eventing.publishEventName}}
78
78
  const handleRowSelection = (e: any) => {
79
+ {{#ifEquals tile.type 'entity-manager'}}
80
+ const selectedRows = e.detail.api.getSelectedRows();
81
+ {{else}}
79
82
  const selectedRows = e.api.getSelectedRows();
83
+ {{/ifEquals}}
80
84
  if (!selectedRows || !selectedRows.length) {
81
85
  actions.eventing.publish{{pascalCase tile.config.eventing.publishEventName}}(null);
82
86
  return;
@@ -100,8 +104,9 @@ export const {{pascalCase tile.componentName}}: React.FC = () => {
100
104
  {{#if tile.config.columns}}
101
105
  const columnDefs: typeof columnDefsTile = [...columnDefsTile];
102
106
  {{/if}}
107
+ {{#ifEquals tile.type 'grid-pro'}}
103
108
  {{#ifAny tile.config.gridOptions tile.config.eventing.publishEventName}}
104
- const gridOptions: { {{#if tile.config.gridOptions}}onRowClicked: GridOptionsConfig['onRowClicked'], {{/if}}{{#if tile.config.eventing.publishEventName}}onSelectionChanged: any, rowSelection: 'multiple' | 'single'{{/if}} } = {
109
+ const gridOptions: GridOptionsConfig = {
105
110
  {{#if tile.config.gridOptions}}
106
111
  onRowClicked: gridOptionsTile?.onRowClicked,
107
112
  {{/if}}
@@ -111,56 +116,28 @@ export const {{pascalCase tile.componentName}}: React.FC = () => {
111
116
  {{/if}}
112
117
  }
113
118
  {{/ifAny}}
114
- const datasourceConfig: { isSnapshot?: boolean, pollingInterval?: string, requestAutoSetup?: string, criteria?: string } = {
119
+ {{/ifEquals}}
120
+ {{#ifAny tile.config.snapshot tile.config.reqrep tile.config.eventing.listener}}
121
+ const datasourceConfig: DatasourceConfiguration = {
115
122
  {{#if tile.config.snapshot}}
116
123
  isSnapshot: {{ tile.config.snapshot }},
117
124
  {{/if}}
118
125
  {{#if tile.config.reqrep}}
119
- pollingInterval: '5000',
120
- requestAutoSetup: 'false',
126
+ pollingInterval: 5000,
127
+ requestAutoSetup: false,
121
128
  {{/if}}
122
129
  {{#if tile.config.eventing.listener}}
123
130
  criteria: useSelector(selectors.eventing.getCriteriaFor{{pascalCase tile.title}}),
124
131
  {{/if}}
125
132
  }
126
- {{#if tile.componentType}}
127
- {{#ifEquals tile.componentType 'grid'}}
128
- useEffect(() => {
129
- const componentElement = layoutComponentsMap.get(LayoutComponentNames.{{constantCase tile.componentName}});
130
- const componentDatasource = componentElement ? getElementByTagFromComponent(componentElement, 'grid-pro-genesis-datasource') : undefined;
131
-
132
- if (componentDatasource) {
133
- {{#if tile.config.eventing.listener}}
134
- componentDatasource.criteria = datasourceConfig.criteria;
135
- {{/if}}
136
- {{#if tile.config.reqrep}}
137
- componentDatasource.requestAutoSetup = datasourceConfig.requestAutoSetup;
138
- componentDatasource.pollingInterval = datasourceConfig.pollingInterval;
139
- {{/if}}
140
- }
141
- }, [datasourceConfig]);
142
- {{/ifEquals}}
143
- {{/if}}
144
- {{#if tile.componentType}}
145
- {{#ifEquals tile.componentType 'manager'}}
146
- useEffect(() => {
147
- const componentElement = layoutComponentsMap.get(LayoutComponentNames.{{constantCase tile.componentName}});
148
- const componentDatasource = componentElement ? getElementByTagFromComponent(componentElement, 'entity-management') : undefined;
149
-
150
- if (componentDatasource) {
151
- componentDatasource.datasourceConfig = datasourceConfig;
152
- }
153
- }, [datasourceConfig]);
154
- {{/ifEquals}}
155
- {{/if}}
156
-
133
+ {{/ifAny}}
157
134
  {{#if tile.config.type}}
158
135
  const chartConfig: ChartConfig = {
159
136
  {{#ifEquals tile.config.type 'pie'}}
160
137
  radius: 0.75,
161
138
  angleField: 'value',
162
139
  colorField: 'groupBy',
163
- {{else~}}
140
+ {{else}}
164
141
  xField: 'groupBy',
165
142
  yField: 'value',
166
143
  {{/ifEquals}}
@@ -6,6 +6,9 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
6
6
  prefix="rapid"
7
7
  enableRowFlashing
8
8
  enableCellFlashing
9
+ {{#ifAny config.snapshot config.reqrep config.eventing.listener}}
10
+ datasourceConfig={datasourceConfig}
11
+ {{/ifAny}}
9
12
  {{#if config.title~}}
10
13
  title="{{ config.title }}"
11
14
  {{/if}}
@@ -13,7 +16,7 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
13
16
  {{#if config.createEvent~}}
14
17
  {{#if config.permissions.updateRight}}
15
18
  createEvent={hasUserPermission('{{config.permissions.updateRight}}') ? '{{ config.createEvent }}' : undefined}
16
- {{else~}}
19
+ {{else}}
17
20
  createEvent="{{ config.createEvent }}"
18
21
  {{/if}}
19
22
  {{#if config.createFormUiSchema}}
@@ -23,7 +26,7 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
23
26
  {{#if config.updateEvent~}}
24
27
  {{#if config.permissions.updateRight}}
25
28
  updateEvent={hasUserPermission('{{config.permissions.updateRight}}') ? '{{ config.updateEvent }}' : undefined}
26
- {{else~}}
29
+ {{else}}
27
30
  updateEvent="{{ config.updateEvent }}"
28
31
  {{/if}}
29
32
  {{#if config.updateFormUiSchema}}
@@ -33,13 +36,16 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
33
36
  {{#if config.deleteEvent~}}
34
37
  {{#if config.permissions.updateRight}}
35
38
  deleteEvent={hasUserPermission('{{config.permissions.updateRight}}') ? '{{ config.deleteEvent }}' : undefined}
36
- {{else~}}
39
+ {{else}}
37
40
  deleteEvent="{{ config.deleteEvent }}"
38
41
  {{/if}}
39
42
  {{/if}}
40
- {{#ifAny config.gridOptions config.eventing.publishEventName}}
43
+ {{#if config.gridOptions}}
41
44
  gridOptions={gridOptions}
42
- {{/ifAny}}
45
+ {{/if}}
46
+ {{#if config.eventing.publishEventName}}
47
+ onSelectionChanged={handleRowSelection}
48
+ {{/if}}
43
49
  {{#if config.columns}}
44
50
  columns={columnDefs}
45
51
  {{/if}}
@@ -17,6 +17,13 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
17
17
  {{#ifAny config.gridOptions config.eventing.publishEventName}}
18
18
  deferredGridOptions={gridOptions}
19
19
  {{/ifAny}}
20
+ {{#if config.eventing.listener}}
21
+ criteria={datasourceConfig.criteria}
22
+ {{/if}}
23
+ {{#if config.reqrep}}
24
+ requestAutoSetup={datasourceConfig.requestAutoSetup}
25
+ pollingInterval={datasourceConfig.pollingInterval}
26
+ {{/if}}
20
27
  />
21
28
  {{#if config.gridOptions}}
22
29
  {gridOptionsTile?.columnDefs?.map((columnDef, index) => (
@@ -1,30 +1,36 @@
1
- <rapid-layout-region type="horizontal">
2
- <rapid-layout-region type="vertical">
3
- {{#each route.tiles}}
4
- {{#ifEquals @index 0}}
5
- <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
6
- <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
7
- </rapid-layout-item>
8
- {{/ifEquals}}
9
- {{#ifEquals @index 1}}
10
- <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
11
- <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
12
- </rapid-layout-item>
13
- {{/ifEquals}}
14
- {{/each}}
15
- </rapid-layout-region>
16
- <rapid-layout-region type="vertical">
17
- {{#each route.tiles}}
18
- {{#ifEquals @index 2}}
19
- <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
20
- <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
21
- </rapid-layout-item>
22
- {{/ifEquals}}
23
- {{#ifEquals @index 3}}
24
- <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
25
- <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
26
- </rapid-layout-item>
27
- {{/ifEquals}}
28
- {{/each}}
29
- </rapid-layout-region>
30
- </rapid-layout-region>
1
+ const defaultLayout = {
2
+ global: { tabEnableClose: false },
3
+ layout: {
4
+ type: 'row',
5
+ children: [
6
+ {
7
+ type: 'row',
8
+ weight: 50,
9
+ children: [
10
+ {{#each route.tiles}}
11
+ {{#ifEquals @index 0}}
12
+ { type: 'tabset', weight: 50, children: [{ type: 'tab', name: '{{this.title}}', component: '{{camelCase this.componentName}}' }] },
13
+ {{/ifEquals}}
14
+ {{#ifEquals @index 2}}
15
+ { type: 'tabset', weight: 50, children: [{ type: 'tab', name: '{{this.title}}', component: '{{camelCase this.componentName}}' }] },
16
+ {{/ifEquals}}
17
+ {{/each}}
18
+ ],
19
+ },
20
+ {
21
+ type: 'row',
22
+ weight: 50,
23
+ children: [
24
+ {{#each route.tiles}}
25
+ {{#ifEquals @index 1}}
26
+ { type: 'tabset', weight: 50, children: [{ type: 'tab', name: '{{this.title}}', component: '{{camelCase this.componentName}}' }] },
27
+ {{/ifEquals}}
28
+ {{#ifEquals @index 3}}
29
+ { type: 'tabset', weight: 50, children: [{ type: 'tab', name: '{{this.title}}', component: '{{camelCase this.componentName}}' }] },
30
+ {{/ifEquals}}
31
+ {{/each}}
32
+ ],
33
+ },
34
+ ],
35
+ },
36
+ };
@@ -1,7 +1,11 @@
1
- <rapid-layout-region>
2
- {{#each route.tiles}}
3
- <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
4
- <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
5
- </rapid-layout-item>
6
- {{/each}}
7
- </rapid-layout-region>
1
+ const defaultLayout = {
2
+ global: { tabEnableClose: false },
3
+ layout: {
4
+ type: 'row',
5
+ children: [
6
+ {{#each route.tiles}}
7
+ { type: 'tabset', children: [{ type: 'tab', name: '{{this.title}}', component: '{{camelCase this.componentName}}' }] },
8
+ {{/each}}
9
+ ],
10
+ },
11
+ };
@@ -1,27 +1,49 @@
1
- import './{{pascalCase route.name}}.css';
2
- import { persistLayout } from '../../utils';
3
- import { setComponentItemsMap } from '../../utils/goldenLayout.helper';
4
- import { LayoutComponentNames, layoutComponentsMap } from '../../store/store';
5
- import { useRef, useEffect } from 'react';
1
+ import React, { useRef } from 'react';
2
+ import { Layout, Model, TabNode } from 'flexlayout-react';
3
+ import { getFlexLayoutStorageKey } from '../../utils/layout';
6
4
  {{#each route.tiles}}
7
5
  import { {{pascalCase this.componentName}} } from './{{pascalCase this.title}}{{pascalCase this.componentType}}';
8
6
  {{/each}}
7
+ import './{{pascalCase route.name}}.css';
8
+
9
+ const STORAGE_KEY = getFlexLayoutStorageKey('{{route.layoutKey}}');
10
+
11
+ {{> (lookup ./route 'layoutType') }}
12
+
13
+ const componentMap: Record<string, React.FC> = {
14
+ {{#each route.tiles}}
15
+ '{{camelCase this.componentName}}': {{pascalCase this.componentName}},
16
+ {{/each}}
17
+ };
18
+
19
+ function loadModel(): Model {
20
+ if (STORAGE_KEY) {
21
+ const saved = localStorage.getItem(STORAGE_KEY);
22
+ if (saved) {
23
+ try { return Model.fromJson(JSON.parse(saved)); } catch {}
24
+ }
25
+ }
26
+ return Model.fromJson(defaultLayout);
27
+ }
9
28
 
10
29
  const {{pascalCase route.name}} = () => {
11
- const layoutRef = useRef<any>(null);
30
+ const modelRef = useRef<Model>(loadModel());
31
+
32
+ const factory = (node: TabNode) => {
33
+ const Component = componentMap[node.getComponent() ?? ''];
34
+ return Component ? <Component /> : null;
35
+ };
12
36
 
13
- useEffect(() => {
14
- if (layoutRef.current) {
15
- setComponentItemsMap(layoutRef.current, layoutComponentsMap);
37
+ const onModelChange = (model: Model) => {
38
+ if (STORAGE_KEY) {
39
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(model.toJson()));
16
40
  }
17
- }, []);
41
+ };
18
42
 
19
43
  return (
20
44
  <section className="{{kebabCase route.name}}-page">
21
45
  {{#if route.tiles}}
22
- <rapid-layout ref={layoutRef} auto-save-key={persistLayout('{{route.layoutKey}}')}>
23
- {{> (lookup ./route 'layoutType') }}
24
- </rapid-layout>
46
+ <Layout model={modelRef.current} factory={factory} onModelChange={onModelChange} />
25
47
  {{else}}
26
48
  Welcome to {{sentenceCase route.name}}
27
49
  {{/if}}
@@ -1,3 +1,4 @@
1
- :host {
2
- height: 100%;
1
+ .{{kebabCase route.name}}-page {
2
+ height: calc(100vh - 48px);
3
+ position: relative;
3
4
  }
@@ -1,14 +1,6 @@
1
1
  import { createStore } from '@genesislcap/foundation-redux';
2
2
  import { eventingSlice } from './slices/eventing.slice';
3
3
 
4
- export const layoutComponentsMap: Map<string, any> = new Map();
5
-
6
- export const LayoutComponentNames = {
7
- {{#each layoutComponents}}
8
- {{constantCase componentName}}: '{{componentName}}',
9
- {{/each}}
10
- } as const;
11
-
12
4
  export const { reduxStore, store, actions, selectors } = createStore([eventingSlice], {
13
5
  eventing: {
14
6
  {{#each events}}
@@ -1,7 +1,17 @@
1
- <rapid-layout-region type="tabs">
2
- {{#each route.tiles}}
3
- <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
4
- <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
5
- </rapid-layout-item>
6
- {{/each}}
7
- </rapid-layout-region>
1
+ const defaultLayout = {
2
+ global: { tabEnableClose: false },
3
+ layout: {
4
+ type: 'row',
5
+ children: [
6
+ {
7
+ type: 'tabset',
8
+ weight: 100,
9
+ children: [
10
+ {{#each route.tiles}}
11
+ { type: 'tab', name: '{{this.title}}', component: '{{camelCase this.componentName}}' },
12
+ {{/each}}
13
+ ],
14
+ },
15
+ ],
16
+ },
17
+ };
@@ -44,24 +44,18 @@ const aggregateFromRoutes = (routes = []) => {
44
44
  mappings: t.config.eventing.listener.mappings || [],
45
45
  }));
46
46
 
47
- const routeLayoutComponents = (route.tiles || [])
48
- .map((t) => ({
49
- componentName: `${route.name}-${t.title.replace(/[^0-9a-z]/gi, '')}-${t.componentType}`,
50
- }));
51
-
52
47
  acc.events = Array.from(new Set(acc.events.concat(routeEvents)));
53
48
  acc.listeners = acc.listeners.concat(routeListeners);
54
- acc.layoutComponents = acc.layoutComponents.concat(routeLayoutComponents);
55
49
  return acc;
56
50
  },
57
- { events: [], listeners: [], layoutComponents: [] },
51
+ { events: [], listeners: [] },
58
52
  );
59
53
 
60
54
  return aggregation;
61
55
  };
62
56
 
63
57
  const generateStore = (routesOrAggregation, { writeFileWithData }, framework) => {
64
- const { events = [], listeners = [], layoutComponents = [] } = Array.isArray(routesOrAggregation)
58
+ const { events = [], listeners = [] } = Array.isArray(routesOrAggregation)
65
59
  ? aggregateFromRoutes(routesOrAggregation)
66
60
  : (routesOrAggregation || {});
67
61
  const sourceTemplateDir = `../${DIR_TEMPLATE_BY_FRAMEWORK[framework]}`;
@@ -79,7 +73,7 @@ const generateStore = (routesOrAggregation, { writeFileWithData }, framework) =>
79
73
  // Write store.ts
80
74
  writeFileWithData(
81
75
  storeTarget,
82
- { events, listeners, layoutComponents },
76
+ { events, listeners },
83
77
  resolve(__dirname, storeTemplate),
84
78
  );
85
79
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "UI": "14.421.1",
2
+ "UI": "14.425.0",
3
3
  "GSF": "8.15.0",
4
4
  "Auth": "8.15.0"
5
5
  }
@@ -10,7 +10,7 @@ jobs:
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
12
  - name: Slack Notification
13
- uses: bryannice/gitactions-slack-notification@2.0.0
13
+ uses: youscan/gitactions-slack-notification@3.0.0
14
14
  env:
15
15
  SLACK_CHANNEL: 'platform-seeds'
16
16
  SLACK_INCOMING_WEBHOOK: ${{secrets.SLACK_WEBHOOK}}
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.11.0-prerelease.2](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0-prerelease.1...v5.11.0-prerelease.2) (2026-04-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * update FUI version [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 13373e5
9
+ * update FUI version [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#562) 9aa31ba
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * update slack notifications [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 19dd19a
15
+ * update slack notifications [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#563) 8f51a0b
16
+
17
+ ## [5.11.0-prerelease.1](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.10.4-prerelease.1...v5.11.0-prerelease.1) (2026-04-27)
18
+
19
+
20
+ ### Features
21
+
22
+ * replace foundation-layout with flex-layout in React [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#561) 13a521f
23
+ * replace foundation-layout with flex-layout in React templates [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 9c13246
24
+ * update FUI version [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 11096f0
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * hide close button in layout items [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 58dd5ec
30
+
3
31
  ## [5.10.4-prerelease.1](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.10.3...v5.10.4-prerelease.1) (2026-04-22)
4
32
 
5
33
 
@@ -33,6 +33,7 @@
33
33
  "test:e2e:debug": "genx test --e2e --debug",
34
34
  "test:e2e:ui": "genx test --e2e --interactive"
35
35
  },
36
+ "prettier": "@genesislcap/prettier-config",
36
37
  "dependencies": {
37
38
  "@microsoft/fast-react-wrapper": ">=0.3.25",
38
39
  "@ag-grid-community/client-side-row-model": "29.2.0",
@@ -67,6 +68,7 @@
67
68
  "@genesislcap/grid-tabulator": "{{versions.UI}}",
68
69
  "@genesislcap/web-core": "{{versions.UI}}",
69
70
  "@genesislcap/g2plot-chart": "{{versions.UI}}",
71
+ "flexlayout-react": "^0.8.19",
70
72
  "change-case": "^4.1.2",
71
73
  "tabulator-tables": "6.3.1",
72
74
  "history": "^5.3.0",
@@ -6,9 +6,11 @@ interface ProtectedRouteProps {
6
6
  }
7
7
 
8
8
  function ProtectedRoute({ children }: ProtectedRouteProps) {
9
+ const location = useLocation();
10
+
9
11
  if (!getUser().isAuthenticated) {
10
- const location = useLocation();
11
- return <Navigate to="/login" state={{ from: location }} replace />;
12
+ const loginState = { from: location };
13
+ return <Navigate to="/login" state={loginState} replace />;
12
14
  }
13
15
 
14
16
  return <>{children}</>;
@@ -1,5 +1,4 @@
1
1
  import { RouteLayouts } from './types/RouteLayouts';
2
- import { environment } from './environments/environment';
3
2
 
4
3
  export const routeLayouts: RouteLayouts = {
5
4
  '/login': 'blank',
@@ -10,7 +9,6 @@ export const AUTH_PATH = 'login';
10
9
  export const NOT_PERMITTED_PATH = 'not-permitted';
11
10
 
12
11
  export const API_DATA = {
13
- URL: environment.API_HOST,
14
12
  AUTH: {
15
13
  username: '', // provide login to a user in given environment
16
14
  password: '', // provide password to a user in given environment
@@ -8,9 +8,6 @@ declare module "react/jsx-runtime" {
8
8
  'rapid-design-system-provider': CustomElement;
9
9
  'client-app-login': CustomElement;
10
10
  'rapid-modal': CustomElement;
11
- 'rapid-layout': CustomElement;
12
- 'rapid-layout-region': CustomElement;
13
- 'rapid-layout-item': CustomElement;
14
11
  'foundation-header': CustomElement;
15
12
  }
16
13
  }
@@ -1,18 +1,16 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import React, { useEffect, useRef, useSyncExternalStore } from 'react';
2
2
  import { RouteObject, useNavigate, Outlet } from 'react-router-dom';
3
3
  import { configureDesignSystem, getNavItems } from '@genesislcap/foundation-ui';
4
- import {
5
- baseLayerLuminance,
6
- StandardLuminance,
7
- } from '@microsoft/fast-components';
4
+ import { baseLayerLuminance, StandardLuminance } from '@microsoft/fast-components';
8
5
  import styles from './DefaultLayout.module.css';
9
6
  import PBCElementsRenderer from '../../pbc/elementsRenderer';
10
7
  import { registerStylesTarget } from '../../pbc/utils';
11
8
  import * as designTokens from '../../styles/design-tokens.json';
12
9
  import { useRoutesContext } from '../../store/RoutesContext';
13
10
  import { useDocumentTitle } from '../../utils/useDocumentTitle';
14
- import { AUTH_PATH } from '../../config';
15
11
  import { LOGOUT_URL } from '@genesislcap/foundation-utils';
12
+ import { DI } from '@genesislcap/web-core';
13
+ import { Connect } from '@genesislcap/foundation-comms';
16
14
  import type { AppTargetId } from '@genesislcap/foundation-shell/app';
17
15
 
18
16
  // Stable target arrays so PBCElementsRenderer effect doesn't re-run on every parent re-render
@@ -29,16 +27,27 @@ type ExtendedRouteObject = RouteObject & {
29
27
  navItems?: any;
30
28
  };
31
29
  path: string;
32
- }
30
+ };
31
+
32
+ const connect = DI.getOrCreateDOMContainer().get(Connect);
33
+
34
+ const subscribe = (onChange: () => void) => {
35
+ const sub = connect.isConnected$?.subscribe(() => onChange());
36
+ return () => sub?.unsubscribe();
37
+ };
38
+
39
+ const getSnapshot = () => connect.isConnected;
33
40
 
34
41
  const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
35
42
  const navigate = useNavigate();
36
43
  const designSystemProviderRef = useRef<HTMLElement>(null);
37
44
  const routes = useRoutesContext() as ExtendedRouteObject[];
38
- const navItems = getNavItems(routes.flatMap((route) => ({
39
- path: route.path || '',
40
- navItems: route.data?.navItems,
41
- })));
45
+ const navItems = getNavItems(
46
+ routes.flatMap((route) => ({
47
+ path: route.path || '',
48
+ navItems: route.data?.navItems,
49
+ })),
50
+ );
42
51
 
43
52
  useDocumentTitle();
44
53
 
@@ -54,6 +63,8 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
54
63
  }
55
64
  };
56
65
 
66
+ const connected = useSyncExternalStore(subscribe, getSnapshot);
67
+
57
68
  useEffect(() => {
58
69
  if (designSystemProviderRef.current) {
59
70
  configureDesignSystem(designSystemProviderRef.current, designTokens);
@@ -61,38 +72,39 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
61
72
  registerStylesTarget(document.body, 'header');
62
73
  registerStylesTarget(document.body, 'content');
63
74
  }
64
-
65
- return () => {
66
- };
67
75
  }, []);
68
76
 
69
77
  const className = `${styles['default-layout']}`;
70
78
 
71
79
  return (
72
80
  <rapid-design-system-provider ref={designSystemProviderRef} class={className}>
73
- <PBCElementsRenderer target={TARGET_LAYOUT_START} />
74
- <foundation-header
81
+ {connected && (
82
+ <>
83
+ <PBCElementsRenderer target={TARGET_LAYOUT_START} />
84
+ <foundation-header
75
85
  {{#if headerLogoSrc}}
76
- logo-src="{{headerLogoSrc}}"
86
+ logo-src="{{headerLogoSrc}}"
77
87
  {{/if}}
78
- onluminance-icon-clicked={onLuminanceToggle}
79
- logout={async () => {
80
- await fetch(LOGOUT_URL);
81
- window.location.reload()
82
- }}
83
- show-luminance-toggle-button
84
- show-misc-toggle-button
85
- routeNavItems={navItems}
86
- navigateTo={(path: string) => navigate(path)}
87
- >
88
- <PBCElementsRenderer target={TARGET_HEADER_NAV} />
89
- </foundation-header>
88
+ onluminance-icon-clicked={onLuminanceToggle}
89
+ logout={async () => {
90
+ await fetch(LOGOUT_URL);
91
+ window.location.reload();
92
+ }}
93
+ show-luminance-toggle-button
94
+ show-misc-toggle-button
95
+ routeNavItems={navItems}
96
+ navigateTo={(path: string) => navigate(path)}
97
+ >
98
+ <PBCElementsRenderer target={TARGET_HEADER_NAV} />
99
+ </foundation-header>
100
+ </>
101
+ )}
90
102
  <section className={styles['content']}>
91
- <PBCElementsRenderer target={TARGET_CONTENT_START} />
103
+ {connected && <PBCElementsRenderer target={TARGET_CONTENT_START} />}
92
104
  <Outlet />
93
- <PBCElementsRenderer target={TARGET_CONTENT} />
105
+ {connected && <PBCElementsRenderer target={TARGET_CONTENT} />}
94
106
  </section>
95
- <PBCElementsRenderer target={TARGET_LAYOUT_END} />
107
+ {connected && <PBCElementsRenderer target={TARGET_LAYOUT_END} />}
96
108
  </rapid-design-system-provider>
97
109
  );
98
110
  };
@@ -6,6 +6,8 @@ import { registerPBCs } from './pbc/utils';
6
6
  import { createLogger } from '@genesislcap/foundation-logger';
7
7
 
8
8
  import './styles/styles.css'
9
+ import 'flexlayout-react/style/dark.css'
10
+ import './styles/flexlayout-theme.css'
9
11
 
10
12
  const logger = createLogger('main');
11
13
 
@@ -13,12 +13,17 @@ interface LocationState {
13
13
  /**
14
14
  * Configure the micro frontend
15
15
  */
16
- export const configureFoundationLogin = ({navigate, location}: { navigate: NavigateFunction, location: RouterLocation<LocationState>}) => {
16
+ export const configureFoundationLogin = ({
17
+ navigate,
18
+ location,
19
+ }: {
20
+ navigate: NavigateFunction;
21
+ location: RouterLocation<LocationState>;
22
+ }) => {
17
23
  const baseElement = document.querySelector('base');
18
24
  const basePath = baseElement?.getAttribute('href') || '';
19
25
  const connect = DI.getOrCreateDOMContainer().get(Connect);
20
26
 
21
-
22
27
  configure({
23
28
  name: 'client-app-login',
24
29
  omitRoutes: ['request-account', 'forgot-password'],
@@ -34,6 +39,5 @@ export const configureFoundationLogin = ({navigate, location}: { navigate: Navig
34
39
  const from = location.state?.from?.pathname || '/';
35
40
  navigate(from, { replace: true });
36
41
  },
37
- })
38
- }
39
-
42
+ });
43
+ };
@@ -1,4 +1,3 @@
1
- import { foundationLayoutComponents } from '@genesislcap/foundation-layout';
2
1
  import { getApp } from '@genesislcap/foundation-shell/app';
3
2
  import { g2plotChartsComponents } from '@genesislcap/g2plot-chart';
4
3
  import * as rapidDesignSystem from '@genesislcap/rapid-design-system';
@@ -41,7 +40,6 @@ export async function registerComponents() {
41
40
  }),
42
41
  rapidGridComponents,
43
42
  g2plotChartsComponents,
44
- foundationLayoutComponents,
45
43
  );
46
44
 
47
45
  configureHeader({
@@ -0,0 +1,102 @@
1
+ /* Genesis design token overrides for flexlayout dark theme */
2
+ .flexlayout__layout {
3
+ --color-text: var(--neutral-foreground-rest, #eeeeee);
4
+ --color-background: var(--neutral-layer-1, #141414);
5
+
6
+ /* Layered grays mapped to Genesis neutral layers */
7
+ --color-1: var(--neutral-layer-4, #1e1e1e);
8
+ --color-2: #252525;
9
+ --color-3: #2d2d2d;
10
+ --color-4: var(--neutral-stroke-rest, #3a3a3a);
11
+ --color-5: #484848;
12
+ --color-6: #555555;
13
+
14
+ /* Drag indicators use accent colour */
15
+ --color-drag1: var(--accent-fill-rest, #00bcd4);
16
+ --color-drag2: var(--accent-fill-rest, #00bcd4);
17
+ --color-drag1-background: rgba(0, 188, 212, 0.12);
18
+ --color-drag2-background: rgba(0, 188, 212, 0.08);
19
+
20
+ --font-size: 13px;
21
+
22
+ /* Tab bar / tabset */
23
+ --color-tabset-background: var(--neutral-layer-1, #141414);
24
+ --color-tabset-background-selected: var(--neutral-layer-1, #141414);
25
+ --color-tabset-background-maximized: var(--neutral-layer-4, #1e1e1e);
26
+ --color-tabset-divider-line: var(--neutral-stroke-rest, #3a3a3a);
27
+ --color-tabset-header-background: var(--neutral-layer-4, #1e1e1e);
28
+ --color-tabset-header: var(--neutral-foreground-rest, #eeeeee);
29
+
30
+ /* Active / inactive tabs */
31
+ --color-tab-content: var(--neutral-layer-1, #141414);
32
+ --color-tab-selected: var(--neutral-foreground-rest, #ffffff);
33
+ --color-tab-selected-background: var(--neutral-layer-1, #141414);
34
+ --color-tab-unselected: var(--neutral-foreground-rest, #a0a0a0);
35
+ --color-tab-unselected-background: transparent;
36
+
37
+ /* Splitters */
38
+ --color-splitter: var(--neutral-stroke-rest, #3a3a3a);
39
+ --color-splitter-hover: var(--accent-fill-rest, #00bcd4);
40
+ --color-splitter-drag: var(--accent-fill-rest, #00bcd4);
41
+
42
+ /* Drag-drop rectangle */
43
+ --color-drag-rect-border: var(--accent-fill-rest, rgba(0, 188, 212, 0.6));
44
+ --color-drag-rect-background: rgba(0, 0, 0, 0.45);
45
+ --color-drag-rect: var(--neutral-foreground-rest, #eeeeee);
46
+
47
+ /* Overflow / icon */
48
+ --color-overflow: var(--neutral-foreground-rest, #a0a0a0);
49
+ --color-icon: var(--neutral-foreground-rest, #a0a0a0);
50
+
51
+ /* Popup menu */
52
+ --color-popup-border: var(--neutral-stroke-rest, #3a3a3a);
53
+ --color-popup-unselected: var(--neutral-foreground-rest, #eeeeee);
54
+ --color-popup-unselected-background: var(--neutral-layer-4, #1e1e1e);
55
+ --color-popup-selected: var(--neutral-foreground-rest, #ffffff);
56
+ --color-popup-selected-background: var(--neutral-stroke-rest, #3a3a3a);
57
+
58
+ --color-toolbar-button-hover: var(--neutral-fill-stealth-active, rgba(255, 255, 255, 0.08));
59
+
60
+ --color-edge-marker: var(--accent-fill-rest, #00bcd4);
61
+ --color-edge-icon: var(--neutral-foreground-rest, #eeeeee);
62
+ }
63
+
64
+ /* Remove dark theme gradients — use flat Genesis layer colours */
65
+ .flexlayout__tabset-selected {
66
+ background-image: none !important;
67
+ }
68
+
69
+ .flexlayout__tabset-maximized {
70
+ background-image: none !important;
71
+ }
72
+
73
+ /* Active tab: accent underline matching Genesis style */
74
+ .flexlayout__tab_button--selected {
75
+ position: relative;
76
+ color: var(--neutral-foreground-rest, #ffffff) !important;
77
+ background-color: var(--neutral-layer-1, #141414) !important;
78
+ }
79
+
80
+ .flexlayout__tab_button--selected::after {
81
+ content: "";
82
+ position: absolute;
83
+ bottom: 0;
84
+ left: 0;
85
+ width: 100%;
86
+ height: 2px;
87
+ background-color: var(--accent-fill-rest, #00bcd4);
88
+ z-index: 10;
89
+ }
90
+
91
+ /* Inactive tab hover */
92
+ .flexlayout__tab_button--unselected:hover {
93
+ color: var(--neutral-foreground-rest, #e0e0e0) !important;
94
+ background-color: var(--neutral-fill-stealth-active, rgba(255, 255, 255, 0.06)) !important;
95
+ }
96
+
97
+ /* Remove border-radius from dark theme tab overrides */
98
+ .flexlayout__tab_top,
99
+ .flexlayout__tab_bottom {
100
+ border-radius: 0 !important;
101
+ box-shadow: none !important;
102
+ }
@@ -1,11 +1,10 @@
1
1
  import { isDev } from '@genesislcap/foundation-utils';
2
2
  import pkg from '../../package.json';
3
3
 
4
- // returning null disables caching
5
- // returning an id (as long as it's not null) enables caching
6
- export const persistLayout = (id: string) => {
4
+ // Returns null to disable persistence in dev unless PERSIST_LAYOUT_IN_DEV=true
5
+ export const getFlexLayoutStorageKey = (id: string): string | null => {
7
6
  if (isDev() && (pkg as any)?.config.PERSIST_LAYOUT_IN_DEV !== true) {
8
7
  return null;
9
8
  }
10
- return id;
9
+ return `flexlayout_${id}`;
11
10
  };
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": "5.10.4-prerelease.1",
4
+ "version": "5.11.0-prerelease.2",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
7
7
  "release": "semantic-release"
package/.genx/.prettierrc DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "singleQuote": true,
3
- "trailingComma": "all",
4
- "bracketSpacing": true
5
- }
@@ -1,8 +0,0 @@
1
- export const environment = {
2
- production: true,
3
- PORT: 4200,
4
- ENABLE_SSO: {{enableSSO}},
5
- HOST: 'localhost',
6
- PROTOCOL: 'http',
7
- BUILDER: 'webpack',
8
- };
@@ -1,9 +0,0 @@
1
- export const environment = {
2
- production: false,
3
- API_HOST: '{{apiHost}}',
4
- PORT: 4200,
5
- ENABLE_SSO: {{enableSSO}},
6
- HOST: 'localhost',
7
- PROTOCOL: 'http',
8
- BUILDER: 'webpack'
9
- };
@@ -1,59 +0,0 @@
1
- const ERROR_PREFIX = 'Golden layout helper - ';
2
-
3
- export const setComponentItemsMap = (
4
- layoutNativeElement: any,
5
- componentMapInstance: Map<string, any>,
6
- ): (() => void) => {
7
- if (!layoutNativeElement || !layoutNativeElement.layout) {
8
- throw new Error(`${ERROR_PREFIX} - layout is not defined`);
9
- }
10
-
11
- const componentSetter = ({ _target: componentItem }: any) => {
12
- componentMapInstance.set(componentItem.componentName, componentItem);
13
- };
14
-
15
- layoutNativeElement.layout.on('componentCreated', componentSetter);
16
-
17
- return () => layoutNativeElement.layout.off('componentCreated', componentSetter);
18
- };
19
-
20
- const getElementRoot = (componentInstance: any): any => {
21
- if (componentInstance) {
22
- const { _element } = componentInstance;
23
- return _element;
24
- }
25
-
26
- throw new Error(`${ERROR_PREFIX} - component instance is not defined`);
27
- };
28
-
29
- export const getElementsBySelectorFromComponent = (
30
- componentInstance: any,
31
- selectorValue: string,
32
- ): any => {
33
- const element = getElementRoot(componentInstance);
34
- return element.querySelectorAll(`[data-selector=${selectorValue}]`);
35
- };
36
-
37
- export const getElementBySelectorFromComponent = (
38
- componentInstance: any,
39
- selector: string,
40
- ): any => {
41
- return getElementsBySelectorFromComponent(componentInstance, selector)[0];
42
- };
43
-
44
- export const getElementsByTagFromComponent = (
45
- componentInstance: any,
46
- tag: string,
47
- ): any => {
48
- const element = getElementRoot(componentInstance);
49
- return element.getElementsByTagName(tag);
50
- };
51
-
52
- export const getElementByTagFromComponent = (
53
- componentInstance: any,
54
- tag: string,
55
- ): any => {
56
- return getElementsByTagFromComponent(componentInstance, tag)[0];
57
- };
58
-
59
-