@xh/hoist 73.0.0-SNAPSHOT.1746647745931 → 73.0.0-SNAPSHOT.1746740884592
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 +4 -1
- package/admin/AppModel.ts +6 -10
- package/admin/columns/Tracking.ts +11 -11
- package/admin/tabs/activity/tracking/ActivityTracking.scss +18 -0
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +15 -6
- package/admin/tabs/activity/tracking/detail/ActivityDetailModel.ts +3 -0
- package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +1 -0
- package/admin/tabs/{client/clients → clients}/ClientsPanel.ts +2 -2
- package/admin/tabs/{client/clients → clients}/activity/ClientDetailModel.ts +2 -2
- package/admin/tabs/{client/clients → clients}/activity/ClientDetailPanel.ts +1 -1
- package/admin/tabs/cluster/ClusterTab.ts +1 -1
- package/admin/tabs/cluster/instances/InstancesTabModel.ts +1 -1
- package/admin/tabs/general/GeneralTab.ts +0 -2
- package/appcontainer/FeedbackDialogModel.ts +12 -13
- package/build/types/admin/columns/Tracking.d.ts +1 -3
- package/build/types/admin/tabs/{client/clients → clients}/activity/ClientDetailModel.d.ts +2 -2
- package/build/types/admin/tabs/{client/clients → clients}/activity/ClientDetailPanel.d.ts +1 -1
- package/build/types/appcontainer/FeedbackDialogModel.d.ts +1 -4
- package/core/exception/ExceptionHandler.ts +14 -15
- package/package.json +1 -1
- package/styles/XH.scss +1 -0
- package/styles/vars.scss +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/admin/tabs/client/ClientTab.ts +0 -24
- package/admin/tabs/client/errors/ClientErrorDetail.ts +0 -129
- package/admin/tabs/client/errors/ClientErrors.scss +0 -52
- package/admin/tabs/client/errors/ClientErrorsModel.ts +0 -227
- package/admin/tabs/client/errors/ClientErrorsPanel.ts +0 -101
- package/admin/tabs/general/feedback/FeedbackPanel.ts +0 -61
- package/build/types/admin/tabs/client/ClientTab.d.ts +0 -1
- package/build/types/admin/tabs/client/errors/ClientErrorDetail.d.ts +0 -2
- package/build/types/admin/tabs/client/errors/ClientErrorsModel.d.ts +0 -30
- package/build/types/admin/tabs/client/errors/ClientErrorsPanel.d.ts +0 -3
- package/build/types/admin/tabs/general/feedback/FeedbackPanel.d.ts +0 -1
- /package/admin/tabs/{client/clients → clients}/ClientsModel.ts +0 -0
- /package/admin/tabs/{client/clients → clients}/activity/ClientDetail.scss +0 -0
- /package/build/types/admin/tabs/{client/clients → clients}/ClientsModel.d.ts +0 -0
- /package/build/types/admin/tabs/{client/clients → clients}/ClientsPanel.d.ts +0 -0
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
import {clientErrorsPanel} from '@xh/hoist/admin/tabs/client/errors/ClientErrorsPanel';
|
|
8
|
-
import {tabContainer} from '@xh/hoist/cmp/tab';
|
|
9
|
-
import {hoistCmp} from '@xh/hoist/core';
|
|
10
|
-
import {Icon} from '@xh/hoist/icon';
|
|
11
|
-
import {clientsPanel} from './clients/ClientsPanel';
|
|
12
|
-
|
|
13
|
-
export const clientTab = hoistCmp.factory(() =>
|
|
14
|
-
tabContainer({
|
|
15
|
-
modelConfig: {
|
|
16
|
-
route: 'default.clients',
|
|
17
|
-
switcher: {orientation: 'left', testId: 'client-tab-switcher'},
|
|
18
|
-
tabs: [
|
|
19
|
-
{id: 'connections', icon: Icon.diff(), content: clientsPanel},
|
|
20
|
-
{id: 'errors', icon: Icon.warning(), content: clientErrorsPanel}
|
|
21
|
-
]
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
);
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
import {correlationId, instance} from '@xh/hoist/admin/columns';
|
|
8
|
-
import {form} from '@xh/hoist/cmp/form';
|
|
9
|
-
import {a, div, h3, hframe, span, vbox} from '@xh/hoist/cmp/layout';
|
|
10
|
-
import {hoistCmp} from '@xh/hoist/core';
|
|
11
|
-
import {formField} from '@xh/hoist/desktop/cmp/form';
|
|
12
|
-
import {jsonInput} from '@xh/hoist/desktop/cmp/input';
|
|
13
|
-
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
14
|
-
import {fmtDateTimeSec} from '@xh/hoist/format';
|
|
15
|
-
import {Icon} from '@xh/hoist/icon';
|
|
16
|
-
import {isNil} from 'lodash';
|
|
17
|
-
import {ClientErrorsModel} from './ClientErrorsModel';
|
|
18
|
-
|
|
19
|
-
export const clientErrorDetail = hoistCmp.factory<ClientErrorsModel>(({model}) => {
|
|
20
|
-
const {selectedRecord, formattedErrorJson, formModel} = model,
|
|
21
|
-
userMsg = formModel.values.msg;
|
|
22
|
-
|
|
23
|
-
if (!selectedRecord) return null;
|
|
24
|
-
|
|
25
|
-
return panel({
|
|
26
|
-
className: 'xh-admin-client-errors-detail',
|
|
27
|
-
collapsedTitle: 'Error Details',
|
|
28
|
-
collapsedIcon: Icon.info(),
|
|
29
|
-
compactHeader: true,
|
|
30
|
-
modelConfig: {
|
|
31
|
-
side: 'bottom',
|
|
32
|
-
defaultSize: 400
|
|
33
|
-
},
|
|
34
|
-
item: form({
|
|
35
|
-
fieldDefaults: {inline: true, readonlyRenderer: valOrNa},
|
|
36
|
-
item: hframe(
|
|
37
|
-
div({
|
|
38
|
-
className: 'xh-admin-client-errors-detail__form',
|
|
39
|
-
style: {width: '400px'},
|
|
40
|
-
items: [
|
|
41
|
-
h3(Icon.info(), 'Error Info'),
|
|
42
|
-
formField({
|
|
43
|
-
field: 'username',
|
|
44
|
-
readonlyRenderer: username => {
|
|
45
|
-
if (!username) return naSpan();
|
|
46
|
-
const {impersonating} = formModel.values,
|
|
47
|
-
impSpan = impersonating
|
|
48
|
-
? span({
|
|
49
|
-
className: 'xh-text-color-accent',
|
|
50
|
-
item: ` (impersonating ${impersonating})`
|
|
51
|
-
})
|
|
52
|
-
: null;
|
|
53
|
-
return span(username, impSpan);
|
|
54
|
-
}
|
|
55
|
-
}),
|
|
56
|
-
formField({
|
|
57
|
-
field: 'appVersion',
|
|
58
|
-
readonlyRenderer: appVersion => {
|
|
59
|
-
if (!appVersion) return naSpan();
|
|
60
|
-
const {appEnvironment} = formModel.values;
|
|
61
|
-
return `${appVersion} (${appEnvironment})`;
|
|
62
|
-
}
|
|
63
|
-
}),
|
|
64
|
-
formField({
|
|
65
|
-
field: 'userAlerted',
|
|
66
|
-
label: 'User Alerted?'
|
|
67
|
-
}),
|
|
68
|
-
formField({
|
|
69
|
-
field: 'url',
|
|
70
|
-
readonlyRenderer: hyperlinkVal
|
|
71
|
-
}),
|
|
72
|
-
formField({
|
|
73
|
-
field: 'instance',
|
|
74
|
-
readonlyRenderer: v => instance.renderer(v, null)
|
|
75
|
-
}),
|
|
76
|
-
formField({
|
|
77
|
-
field: 'correlationId',
|
|
78
|
-
readonlyRenderer: v => correlationId.renderer(v, null)
|
|
79
|
-
}),
|
|
80
|
-
formField({
|
|
81
|
-
field: 'dateCreated',
|
|
82
|
-
readonlyRenderer: v => fmtDateTimeSec(v)
|
|
83
|
-
}),
|
|
84
|
-
h3(Icon.desktop(), 'Device / Browser'),
|
|
85
|
-
formField({field: 'device'}),
|
|
86
|
-
formField({field: 'browser'}),
|
|
87
|
-
formField({field: 'userAgent'})
|
|
88
|
-
]
|
|
89
|
-
}),
|
|
90
|
-
vbox({
|
|
91
|
-
flex: 1,
|
|
92
|
-
className: 'xh-border-left',
|
|
93
|
-
items: [
|
|
94
|
-
panel({
|
|
95
|
-
flex: 0.5,
|
|
96
|
-
className: 'xh-border-bottom',
|
|
97
|
-
items: [
|
|
98
|
-
h3(Icon.comment(), 'User Message'),
|
|
99
|
-
div({
|
|
100
|
-
className: `xh-pad`,
|
|
101
|
-
style: {overflowY: 'auto'},
|
|
102
|
-
item: userMsg
|
|
103
|
-
})
|
|
104
|
-
],
|
|
105
|
-
omit: !userMsg
|
|
106
|
-
}),
|
|
107
|
-
panel({
|
|
108
|
-
flex: 1,
|
|
109
|
-
items: [
|
|
110
|
-
h3(Icon.json(), 'Additional Data'),
|
|
111
|
-
jsonInput({
|
|
112
|
-
readonly: true,
|
|
113
|
-
width: '100%',
|
|
114
|
-
height: '100%',
|
|
115
|
-
showCopyButton: true,
|
|
116
|
-
value: formattedErrorJson ?? '{}'
|
|
117
|
-
})
|
|
118
|
-
]
|
|
119
|
-
})
|
|
120
|
-
]
|
|
121
|
-
})
|
|
122
|
-
)
|
|
123
|
-
})
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const valOrNa = v => (!isNil(v) ? v.toString() : naSpan());
|
|
128
|
-
const naSpan = () => span({item: 'N/A', className: 'xh-text-color-muted'});
|
|
129
|
-
const hyperlinkVal = v => (v ? a({href: v, item: v, target: '_blank'}) : '-');
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
.xh-admin-client-errors-detail {
|
|
9
|
-
&__form {
|
|
10
|
-
overflow-y: auto;
|
|
11
|
-
|
|
12
|
-
.xh-form-field {
|
|
13
|
-
margin: 0;
|
|
14
|
-
padding: 2px var(--xh-pad-px);
|
|
15
|
-
|
|
16
|
-
&.xh-form-field-readonly .xh-form-field-readonly-display {
|
|
17
|
-
padding: var(--xh-pad-half-px);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
&:nth-child(odd) {
|
|
21
|
-
background-color: var(--xh-grid-bg-odd);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.xh-form-field-label {
|
|
26
|
-
font-size: var(--xh-font-size-small-px);
|
|
27
|
-
padding: 0;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
h3 {
|
|
32
|
-
color: var(--xh-text-color-headings);
|
|
33
|
-
background-color: var(--xh-blue-gray-light);
|
|
34
|
-
margin: 0 !important;
|
|
35
|
-
font-size: var(--xh-font-size-px);
|
|
36
|
-
font-weight: normal;
|
|
37
|
-
padding: var(--xh-pad-half-px) !important;
|
|
38
|
-
border-bottom: var(--xh-border-solid);
|
|
39
|
-
|
|
40
|
-
&:not(:first-child) {
|
|
41
|
-
border-top: var(--xh-border-solid);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.xh-dark & {
|
|
45
|
-
background-color: var(--xh-blue-gray-dark);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.xh-icon {
|
|
49
|
-
margin-right: var(--xh-pad-px);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
import {exportFilename} from '@xh/hoist/admin/AdminUtils';
|
|
8
|
-
import * as Col from '@xh/hoist/admin/columns';
|
|
9
|
-
import {FilterChooserModel} from '@xh/hoist/cmp/filter';
|
|
10
|
-
import {FormModel} from '@xh/hoist/cmp/form';
|
|
11
|
-
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
12
|
-
import {HoistModel, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
13
|
-
import {StoreRecord} from '@xh/hoist/data';
|
|
14
|
-
import {fmtJson} from '@xh/hoist/format';
|
|
15
|
-
import {action, bindable, comparer, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
16
|
-
import {LocalDate} from '@xh/hoist/utils/datetime';
|
|
17
|
-
|
|
18
|
-
export class ClientErrorsModel extends HoistModel {
|
|
19
|
-
override persistWith = {localStorageKey: 'xhAdminClientErrorsState'};
|
|
20
|
-
|
|
21
|
-
@bindable.ref startDay: LocalDate;
|
|
22
|
-
@bindable.ref endDay: LocalDate;
|
|
23
|
-
|
|
24
|
-
@managed gridModel: GridModel;
|
|
25
|
-
@managed formModel: FormModel;
|
|
26
|
-
@managed filterChooserModel: FilterChooserModel;
|
|
27
|
-
|
|
28
|
-
get selectedRecord() {
|
|
29
|
-
return this.gridModel.selectedRecord;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Parsed and JSON-formatted stacktrace / additional data for selected error. */
|
|
33
|
-
@observable formattedErrorJson: string;
|
|
34
|
-
|
|
35
|
-
constructor() {
|
|
36
|
-
super();
|
|
37
|
-
makeObservable(this);
|
|
38
|
-
this.startDay = this.defaultStartDay;
|
|
39
|
-
this.endDay = this.defaultEndDay;
|
|
40
|
-
|
|
41
|
-
const hidden = true,
|
|
42
|
-
pinned = true;
|
|
43
|
-
|
|
44
|
-
this.gridModel = new GridModel({
|
|
45
|
-
persistWith: this.persistWith,
|
|
46
|
-
colChooserModel: true,
|
|
47
|
-
enableExport: true,
|
|
48
|
-
exportOptions: {
|
|
49
|
-
filename: exportFilename('client-errors'),
|
|
50
|
-
columns: 'ALL'
|
|
51
|
-
},
|
|
52
|
-
emptyText: 'No errors reported...',
|
|
53
|
-
sortBy: 'dateCreated|desc',
|
|
54
|
-
columns: [
|
|
55
|
-
{...Col.entryId, hidden},
|
|
56
|
-
{...Col.userMessageFlag, pinned},
|
|
57
|
-
{...Col.userAlertedFlag, pinned},
|
|
58
|
-
{...Col.impersonatingFlag, pinned},
|
|
59
|
-
{...Col.username, pinned},
|
|
60
|
-
{...Col.impersonating, hidden},
|
|
61
|
-
{...Col.browser},
|
|
62
|
-
{...Col.device},
|
|
63
|
-
{...Col.userAgent, hidden},
|
|
64
|
-
{...Col.appVersion},
|
|
65
|
-
{...Col.appEnvironment},
|
|
66
|
-
{...Col.msg, displayName: 'User Message', hidden},
|
|
67
|
-
{...Col.errorName},
|
|
68
|
-
{...Col.errorMessage},
|
|
69
|
-
{...Col.error, hidden},
|
|
70
|
-
{...Col.url},
|
|
71
|
-
{...Col.correlationId},
|
|
72
|
-
{...Col.instance, hidden},
|
|
73
|
-
{...Col.day},
|
|
74
|
-
{...Col.dateCreatedWithSec, displayName: 'Timestamp'}
|
|
75
|
-
]
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const enableValues = true;
|
|
79
|
-
this.filterChooserModel = new FilterChooserModel({
|
|
80
|
-
fieldSpecs: [
|
|
81
|
-
{field: 'correlationId'},
|
|
82
|
-
{field: 'username', displayName: 'User', enableValues},
|
|
83
|
-
{field: 'browser', enableValues},
|
|
84
|
-
{field: 'device', enableValues},
|
|
85
|
-
{field: 'appVersion'},
|
|
86
|
-
{field: 'appEnvironment', displayName: 'Environment', enableValues},
|
|
87
|
-
{field: 'userAlerted'},
|
|
88
|
-
{field: 'userAgent'},
|
|
89
|
-
{field: 'msg', displayName: 'User Message'},
|
|
90
|
-
{field: 'error'},
|
|
91
|
-
{field: 'url', displayName: 'URL'},
|
|
92
|
-
{field: 'instance'},
|
|
93
|
-
{field: 'impersonating'}
|
|
94
|
-
]
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
this.loadFieldSpecValues();
|
|
98
|
-
|
|
99
|
-
this.formModel = new FormModel({
|
|
100
|
-
readonly: true,
|
|
101
|
-
fields: this.gridModel
|
|
102
|
-
.getLeafColumns()
|
|
103
|
-
.map(it => ({name: it.field, displayName: it.headerName as string}))
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
this.addReaction({
|
|
107
|
-
track: () => this.query,
|
|
108
|
-
run: () => this.loadAsync(),
|
|
109
|
-
equals: comparer.structural
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
this.addReaction({
|
|
113
|
-
track: () => this.gridModel.selectedRecord,
|
|
114
|
-
run: detailRec => this.showEntryDetail(detailRec)
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@action
|
|
119
|
-
resetQuery() {
|
|
120
|
-
this.startDay = this.defaultStartDay;
|
|
121
|
-
this.endDay = this.defaultEndDay;
|
|
122
|
-
this.filterChooserModel.setValue(null);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
override async doLoadAsync(loadSpec: LoadSpec) {
|
|
126
|
-
const {query, gridModel} = this;
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const data: PlainObject[] = await XH.fetchService.postJson({
|
|
130
|
-
url: 'clientErrorAdmin',
|
|
131
|
-
body: query,
|
|
132
|
-
loadSpec
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Parse name + message from JSON-serialized error object out to top-level properties.
|
|
136
|
-
data.forEach(it => {
|
|
137
|
-
try {
|
|
138
|
-
const error = JSON.parse(it.error);
|
|
139
|
-
it.errorName = error?.name;
|
|
140
|
-
it.errorMessage = error?.message;
|
|
141
|
-
} catch (ignored) {}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
gridModel.loadData(data);
|
|
145
|
-
await gridModel.preSelectFirstAsync();
|
|
146
|
-
} catch (e) {
|
|
147
|
-
if (loadSpec.isStale || loadSpec.isAutoRefresh) return;
|
|
148
|
-
|
|
149
|
-
gridModel.clear();
|
|
150
|
-
XH.handleException(e);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
@action
|
|
155
|
-
showEntryDetail(detailRec: StoreRecord) {
|
|
156
|
-
const recData = detailRec?.data ?? {},
|
|
157
|
-
errorData = recData.error;
|
|
158
|
-
|
|
159
|
-
this.formModel.init(recData);
|
|
160
|
-
|
|
161
|
-
let formattedErrorJson = errorData;
|
|
162
|
-
if (formattedErrorJson) {
|
|
163
|
-
try {
|
|
164
|
-
formattedErrorJson = fmtJson(errorData);
|
|
165
|
-
} catch (ignored) {}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
this.formattedErrorJson = formattedErrorJson;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
@action
|
|
172
|
-
adjustDates(dir: 'subtract' | 'add') {
|
|
173
|
-
const appDay = LocalDate.currentAppDay(),
|
|
174
|
-
start = this.startDay,
|
|
175
|
-
end = this.endDay,
|
|
176
|
-
diff = end.diff(start),
|
|
177
|
-
incr = diff + 1;
|
|
178
|
-
|
|
179
|
-
let newStart = start[dir](incr),
|
|
180
|
-
newEnd = end[dir](incr);
|
|
181
|
-
|
|
182
|
-
if (newEnd > appDay) {
|
|
183
|
-
newStart = appDay.subtract(Math.abs(diff));
|
|
184
|
-
newEnd = appDay;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
this.startDay = newStart;
|
|
188
|
-
this.endDay = newEnd;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Set the start date by taking the end date and pushing back [value] [units] - then pushing
|
|
192
|
-
// forward one day as the day range query is inclusive.
|
|
193
|
-
@action
|
|
194
|
-
adjustStartDate(value, unit) {
|
|
195
|
-
this.startDay = this.endDay.subtract(value, unit).nextDay();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
isInterval(value, unit) {
|
|
199
|
-
return this.startDay === this.endDay.subtract(value, unit).nextDay();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
@computed
|
|
203
|
-
private get query() {
|
|
204
|
-
return {
|
|
205
|
-
startDay: this.startDay,
|
|
206
|
-
endDay: this.endDay,
|
|
207
|
-
filters: this.filterChooserModel.value
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
private get defaultStartDay() {
|
|
212
|
-
return LocalDate.currentAppDay();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private get defaultEndDay() {
|
|
216
|
-
return LocalDate.currentAppDay();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private async loadFieldSpecValues() {
|
|
220
|
-
const lookups = await XH.fetchJson({url: 'clientErrorAdmin/lookups'});
|
|
221
|
-
|
|
222
|
-
this.filterChooserModel.fieldSpecs.forEach(spec => {
|
|
223
|
-
const {field} = spec;
|
|
224
|
-
if (lookups[field]) spec.values = lookups[field];
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
|
|
8
|
-
import {hspacer} from '@xh/hoist/cmp/layout';
|
|
9
|
-
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
10
|
-
import {button, buttonGroup, colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button';
|
|
11
|
-
import {filterChooser} from '@xh/hoist/desktop/cmp/filter';
|
|
12
|
-
import {dateInput, DateInputProps} from '@xh/hoist/desktop/cmp/input';
|
|
13
|
-
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
14
|
-
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
15
|
-
import {Icon} from '@xh/hoist/icon';
|
|
16
|
-
import {LocalDate} from '@xh/hoist/utils/datetime';
|
|
17
|
-
import {clientErrorDetail} from './ClientErrorDetail';
|
|
18
|
-
import {ClientErrorsModel} from './ClientErrorsModel';
|
|
19
|
-
import './ClientErrors.scss';
|
|
20
|
-
|
|
21
|
-
export const clientErrorsPanel = hoistCmp.factory({
|
|
22
|
-
model: creates(ClientErrorsModel),
|
|
23
|
-
|
|
24
|
-
render() {
|
|
25
|
-
return panel({
|
|
26
|
-
className: 'xh-admin-client-errors-panel',
|
|
27
|
-
tbar: tbar(),
|
|
28
|
-
items: [grid(), clientErrorDetail()],
|
|
29
|
-
mask: 'onLoad'
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const tbar = hoistCmp.factory<ClientErrorsModel>(({model}) => {
|
|
35
|
-
return toolbar(
|
|
36
|
-
button({
|
|
37
|
-
icon: Icon.angleLeft(),
|
|
38
|
-
onClick: () => model.adjustDates('subtract')
|
|
39
|
-
}),
|
|
40
|
-
dateInput({bind: 'startDay', ...dateInputProps}),
|
|
41
|
-
Icon.caretRight(),
|
|
42
|
-
dateInput({bind: 'endDay', ...dateInputProps}),
|
|
43
|
-
button({
|
|
44
|
-
icon: Icon.angleRight(),
|
|
45
|
-
onClick: () => model.adjustDates('add'),
|
|
46
|
-
disabled: model.endDay >= LocalDate.currentAppDay()
|
|
47
|
-
}),
|
|
48
|
-
buttonGroup(
|
|
49
|
-
button({
|
|
50
|
-
text: '6m',
|
|
51
|
-
outlined: true,
|
|
52
|
-
width: 40,
|
|
53
|
-
onClick: () => model.adjustStartDate(6, 'months'),
|
|
54
|
-
active: model.isInterval(6, 'months')
|
|
55
|
-
}),
|
|
56
|
-
button({
|
|
57
|
-
text: '1m',
|
|
58
|
-
outlined: true,
|
|
59
|
-
width: 40,
|
|
60
|
-
onClick: () => model.adjustStartDate(1, 'months'),
|
|
61
|
-
active: model.isInterval(1, 'months')
|
|
62
|
-
}),
|
|
63
|
-
button({
|
|
64
|
-
text: '7d',
|
|
65
|
-
outlined: true,
|
|
66
|
-
width: 40,
|
|
67
|
-
onClick: () => model.adjustStartDate(7, 'days'),
|
|
68
|
-
active: model.isInterval(7, 'days')
|
|
69
|
-
}),
|
|
70
|
-
button({
|
|
71
|
-
text: '1d',
|
|
72
|
-
outlined: true,
|
|
73
|
-
width: 40,
|
|
74
|
-
onClick: () => model.adjustStartDate(1, 'days'),
|
|
75
|
-
active: model.isInterval(1, 'days')
|
|
76
|
-
})
|
|
77
|
-
),
|
|
78
|
-
hspacer(),
|
|
79
|
-
filterChooser({
|
|
80
|
-
flex: 1,
|
|
81
|
-
enableClear: true
|
|
82
|
-
}),
|
|
83
|
-
button({
|
|
84
|
-
icon: Icon.reset(),
|
|
85
|
-
intent: 'danger',
|
|
86
|
-
title: 'Reset query to defaults',
|
|
87
|
-
onClick: () => model.resetQuery()
|
|
88
|
-
}),
|
|
89
|
-
'-',
|
|
90
|
-
gridCountLabel({unit: 'error'}),
|
|
91
|
-
'-',
|
|
92
|
-
colChooserButton(),
|
|
93
|
-
exportButton()
|
|
94
|
-
);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const dateInputProps: DateInputProps = {
|
|
98
|
-
popoverPosition: 'bottom',
|
|
99
|
-
valueType: 'localDate',
|
|
100
|
-
width: 120
|
|
101
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
|
|
8
|
-
import {AppModel} from '@xh/hoist/admin/AppModel';
|
|
9
|
-
import * as Col from '@xh/hoist/admin/columns';
|
|
10
|
-
import {hoistCmp} from '@xh/hoist/core';
|
|
11
|
-
import {FieldSpec} from '@xh/hoist/data';
|
|
12
|
-
import {textArea} from '@xh/hoist/desktop/cmp/input';
|
|
13
|
-
import {deleteAction, restGrid, RestGridConfig} from '@xh/hoist/desktop/cmp/rest';
|
|
14
|
-
|
|
15
|
-
export const feedbackPanel = hoistCmp.factory(() =>
|
|
16
|
-
restGrid({modelConfig: {...modelSpec, readonly: AppModel.readonly}})
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const modelSpec: RestGridConfig = {
|
|
20
|
-
persistWith: {localStorageKey: 'xhAdminFeedbackState'},
|
|
21
|
-
colChooserModel: true,
|
|
22
|
-
enableExport: true,
|
|
23
|
-
exportOptions: {filename: exportFilenameWithDate('feedback')},
|
|
24
|
-
emptyText: 'No feedback reported...',
|
|
25
|
-
store: {
|
|
26
|
-
url: 'rest/feedbackAdmin',
|
|
27
|
-
fields: [
|
|
28
|
-
Col.username.field,
|
|
29
|
-
Col.msg.field,
|
|
30
|
-
Col.browser.field,
|
|
31
|
-
Col.device.field,
|
|
32
|
-
Col.appEnvironment.field,
|
|
33
|
-
{...(Col.appVersion.field as FieldSpec), displayName: 'Version'},
|
|
34
|
-
{...(Col.dateCreated.field as FieldSpec), displayName: 'Date'}
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
toolbarActions: [deleteAction],
|
|
38
|
-
menuActions: [deleteAction],
|
|
39
|
-
formActions: [deleteAction],
|
|
40
|
-
unit: 'report',
|
|
41
|
-
sortBy: 'dateCreated|desc',
|
|
42
|
-
filterFields: ['username', 'msg'],
|
|
43
|
-
columns: [
|
|
44
|
-
{...Col.username},
|
|
45
|
-
{...Col.browser},
|
|
46
|
-
{...Col.device},
|
|
47
|
-
{...Col.appVersion, displayName: 'Version'},
|
|
48
|
-
{...Col.appEnvironment},
|
|
49
|
-
{...Col.msg},
|
|
50
|
-
{...Col.dateCreated, displayName: 'Date'}
|
|
51
|
-
],
|
|
52
|
-
editors: [
|
|
53
|
-
{field: 'username'},
|
|
54
|
-
{field: 'msg', formField: {item: textArea({height: 100})}},
|
|
55
|
-
{field: 'browser'},
|
|
56
|
-
{field: 'device'},
|
|
57
|
-
{field: 'appVersion'},
|
|
58
|
-
{field: 'appEnvironment'},
|
|
59
|
-
{field: 'dateCreated'}
|
|
60
|
-
]
|
|
61
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const clientTab: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<import("@xh/hoist/core").HoistModel>>;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { FilterChooserModel } from '@xh/hoist/cmp/filter';
|
|
2
|
-
import { FormModel } from '@xh/hoist/cmp/form';
|
|
3
|
-
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
4
|
-
import { HoistModel, LoadSpec } from '@xh/hoist/core';
|
|
5
|
-
import { StoreRecord } from '@xh/hoist/data';
|
|
6
|
-
import { LocalDate } from '@xh/hoist/utils/datetime';
|
|
7
|
-
export declare class ClientErrorsModel extends HoistModel {
|
|
8
|
-
persistWith: {
|
|
9
|
-
localStorageKey: string;
|
|
10
|
-
};
|
|
11
|
-
startDay: LocalDate;
|
|
12
|
-
endDay: LocalDate;
|
|
13
|
-
gridModel: GridModel;
|
|
14
|
-
formModel: FormModel;
|
|
15
|
-
filterChooserModel: FilterChooserModel;
|
|
16
|
-
get selectedRecord(): StoreRecord;
|
|
17
|
-
/** Parsed and JSON-formatted stacktrace / additional data for selected error. */
|
|
18
|
-
formattedErrorJson: string;
|
|
19
|
-
constructor();
|
|
20
|
-
resetQuery(): void;
|
|
21
|
-
doLoadAsync(loadSpec: LoadSpec): Promise<void>;
|
|
22
|
-
showEntryDetail(detailRec: StoreRecord): void;
|
|
23
|
-
adjustDates(dir: 'subtract' | 'add'): void;
|
|
24
|
-
adjustStartDate(value: any, unit: any): void;
|
|
25
|
-
isInterval(value: any, unit: any): boolean;
|
|
26
|
-
private get query();
|
|
27
|
-
private get defaultStartDay();
|
|
28
|
-
private get defaultEndDay();
|
|
29
|
-
private loadFieldSpecValues;
|
|
30
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const feedbackPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<import("@xh/hoist/core").HoistModel>>;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|