@xh/hoist 72.1.0 → 72.3.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/CHANGELOG.md +44 -0
- package/admin/tabs/activity/clienterrors/ClientErrorsModel.ts +23 -4
- package/admin/tabs/cluster/instances/InstancesTabModel.ts +4 -3
- package/admin/tabs/cluster/instances/logs/LogDisplay.ts +12 -14
- package/admin/tabs/cluster/instances/logs/LogDisplayModel.ts +0 -2
- package/admin/tabs/cluster/instances/logs/LogViewer.ts +6 -5
- package/admin/tabs/cluster/instances/logs/LogViewerModel.ts +8 -1
- package/admin/tabs/cluster/instances/memory/MemoryMonitorModel.ts +1 -0
- package/admin/tabs/cluster/instances/services/DetailsModel.ts +1 -2
- package/admin/tabs/cluster/instances/services/DetailsPanel.ts +19 -14
- package/admin/tabs/cluster/instances/services/ServiceModel.ts +14 -6
- package/admin/tabs/cluster/instances/services/ServicePanel.ts +9 -10
- package/admin/tabs/cluster/instances/websocket/WebSocketColumns.ts +9 -0
- package/admin/tabs/cluster/instances/websocket/WebSocketModel.ts +2 -1
- package/admin/tabs/userData/roles/RoleModel.ts +1 -1
- package/admin/tabs/userData/roles/details/RoleDetailsModel.ts +2 -1
- package/admin/tabs/userData/roles/recategorize/RecategorizeDialogModel.ts +1 -1
- package/appcontainer/AppStateModel.ts +6 -1
- package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +4 -1
- package/build/types/admin/tabs/cluster/instances/services/DetailsModel.d.ts +2 -3
- package/build/types/admin/tabs/cluster/instances/websocket/WebSocketColumns.d.ts +1 -0
- package/build/types/appcontainer/AppStateModel.d.ts +5 -1
- package/build/types/cmp/tab/TabContainerModel.d.ts +5 -5
- package/build/types/core/HoistProps.d.ts +1 -0
- package/build/types/core/XH.d.ts +5 -5
- package/build/types/core/types/Interfaces.d.ts +9 -0
- package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +1 -0
- package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +1 -0
- package/build/types/desktop/cmp/tab/TabSwitcher.d.ts +1 -1
- package/build/types/kit/blueprint/Wrappers.d.ts +1 -1
- package/build/types/kit/swiper/index.d.ts +4 -4
- package/build/types/security/BaseOAuthClient.d.ts +25 -28
- package/build/types/security/Token.d.ts +0 -1
- package/build/types/security/Types.d.ts +39 -0
- package/build/types/security/authzero/AuthZeroClient.d.ts +3 -4
- package/build/types/security/msal/MsalClient.d.ts +14 -4
- package/build/types/svc/TrackService.d.ts +31 -1
- package/build/types/utils/js/BrowserUtils.d.ts +38 -1
- package/cmp/tab/TabContainerModel.ts +5 -5
- package/core/HoistProps.ts +1 -0
- package/core/XH.ts +13 -5
- package/core/exception/Exception.ts +19 -12
- package/core/types/Interfaces.ts +11 -0
- package/data/Store.ts +3 -0
- package/desktop/appcontainer/ExceptionDialog.ts +1 -1
- package/desktop/cmp/dash/canvas/DashCanvas.ts +2 -1
- package/desktop/cmp/grid/editors/BooleanEditor.ts +15 -3
- package/desktop/cmp/tab/TabSwitcher.ts +1 -1
- package/package.json +2 -2
- package/security/BaseOAuthClient.ts +52 -45
- package/security/Token.ts +0 -2
- package/security/Types.ts +51 -0
- package/security/authzero/AuthZeroClient.ts +6 -8
- package/security/msal/MsalClient.ts +130 -27
- package/svc/FetchService.ts +3 -2
- package/svc/TrackService.ts +94 -8
- package/svc/WebSocketService.ts +1 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/js/BrowserUtils.ts +72 -21
- package/utils/react/LayoutPropUtils.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v72.3.0 - 2025-04-08
|
|
4
|
+
|
|
5
|
+
### 🎁 New Features
|
|
6
|
+
|
|
7
|
+
* Added support for posting a "Client Health Report" track message on a configurable interval. This
|
|
8
|
+
message will include basic client information, and can be extended to include any other desired
|
|
9
|
+
data via `XH.trackService.addClientHealthReportSource()`. Enable by updating your app's
|
|
10
|
+
`xhActivityTrackingConfig` to include `clientHealthReport: {intervalMins: XXXX}`.
|
|
11
|
+
* Enabled opt-in support for telemetry in `MsalClient`, leveraging hooks built-in to MSAL to collect
|
|
12
|
+
timing and success/failure count for all events emitted by the library.
|
|
13
|
+
* Added the reported client app version as a column in the Admin Console WebSockets tab.
|
|
14
|
+
|
|
15
|
+
### 🐞 Bug Fixes
|
|
16
|
+
|
|
17
|
+
* Improved fetch request tracking to include time spent loading headers as specified by application.
|
|
18
|
+
|
|
19
|
+
### ⚙️ Technical
|
|
20
|
+
|
|
21
|
+
* Update shape of returned `BrowserUtils.getClientDeviceInfo()` to nest several properties under new
|
|
22
|
+
top-level `window` key and report JS heap size / usage values under the `memory` block in MB.
|
|
23
|
+
|
|
24
|
+
### 📚 Libraries
|
|
25
|
+
|
|
26
|
+
* @azure/msal-browser `3.28 → 4.8.0`
|
|
27
|
+
|
|
28
|
+
## v72.2.0 - 2025-03-13
|
|
29
|
+
|
|
30
|
+
### 🎁 New Features
|
|
31
|
+
|
|
32
|
+
* Modified `TabContainerModel` to make more methods `protected`, improving extensibility for
|
|
33
|
+
advanced use-cases.
|
|
34
|
+
* Enhanced `XH.reloadApp` with new argument to clear query parameters before loading.
|
|
35
|
+
* Enhanced exception handling in `FetchService` to capture messages returned as raw strings, or
|
|
36
|
+
without explicit names.
|
|
37
|
+
* Added dedicated columns to the Admin Console "Client Errors" tab for error names and messages.
|
|
38
|
+
* `BaseOAuthClient` has been enhanced to allow `lazy` loading of Access Tokens, and also made more
|
|
39
|
+
robust such that Access Tokens that fail to load will never prevent the client from
|
|
40
|
+
initialization.
|
|
41
|
+
|
|
42
|
+
### 🐞 Bug Fixes
|
|
43
|
+
|
|
44
|
+
* Prevented native browser context menu from showing on `DashCanvas` surfaces and obscuring the
|
|
45
|
+
`DashCanvas` custom context menu.
|
|
46
|
+
|
|
3
47
|
## v72.1.0 - 2025-02-13
|
|
4
48
|
|
|
5
49
|
### 🎁 New Features
|
|
@@ -9,7 +9,7 @@ import * as Col from '@xh/hoist/admin/columns';
|
|
|
9
9
|
import {FilterChooserModel} from '@xh/hoist/cmp/filter';
|
|
10
10
|
import {FormModel} from '@xh/hoist/cmp/form';
|
|
11
11
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
12
|
-
import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
|
|
12
|
+
import {HoistModel, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
13
13
|
import {StoreRecord} from '@xh/hoist/data';
|
|
14
14
|
import {fmtJson} from '@xh/hoist/format';
|
|
15
15
|
import {action, bindable, comparer, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
@@ -62,6 +62,14 @@ export class ClientErrorsModel extends HoistModel {
|
|
|
62
62
|
{...Col.appVersion},
|
|
63
63
|
{...Col.appEnvironment},
|
|
64
64
|
{...Col.msg, displayName: 'User Message', hidden},
|
|
65
|
+
{
|
|
66
|
+
field: {name: 'errorName', type: 'string'},
|
|
67
|
+
autosizeMaxWidth: 400
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
field: {name: 'errorMessage', type: 'string'},
|
|
71
|
+
autosizeMaxWidth: 400
|
|
72
|
+
},
|
|
65
73
|
{...Col.error, hidden},
|
|
66
74
|
{...Col.url},
|
|
67
75
|
{...Col.correlationId},
|
|
@@ -119,18 +127,29 @@ export class ClientErrorsModel extends HoistModel {
|
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
override async doLoadAsync(loadSpec: LoadSpec) {
|
|
122
|
-
const {gridModel} = this;
|
|
130
|
+
const {query, gridModel} = this;
|
|
123
131
|
|
|
124
132
|
try {
|
|
125
|
-
const data = await XH.fetchService.postJson({
|
|
133
|
+
const data: PlainObject[] = await XH.fetchService.postJson({
|
|
126
134
|
url: 'clientErrorAdmin',
|
|
127
|
-
body:
|
|
135
|
+
body: query,
|
|
128
136
|
loadSpec
|
|
129
137
|
});
|
|
130
138
|
|
|
139
|
+
// Parse name + message from JSON-serialized error object out to top-level properties.
|
|
140
|
+
data.forEach(it => {
|
|
141
|
+
try {
|
|
142
|
+
const error = JSON.parse(it.error);
|
|
143
|
+
it.errorName = error?.name;
|
|
144
|
+
it.errorMessage = error?.message;
|
|
145
|
+
} catch (ignored) {}
|
|
146
|
+
});
|
|
147
|
+
|
|
131
148
|
gridModel.loadData(data);
|
|
132
149
|
await gridModel.preSelectFirstAsync();
|
|
133
150
|
} catch (e) {
|
|
151
|
+
if (loadSpec.isStale || loadSpec.isAutoRefresh) return;
|
|
152
|
+
|
|
134
153
|
gridModel.clear();
|
|
135
154
|
XH.handleException(e);
|
|
136
155
|
}
|
|
@@ -149,7 +149,7 @@ export class InstancesTabModel extends HoistModel {
|
|
|
149
149
|
},
|
|
150
150
|
{
|
|
151
151
|
...usedHeapMb,
|
|
152
|
-
headerName: 'Heap (
|
|
152
|
+
headerName: 'Heap (mb)'
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
...usedPctMax,
|
|
@@ -214,11 +214,12 @@ export class InstancesTabModel extends HoistModel {
|
|
|
214
214
|
private async shutdownInstanceAsync(instance: PlainObject) {
|
|
215
215
|
if (
|
|
216
216
|
!(await XH.confirm({
|
|
217
|
-
message: `Are you
|
|
217
|
+
message: `Are you sure you wish to immediately terminate instance ${instance.name}?`,
|
|
218
218
|
confirmProps: {
|
|
219
219
|
icon: Icon.skull(),
|
|
220
|
-
text: '
|
|
220
|
+
text: 'Yes, kill the instance',
|
|
221
221
|
intent: 'danger',
|
|
222
|
+
outlined: true,
|
|
222
223
|
autoFocus: false
|
|
223
224
|
}
|
|
224
225
|
}))
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {clock} from '@xh/hoist/cmp/clock';
|
|
8
8
|
import {grid} from '@xh/hoist/cmp/grid';
|
|
9
|
-
import {
|
|
9
|
+
import {filler, fragment, hspacer, label, placeholder} from '@xh/hoist/cmp/layout';
|
|
10
|
+
import {loadingIndicator} from '@xh/hoist/cmp/loadingindicator';
|
|
10
11
|
import {hoistCmp, uses, XH} from '@xh/hoist/core';
|
|
11
12
|
import {button, modalToggleButton} from '@xh/hoist/desktop/cmp/button';
|
|
12
13
|
import {gridFindField} from '@xh/hoist/desktop/cmp/grid';
|
|
13
14
|
import {numberInput, switchInput, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
14
|
-
import {loadingIndicator} from '@xh/hoist/cmp/loadingindicator';
|
|
15
15
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
16
16
|
import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
|
|
17
17
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -48,7 +48,7 @@ const tbar = hoistCmp.factory<LogDisplayModel>(({model}) => {
|
|
|
48
48
|
numberInput({
|
|
49
49
|
bind: 'startLine',
|
|
50
50
|
min: 1,
|
|
51
|
-
width:
|
|
51
|
+
width: 70,
|
|
52
52
|
disabled: model.tail,
|
|
53
53
|
displayWithCommas: true
|
|
54
54
|
}),
|
|
@@ -57,13 +57,13 @@ const tbar = hoistCmp.factory<LogDisplayModel>(({model}) => {
|
|
|
57
57
|
numberInput({
|
|
58
58
|
bind: 'maxLines',
|
|
59
59
|
min: 1,
|
|
60
|
-
width:
|
|
60
|
+
width: 70,
|
|
61
61
|
displayWithCommas: true
|
|
62
62
|
}),
|
|
63
63
|
'-',
|
|
64
64
|
textInput({
|
|
65
65
|
bind: 'pattern',
|
|
66
|
-
placeholder: 'Filter',
|
|
66
|
+
placeholder: 'Filter lines...',
|
|
67
67
|
leftIcon: Icon.filter(),
|
|
68
68
|
flex: 1,
|
|
69
69
|
rightElement: fragment(
|
|
@@ -85,7 +85,7 @@ const tbar = hoistCmp.factory<LogDisplayModel>(({model}) => {
|
|
|
85
85
|
})
|
|
86
86
|
)
|
|
87
87
|
}),
|
|
88
|
-
gridFindField({flex: 1}),
|
|
88
|
+
gridFindField({flex: 1, placeholder: 'Find lines...'}),
|
|
89
89
|
'-',
|
|
90
90
|
switchInput({
|
|
91
91
|
bind: 'tail',
|
|
@@ -123,16 +123,14 @@ const bbar = hoistCmp.factory<LogDisplayModel>({
|
|
|
123
123
|
}),
|
|
124
124
|
filler(),
|
|
125
125
|
Icon.clock(),
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
})
|
|
132
|
-
),
|
|
126
|
+
clock({
|
|
127
|
+
timezone: zone,
|
|
128
|
+
format: 'HH:mm',
|
|
129
|
+
suffix: fmtTimeZone(zone, offset)
|
|
130
|
+
}),
|
|
133
131
|
fragment({
|
|
134
132
|
omit: !logRootPath,
|
|
135
|
-
items: [toolbarSep(), Icon.folder(),
|
|
133
|
+
items: [toolbarSep(), Icon.folder({className: 'xh-margin-right'}), logRootPath]
|
|
136
134
|
})
|
|
137
135
|
);
|
|
138
136
|
}
|
|
@@ -117,8 +117,6 @@ export class LogDisplayModel extends HoistModel {
|
|
|
117
117
|
},
|
|
118
118
|
loadSpec
|
|
119
119
|
});
|
|
120
|
-
// Backward compatibility for Hoist Core < v22, which returned exception in-band
|
|
121
|
-
if (!response.success) throw XH.exception(response.exception);
|
|
122
120
|
this.updateGridData(response.content);
|
|
123
121
|
}
|
|
124
122
|
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {logLevelDialog} from '@xh/hoist/admin/tabs/cluster/instances/logs/levels/LogLevelDialog';
|
|
8
|
-
import {grid} from '@xh/hoist/cmp/grid';
|
|
9
|
-
import {hframe} from '@xh/hoist/cmp/layout';
|
|
8
|
+
import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
|
|
9
|
+
import {filler, hframe} from '@xh/hoist/cmp/layout';
|
|
10
10
|
import {storeFilterField} from '@xh/hoist/cmp/store';
|
|
11
11
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
12
12
|
import {errorMessage} from '@xh/hoist/cmp/error';
|
|
@@ -37,9 +37,8 @@ export const logViewer = hoistCmp.factory({
|
|
|
37
37
|
side: 'left',
|
|
38
38
|
defaultSize: 380
|
|
39
39
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
storeFilterField({flex: 1}),
|
|
40
|
+
tbar: [
|
|
41
|
+
storeFilterField({flex: 1, placeholder: 'Filter files...'}),
|
|
43
42
|
select({
|
|
44
43
|
leftIcon: Icon.server(),
|
|
45
44
|
bind: 'instanceOnly',
|
|
@@ -53,6 +52,8 @@ export const logViewer = hoistCmp.factory({
|
|
|
53
52
|
]
|
|
54
53
|
})
|
|
55
54
|
],
|
|
55
|
+
item: grid(),
|
|
56
|
+
bbar: [filler(), gridCountLabel({unit: 'log file'})],
|
|
56
57
|
mask: 'onLoad'
|
|
57
58
|
}),
|
|
58
59
|
logDisplay(),
|
|
@@ -13,6 +13,7 @@ import {RecordActionSpec} from '@xh/hoist/data';
|
|
|
13
13
|
import {compactDateRenderer, fmtNumber} from '@xh/hoist/format';
|
|
14
14
|
import {Icon} from '@xh/hoist/icon';
|
|
15
15
|
import {bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
16
|
+
import {pluralize} from '@xh/hoist/utils/js';
|
|
16
17
|
import download from 'downloadjs';
|
|
17
18
|
import {LogDisplayModel} from './LogDisplayModel';
|
|
18
19
|
|
|
@@ -119,7 +120,13 @@ export class LogViewerModel extends BaseInstanceModel {
|
|
|
119
120
|
if (!count) return;
|
|
120
121
|
|
|
121
122
|
const confirmed = await XH.confirm({
|
|
122
|
-
message: `
|
|
123
|
+
message: `Are you sure you want to delete ${pluralize('log file', count, true)}? This cannot be undone.`,
|
|
124
|
+
confirmProps: {
|
|
125
|
+
text: `Yes, delete the ${pluralize('file', count)}`,
|
|
126
|
+
intent: 'danger',
|
|
127
|
+
outlined: true,
|
|
128
|
+
autoFocus: false
|
|
129
|
+
}
|
|
123
130
|
});
|
|
124
131
|
if (!confirmed) return;
|
|
125
132
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {HoistModel, LoadSpec, lookup, PlainObject, XH} from '@xh/hoist/core';
|
|
8
|
-
import {StoreRecord} from '@xh/hoist/data';
|
|
9
8
|
import {bindable} from '@xh/hoist/mobx';
|
|
10
9
|
import {ServiceModel} from './ServiceModel';
|
|
11
10
|
|
|
@@ -14,7 +13,7 @@ export class DetailsModel extends HoistModel {
|
|
|
14
13
|
parent: ServiceModel;
|
|
15
14
|
|
|
16
15
|
@bindable.ref
|
|
17
|
-
svcName:
|
|
16
|
+
svcName: String;
|
|
18
17
|
|
|
19
18
|
@bindable.ref
|
|
20
19
|
stats: PlainObject;
|
|
@@ -11,6 +11,7 @@ import {errorMessage} from '@xh/hoist/cmp/error';
|
|
|
11
11
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
12
12
|
import {jsonInput} from '@xh/hoist/desktop/cmp/input';
|
|
13
13
|
import {Icon} from '@xh/hoist/icon';
|
|
14
|
+
import {isEmpty} from 'lodash';
|
|
14
15
|
|
|
15
16
|
export const detailsPanel = hoistCmp.factory({
|
|
16
17
|
model: creates(DetailsModel),
|
|
@@ -18,7 +19,7 @@ export const detailsPanel = hoistCmp.factory({
|
|
|
18
19
|
render({model}) {
|
|
19
20
|
const {svcName} = model;
|
|
20
21
|
return panel({
|
|
21
|
-
title: svcName
|
|
22
|
+
title: svcName ?? 'Stats',
|
|
22
23
|
mask: 'onLoad',
|
|
23
24
|
icon: Icon.info(),
|
|
24
25
|
compactHeader: true,
|
|
@@ -42,18 +43,22 @@ const stats = hoistCmp.factory<DetailsModel>({
|
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
return isEmpty(stats)
|
|
47
|
+
? placeholder(
|
|
48
|
+
...(loadModel.isPending
|
|
49
|
+
? []
|
|
50
|
+
: [Icon.questionCircle(), 'This service does not report any admin stats.'])
|
|
51
|
+
)
|
|
52
|
+
: panel(
|
|
53
|
+
jsonInput({
|
|
54
|
+
readonly: true,
|
|
55
|
+
width: '100%',
|
|
56
|
+
height: '100%',
|
|
57
|
+
enableSearch: true,
|
|
58
|
+
showFullscreenButton: false,
|
|
59
|
+
editorProps: {lineNumbers: false},
|
|
60
|
+
value: model.parent.fmtStats(stats)
|
|
61
|
+
})
|
|
62
|
+
);
|
|
58
63
|
}
|
|
59
64
|
});
|
|
@@ -10,7 +10,7 @@ import {timestampNoYear} from '@xh/hoist/admin/columns';
|
|
|
10
10
|
import {BaseInstanceModel} from '@xh/hoist/admin/tabs/cluster/instances/BaseInstanceModel';
|
|
11
11
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
12
12
|
import {br, fragment} from '@xh/hoist/cmp/layout';
|
|
13
|
-
import {LoadSpec, managed, XH} from '@xh/hoist/core';
|
|
13
|
+
import {LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
14
14
|
import {FilterLike, FilterTestFn, RecordActionSpec} from '@xh/hoist/data';
|
|
15
15
|
import {Icon} from '@xh/hoist/icon';
|
|
16
16
|
import {bindable, makeObservable} from '@xh/hoist/mobx';
|
|
@@ -52,15 +52,16 @@ export class ServiceModel extends BaseInstanceModel {
|
|
|
52
52
|
selModel: 'multiple',
|
|
53
53
|
enableExport: true,
|
|
54
54
|
exportOptions: {filename: exportFilenameWithDate('services')},
|
|
55
|
+
groupBy: 'provider',
|
|
55
56
|
store: {
|
|
56
57
|
idSpec: 'name',
|
|
57
58
|
processRawData: this.processRawData,
|
|
58
59
|
fields: [
|
|
59
60
|
{name: 'provider', type: 'string'},
|
|
60
61
|
{name: 'name', type: 'string'},
|
|
61
|
-
{name: 'displayName', type: 'string'},
|
|
62
|
+
{name: 'displayName', type: 'string', displayName: 'Service'},
|
|
62
63
|
{name: 'initializedDate', type: 'date', displayName: 'Initialized'},
|
|
63
|
-
{name: 'lastCachesCleared', type: 'date', displayName: 'Last Cleared'}
|
|
64
|
+
{name: 'lastCachesCleared', type: 'date', displayName: 'Caches Last Cleared'}
|
|
64
65
|
]
|
|
65
66
|
},
|
|
66
67
|
sortBy: ['provider', 'displayName'],
|
|
@@ -81,6 +82,7 @@ export class ServiceModel extends BaseInstanceModel {
|
|
|
81
82
|
constructor() {
|
|
82
83
|
super();
|
|
83
84
|
makeObservable(this);
|
|
85
|
+
|
|
84
86
|
this.addReaction({
|
|
85
87
|
track: () => [this.textFilter, this.typeFilter],
|
|
86
88
|
run: this.applyFilters,
|
|
@@ -91,6 +93,7 @@ export class ServiceModel extends BaseInstanceModel {
|
|
|
91
93
|
async clearCachesAsync(entireCluster: boolean) {
|
|
92
94
|
const {gridModel, instanceName, loadModel} = this,
|
|
93
95
|
{selectedRecords} = gridModel;
|
|
96
|
+
|
|
94
97
|
if (isEmpty(selectedRecords)) return;
|
|
95
98
|
|
|
96
99
|
const cacheStr =
|
|
@@ -131,19 +134,24 @@ export class ServiceModel extends BaseInstanceModel {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
override async doLoadAsync(loadSpec: LoadSpec) {
|
|
137
|
+
const {gridModel, instanceName: instance} = this;
|
|
134
138
|
try {
|
|
135
139
|
const data = await XH.fetchJson({
|
|
136
140
|
url: 'serviceManagerAdmin/listServices',
|
|
137
|
-
params: {instance
|
|
141
|
+
params: {instance},
|
|
138
142
|
loadSpec
|
|
139
143
|
});
|
|
140
|
-
|
|
144
|
+
|
|
145
|
+
if (!loadSpec.isStale) {
|
|
146
|
+
gridModel.loadData(data);
|
|
147
|
+
gridModel.preSelectFirstAsync();
|
|
148
|
+
}
|
|
141
149
|
} catch (e) {
|
|
142
150
|
this.handleLoadException(e, loadSpec);
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
|
|
146
|
-
private processRawData(r) {
|
|
154
|
+
private processRawData(r: PlainObject) {
|
|
147
155
|
const provider = r.name && r.name.startsWith('hoistCore') ? 'Hoist' : 'App';
|
|
148
156
|
const displayName = lowerFirst(r.name.replace('hoistCore', ''));
|
|
149
157
|
return {provider, displayName, ...r};
|
|
@@ -9,11 +9,11 @@ import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
|
|
|
9
9
|
import {filler, hframe} from '@xh/hoist/cmp/layout';
|
|
10
10
|
import {storeFilterField} from '@xh/hoist/cmp/store';
|
|
11
11
|
import {creates, hoistCmp, uses} from '@xh/hoist/core';
|
|
12
|
-
import {exportButton} from '@xh/hoist/desktop/cmp/button';
|
|
12
|
+
import {button, exportButton} from '@xh/hoist/desktop/cmp/button';
|
|
13
|
+
import {buttonGroupInput} from '@xh/hoist/desktop/cmp/input';
|
|
13
14
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
14
15
|
import {recordActionBar} from '@xh/hoist/desktop/cmp/record';
|
|
15
16
|
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
16
|
-
import {select} from '@xh/hoist/desktop/cmp/input';
|
|
17
17
|
import {ServiceModel} from './ServiceModel';
|
|
18
18
|
|
|
19
19
|
export const servicePanel = hoistCmp.factory({
|
|
@@ -52,15 +52,14 @@ const bbar = hoistCmp.factory({
|
|
|
52
52
|
filler(),
|
|
53
53
|
gridCountLabel({unit: 'service'}),
|
|
54
54
|
'-',
|
|
55
|
-
|
|
56
|
-
options: [
|
|
57
|
-
{value: 'all', label: 'All'},
|
|
58
|
-
{value: 'app', label: 'App Only'},
|
|
59
|
-
{value: 'hoist', label: 'Hoist Only'}
|
|
60
|
-
],
|
|
61
|
-
width: 125,
|
|
55
|
+
buttonGroupInput({
|
|
62
56
|
bind: 'typeFilter',
|
|
63
|
-
|
|
57
|
+
outlined: true,
|
|
58
|
+
items: [
|
|
59
|
+
button({value: 'all', text: 'All'}),
|
|
60
|
+
button({value: 'app', text: 'App'}),
|
|
61
|
+
button({value: 'hoist', text: 'Hoist'})
|
|
62
|
+
]
|
|
64
63
|
}),
|
|
65
64
|
storeFilterField({
|
|
66
65
|
matchMode: 'any',
|
|
@@ -72,3 +72,12 @@ export const lastReceivedTime: ColumnSpec = {
|
|
|
72
72
|
...Col.compactDate,
|
|
73
73
|
width: 140
|
|
74
74
|
};
|
|
75
|
+
|
|
76
|
+
export const clientAppVersion: ColumnSpec = {
|
|
77
|
+
field: {
|
|
78
|
+
name: 'clientAppVersion',
|
|
79
|
+
type: 'string',
|
|
80
|
+
displayName: 'Client Version'
|
|
81
|
+
},
|
|
82
|
+
width: 120
|
|
83
|
+
};
|
|
@@ -275,7 +275,7 @@ export class RoleModel extends HoistModel {
|
|
|
275
275
|
return new GridModel({
|
|
276
276
|
treeMode: true,
|
|
277
277
|
treeStyle: TreeStyle.HIGHLIGHTS_AND_BORDERS,
|
|
278
|
-
autosizeOptions: {mode: 'managed'},
|
|
278
|
+
autosizeOptions: {mode: 'managed', includeCollapsedChildren: true},
|
|
279
279
|
selModel: 'multiple',
|
|
280
280
|
emptyText: 'No roles found.',
|
|
281
281
|
colChooserModel: true,
|
|
@@ -44,7 +44,7 @@ export class RoleDetailsModel extends HoistModel {
|
|
|
44
44
|
);
|
|
45
45
|
|
|
46
46
|
this.setTabTitle('users', 'Users', role?.effectiveUsers);
|
|
47
|
-
this.setTabTitle('directories', '
|
|
47
|
+
this.setTabTitle('directories', 'Dir. Groups', role?.effectiveDirectoryGroups);
|
|
48
48
|
this.setTabTitle('effectiveRoles', 'Granted To', role?.effectiveRoles);
|
|
49
49
|
this.setTabTitle('inheritedRoles', 'Inheriting From', role?.inheritedRoles);
|
|
50
50
|
},
|
|
@@ -77,6 +77,7 @@ export class RoleDetailsModel extends HoistModel {
|
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
id: 'directories',
|
|
80
|
+
title: 'Dir. Groups',
|
|
80
81
|
omit: !this.roleModel.moduleConfig.directoryGroupsSupported,
|
|
81
82
|
content: directoryMembers
|
|
82
83
|
},
|
|
@@ -24,8 +24,13 @@ export class AppStateModel extends HoistModel {
|
|
|
24
24
|
suspendData: AppSuspendData;
|
|
25
25
|
accessDeniedMessage: string = 'Access Denied';
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Timestamp when the app first started loading, prior to even JS download/eval.
|
|
29
|
+
* Read from timestamp set on window within index.html.
|
|
30
|
+
*/
|
|
31
|
+
readonly loadStarted: number = window['_xhLoadTimestamp'];
|
|
32
|
+
|
|
27
33
|
private timings: Record<AppState, number> = {} as Record<AppState, number>;
|
|
28
|
-
private loadStarted: number = window['_xhLoadTimestamp']; // set in index.html
|
|
29
34
|
private lastStateChangeTime: number = this.loadStarted;
|
|
30
35
|
|
|
31
36
|
constructor() {
|
|
@@ -26,7 +26,10 @@ export declare class ActivityTrackingModel extends HoistModel {
|
|
|
26
26
|
*/
|
|
27
27
|
get queryDisplayString(): string;
|
|
28
28
|
get endDay(): LocalDate;
|
|
29
|
-
get maxRowOptions():
|
|
29
|
+
get maxRowOptions(): {
|
|
30
|
+
value: number;
|
|
31
|
+
label: string;
|
|
32
|
+
}[];
|
|
30
33
|
get maxRows(): number;
|
|
31
34
|
/** True if data loaded from the server has been topped by maxRows. */
|
|
32
35
|
get maxRowsReached(): boolean;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { HoistModel, LoadSpec, PlainObject } from '@xh/hoist/core';
|
|
2
|
-
import { StoreRecord } from '@xh/hoist/data';
|
|
3
2
|
import { ServiceModel } from './ServiceModel';
|
|
4
3
|
export declare class DetailsModel extends HoistModel {
|
|
5
4
|
parent: ServiceModel;
|
|
6
|
-
svcName:
|
|
5
|
+
svcName: String;
|
|
7
6
|
stats: PlainObject;
|
|
8
|
-
get selectedRecord(): StoreRecord;
|
|
7
|
+
get selectedRecord(): import("../../../../../data").StoreRecord;
|
|
9
8
|
onLinked(): void;
|
|
10
9
|
doLoadAsync(loadSpec: LoadSpec): Promise<void>;
|
|
11
10
|
}
|
|
@@ -10,8 +10,12 @@ export declare class AppStateModel extends HoistModel {
|
|
|
10
10
|
lastActivityMs: number;
|
|
11
11
|
suspendData: AppSuspendData;
|
|
12
12
|
accessDeniedMessage: string;
|
|
13
|
+
/**
|
|
14
|
+
* Timestamp when the app first started loading, prior to even JS download/eval.
|
|
15
|
+
* Read from timestamp set on window within index.html.
|
|
16
|
+
*/
|
|
17
|
+
readonly loadStarted: number;
|
|
13
18
|
private timings;
|
|
14
|
-
private loadStarted;
|
|
15
19
|
private lastStateChangeTime;
|
|
16
20
|
constructor();
|
|
17
21
|
setAppState(nextState: AppState): void;
|
|
@@ -68,7 +68,7 @@ export declare class TabContainerModel extends HoistModel implements Persistable
|
|
|
68
68
|
refreshMode: RefreshMode;
|
|
69
69
|
emptyText: ReactNode;
|
|
70
70
|
refreshContextModel: RefreshContextModel;
|
|
71
|
-
|
|
71
|
+
protected lastActiveTabId: string;
|
|
72
72
|
constructor({ tabs, defaultTabId, route, switcher, track, renderMode, refreshMode, persistWith, emptyText, xhImpl }: TabContainerConfig);
|
|
73
73
|
/** Set/replace all tabs within the container. */
|
|
74
74
|
setTabs(tabs: Array<TabModel | TabConfig>): void;
|
|
@@ -110,10 +110,10 @@ export declare class TabContainerModel extends HoistModel implements Persistable
|
|
|
110
110
|
setPersistableState(state: PersistableState<{
|
|
111
111
|
activeTabId: string;
|
|
112
112
|
}>): void;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
protected setActiveTabIdInternal(id: any): void;
|
|
114
|
+
protected syncWithRouter(): void;
|
|
115
|
+
protected forwardRouterToTab(id: any): void;
|
|
116
|
+
protected calculateActiveTabId(tabs: any): any;
|
|
117
117
|
}
|
|
118
118
|
export interface AddTabOptions {
|
|
119
119
|
/** Index in tab collection where tab is to be added. */
|
package/build/types/core/XH.d.ts
CHANGED
|
@@ -9,9 +9,9 @@ import { AppContainerModel } from '../appcontainer/AppContainerModel';
|
|
|
9
9
|
import { BannerModel } from '../appcontainer/BannerModel';
|
|
10
10
|
import { ToastModel } from '../appcontainer/ToastModel';
|
|
11
11
|
import '../styles/XH.scss';
|
|
12
|
-
import { AppSpec, AppState, AppSuspendData, BannerSpec, ExceptionHandler, ExceptionHandlerOptions, HoistAppModel, HoistException, HoistService, HoistServiceClass, HoistUser, MessageSpec, PageState, PlainObject, SizingMode, TaskObserver, Theme, ToastSpec, TrackOptions } from './';
|
|
12
|
+
import { AppSpec, AppState, AppSuspendData, BannerSpec, ExceptionHandler, ExceptionHandlerOptions, HoistAppModel, HoistException, HoistService, HoistServiceClass, HoistUser, MessageSpec, PageState, PlainObject, ReloadAppOptions, SizingMode, TaskObserver, Theme, ToastSpec, TrackOptions } from './';
|
|
13
13
|
import { HoistModel, ModelSelector, RefreshContextModel } from './model';
|
|
14
|
-
export declare const MIN_HOIST_CORE_VERSION = "
|
|
14
|
+
export declare const MIN_HOIST_CORE_VERSION = "28.0";
|
|
15
15
|
/**
|
|
16
16
|
* Top-level Singleton model for Hoist. This is the main entry point for the API.
|
|
17
17
|
*
|
|
@@ -193,13 +193,13 @@ export declare class XHApi {
|
|
|
193
193
|
/**
|
|
194
194
|
* Trigger a full reload of the current application.
|
|
195
195
|
*
|
|
196
|
-
* @param
|
|
197
|
-
*
|
|
196
|
+
* @param opts - options to govern reload. To support legacy usages, a provided
|
|
197
|
+
* string will be treated as `ReloadAppOptions.path`.
|
|
198
198
|
*
|
|
199
199
|
* This method will reload the entire application document in the browser - to trigger a
|
|
200
200
|
* refresh of the loadable content within the app, use {@link refreshAppAsync} instead.
|
|
201
201
|
*/
|
|
202
|
-
reloadApp(
|
|
202
|
+
reloadApp(opts?: ReloadAppOptions | string): void;
|
|
203
203
|
/**
|
|
204
204
|
* Refresh the current application.
|
|
205
205
|
*
|