@genesislcap/blank-app-seed 5.7.0-prerelease.2 → 5.7.0-prerelease.20

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 (77) hide show
  1. package/.genx/configure.js +114 -0
  2. package/.genx/package.json +5 -2
  3. package/.genx/prompts/api.js +25 -0
  4. package/.genx/prompts/ui.js +12 -0
  5. package/.genx/prompts.js +8 -2
  6. package/.genx/static.js +1 -0
  7. package/.genx/templates/angular/component/component.hbs +31 -3
  8. package/.genx/templates/angular/entityManager.hbs +17 -6
  9. package/.genx/templates/angular/grid.hbs +5 -2
  10. package/.genx/templates/angular/slices/eventing.slice.hbs +41 -0
  11. package/.genx/templates/angular/store.hbs +16 -0
  12. package/.genx/templates/react/component/component.hbs +84 -15
  13. package/.genx/templates/react/entityManager.hbs +11 -17
  14. package/.genx/templates/react/grid.hbs +5 -9
  15. package/.genx/templates/react/gridLayout.hbs +4 -4
  16. package/.genx/templates/react/horizontalLayout.hbs +1 -1
  17. package/.genx/templates/react/route.hbs +12 -1
  18. package/.genx/templates/react/slices/eventing.slice.hbs +39 -0
  19. package/.genx/templates/react/store.hbs +24 -0
  20. package/.genx/templates/react/tabsLayout.hbs +1 -1
  21. package/.genx/templates/web-components/component/component.hbs +26 -0
  22. package/.genx/templates/web-components/entityManager.hbs +17 -9
  23. package/.genx/templates/web-components/grid.hbs +13 -5
  24. package/.genx/templates/web-components/slices/eventing.slice.hbs +44 -0
  25. package/.genx/templates/web-components/store.hbs +18 -0
  26. package/.genx/utils/fontUtils.js +75 -0
  27. package/.genx/utils/formatRouteData.js +5 -0
  28. package/.genx/utils/generateRoute.js +1 -1
  29. package/.genx/utils/generateStore.js +98 -0
  30. package/.genx/utils/index.js +4 -0
  31. package/.genx/versions.json +3 -3
  32. package/.github/CODEOWNERS +1 -1
  33. package/.github/pull_request_template.md +6 -0
  34. package/.github/workflows/upgrade.yml +0 -1
  35. package/CHANGELOG.md +160 -0
  36. package/client-tmp/angular/package.json +5 -0
  37. package/client-tmp/angular/src/app/app.component.ts +2 -31
  38. package/client-tmp/angular/src/app/app.module.ts +3 -0
  39. package/client-tmp/angular/src/app/layouts/default/default.layout.css +8 -0
  40. package/client-tmp/angular/src/app/layouts/default/default.layout.html +3 -0
  41. package/client-tmp/angular/src/styles/design-tokens.json +1 -1
  42. package/client-tmp/angular/src/styles/styles.css +15 -1
  43. package/client-tmp/react/index.html +0 -1
  44. package/client-tmp/react/package.json +16 -28
  45. package/client-tmp/react/public/index.html +3 -1
  46. package/client-tmp/react/src/App.tsx +12 -29
  47. package/client-tmp/react/src/components/routes/AppRoutes.tsx +4 -4
  48. package/client-tmp/react/src/config.ts +1 -1
  49. package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +1 -1
  50. package/client-tmp/react/src/layouts/default/DefaultLayout.module.css +8 -0
  51. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +7 -4
  52. package/client-tmp/react/src/main.tsx +1 -1
  53. package/client-tmp/react/src/pages/AuthPage/AuthPage.tsx +1 -1
  54. package/client-tmp/react/src/pages/NotFoundPage/NotFoundPage.tsx +1 -1
  55. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +1 -1
  56. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +1 -1
  57. package/client-tmp/react/src/pbc/container.tsx +1 -1
  58. package/client-tmp/react/src/share/foundation-login.ts +2 -2
  59. package/client-tmp/react/src/store/RoutesContext.tsx +6 -6
  60. package/client-tmp/react/src/styles/design-tokens.json +1 -1
  61. package/client-tmp/react/src/styles/styles.css +15 -1
  62. package/client-tmp/react/src/utils/getLayoutNameByRoute.ts +1 -1
  63. package/client-tmp/react/src/utils/goldenLayout.helper.ts +59 -0
  64. package/client-tmp/react/test/e2e/fixture.ts +1 -1
  65. package/client-tmp/web-components/package.json +4 -0
  66. package/client-tmp/web-components/src/layouts/default.ts +12 -2
  67. package/client-tmp/web-components/src/main/main.css +12 -0
  68. package/client-tmp/web-components/src/main/main.template.ts +0 -1
  69. package/client-tmp/web-components/src/main/main.ts +1 -34
  70. package/client-tmp/web-components/src/styles/design-tokens.json +1 -1
  71. package/client-tmp/web-components/src/styles/typography.ts +6 -4
  72. package/package.json +1 -1
  73. package/client-tmp/angular/src/app/store/store.ts +0 -34
  74. package/client-tmp/react/lint-css.ts +0 -19
  75. package/client-tmp/react/src/services/store.service.ts +0 -48
  76. package/client-tmp/react/vite.config.ts +0 -70
  77. package/client-tmp/web-components/src/store/store.ts +0 -29
@@ -3,7 +3,7 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
3
3
  {{/if}}
4
4
  <rapid-grid-pro
5
5
  header-case-type="capitalCase"
6
- {{#if config.useOnlyTemplateCols~}}
6
+ {{#if config.useOnlyTemplateCols}}
7
7
  only-template-col-defs
8
8
  {{/if}}
9
9
  enable-row-flashing
@@ -11,16 +11,12 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
11
11
  >
12
12
  <grid-pro-genesis-datasource
13
13
  resource-name="{{config.resourceName}}"
14
- {{#if config.snapshot~}}
14
+ {{#if config.snapshot}}
15
15
  isSnapshot="{{config.snapshot}}"
16
16
  {{/if}}
17
- {{#if config.reqrep~}}
18
- polling-interval="5000"
19
- request-auto-setup="false"
20
- {{/if}}
21
- {{#if config.gridOptions~}}
22
- deferredGridOptions={deferredGridOptions}
23
- {{/if}}
17
+ {{#ifAny config.gridOptions config.eventing.publishEventName}}
18
+ deferredGridOptions={gridOptions}
19
+ {{/ifAny}}
24
20
  ></grid-pro-genesis-datasource>
25
21
  {{#if config.gridOptions}}
26
22
  {gridOptionsTile?.columnDefs?.map((columnDef, index) => (
@@ -2,12 +2,12 @@
2
2
  <rapid-layout-region type="vertical">
3
3
  {{#each route.tiles}}
4
4
  {{#ifEquals @index 0}}
5
- <rapid-layout-item title="{{this.title}}">
5
+ <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
6
6
  <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
7
7
  </rapid-layout-item>
8
8
  {{/ifEquals}}
9
9
  {{#ifEquals @index 1}}
10
- <rapid-layout-item title="{{this.title}}">
10
+ <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
11
11
  <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
12
12
  </rapid-layout-item>
13
13
  {{/ifEquals}}
@@ -16,12 +16,12 @@
16
16
  <rapid-layout-region type="vertical">
17
17
  {{#each route.tiles}}
18
18
  {{#ifEquals @index 2}}
19
- <rapid-layout-item title="{{this.title}}">
19
+ <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
20
20
  <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
21
21
  </rapid-layout-item>
22
22
  {{/ifEquals}}
23
23
  {{#ifEquals @index 3}}
24
- <rapid-layout-item title="{{this.title}}">
24
+ <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
25
25
  <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
26
26
  </rapid-layout-item>
27
27
  {{/ifEquals}}
@@ -1,6 +1,6 @@
1
1
  <rapid-layout-region>
2
2
  {{#each route.tiles}}
3
- <rapid-layout-item title="{{this.title}}">
3
+ <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
4
4
  <{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
5
5
  </rapid-layout-item>
6
6
  {{/each}}
@@ -1,14 +1,25 @@
1
1
  import './{{pascalCase route.name}}.css';
2
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';
3
6
  {{#each route.tiles}}
4
7
  import { {{pascalCase this.componentName}} } from './{{pascalCase this.title}}{{pascalCase this.componentType}}';
5
8
  {{/each}}
6
9
 
7
10
  const {{pascalCase route.name}} = () => {
11
+ const layoutRef = useRef<any>(null);
12
+
13
+ useEffect(() => {
14
+ if (layoutRef.current) {
15
+ setComponentItemsMap(layoutRef.current, layoutComponentsMap);
16
+ }
17
+ }, []);
18
+
8
19
  return (
9
20
  <section className="{{kebabCase route.name}}-page">
10
21
  {{#if route.tiles}}
11
- <rapid-layout auto-save-key={persistLayout('{{route.layoutKey}}')}>
22
+ <rapid-layout ref={layoutRef} auto-save-key={persistLayout('{{route.layoutKey}}')}>
12
23
  {{> (lookup ./route 'layoutType') }}
13
24
  </rapid-layout>
14
25
  {{else}}
@@ -0,0 +1,39 @@
1
+ import { CriteriaBuilder, ExpressionBuilder, Serialisers } from '@genesislcap/foundation-criteria';
2
+ import { createSlice } from '@genesislcap/foundation-redux';
3
+
4
+ export const eventingSlice = createSlice({
5
+ name: 'eventing',
6
+ initialState: {
7
+ {{#each events}}
8
+ {{this}}: null{{#unless @last}},{{/unless}}
9
+ {{/each}}
10
+ },
11
+ reducers: {
12
+ {{#each events}}
13
+ publish{{pascalCase this}}: (state, action) => {
14
+ state.{{this}} = action.payload;
15
+ }{{#unless @last}},{{/unless}}
16
+ {{/each}}
17
+ },
18
+ selectors: {
19
+ {{#each listeners}}
20
+ getCriteriaFor{{pascalCase tileName}}: (state) => {
21
+ const criteriaBuilder = new CriteriaBuilder();
22
+ const data = state.{{eventName}};
23
+ if (!data) return '';
24
+
25
+ {{#each mappings}}
26
+ if (data.{{sourceField}}) {
27
+ criteriaBuilder.And(new ExpressionBuilder()
28
+ .withField('{{targetField}}')
29
+ .withValue(data.{{sourceField}})
30
+ .withSerialiser(Serialisers.EQ)
31
+ .build());
32
+ }
33
+ {{/each}}
34
+
35
+ return criteriaBuilder.build();
36
+ }{{#unless @last}},{{/unless}}
37
+ {{/each}}
38
+ },
39
+ });
@@ -0,0 +1,24 @@
1
+ import { createStore } from '@genesislcap/foundation-redux';
2
+ import { eventingSlice } from './slices/eventing.slice';
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
+ export const reduxStore = createStore([eventingSlice], {
13
+ eventing: {
14
+ {{#each events}}
15
+ {{this}}: null{{#unless @last}},{{/unless}}
16
+ {{/each}}
17
+ },
18
+ });
19
+
20
+ export const { store, actions, selectors } = reduxStore;
21
+
22
+ export type Store = typeof store;
23
+ export type Actions = typeof actions;
24
+ export type Selectors = typeof selectors;
@@ -1,6 +1,6 @@
1
1
  <rapid-layout-region type="tabs">
2
2
  {{#each route.tiles}}
3
- <rapid-layout-item title="{{this.title}}">
3
+ <rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
4
4
  {{> (lookup . 'type') }}
5
5
  </rapid-layout-item>
6
6
  {{/each}}
@@ -1,6 +1,15 @@
1
1
  import { Connect } from '@genesislcap/foundation-comms';
2
2
  import { User } from '@genesislcap/foundation-user';
3
3
  import { customElement, GenesisElement, observable } from '@genesislcap/web-core';
4
+ {{#if tile.config.gridOptions}}
5
+ import { gridOptions as baseGridOptions } from './{{kebabCase tile.title}}.gridOptions';
6
+ {{/if}}
7
+ {{#if tile.config.eventing.publishEventName}}
8
+ import { actions } from '../../../store/store';
9
+ {{/if}}
10
+ {{#if tile.config.eventing.listener}}
11
+ import { selectors } from '../../../store/store';
12
+ {{/if}}
4
13
  import { {{pascalCase tile.title}}Styles as styles } from './{{kebabCase tile.title}}.styles';
5
14
  import { {{pascalCase tile.title}}Template as template } from './{{kebabCase tile.title}}.template';
6
15
  {{#if tile.config.columns}}
@@ -31,4 +40,21 @@ export class {{pascalCase tile.componentName}} extends GenesisElement {
31
40
  ...columnDefs,
32
41
  ];
33
42
  {{/if}}
43
+ {{#if tile.config.eventing.publishEventName}}
44
+ public handleRowSelection = (e: any) => {
45
+ const selectedRows = e.api.getSelectedRows();
46
+ if (!selectedRows || !selectedRows.length) {
47
+ actions.eventing.publish{{pascalCase tile.config.eventing.publishEventName}}(null);
48
+ return;
49
+ }
50
+ const { TIMESTAMP, RECORD_ID, ROW_REF, ...data } = selectedRows[0];
51
+ actions.eventing.publish{{pascalCase tile.config.eventing.publishEventName}}(data);
52
+ };
53
+ {{/if}}
54
+ {{#if tile.config.eventing.listener}}
55
+ getCriteria() {
56
+ const c = selectors.eventing.getCriteriaFor{{pascalCase tile.title}}();
57
+ return c || '';
58
+ }
59
+ {{/if}}
34
60
  }
@@ -25,15 +25,23 @@ ${whenElse(
25
25
  {{#if config.deleteEvent}}
26
26
  deleteEvent="${(x) => getViewUpdateRightComponent(x.user, '{{config.permissions.updateRight}}', '{{ config.deleteEvent }}')}"
27
27
  {{/if}}
28
- {{#if config.gridOptions}}
29
- :gridOptions=${() => gridOptions }
30
- {{/if}}
31
- {{#if config.snapshot}}
32
- :datasourceConfig=${() => ({isSnapshot: {{ config.snapshot }} })}
33
- {{/if}}
34
- {{#if config.reqrep}}
35
- :datasourceConfig=${() => ({pollingInterval: 5000, requestAutoSetup: false})}
36
- {{/if}}
28
+ {{#ifAny config.gridOptions config.eventing.publishEventName}}
29
+ :gridOptions=${(x) => ({
30
+ {{#if config.gridOptions}}
31
+ ...gridOptions,{{/if}}{{#if config.eventing.publishEventName}}
32
+ onSelectionChanged: x.handleRowSelection,
33
+ {{/if}}
34
+ })}
35
+ {{/ifAny}}
36
+ {{#ifAny config.reqrep config.eventing.listener}}
37
+ :datasourceConfig=${(x) => ({
38
+ {{#if config.reqrep}}
39
+ pollingInterval: 5000,
40
+ requestAutoSetup: false,{{/if}}{{#if config.eventing.listener}}
41
+ criteria: x.getCriteria(),
42
+ {{/if}}
43
+ })}
44
+ {{/ifAny}}
37
45
  {{#if config.entityName}}
38
46
  entityLabel="{{ config.entityName }}"
39
47
  {{/if}}
@@ -9,16 +9,24 @@ ${whenElse(
9
9
  >
10
10
  <grid-pro-genesis-datasource
11
11
  resource-name="{{config.resourceName}}"
12
- {{#if config.snapshot}}
13
- isSnapshot="{{config.snapshot}}"
14
- {{/if}}
15
12
  {{#if config.reqrep}}
16
13
  request-auto-setup="false"
17
14
  polling-interval="5000"
18
15
  {{/if}}
19
- {{#if config.gridOptions}}
20
- :deferredGridOptions=${() => ({ onRowClicked: gridOptions?.onRowClicked })}
16
+ {{#if config.eventing.listener}}
17
+ criteria=${(x) => x.getCriteria()}
21
18
  {{/if}}
19
+ {{#ifAny config.gridOptions config.eventing.publishEventName}}
20
+ :deferredGridOptions=${(x) => ({
21
+ {{#if config.gridOptions}}
22
+ onRowClicked: gridOptions?.onRowClicked,
23
+ {{/if}}
24
+ {{#if config.eventing.publishEventName}}
25
+ onSelectionChanged: x.handleRowSelection,
26
+ rowSelection: 'single',
27
+ {{/if}}
28
+ })}
29
+ {{/ifAny}}
22
30
  ></grid-pro-genesis-datasource>
23
31
  {{#if config.gridOptions}}
24
32
  ${repeat(
@@ -0,0 +1,44 @@
1
+ import { CriteriaBuilder, ExpressionBuilder, Serialisers } from '@genesislcap/foundation-criteria';
2
+ import { createSlice } from '@genesislcap/foundation-redux';
3
+
4
+ export const eventingSlice = createSlice({
5
+ name: 'eventing',
6
+ {{#if events}}
7
+ initialState: {
8
+ {{#each events}}
9
+ {{this}}: null{{#unless @last}},{{/unless}}
10
+ {{/each}} },
11
+ reducers: {
12
+ {{#each events}}
13
+ publish{{pascalCase this}}: (state, action) => {
14
+ state.{{this}} = action.payload;
15
+ }{{#unless @last}},{{/unless}}
16
+ {{/each}}
17
+ },
18
+ selectors: {
19
+ {{#each listeners}}
20
+ getCriteriaFor{{pascalCase tileName}}: (state) => {
21
+ const criteriaBuilder = new CriteriaBuilder();
22
+ const data = state.{{eventName}};
23
+ if (!data) return '';
24
+
25
+ {{#each mappings}}
26
+ if (data.{{sourceField}}) {
27
+ criteriaBuilder.And(new ExpressionBuilder()
28
+ .withField('{{targetField}}')
29
+ .withValue(data.{{sourceField}})
30
+ .withSerialiser(Serialisers.EQ)
31
+ .build());
32
+ }
33
+ {{/each}}
34
+
35
+ return criteriaBuilder.build();
36
+ }{{#unless @last}},{{/unless}}
37
+ {{/each}}
38
+ },
39
+ {{else}}
40
+ initialState: {},
41
+ reducers: {},
42
+ selectors: {},
43
+ {{/if}}
44
+ });
@@ -0,0 +1,18 @@
1
+ import { createStore } from '@genesislcap/foundation-redux';
2
+ import { eventingSlice } from './slices/eventing.slice';
3
+
4
+ export const { store, actions, selectors } = createStore([eventingSlice], {
5
+ {{#if events}}
6
+ eventing: {
7
+ {{#each events}}
8
+ {{this}}: null{{#unless @last}},{{/unless}}
9
+ {{/each}}
10
+ },
11
+ {{else}}
12
+ eventing: {},
13
+ {{/if}}
14
+ });
15
+
16
+ export type Store = typeof store;
17
+ export type Actions = typeof actions;
18
+ export type Selectors = typeof selectors;
@@ -0,0 +1,75 @@
1
+ const path = require('path');
2
+
3
+ // Extract font family from filename
4
+ // "Overpass-Regular.ttf" -> "Overpass"
5
+ // "Open-Sans-Bold.woff2" -> "Open Sans"
6
+ // "RobotoMono-Regular.ttf" -> "RobotoMono"
7
+ function extractFontFamily(filename) {
8
+ const baseName = path.basename(filename, path.extname(filename));
9
+
10
+ // Remove common weight/style keywords
11
+ const keywords = /(Regular|Bold|Light|Medium|Thin|Black|Heavy|Italic|Oblique|SemiBold|ExtraBold|UltraLight|DemiBold|ExtraLight|UltraBold)$/i;
12
+ const withoutKeywords = baseName.replace(keywords, '').replace(/[-_]$/, '');
13
+
14
+ // Convert hyphens between capitalized words to spaces (Open-Sans -> Open Sans)
15
+ // But keep single-word fonts intact (RobotoMono stays RobotoMono)
16
+ const parts = withoutKeywords.split(/[-_]/);
17
+ if (parts.length > 1 && parts.every(p => p && p[0] === p[0].toUpperCase())) {
18
+ return parts.join(' ').trim();
19
+ }
20
+
21
+ return withoutKeywords.trim() || baseName;
22
+ }
23
+
24
+ // Infer font weight from filename
25
+ function inferFontWeight(filename) {
26
+ const lower = filename.toLowerCase();
27
+ if (lower.includes('thin')) return '100';
28
+ if (lower.includes('extralight') || lower.includes('ultralight')) return '200';
29
+ if (lower.includes('light')) return '300';
30
+ if (lower.includes('regular') || lower.includes('normal')) return '400';
31
+ if (lower.includes('medium')) return '500';
32
+ if (lower.includes('semibold') || lower.includes('demibold')) return '600';
33
+ if (lower.includes('bold') && !lower.includes('semi') && !lower.includes('extra')) return '700';
34
+ if (lower.includes('extrabold') || lower.includes('ultrabold')) return '800';
35
+ if (lower.includes('black') || lower.includes('heavy')) return '900';
36
+ return '400'; // default
37
+ }
38
+
39
+ // Get font format from extension
40
+ function getFontFormat(filename) {
41
+ const ext = path.extname(filename).toLowerCase();
42
+ const formatMap = {
43
+ '.ttf': 'truetype',
44
+ '.otf': 'opentype',
45
+ '.woff': 'woff',
46
+ '.woff2': 'woff2',
47
+ '.eot': 'embedded-opentype'
48
+ };
49
+ return formatMap[ext] || 'truetype';
50
+ }
51
+
52
+ // Process font files and prepare data
53
+ function processFontFiles(customFonts) {
54
+ if (!customFonts || !customFonts.files || !customFonts.files.length) {
55
+ return null;
56
+ }
57
+
58
+ const files = Array.isArray(customFonts.files) ? customFonts.files : [customFonts.files];
59
+ const fontFamily = customFonts.family || extractFontFamily(files[0]);
60
+
61
+ const fontData = files.map(file => ({
62
+ filename: path.basename(file),
63
+ weight: inferFontWeight(file),
64
+ format: getFontFormat(file)
65
+ }));
66
+
67
+ return { fontFamily, fontData };
68
+ }
69
+
70
+ module.exports = {
71
+ extractFontFamily,
72
+ inferFontWeight,
73
+ getFontFormat,
74
+ processFontFiles,
75
+ };
@@ -43,6 +43,7 @@ const formatRouteData = (framework, route) => {
43
43
  uischema,
44
44
  columns,
45
45
  customEvents,
46
+ eventing,
46
47
  } = config;
47
48
 
48
49
  return {
@@ -59,6 +60,10 @@ const formatRouteData = (framework, route) => {
59
60
  uischema: formatJSONValue(uischema),
60
61
  columns: gridColumnsSerializer(columns),
61
62
  customEvents: formatCustomEvents(customEvents),
63
+ eventing: {
64
+ publishEventName: eventing?.publishEventName || null,
65
+ listener: eventing?.listener || null,
66
+ },
62
67
  },
63
68
  metadata: {
64
69
  ...metadata,
@@ -80,6 +80,6 @@ const generateRoute = (route, { changeCase, writeFileWithData }, framework) => {
80
80
  generateTile(tile, route, { changeCase, writeFileWithData }, framework);
81
81
  });
82
82
  }
83
- };
83
+ }
84
84
 
85
85
  module.exports = generateRoute;
@@ -0,0 +1,98 @@
1
+ const { resolve } = require('node:path');
2
+ const makeDirectory = require('./makeDirectory');
3
+ const {
4
+ FRAMEWORK_WEB_COMPONENTS_ALIAS,
5
+ FRAMEWORK_ANGULAR_ALIAS,
6
+ FRAMEWORK_REACT_ALIAS,
7
+ DIR_TEMPLATE_BY_FRAMEWORK,
8
+ } = require('../static');
9
+
10
+ const getStorePathByFramework = (framework) => {
11
+ if (framework === FRAMEWORK_WEB_COMPONENTS_ALIAS) {
12
+ return `../../client/src/store/store.ts`;
13
+ }
14
+ if (framework === FRAMEWORK_REACT_ALIAS) {
15
+ return `../../client/src/store/store.ts`;
16
+ }
17
+ if (framework === FRAMEWORK_ANGULAR_ALIAS) {
18
+ return `../../client/src/app/store/store.ts`;
19
+ }
20
+ return null;
21
+ };
22
+
23
+ const aggregateFromRoutes = (routes = []) => {
24
+ const aggregation = routes.reduce(
25
+ (acc, route) => {
26
+ const routeEvents = Array.from(
27
+ new Set(
28
+ (route.tiles || [])
29
+ .map((t) => t.config?.eventing?.publishEventName)
30
+ .filter(Boolean)
31
+ .concat(
32
+ (route.tiles || [])
33
+ .map((t) => t.config?.eventing?.listener?.eventName)
34
+ .filter(Boolean),
35
+ ),
36
+ ),
37
+ );
38
+
39
+ const routeListeners = (route.tiles || [])
40
+ .filter((t) => t.config?.eventing?.listener)
41
+ .map((t) => ({
42
+ tileName: t.title,
43
+ eventName: t.config.eventing.listener.eventName,
44
+ mappings: t.config.eventing.listener.mappings || [],
45
+ }));
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
+ acc.events = Array.from(new Set(acc.events.concat(routeEvents)));
53
+ acc.listeners = acc.listeners.concat(routeListeners);
54
+ acc.layoutComponents = acc.layoutComponents.concat(routeLayoutComponents);
55
+ return acc;
56
+ },
57
+ { events: [], listeners: [], layoutComponents: [] },
58
+ );
59
+
60
+ return aggregation;
61
+ };
62
+
63
+ const generateStore = (routesOrAggregation, { writeFileWithData }, framework) => {
64
+ const { events = [], listeners = [], layoutComponents = [] } = Array.isArray(routesOrAggregation)
65
+ ? aggregateFromRoutes(routesOrAggregation)
66
+ : (routesOrAggregation || {});
67
+ const sourceTemplateDir = `../${DIR_TEMPLATE_BY_FRAMEWORK[framework]}`;
68
+ const storeTemplate = `${sourceTemplateDir}/store.hbs`;
69
+ const sliceTemplate = `${sourceTemplateDir}/slices/eventing.slice.hbs`;
70
+
71
+ const storeTargetRelative = getStorePathByFramework(framework);
72
+ if (!storeTargetRelative) return;
73
+
74
+ const storeTarget = resolve(__dirname, storeTargetRelative);
75
+
76
+ // Ensure target directories exist
77
+ makeDirectory(resolve(__dirname, storeTarget.replace(/\/store\.ts$/, '')));
78
+
79
+ // Write store.ts
80
+ writeFileWithData(
81
+ storeTarget,
82
+ { events, listeners, layoutComponents },
83
+ resolve(__dirname, storeTemplate),
84
+ );
85
+
86
+ // Write slices/eventing.slice.ts next to store for all frameworks
87
+ const sliceTarget = storeTarget.replace(/store\.ts$/, 'slices/eventing.slice.ts');
88
+ makeDirectory(resolve(__dirname, sliceTarget.replace(/\/slices\/eventing\.slice\.ts$/, '/slices')));
89
+ writeFileWithData(
90
+ sliceTarget,
91
+ { events, listeners },
92
+ resolve(__dirname, sliceTemplate),
93
+ );
94
+ };
95
+
96
+ module.exports = generateStore;
97
+
98
+
@@ -4,12 +4,14 @@ const formatRouteData = require('./formatRouteData');
4
4
  const generateRoute = require('./generateRoute');
5
5
  const generateCsv = require('./generateCsv');
6
6
  const getCombinedCsvData = require('./getCombinedCsvData');
7
+ const generateStore = require('./generateStore');
7
8
  const makeDirectory = require('./makeDirectory');
8
9
  const normalizeFrameworkAlias = require('./normalizeFrameworkAlias');
9
10
  const parseJSONArgument = require('./parseJSONArgument');
10
11
  const registerPartials = require('./registerPartials');
11
12
  const validateRoute = require('./validateRoute');
12
13
  const validateFrameworkAlias = require('./validateFrameworkAlias');
14
+ const fontUtils = require('./fontUtils');
13
15
 
14
16
  module.exports = {
15
17
  deleteGradleWrappers,
@@ -18,10 +20,12 @@ module.exports = {
18
20
  generateRoute,
19
21
  generateCsv,
20
22
  getCombinedCsvData,
23
+ generateStore,
21
24
  makeDirectory,
22
25
  normalizeFrameworkAlias,
23
26
  parseJSONArgument,
24
27
  registerPartials,
25
28
  validateFrameworkAlias,
26
29
  validateRoute,
30
+ fontUtils,
27
31
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "UI": "14.333.0",
3
- "GSF": "8.13.12",
4
- "Auth": "8.13.3"
2
+ "UI": "14.347.1",
3
+ "GSF": "8.14.0-beta11",
4
+ "Auth": "8.14.0-beta11"
5
5
  }
@@ -1,3 +1,3 @@
1
1
  # for reference see https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file
2
2
 
3
- * @MrBrunoWolff @skawian @kievitsp @jacinpoz @khouari1 @rafaelnferreira @matteematt @SzymonZur @ArturKrasinski @jay-taylerson
3
+ * @skawian @kievitsp @jacinpoz @khouari1 @matteematt @SzymonZur @ArturKrasinski @jay-taylerson @Gareth-Spencer-Genesis @patrickoneill-genesis
@@ -33,6 +33,12 @@ Add file / directory pointers.
33
33
  rm -rf blankappseedtest && npx -y @genesislcap/genx@latest init blankappseedtest -x --ref %YOUR-BRANCH-NAME% --no-npm
34
34
  ```
35
35
 
36
+ ### Design tokens and header logo parameter handling test
37
+
38
+ ```
39
+ rm -rf blankappseedtest && npx -y @genesislcap/genx@latest init blankappseedtest -x --ref %YOUR-BRANCH-NAME% --no-npm --designTokens '{"design_tokens":{"color":{"accent":{"$value":"#0EAFE2","$type":"color"},"neutral":{"$value":"#7C909B","$type":"color"}},"fontFamily":{"bodyFont":{"$value":"Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif","$type":"fontFamily"}},"typography":{"baseFontSize":{"$value":"14px","$type":"dimension"},"baseLineHeight":{"$value":"20px","$type":"dimension"}},"mode":{"luminance":{"$value":1,"$type":"number"}},"style":{"density":{"$value":1,"$type":"number"},"borderRadius":{"$value":4,"$type":"number"},"strokeWidth":{"$value":1,"$type":"number"}},"space":{"designUnit":{"$value":4,"$type":"number"}}}}' --headerLogo ./logo.png
40
+ ```
41
+
36
42
  ### Route and CSV parameter handling test
37
43
 
38
44
  ```
@@ -34,7 +34,6 @@ jobs:
34
34
  steps:
35
35
  - uses: actions/checkout@v4
36
36
  with:
37
- ref: ${{ inputs.branch }}
38
37
  ref: ${{ inputs.branch }}
39
38
  token: ${{secrets.GH_USER_TOKEN}}
40
39