@xh/hoist 68.0.0-SNAPSHOT.1726237236739 → 68.0.0-SNAPSHOT.1726599173889
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 +10 -0
- package/admin/columns/Tracking.ts +1 -2
- package/admin/tabs/cluster/BaseInstanceModel.ts +21 -6
- package/admin/tabs/cluster/ClusterTab.ts +40 -15
- package/admin/tabs/cluster/ClusterTabModel.ts +21 -21
- package/admin/tabs/cluster/connpool/ConnPoolMonitorPanel.ts +2 -1
- package/admin/tabs/cluster/environment/ServerEnvPanel.ts +2 -1
- package/admin/tabs/cluster/hzobject/HzObjectPanel.ts +2 -1
- package/admin/tabs/cluster/logs/LogDisplayModel.ts +18 -24
- package/admin/tabs/cluster/logs/LogViewer.ts +2 -2
- package/admin/tabs/cluster/logs/LogViewerModel.ts +0 -3
- package/admin/tabs/cluster/memory/MemoryMonitorPanel.ts +2 -1
- package/admin/tabs/cluster/services/DetailsModel.ts +0 -8
- package/admin/tabs/cluster/services/ServicePanel.ts +2 -1
- package/admin/tabs/cluster/websocket/WebSocketModel.ts +1 -7
- package/admin/tabs/general/alertBanner/AlertBannerModel.ts +23 -24
- package/admin/tabs/general/alertBanner/AlertBannerPanel.ts +3 -5
- package/build/types/admin/tabs/cluster/BaseInstanceModel.d.ts +4 -1
- package/build/types/admin/tabs/cluster/ClusterTab.d.ts +2 -0
- package/build/types/admin/tabs/cluster/ClusterTabModel.d.ts +3 -2
- package/build/types/admin/tabs/cluster/logs/LogViewerModel.d.ts +0 -2
- package/build/types/admin/tabs/cluster/services/DetailsModel.d.ts +0 -1
- package/build/types/admin/tabs/cluster/websocket/WebSocketModel.d.ts +0 -2
- package/build/types/admin/tabs/general/alertBanner/AlertBannerModel.d.ts +3 -3
- package/build/types/admin/tabs/general/config/ConfigPanelModel.d.ts +1 -1
- package/build/types/admin/tabs/userData/jsonblob/JsonBlobModel.d.ts +1 -1
- package/build/types/admin/tabs/userData/prefs/editor/PrefEditorModel.d.ts +1 -1
- package/build/types/admin/tabs/userData/users/UserModel.d.ts +1 -1
- package/build/types/desktop/cmp/rest/RestGridModel.d.ts +2 -2
- package/build/types/desktop/cmp/rest/impl/RestFormModel.d.ts +1 -1
- package/build/types/promise/Promise.d.ts +5 -5
- package/build/types/svc/AlertBannerService.d.ts +3 -7
- package/build/types/svc/EnvironmentService.d.ts +6 -2
- package/build/types/svc/IdentityService.d.ts +1 -1
- package/core/exception/ExceptionHandler.ts +1 -1
- package/package.json +1 -1
- package/promise/Promise.ts +11 -12
- package/svc/AlertBannerService.ts +6 -30
- package/svc/EnvironmentService.ts +29 -21
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## 68.0.0-SNAPSHOT - unreleased
|
|
4
4
|
|
|
5
|
+
### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW - Hoist Core update only)
|
|
6
|
+
|
|
7
|
+
* Requires `hoist-core >= 21.1` for consolidated polling of Alert Banner updates (see below).
|
|
8
|
+
|
|
5
9
|
### ⚙️ Technical
|
|
6
10
|
|
|
7
11
|
* Updated Admin Console's Cluster tab to refresh more frequently.
|
|
12
|
+
* Consolidated the polling check for Alert Banner updates into existing `EnvironmentService`
|
|
13
|
+
polling, avoiding an extra request and improving alert banner responsiveness.
|
|
14
|
+
|
|
15
|
+
### ⚙️ Typescript API Adjustments
|
|
16
|
+
|
|
17
|
+
* Corrected types of enhanced `Promise` methods.
|
|
8
18
|
|
|
9
19
|
## 67.0.0 - 2024-09-03
|
|
10
20
|
|
|
@@ -9,8 +9,12 @@ import {HoistModel, LoadSpec, lookup, PlainObject, XH} from '@xh/hoist/core';
|
|
|
9
9
|
import {fmtDateTimeSec, fmtJson} from '@xh/hoist/format';
|
|
10
10
|
import {DAYS} from '@xh/hoist/utils/datetime';
|
|
11
11
|
import {cloneDeep, forOwn, isArray, isNumber, isPlainObject} from 'lodash';
|
|
12
|
+
import {createRef} from 'react';
|
|
13
|
+
import {isDisplayed} from '@xh/hoist/utils/js';
|
|
12
14
|
|
|
13
15
|
export class BaseInstanceModel extends HoistModel {
|
|
16
|
+
viewRef = createRef<HTMLElement>();
|
|
17
|
+
|
|
14
18
|
@lookup(() => ClusterTabModel) parent: ClusterTabModel;
|
|
15
19
|
|
|
16
20
|
get instanceName(): string {
|
|
@@ -24,25 +28,36 @@ export class BaseInstanceModel extends HoistModel {
|
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
handleLoadException(e: unknown, loadSpec: LoadSpec) {
|
|
27
|
-
const instanceNotFound = this.isInstanceNotFound(e)
|
|
31
|
+
const instanceNotFound = this.isInstanceNotFound(e),
|
|
32
|
+
connDown = this.parent.lastLoadException,
|
|
33
|
+
{isVisible} = this,
|
|
34
|
+
{isAutoRefresh} = loadSpec;
|
|
28
35
|
XH.handleException(e, {
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
alertType: 'toast',
|
|
37
|
+
showAlert: !instanceNotFound && !connDown && isVisible,
|
|
38
|
+
logOnServer: !instanceNotFound && !connDown && isVisible && !isAutoRefresh
|
|
31
39
|
});
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
return
|
|
42
|
+
get isVisible() {
|
|
43
|
+
return isDisplayed(this.viewRef.current);
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
//-------------------
|
|
39
47
|
// Implementation
|
|
40
48
|
//-------------------
|
|
49
|
+
private isInstanceNotFound(e: unknown): boolean {
|
|
50
|
+
return e['name'] == 'InstanceNotFoundException';
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
private processTimestamps(stats: PlainObject) {
|
|
42
54
|
forOwn(stats, (v, k) => {
|
|
43
55
|
// Convert numbers that look like recent timestamps to date values.
|
|
44
56
|
if (
|
|
45
|
-
(k.endsWith('Time') ||
|
|
57
|
+
(k.endsWith('Time') ||
|
|
58
|
+
k.endsWith('Date') ||
|
|
59
|
+
k.endsWith('Timestamp') ||
|
|
60
|
+
k == 'timestamp') &&
|
|
46
61
|
isNumber(v) &&
|
|
47
62
|
v > Date.now() - 365 * DAYS
|
|
48
63
|
) {
|
|
@@ -10,14 +10,13 @@ import {creates, hoistCmp} from '@xh/hoist/core';
|
|
|
10
10
|
import {mask} from '@xh/hoist/desktop/cmp/mask';
|
|
11
11
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
12
12
|
import {tabSwitcher} from '@xh/hoist/desktop/cmp/tab';
|
|
13
|
-
import {box, hspacer, placeholder, vframe} from '@xh/hoist/cmp/layout';
|
|
13
|
+
import {box, div, hspacer, p, placeholder, vframe} from '@xh/hoist/cmp/layout';
|
|
14
14
|
import {ClusterTabModel} from './ClusterTabModel';
|
|
15
15
|
import {Icon} from '@xh/hoist/icon';
|
|
16
16
|
|
|
17
17
|
export const clusterTab = hoistCmp.factory({
|
|
18
18
|
model: creates(ClusterTabModel),
|
|
19
19
|
render({model}) {
|
|
20
|
-
const {instance} = model;
|
|
21
20
|
return vframe(
|
|
22
21
|
panel({
|
|
23
22
|
modelConfig: {
|
|
@@ -29,19 +28,45 @@ export const clusterTab = hoistCmp.factory({
|
|
|
29
28
|
},
|
|
30
29
|
item: grid()
|
|
31
30
|
}),
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
compactHeader: true,
|
|
35
|
-
tbar: [
|
|
36
|
-
box({width: 150, item: model.formatInstance(instance)}),
|
|
37
|
-
hspacer(25),
|
|
38
|
-
tabSwitcher()
|
|
39
|
-
],
|
|
40
|
-
flex: 1,
|
|
41
|
-
item: tabContainer()
|
|
42
|
-
})
|
|
43
|
-
: placeholder(Icon.server(), 'Select a running instance above.'),
|
|
44
|
-
mask({bind: model.loadModel})
|
|
31
|
+
detailPanel(),
|
|
32
|
+
failedConnectionMask()
|
|
45
33
|
);
|
|
46
34
|
}
|
|
47
35
|
});
|
|
36
|
+
|
|
37
|
+
export const detailPanel = hoistCmp.factory<ClusterTabModel>({
|
|
38
|
+
render({model}) {
|
|
39
|
+
const {instance, lastLoadException} = model;
|
|
40
|
+
if (!instance?.isReady) {
|
|
41
|
+
return placeholder({
|
|
42
|
+
items: [Icon.server(), 'Select a running instance above.'],
|
|
43
|
+
omit: lastLoadException
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return panel({
|
|
48
|
+
compactHeader: true,
|
|
49
|
+
tbar: [
|
|
50
|
+
box({width: 150, item: model.formatInstance(instance)}),
|
|
51
|
+
hspacer(25),
|
|
52
|
+
tabSwitcher()
|
|
53
|
+
],
|
|
54
|
+
flex: 1,
|
|
55
|
+
item: tabContainer()
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const failedConnectionMask = hoistCmp.factory<ClusterTabModel>({
|
|
61
|
+
render({model}) {
|
|
62
|
+
return mask({
|
|
63
|
+
message: div(
|
|
64
|
+
p('Attempting to connect to cluster.'),
|
|
65
|
+
p('Local instance may be unavailable, please wait.')
|
|
66
|
+
),
|
|
67
|
+
isDisplayed: true,
|
|
68
|
+
spinner: true,
|
|
69
|
+
omit: !model.lastLoadException
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
@@ -18,10 +18,11 @@ import {badge} from '@xh/hoist/cmp/badge';
|
|
|
18
18
|
import {GridModel, numberCol} from '@xh/hoist/cmp/grid';
|
|
19
19
|
import {hbox} from '@xh/hoist/cmp/layout';
|
|
20
20
|
import {getRelativeTimestamp} from '@xh/hoist/cmp/relativetimestamp';
|
|
21
|
-
import {TabContainerModel} from '@xh/hoist/cmp/tab';
|
|
22
|
-
import {HoistModel, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
21
|
+
import {TabContainerModel, TabModel} from '@xh/hoist/cmp/tab';
|
|
22
|
+
import {HoistModel, LoadSpec, lookup, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
23
23
|
import {RecordActionSpec} from '@xh/hoist/data';
|
|
24
24
|
import {Icon} from '@xh/hoist/icon';
|
|
25
|
+
import {makeObservable} from '@xh/hoist/mobx';
|
|
25
26
|
import {Timer} from '@xh/hoist/utils/async';
|
|
26
27
|
import {SECONDS} from '@xh/hoist/utils/datetime';
|
|
27
28
|
import {ReactNode} from 'react';
|
|
@@ -29,6 +30,8 @@ import {ReactNode} from 'react';
|
|
|
29
30
|
export class ClusterTabModel extends HoistModel {
|
|
30
31
|
override persistWith = {localStorageKey: 'xhAdminClusterTabState'};
|
|
31
32
|
|
|
33
|
+
@lookup(TabModel) private tabModel: TabModel;
|
|
34
|
+
|
|
32
35
|
shutdownAction: RecordActionSpec = {
|
|
33
36
|
icon: Icon.skull(),
|
|
34
37
|
text: 'Shutdown Instance',
|
|
@@ -39,7 +42,7 @@ export class ClusterTabModel extends HoistModel {
|
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
@managed readonly gridModel: GridModel = this.createGridModel();
|
|
42
|
-
@managed readonly
|
|
45
|
+
@managed readonly tabContainerModel: TabContainerModel = this.createTabContainerModel();
|
|
43
46
|
@managed readonly timer: Timer;
|
|
44
47
|
|
|
45
48
|
get instance(): PlainObject {
|
|
@@ -56,28 +59,25 @@ export class ClusterTabModel extends HoistModel {
|
|
|
56
59
|
|
|
57
60
|
override async doLoadAsync(loadSpec: LoadSpec) {
|
|
58
61
|
const {gridModel} = this;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
gridModel.loadData(data);
|
|
69
|
-
await gridModel.preSelectFirstAsync();
|
|
70
|
-
} catch (e) {
|
|
71
|
-
gridModel.clear();
|
|
72
|
-
XH.handleException(e);
|
|
73
|
-
}
|
|
62
|
+
let data = await XH.fetchJson({url: 'clusterAdmin/allInstances', loadSpec});
|
|
63
|
+
data = data.map(row => ({
|
|
64
|
+
...row,
|
|
65
|
+
isLocal: row.name == XH.environmentService.serverInstance,
|
|
66
|
+
usedHeapMb: row.memory?.usedHeapMb,
|
|
67
|
+
usedPctMax: row.memory?.usedPctMax
|
|
68
|
+
}));
|
|
69
|
+
gridModel.loadData(data);
|
|
70
|
+
await gridModel.preSelectFirstAsync();
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
constructor() {
|
|
77
74
|
super();
|
|
75
|
+
makeObservable(this);
|
|
78
76
|
|
|
79
77
|
this.timer = Timer.create({
|
|
80
|
-
runFn:
|
|
78
|
+
runFn: () => {
|
|
79
|
+
if (this.tabModel?.isActive) this.autoRefreshAsync();
|
|
80
|
+
},
|
|
81
81
|
interval: 5 * SECONDS,
|
|
82
82
|
delay: true
|
|
83
83
|
});
|
|
@@ -86,7 +86,7 @@ export class ClusterTabModel extends HoistModel {
|
|
|
86
86
|
{
|
|
87
87
|
track: () => this.instanceName,
|
|
88
88
|
run: instName => {
|
|
89
|
-
if (instName) this.
|
|
89
|
+
if (instName) this.tabContainerModel.refreshContextModel.refreshAsync();
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -161,7 +161,7 @@ export class ClusterTabModel extends HoistModel {
|
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
createTabContainerModel() {
|
|
165
165
|
return new TabContainerModel({
|
|
166
166
|
route: 'default.cluster',
|
|
167
167
|
switcher: false,
|
|
@@ -11,7 +11,7 @@ import {Icon} from '@xh/hoist/icon';
|
|
|
11
11
|
import {bindable, makeObservable} from '@xh/hoist/mobx';
|
|
12
12
|
import {Timer} from '@xh/hoist/utils/async';
|
|
13
13
|
import {olderThan, ONE_SECOND, SECONDS} from '@xh/hoist/utils/datetime';
|
|
14
|
-
import {debounced
|
|
14
|
+
import {debounced} from '@xh/hoist/utils/js';
|
|
15
15
|
import {escapeRegExp, maxBy} from 'lodash';
|
|
16
16
|
import {LogViewerModel} from './LogViewerModel';
|
|
17
17
|
|
|
@@ -105,26 +105,21 @@ export class LogDisplayModel extends HoistModel {
|
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
} catch (e) {
|
|
124
|
-
// Show errors inline in the viewer vs. a modal alert or catchDefault().
|
|
125
|
-
const msg = e.message || 'An unknown error occurred';
|
|
126
|
-
this.updateGridData([[0, `Error: ${msg}`]]);
|
|
127
|
-
}
|
|
108
|
+
const response = await XH.fetchJson({
|
|
109
|
+
url: 'logViewerAdmin/getFile',
|
|
110
|
+
params: {
|
|
111
|
+
filename: parent.file,
|
|
112
|
+
startLine: this.startLine,
|
|
113
|
+
maxLines: this.maxLines,
|
|
114
|
+
pattern: this.regexOption ? this.pattern : escapeRegExp(this.pattern),
|
|
115
|
+
caseSensitive: this.caseSensitive,
|
|
116
|
+
instance: parent.instanceName
|
|
117
|
+
},
|
|
118
|
+
loadSpec
|
|
119
|
+
});
|
|
120
|
+
// Backward compatibility for Hoist Core < v22, which returned exception in-band
|
|
121
|
+
if (!response.success) throw XH.exception(response.exception);
|
|
122
|
+
this.updateGridData(response.content);
|
|
128
123
|
}
|
|
129
124
|
|
|
130
125
|
async scrollToTail() {
|
|
@@ -209,14 +204,13 @@ export class LogDisplayModel extends HoistModel {
|
|
|
209
204
|
}
|
|
210
205
|
|
|
211
206
|
private autoRefreshLines() {
|
|
212
|
-
const {tailActive
|
|
213
|
-
{viewRef} = parent;
|
|
207
|
+
const {tailActive} = this;
|
|
214
208
|
|
|
215
209
|
if (
|
|
216
210
|
tailActive &&
|
|
217
211
|
olderThan(this.lastLoadCompleted, 5 * SECONDS) &&
|
|
218
212
|
!this.loadModel.isPending &&
|
|
219
|
-
|
|
213
|
+
this.parent.isVisible
|
|
220
214
|
) {
|
|
221
215
|
this.loadLog();
|
|
222
216
|
}
|
|
@@ -29,7 +29,6 @@ export const logViewer = hoistCmp.factory({
|
|
|
29
29
|
|
|
30
30
|
return hframe({
|
|
31
31
|
className,
|
|
32
|
-
ref: model.viewRef,
|
|
33
32
|
items: [
|
|
34
33
|
panel({
|
|
35
34
|
collapsedTitle: 'Log Files',
|
|
@@ -57,7 +56,8 @@ export const logViewer = hoistCmp.factory({
|
|
|
57
56
|
}),
|
|
58
57
|
logDisplay(),
|
|
59
58
|
model.showLogLevelDialog ? logLevelDialog() : null
|
|
60
|
-
]
|
|
59
|
+
],
|
|
60
|
+
ref: model.viewRef
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
});
|
|
@@ -14,7 +14,6 @@ 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
16
|
import download from 'downloadjs';
|
|
17
|
-
import {createRef} from 'react';
|
|
18
17
|
import {LogDisplayModel} from './LogDisplayModel';
|
|
19
18
|
|
|
20
19
|
/**
|
|
@@ -23,8 +22,6 @@ import {LogDisplayModel} from './LogDisplayModel';
|
|
|
23
22
|
export class LogViewerModel extends BaseInstanceModel {
|
|
24
23
|
@observable file: string = null;
|
|
25
24
|
|
|
26
|
-
viewRef = createRef<HTMLElement>();
|
|
27
|
-
|
|
28
25
|
@managed
|
|
29
26
|
logDisplayModel = new LogDisplayModel(this);
|
|
30
27
|
|
|
@@ -48,14 +48,6 @@ export class DetailsModel extends HoistModel {
|
|
|
48
48
|
loadSpec
|
|
49
49
|
});
|
|
50
50
|
if (loadSpec.isStale) return;
|
|
51
|
-
this.preprocessRawData(resp);
|
|
52
51
|
this.stats = resp;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
private preprocessRawData(resp) {
|
|
56
|
-
// Format distributed objects for readability
|
|
57
|
-
resp.distributedObjects?.forEach(obj => {
|
|
58
|
-
obj.name = obj.name.substring(obj.name.indexOf('_') + 1);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
53
|
}
|
|
@@ -15,16 +15,12 @@ import {Icon} from '@xh/hoist/icon';
|
|
|
15
15
|
import {makeObservable, observable, runInAction} from '@xh/hoist/mobx';
|
|
16
16
|
import {Timer} from '@xh/hoist/utils/async';
|
|
17
17
|
import {SECONDS} from '@xh/hoist/utils/datetime';
|
|
18
|
-
import {isDisplayed} from '@xh/hoist/utils/js';
|
|
19
18
|
import {isEmpty} from 'lodash';
|
|
20
|
-
import {createRef} from 'react';
|
|
21
19
|
import * as WSCol from './WebSocketColumns';
|
|
22
20
|
import {RecordActionSpec} from '@xh/hoist/data';
|
|
23
21
|
import {AppModel} from '@xh/hoist/admin/AppModel';
|
|
24
22
|
|
|
25
23
|
export class WebSocketModel extends BaseInstanceModel {
|
|
26
|
-
viewRef = createRef<HTMLElement>();
|
|
27
|
-
|
|
28
24
|
@observable
|
|
29
25
|
lastRefresh: number;
|
|
30
26
|
|
|
@@ -87,9 +83,7 @@ export class WebSocketModel extends BaseInstanceModel {
|
|
|
87
83
|
|
|
88
84
|
this._timer = Timer.create({
|
|
89
85
|
runFn: () => {
|
|
90
|
-
if (
|
|
91
|
-
this.autoRefreshAsync();
|
|
92
|
-
}
|
|
86
|
+
if (this.isVisible) this.autoRefreshAsync();
|
|
93
87
|
},
|
|
94
88
|
interval: 5 * SECONDS,
|
|
95
89
|
delay: true
|
|
@@ -10,13 +10,13 @@ import {FormModel} from '@xh/hoist/cmp/form';
|
|
|
10
10
|
import {fragment, p} from '@xh/hoist/cmp/layout';
|
|
11
11
|
import {HoistModel, Intent, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
12
12
|
import {dateIs, required} from '@xh/hoist/data';
|
|
13
|
-
import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
13
|
+
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
14
14
|
import {AlertBannerSpec} from '@xh/hoist/svc';
|
|
15
15
|
import {isEqual, isMatch, sortBy, without} from 'lodash';
|
|
16
16
|
|
|
17
17
|
export class AlertBannerModel extends HoistModel {
|
|
18
|
-
savedValue;
|
|
19
|
-
@
|
|
18
|
+
savedValue: AlertBannerSpec;
|
|
19
|
+
@bindable.ref savedPresets: PlainObject[] = [];
|
|
20
20
|
|
|
21
21
|
@managed
|
|
22
22
|
formModel = new FormModel({
|
|
@@ -118,7 +118,6 @@ export class AlertBannerModel extends HoistModel {
|
|
|
118
118
|
this.formModel.setValues({...preset, expires: null});
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
@action
|
|
122
121
|
addPreset() {
|
|
123
122
|
const {message, intent, iconName, enableClose, clientApps} = this.formModel.values,
|
|
124
123
|
dateCreated = Date.now(),
|
|
@@ -188,24 +187,6 @@ export class AlertBannerModel extends HoistModel {
|
|
|
188
187
|
}
|
|
189
188
|
}
|
|
190
189
|
|
|
191
|
-
async saveBannerSpecAsync(spec: AlertBannerSpec) {
|
|
192
|
-
const {active, message, intent, iconName, enableClose, clientApps} = spec;
|
|
193
|
-
try {
|
|
194
|
-
await XH.fetchService.postJson({
|
|
195
|
-
url: 'alertBannerAdmin/setAlertSpec',
|
|
196
|
-
body: spec,
|
|
197
|
-
track: {
|
|
198
|
-
category: 'Audit',
|
|
199
|
-
message: 'Updated Alert Banner',
|
|
200
|
-
data: {active, message, intent, iconName, enableClose, clientApps},
|
|
201
|
-
logData: ['active']
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
} catch (e) {
|
|
205
|
-
XH.handleException(e);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
190
|
//----------------
|
|
210
191
|
// Implementation
|
|
211
192
|
//----------------
|
|
@@ -232,7 +213,7 @@ export class AlertBannerModel extends HoistModel {
|
|
|
232
213
|
let preservedPublishDate = null;
|
|
233
214
|
|
|
234
215
|
// Ask some questions if we are dealing with live stuff
|
|
235
|
-
if (
|
|
216
|
+
if (active || savedValue?.active) {
|
|
236
217
|
// Question 1. Reshow when modifying an active && already active, closable banner?
|
|
237
218
|
if (
|
|
238
219
|
active &&
|
|
@@ -299,7 +280,25 @@ export class AlertBannerModel extends HoistModel {
|
|
|
299
280
|
};
|
|
300
281
|
|
|
301
282
|
await this.saveBannerSpecAsync(value);
|
|
302
|
-
await XH.
|
|
283
|
+
await XH.environmentService.pollServerAsync();
|
|
303
284
|
await this.refreshAsync();
|
|
304
285
|
}
|
|
286
|
+
|
|
287
|
+
private async saveBannerSpecAsync(spec: AlertBannerSpec) {
|
|
288
|
+
const {active, message, intent, iconName, enableClose, clientApps} = spec;
|
|
289
|
+
try {
|
|
290
|
+
await XH.fetchService.postJson({
|
|
291
|
+
url: 'alertBannerAdmin/setAlertSpec',
|
|
292
|
+
body: spec,
|
|
293
|
+
track: {
|
|
294
|
+
category: 'Audit',
|
|
295
|
+
message: 'Updated Alert Banner',
|
|
296
|
+
data: {active, message, intent, iconName, enableClose, clientApps},
|
|
297
|
+
logData: ['active']
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
} catch (e) {
|
|
301
|
+
XH.handleException(e);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
305
304
|
}
|
|
@@ -37,7 +37,7 @@ import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
|
37
37
|
import {dateTimeRenderer} from '@xh/hoist/format';
|
|
38
38
|
import {Icon} from '@xh/hoist/icon';
|
|
39
39
|
import {menu, menuItem, popover} from '@xh/hoist/kit/blueprint';
|
|
40
|
-
import {LocalDate
|
|
40
|
+
import {LocalDate} from '@xh/hoist/utils/datetime';
|
|
41
41
|
import {isEmpty} from 'lodash';
|
|
42
42
|
import {ReactNode} from 'react';
|
|
43
43
|
import {AlertBannerModel} from './AlertBannerModel';
|
|
@@ -72,15 +72,13 @@ const formPanel = hoistCmp.factory<AlertBannerModel>(({model}) => {
|
|
|
72
72
|
labelWidth: 100
|
|
73
73
|
},
|
|
74
74
|
items: [
|
|
75
|
-
XH.
|
|
75
|
+
XH.getConf('xhAlertBannerConfig', {}).enabled
|
|
76
76
|
? div({
|
|
77
77
|
className: `${baseClassName}__intro`,
|
|
78
78
|
items: [
|
|
79
79
|
p(`Show an alert banner to all ${XH.appName} users.`),
|
|
80
80
|
p(
|
|
81
|
-
|
|
82
|
-
XH.alertBannerService.interval / SECONDS
|
|
83
|
-
}s once marked Active and saved.`
|
|
81
|
+
'Configure and preview below. Presets can be saved and loaded via bottom bar menu. Banner will appear to all users once marked Active and saved.'
|
|
84
82
|
)
|
|
85
83
|
]
|
|
86
84
|
})
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
1
2
|
import { ClusterTabModel } from '@xh/hoist/admin/tabs/cluster/ClusterTabModel';
|
|
2
3
|
import { HoistModel, LoadSpec, PlainObject } from '@xh/hoist/core';
|
|
3
4
|
export declare class BaseInstanceModel extends HoistModel {
|
|
5
|
+
viewRef: import("react").RefObject<HTMLElement>;
|
|
4
6
|
parent: ClusterTabModel;
|
|
5
7
|
get instanceName(): string;
|
|
6
8
|
fmtStats(stats: PlainObject): string;
|
|
7
9
|
handleLoadException(e: unknown, loadSpec: LoadSpec): void;
|
|
8
|
-
|
|
10
|
+
get isVisible(): boolean;
|
|
11
|
+
private isInstanceNotFound;
|
|
9
12
|
private processTimestamps;
|
|
10
13
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { ClusterTabModel } from './ClusterTabModel';
|
|
2
2
|
export declare const clusterTab: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<ClusterTabModel>>;
|
|
3
|
+
export declare const detailPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<ClusterTabModel>>;
|
|
4
|
+
export declare const failedConnectionMask: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<ClusterTabModel>>;
|
|
@@ -8,9 +8,10 @@ export declare class ClusterTabModel extends HoistModel {
|
|
|
8
8
|
persistWith: {
|
|
9
9
|
localStorageKey: string;
|
|
10
10
|
};
|
|
11
|
+
private tabModel;
|
|
11
12
|
shutdownAction: RecordActionSpec;
|
|
12
13
|
readonly gridModel: GridModel;
|
|
13
|
-
readonly
|
|
14
|
+
readonly tabContainerModel: TabContainerModel;
|
|
14
15
|
readonly timer: Timer;
|
|
15
16
|
get instance(): PlainObject;
|
|
16
17
|
get instanceName(): string;
|
|
@@ -18,7 +19,7 @@ export declare class ClusterTabModel extends HoistModel {
|
|
|
18
19
|
doLoadAsync(loadSpec: LoadSpec): Promise<void>;
|
|
19
20
|
constructor();
|
|
20
21
|
private createGridModel;
|
|
21
|
-
|
|
22
|
+
createTabContainerModel(): TabContainerModel;
|
|
22
23
|
formatInstance(instance: PlainObject): ReactNode;
|
|
23
24
|
shutdownInstanceAsync(instance: PlainObject): Promise<void>;
|
|
24
25
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { BaseInstanceModel } from '@xh/hoist/admin/tabs/cluster/BaseInstanceModel';
|
|
3
2
|
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
4
3
|
import { LoadSpec } from '@xh/hoist/core';
|
|
@@ -9,7 +8,6 @@ import { LogDisplayModel } from './LogDisplayModel';
|
|
|
9
8
|
*/
|
|
10
9
|
export declare class LogViewerModel extends BaseInstanceModel {
|
|
11
10
|
file: string;
|
|
12
|
-
viewRef: import("react").RefObject<HTMLElement>;
|
|
13
11
|
logDisplayModel: LogDisplayModel;
|
|
14
12
|
filesGridModel: GridModel;
|
|
15
13
|
instanceOnly: boolean;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { BaseInstanceModel } from '@xh/hoist/admin/tabs/cluster/BaseInstanceModel';
|
|
3
2
|
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
4
3
|
import { LoadSpec } from '@xh/hoist/core';
|
|
5
4
|
import { RecordActionSpec } from '@xh/hoist/data';
|
|
6
5
|
export declare class WebSocketModel extends BaseInstanceModel {
|
|
7
|
-
viewRef: import("react").RefObject<HTMLElement>;
|
|
8
6
|
lastRefresh: number;
|
|
9
7
|
gridModel: GridModel;
|
|
10
8
|
private _timer;
|