@genesislcap/blank-app-seed 5.11.0 → 5.12.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.
- package/.genx/package.json +1 -1
- package/.genx/templates/react/component/component.column.defs.hbs +6 -1
- package/.genx/templates/react/component/component.gridOptions.hbs +6 -1
- package/.genx/templates/react/route.hbs +9 -1
- package/.genx/templates/react/slices/eventing.slice.hbs +2 -0
- package/.genx/utils/formatRouteData.js +11 -0
- package/.genx/versions.json +3 -3
- package/CHANGELOG.md +20 -15
- package/client-tmp/react/src/App.tsx +2 -4
- package/client-tmp/react/src/components/error-boundary/ErrorBoundary.tsx +250 -0
- package/client-tmp/react/src/components/error-boundary/ErrorBoundaryStyles.ts +161 -0
- package/client-tmp/react/src/components/routes/AppRoutes.tsx +2 -0
- package/client-tmp/react/src/main.tsx +4 -1
- package/client-tmp/react/src/store/RoutesContext.tsx +3 -2
- package/client-tmp/react/src/types/AppRoute.ts +15 -0
- package/client-tmp/react/src/utils/index.ts +0 -1
- package/package.json +1 -1
- package/client-tmp/react/src/services/connect.service.ts +0 -26
- package/client-tmp/react/src/utils/setApiHost.ts +0 -9
package/.genx/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{{#if tile.config.columns~}}
|
|
2
2
|
import { ColDef } from '@ag-grid-community/core';
|
|
3
3
|
{{/if}}
|
|
4
|
-
|
|
4
|
+
{{#if tile.config.hasNumberFormatter~}}
|
|
5
|
+
import { getNumberFormatter } from '@genesislcap/foundation-utils';
|
|
6
|
+
{{/if}}
|
|
7
|
+
{{#if tile.config.hasDateFormatter~}}
|
|
8
|
+
import { getDateFormatter } from '@genesislcap/foundation-utils';
|
|
9
|
+
{{/if}}
|
|
5
10
|
{{#if route.FDC3EventHandlersEnabled~}}
|
|
6
11
|
import { sendEventOnChannel } from '../../../utils';
|
|
7
12
|
{{/if}}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{{#if tile.config.gridOptions~}}
|
|
2
2
|
import { GridOptionsConfig } from '@genesislcap/rapid-grid-pro';
|
|
3
3
|
{{/if}}
|
|
4
|
-
|
|
4
|
+
{{#if tile.config.hasNumberFormatter~}}
|
|
5
|
+
import { getNumberFormatter } from '@genesislcap/foundation-utils';
|
|
6
|
+
{{/if}}
|
|
7
|
+
{{#if tile.config.hasDateFormatter~}}
|
|
8
|
+
import { getDateFormatter } from '@genesislcap/foundation-utils';
|
|
9
|
+
{{/if}}
|
|
5
10
|
{{#if route.FDC3EventHandlersEnabled~}}
|
|
6
11
|
import { sendEventOnChannel } from '../../../utils';
|
|
7
12
|
{{/if}}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useRef } from 'react';
|
|
2
2
|
import { Layout, Model, TabNode } from 'flexlayout-react';
|
|
3
3
|
import { getFlexLayoutStorageKey } from '../../utils/layout';
|
|
4
|
+
import { TileErrorBoundary } from '../../components/error-boundary/ErrorBoundary';
|
|
4
5
|
{{#each route.tiles}}
|
|
5
6
|
import { {{pascalCase this.componentName}} } from './{{pascalCase this.title}}{{pascalCase this.componentType}}';
|
|
6
7
|
{{/each}}
|
|
@@ -31,7 +32,14 @@ const {{pascalCase route.name}} = () => {
|
|
|
31
32
|
|
|
32
33
|
const factory = (node: TabNode) => {
|
|
33
34
|
const Component = componentMap[node.getComponent() ?? ''];
|
|
34
|
-
return Component ?
|
|
35
|
+
return Component ? (
|
|
36
|
+
<TileErrorBoundary
|
|
37
|
+
title={node.getName() ?? node.getComponent() ?? ''}
|
|
38
|
+
tileRegistration={node.getComponent() ?? ''}
|
|
39
|
+
>
|
|
40
|
+
<Component />
|
|
41
|
+
</TileErrorBoundary>
|
|
42
|
+
) : null;
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
const onModelChange = (model: Model) => {
|
|
@@ -47,6 +47,11 @@ const formatRouteData = (framework, route) => {
|
|
|
47
47
|
eventing,
|
|
48
48
|
} = config;
|
|
49
49
|
|
|
50
|
+
const hasFormatter = (name, colsOrOpts) => {
|
|
51
|
+
const cols = Array.isArray(colsOrOpts) ? colsOrOpts : colsOrOpts?.columns;
|
|
52
|
+
return cols?.some((col) => col.valueFormatter?.name === name);
|
|
53
|
+
};
|
|
54
|
+
|
|
50
55
|
return {
|
|
51
56
|
...tile,
|
|
52
57
|
componentName,
|
|
@@ -61,6 +66,12 @@ const formatRouteData = (framework, route) => {
|
|
|
61
66
|
filterFormUiSchema: formatJSONValue(filterFormUiSchema),
|
|
62
67
|
uischema: formatJSONValue(uischema),
|
|
63
68
|
columns: gridColumnsSerializer(columns),
|
|
69
|
+
hasNumberFormatter:
|
|
70
|
+
hasFormatter('getNumberFormatter', columns) ||
|
|
71
|
+
hasFormatter('getNumberFormatter', gridOptions),
|
|
72
|
+
hasDateFormatter:
|
|
73
|
+
hasFormatter('getDateFormatter', columns) ||
|
|
74
|
+
hasFormatter('getDateFormatter', gridOptions),
|
|
64
75
|
customEvents: formatCustomEvents(customEvents),
|
|
65
76
|
eventing: {
|
|
66
77
|
publishEventName: eventing?.publishEventName || null,
|
package/.genx/versions.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [5.
|
|
3
|
+
## [5.12.0-prerelease.2](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.12.0-prerelease.1...v5.12.0-prerelease.2) (2026-05-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **react:** resolve TypeScript errors in generated React app templates [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) ab0903c
|
|
9
|
+
* resolve TypeScript errors in generated React app templates [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#570) 5e3a583
|
|
10
|
+
|
|
11
|
+
## [5.12.0-prerelease.1](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0...v5.12.0-prerelease.1) (2026-05-05)
|
|
4
12
|
|
|
5
13
|
|
|
6
14
|
### Features
|
|
7
15
|
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* replace foundation-layout with flex-layout in React templates [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 9c13246
|
|
11
|
-
* update FUI version [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 13373e5
|
|
12
|
-
* update FUI version [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 11096f0
|
|
13
|
-
* update FUI version [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#562) 9aa31ba
|
|
16
|
+
* add default error boundary for components [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 0353f8d
|
|
17
|
+
* add default error boundary for React generated apps (#567) fd39752
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
### Bug Fixes
|
|
17
21
|
|
|
18
|
-
* backport main [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*
|
|
22
|
+
* backport main [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#568) 2ef601d
|
|
23
|
+
|
|
24
|
+
## [5.11.0-prerelease.4](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0-prerelease.3...v5.11.0-prerelease.4) (2026-05-04)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* add default error boundary for components [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 0353f8d
|
|
30
|
+
* add default error boundary for React generated apps (#567) fd39752
|
|
26
31
|
|
|
27
32
|
## [5.11.0-prerelease.3](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0-prerelease.2...v5.11.0-prerelease.3) (2026-04-29)
|
|
28
33
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { BrowserRouter as Router } from 'react-router-dom';
|
|
3
|
+
{{#if FDC3.channels.length~}}
|
|
3
4
|
import {
|
|
4
|
-
setApiHost,
|
|
5
|
-
{{#if FDC3.channels.length~}}
|
|
6
5
|
listenToChannel,
|
|
7
6
|
onFDC3Ready,
|
|
8
|
-
{{/if}}
|
|
9
7
|
} from './utils';
|
|
8
|
+
{{/if}}
|
|
10
9
|
import { customEventFactory, registerStylesTarget } from './pbc/utils';
|
|
11
10
|
import { RoutesProvider } from './store/RoutesContext';
|
|
12
11
|
import { registerComponents as genesisRegisterComponents } from './share/genesis-components';
|
|
@@ -41,7 +40,6 @@ const App: React.FC<AppProps> = ({ rootElement }) => {
|
|
|
41
40
|
}, []);
|
|
42
41
|
useEffect(() => {
|
|
43
42
|
let mounted = true;
|
|
44
|
-
setApiHost();
|
|
45
43
|
(async () => {
|
|
46
44
|
await genesisRegisterComponents();
|
|
47
45
|
if (mounted) {
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { errorBoundaryStyles } from './ErrorBoundaryStyles';
|
|
3
|
+
|
|
4
|
+
type ErrorBoundaryScope = 'application' | 'tile';
|
|
5
|
+
|
|
6
|
+
type ErrorBoundaryFallbackProps = {
|
|
7
|
+
scope: ErrorBoundaryScope;
|
|
8
|
+
referenceId: string;
|
|
9
|
+
title: string;
|
|
10
|
+
subtitle: string;
|
|
11
|
+
details: string;
|
|
12
|
+
onRetry: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type BaseErrorBoundaryProps = {
|
|
16
|
+
scope: ErrorBoundaryScope;
|
|
17
|
+
title: string;
|
|
18
|
+
tileRegistration?: string;
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type BaseErrorBoundaryState = {
|
|
23
|
+
hasError: boolean;
|
|
24
|
+
error: Error | null;
|
|
25
|
+
componentStack: string;
|
|
26
|
+
capturedAt: string;
|
|
27
|
+
referenceId: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const initialBoundaryState: BaseErrorBoundaryState = {
|
|
31
|
+
hasError: false,
|
|
32
|
+
error: null,
|
|
33
|
+
componentStack: '',
|
|
34
|
+
capturedAt: '',
|
|
35
|
+
referenceId: '',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const toError = (value: unknown): Error => {
|
|
39
|
+
if (value instanceof Error) {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof value === 'string') {
|
|
44
|
+
return new Error(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
return new Error(JSON.stringify(value));
|
|
49
|
+
} catch {
|
|
50
|
+
return new Error('Unknown non-serializable error');
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const createReferenceId = (): string => {
|
|
55
|
+
const timestamp = new Date().toISOString().split(':').join('-');
|
|
56
|
+
const random = Math.random().toString(36).slice(2, 8).toUpperCase();
|
|
57
|
+
return `APP-${timestamp}-${random}`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const ErrorBoundaryFallback: React.FC<ErrorBoundaryFallbackProps> = ({
|
|
61
|
+
scope,
|
|
62
|
+
referenceId,
|
|
63
|
+
title,
|
|
64
|
+
subtitle,
|
|
65
|
+
details,
|
|
66
|
+
onRetry,
|
|
67
|
+
}) => {
|
|
68
|
+
const [copied, setCopied] = useState(false);
|
|
69
|
+
const [copyHelpVisible, setCopyHelpVisible] = useState(false);
|
|
70
|
+
|
|
71
|
+
const handleCopy = async () => {
|
|
72
|
+
try {
|
|
73
|
+
await navigator.clipboard.writeText(details);
|
|
74
|
+
setCopied(true);
|
|
75
|
+
setCopyHelpVisible(false);
|
|
76
|
+
window.setTimeout(() => {
|
|
77
|
+
setCopied(false);
|
|
78
|
+
}, 2_000);
|
|
79
|
+
} catch {
|
|
80
|
+
setCopyHelpVisible(true);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<section className="error-boundary" role="alert">
|
|
86
|
+
<style>{errorBoundaryStyles}</style>
|
|
87
|
+
<div className="error-boundary__top">
|
|
88
|
+
<span className="error-boundary__status">
|
|
89
|
+
{scope === 'application' ? 'Application incident' : 'Tile incident'}
|
|
90
|
+
</span>
|
|
91
|
+
<span className="error-boundary__reference">Ref: {referenceId}</span>
|
|
92
|
+
</div>
|
|
93
|
+
<h2 className="error-boundary__title">{title}</h2>
|
|
94
|
+
<p className="error-boundary__subtitle">{subtitle}</p>
|
|
95
|
+
<p className="error-boundary__hint">
|
|
96
|
+
Tip: copy diagnostics and paste directly into Cursor to speed up debugging.
|
|
97
|
+
</p>
|
|
98
|
+
<textarea
|
|
99
|
+
className="error-boundary__details"
|
|
100
|
+
readOnly
|
|
101
|
+
value={details}
|
|
102
|
+
aria-label="Error diagnostics details"
|
|
103
|
+
/>
|
|
104
|
+
<div className="error-boundary__actions">
|
|
105
|
+
<button type="button" className="error-boundary__button" onClick={onRetry}>
|
|
106
|
+
Retry
|
|
107
|
+
</button>
|
|
108
|
+
<button
|
|
109
|
+
type="button"
|
|
110
|
+
className="error-boundary__button error-boundary__button--secondary"
|
|
111
|
+
onClick={handleCopy}
|
|
112
|
+
>
|
|
113
|
+
{copied ? 'Copied' : 'Copy diagnostics'}
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
{copyHelpVisible ? (
|
|
117
|
+
<p className="error-boundary__manual-copy-help">
|
|
118
|
+
Clipboard access is blocked in this browser context. Please select the diagnostics text
|
|
119
|
+
and copy it manually.
|
|
120
|
+
</p>
|
|
121
|
+
) : null}
|
|
122
|
+
</section>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
class BaseErrorBoundary extends React.Component<BaseErrorBoundaryProps, BaseErrorBoundaryState> {
|
|
127
|
+
public constructor(props: BaseErrorBoundaryProps) {
|
|
128
|
+
super(props);
|
|
129
|
+
this.state = initialBoundaryState;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public static getDerivedStateFromError(error: unknown): Partial<BaseErrorBoundaryState> {
|
|
133
|
+
const normalized = toError(error);
|
|
134
|
+
return {
|
|
135
|
+
hasError: true,
|
|
136
|
+
error: normalized,
|
|
137
|
+
capturedAt: new Date().toISOString(),
|
|
138
|
+
referenceId: createReferenceId(),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void {
|
|
143
|
+
const normalized = toError(error);
|
|
144
|
+
this.setState({
|
|
145
|
+
componentStack: errorInfo.componentStack ?? '',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
console.error(`[${this.props.scope}] error boundary captured an error`, {
|
|
149
|
+
referenceId: this.state.referenceId,
|
|
150
|
+
title: this.props.title,
|
|
151
|
+
tileRegistration: this.props.tileRegistration,
|
|
152
|
+
error: normalized,
|
|
153
|
+
componentStack: errorInfo.componentStack,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private readonly handleRetry = (): void => {
|
|
158
|
+
this.setState(initialBoundaryState);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
public render(): React.ReactNode {
|
|
162
|
+
if (!this.state.hasError || !this.state.error) {
|
|
163
|
+
return this.props.children;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { scope, title, tileRegistration } = this.props;
|
|
167
|
+
const diagnostics = [
|
|
168
|
+
`Reference: ${this.state.referenceId}`,
|
|
169
|
+
`Scope: ${scope}`,
|
|
170
|
+
`Title: ${title}`,
|
|
171
|
+
`Tile registration: ${tileRegistration ?? 'N/A'}`,
|
|
172
|
+
`Captured at: ${this.state.capturedAt}`,
|
|
173
|
+
`URL: ${window.location.href}`,
|
|
174
|
+
`User agent: ${window.navigator.userAgent}`,
|
|
175
|
+
`Error name: ${this.state.error.name}`,
|
|
176
|
+
`Error message: ${this.state.error.message}`,
|
|
177
|
+
'Error stack:',
|
|
178
|
+
this.state.error.stack ?? 'No stack available',
|
|
179
|
+
'Component stack:',
|
|
180
|
+
this.state.componentStack || 'No component stack available',
|
|
181
|
+
].join('\n');
|
|
182
|
+
|
|
183
|
+
const fallbackTitle =
|
|
184
|
+
scope === 'application'
|
|
185
|
+
? 'Something went wrong'
|
|
186
|
+
: `Something went wrong in "${title}"`;
|
|
187
|
+
const fallbackSubtitle =
|
|
188
|
+
scope === 'application'
|
|
189
|
+
? 'The app hit an unexpected error. You can retry or copy diagnostics for a fast fix.'
|
|
190
|
+
: 'This tile crashed, but the rest of the application keeps running. Retry or copy diagnostics.';
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<ErrorBoundaryFallback
|
|
194
|
+
scope={scope}
|
|
195
|
+
referenceId={this.state.referenceId}
|
|
196
|
+
title={fallbackTitle}
|
|
197
|
+
subtitle={fallbackSubtitle}
|
|
198
|
+
details={diagnostics}
|
|
199
|
+
onRetry={this.handleRetry}
|
|
200
|
+
/>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
type AppErrorBoundaryProps = {
|
|
206
|
+
children: React.ReactNode;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export const AppErrorBoundary: React.FC<AppErrorBoundaryProps> = ({ children }) => {
|
|
210
|
+
return (
|
|
211
|
+
<BaseErrorBoundary scope="application" title="Application">
|
|
212
|
+
{children}
|
|
213
|
+
</BaseErrorBoundary>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
type TileErrorBoundaryProps = {
|
|
218
|
+
title: string;
|
|
219
|
+
tileRegistration: string;
|
|
220
|
+
children: React.ReactNode;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const TileErrorBoundary: React.FC<TileErrorBoundaryProps> = ({
|
|
224
|
+
title,
|
|
225
|
+
tileRegistration,
|
|
226
|
+
children,
|
|
227
|
+
}) => {
|
|
228
|
+
return (
|
|
229
|
+
<BaseErrorBoundary scope="tile" title={title} tileRegistration={tileRegistration}>
|
|
230
|
+
{children}
|
|
231
|
+
</BaseErrorBoundary>
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const withTileErrorBoundary = (
|
|
236
|
+
Component: React.ComponentType,
|
|
237
|
+
title: string,
|
|
238
|
+
tileRegistration: string,
|
|
239
|
+
): React.FC => {
|
|
240
|
+
const WrappedWithErrorBoundary: React.FC = () => {
|
|
241
|
+
return (
|
|
242
|
+
<TileErrorBoundary title={title} tileRegistration={tileRegistration}>
|
|
243
|
+
<Component />
|
|
244
|
+
</TileErrorBoundary>
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
WrappedWithErrorBoundary.displayName = `WithTileErrorBoundary(${title}:${tileRegistration}:${Component.displayName || Component.name || 'TileComponent'})`;
|
|
249
|
+
return WrappedWithErrorBoundary;
|
|
250
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export const errorBoundaryStyles = `
|
|
2
|
+
.error-boundary,
|
|
3
|
+
.error-boundary__tile-wrap {
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.error-boundary {
|
|
9
|
+
--eb-bg: #f6f8fb;
|
|
10
|
+
--eb-surface: #ffffff;
|
|
11
|
+
--eb-text: #18212f;
|
|
12
|
+
--eb-text-muted: #5f6f84;
|
|
13
|
+
--eb-border: #d3dae6;
|
|
14
|
+
--eb-accent: #2f6fed;
|
|
15
|
+
--eb-accent-hover: #255dcc;
|
|
16
|
+
--eb-accent-foreground: #ffffff;
|
|
17
|
+
--eb-error: #c53d3d;
|
|
18
|
+
--eb-focus: #2f6fed;
|
|
19
|
+
--eb-font: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
20
|
+
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 12px;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
padding: 16px;
|
|
27
|
+
background: var(--eb-bg);
|
|
28
|
+
color: var(--eb-text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.error-boundary__top,
|
|
32
|
+
.error-boundary__title,
|
|
33
|
+
.error-boundary__subtitle,
|
|
34
|
+
.error-boundary__hint,
|
|
35
|
+
.error-boundary__details,
|
|
36
|
+
.error-boundary__actions,
|
|
37
|
+
.error-boundary__manual-copy-help {
|
|
38
|
+
width: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.error-boundary__top {
|
|
42
|
+
display: flex;
|
|
43
|
+
width: 100%;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
gap: 10px;
|
|
47
|
+
flex-wrap: wrap;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.error-boundary__status {
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
border: 1px solid var(--eb-error);
|
|
54
|
+
border-radius: 999px;
|
|
55
|
+
padding: 5px 10px;
|
|
56
|
+
font-family: var(--eb-font);
|
|
57
|
+
text-transform: uppercase;
|
|
58
|
+
letter-spacing: 0.13em;
|
|
59
|
+
font-size: 11px;
|
|
60
|
+
font-weight: 700;
|
|
61
|
+
color: var(--eb-error);
|
|
62
|
+
background: #fff4f4;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.error-boundary__reference {
|
|
66
|
+
font-family: 'Menlo', 'Consolas', 'Liberation Mono', monospace;
|
|
67
|
+
font-size: 11px;
|
|
68
|
+
letter-spacing: 0.08em;
|
|
69
|
+
color: var(--eb-text-muted);
|
|
70
|
+
text-transform: uppercase;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.error-boundary__title {
|
|
74
|
+
margin: 0;
|
|
75
|
+
font-family: var(--eb-font);
|
|
76
|
+
font-size: clamp(22px, 3vw, 30px);
|
|
77
|
+
line-height: 1.15;
|
|
78
|
+
font-weight: 700;
|
|
79
|
+
color: var(--eb-text);
|
|
80
|
+
text-wrap: balance;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.error-boundary__subtitle {
|
|
84
|
+
margin: 0;
|
|
85
|
+
max-width: 80ch;
|
|
86
|
+
font-family: var(--eb-font);
|
|
87
|
+
color: var(--eb-text);
|
|
88
|
+
line-height: 1.45;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.error-boundary__hint {
|
|
92
|
+
margin: -2px 0 2px;
|
|
93
|
+
font-family: var(--eb-font);
|
|
94
|
+
color: var(--eb-text-muted);
|
|
95
|
+
font-size: 13px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.error-boundary__details {
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
width: 100%;
|
|
101
|
+
min-height: 190px;
|
|
102
|
+
max-height: 360px;
|
|
103
|
+
padding: 12px;
|
|
104
|
+
border: 1px solid var(--eb-border);
|
|
105
|
+
border-radius: 10px;
|
|
106
|
+
background: var(--eb-surface);
|
|
107
|
+
color: var(--eb-text);
|
|
108
|
+
font-family: 'Menlo', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
|
|
109
|
+
font-size: 12px;
|
|
110
|
+
line-height: 1.5;
|
|
111
|
+
resize: vertical;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.error-boundary__actions {
|
|
115
|
+
display: flex;
|
|
116
|
+
gap: 10px;
|
|
117
|
+
flex-wrap: wrap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.error-boundary__button {
|
|
121
|
+
border: 1px solid var(--eb-accent);
|
|
122
|
+
border-radius: 999px;
|
|
123
|
+
background: var(--eb-accent);
|
|
124
|
+
color: var(--eb-accent-foreground);
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
padding: 9px 16px;
|
|
127
|
+
font-family: var(--eb-font);
|
|
128
|
+
font-size: 12px;
|
|
129
|
+
font-weight: 700;
|
|
130
|
+
letter-spacing: 0.04em;
|
|
131
|
+
text-transform: uppercase;
|
|
132
|
+
transition: transform 180ms ease, background-color 180ms ease;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.error-boundary__button--secondary {
|
|
136
|
+
border-color: var(--eb-border);
|
|
137
|
+
background: var(--eb-surface);
|
|
138
|
+
color: var(--eb-text);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.error-boundary__button:hover {
|
|
142
|
+
background: var(--eb-accent-hover);
|
|
143
|
+
transform: translateY(-1px);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.error-boundary__button--secondary:hover {
|
|
147
|
+
background: #eef2f8;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.error-boundary__button:focus-visible {
|
|
151
|
+
outline: 2px solid var(--eb-focus);
|
|
152
|
+
outline-offset: 2px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.error-boundary__manual-copy-help {
|
|
156
|
+
margin: 2px 0 0;
|
|
157
|
+
font-size: 13px;
|
|
158
|
+
color: var(--eb-text-muted);
|
|
159
|
+
line-height: 1.4;
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
2
2
|
import AuthPage from '../../pages/AuthPage/AuthPage';
|
|
3
|
+
{{#if routes.[0]}}
|
|
3
4
|
import {{pascalCase routes.[0].name}} from '../../pages/{{pascalCase routes.[0].name}}/{{pascalCase routes.[0].name}}';
|
|
5
|
+
{{/if}}
|
|
4
6
|
import DefaultLayout from '../../layouts/default/DefaultLayout';
|
|
5
7
|
import ProtectedRoute from './ProtectedRoute';
|
|
6
8
|
import { useRoutesContext } from '../../store/RoutesContext';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom/client'
|
|
3
3
|
import App from './App.tsx'
|
|
4
|
+
import { AppErrorBoundary } from './components/error-boundary/ErrorBoundary'
|
|
4
5
|
|
|
5
6
|
import { registerPBCs } from './pbc/utils';
|
|
6
7
|
import { createLogger } from '@genesislcap/foundation-logger';
|
|
@@ -16,7 +17,9 @@ function bootstrapApp() {
|
|
|
16
17
|
if (rootEelement) {
|
|
17
18
|
ReactDOM.createRoot(rootEelement!).render(
|
|
18
19
|
<React.StrictMode>
|
|
19
|
-
<
|
|
20
|
+
<AppErrorBoundary>
|
|
21
|
+
<App rootElement={rootEelement} />
|
|
22
|
+
</AppErrorBoundary>
|
|
20
23
|
</React.StrictMode>,
|
|
21
24
|
)
|
|
22
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { createContext, useContext, ReactNode } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Navigate } from 'react-router-dom';
|
|
3
|
+
import type { AppRoute } from '../types/AppRoute';
|
|
3
4
|
import { getApp } from '@genesislcap/foundation-shell/app';
|
|
4
5
|
import AuthPage from '../pages/AuthPage/AuthPage';
|
|
5
6
|
import NotFoundPage from '../pages/NotFoundPage/NotFoundPage';
|
|
@@ -49,7 +50,7 @@ const routes = [
|
|
|
49
50
|
{{/each}}
|
|
50
51
|
];
|
|
51
52
|
|
|
52
|
-
const RoutesContext = createContext<
|
|
53
|
+
const RoutesContext = createContext<AppRoute[]>([]);
|
|
53
54
|
|
|
54
55
|
export const RoutesProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
55
56
|
const pbcRoutes = getApp().routes.map((route) => ({
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AppRoute as FoundationAppRoute } from '@genesislcap/foundation-shell/app';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
export interface AppRoute {
|
|
5
|
+
path?: string;
|
|
6
|
+
element?: ReactNode;
|
|
7
|
+
children?: AppRoute[];
|
|
8
|
+
data?: {
|
|
9
|
+
permissionCode?: string;
|
|
10
|
+
navItems?: unknown[];
|
|
11
|
+
pbcElement?: FoundationAppRoute['element'];
|
|
12
|
+
pbcElementTag?: unknown;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
};
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { DI } from '@microsoft/fast-foundation';
|
|
2
|
-
import { Connect } from '@genesislcap/foundation-comms';
|
|
3
|
-
import { API_DATA } from '../config';
|
|
4
|
-
|
|
5
|
-
class ConnectService {
|
|
6
|
-
private container = DI.getOrCreateDOMContainer();
|
|
7
|
-
private connect: Connect = this.container.get(Connect);
|
|
8
|
-
|
|
9
|
-
getContainer() {
|
|
10
|
-
return this.container;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
getConnect() {
|
|
14
|
-
return this.connect;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
isConnected() {
|
|
18
|
-
return this.connect.isConnected;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
init() {
|
|
22
|
-
return this.connect.connect(API_DATA.URL);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const connectService = new ConnectService();
|