@xh/hoist 73.0.0-SNAPSHOT.1746476925456 → 73.0.0-SNAPSHOT.1746482507483
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 +15 -3
- package/admin/columns/Rest.ts +0 -1
- package/admin/columns/Tracking.ts +6 -9
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +14 -47
- package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +1 -1
- package/admin/tabs/activity/tracking/detail/ActivityDetailModel.ts +35 -51
- package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +5 -7
- package/admin/tabs/client/clients/ClientsModel.ts +2 -7
- package/admin/tabs/client/clients/ClientsPanel.ts +2 -3
- package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +2 -7
- package/build/types/admin/tabs/activity/tracking/detail/ActivityDetailModel.d.ts +6 -18
- package/build/types/cmp/grid/Types.d.ts +4 -1
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.d.ts +2 -0
- package/build/types/desktop/cmp/input/DateInput.d.ts +3 -2
- package/build/types/desktop/cmp/input/NumberInput.d.ts +3 -2
- package/build/types/desktop/cmp/input/TextInput.d.ts +3 -2
- package/build/types/mobile/cmp/input/DateInput.d.ts +3 -2
- package/build/types/mobile/cmp/input/NumberInput.d.ts +3 -2
- package/build/types/mobile/cmp/input/SearchInput.d.ts +3 -2
- package/build/types/mobile/cmp/input/TextInput.d.ts +3 -2
- package/cmp/grid/Types.ts +4 -1
- package/cmp/grid/filter/GridFilterModel.ts +1 -1
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTab.scss +13 -0
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTab.ts +29 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/values/ValuesTabModel.ts +37 -15
- package/desktop/cmp/input/DateInput.ts +3 -2
- package/desktop/cmp/input/NumberInput.ts +3 -2
- package/desktop/cmp/input/TextInput.ts +3 -2
- package/mobile/cmp/input/DateInput.ts +3 -2
- package/mobile/cmp/input/NumberInput.ts +3 -2
- package/mobile/cmp/input/SearchInput.ts +3 -2
- package/mobile/cmp/input/TextInput.ts +3 -2
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/admin/tabs/client/clients/activity/ClientDetail.scss +0 -24
- package/admin/tabs/client/clients/activity/ClientDetailModel.ts +0 -83
- package/admin/tabs/client/clients/activity/ClientDetailPanel.ts +0 -63
- package/build/types/admin/tabs/client/clients/activity/ClientDetailModel.d.ts +0 -21
- package/build/types/admin/tabs/client/clients/activity/ClientDetailPanel.d.ts +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
### 💥 Breaking Changes (upgrade difficulty: 🟢 TRIVIAL - minor upgrade to Hoist Core)
|
|
6
6
|
|
|
7
|
-
Requires `hoist-core >= 30.0` with new APIs to support the consolidated Admin Console "Clients"
|
|
8
|
-
and new properties on `TrackLog`.
|
|
7
|
+
* Requires `hoist-core >= 30.0` with new APIs to support the consolidated Admin Console "Clients"
|
|
8
|
+
tab and new properties on `TrackLog`.
|
|
9
|
+
* Apps with a custom `AppModel` for their admin app that extends `@xh/hoist/admin/AppModel` must
|
|
10
|
+
ensure they call `super.initAsync()` within their override of that lifecycle method, if
|
|
11
|
+
applicable. This did not previously have any effect, but is required now for the superclass to
|
|
12
|
+
initialize a new `ViewManagerModel`.
|
|
13
|
+
* For clarity, [here is where Toolbox makes that call](https://github.com/xh/toolbox/blob/f15a8018ce36c2ae998b45724b48a16320b88e49/client-app/src/admin/AppModel.ts#L12).
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
### 🎁 New Features
|
|
11
17
|
|
|
@@ -32,6 +38,8 @@ and new properties on `TrackLog`.
|
|
|
32
38
|
|
|
33
39
|
* Corrected `GridGroupSortFn` param types.
|
|
34
40
|
* Corrected `StoreCountLabelProps` interface.
|
|
41
|
+
* Corrected `textAlign` type in `DateInputProps`, `NumberInputProps` `SearchInputProps` and
|
|
42
|
+
`TextInputProps`.
|
|
35
43
|
|
|
36
44
|
### ⚙️ Technical
|
|
37
45
|
|
|
@@ -41,7 +49,7 @@ and new properties on `TrackLog`.
|
|
|
41
49
|
* The two versions *should* be the same, but in cases where a browser "restores" a tab and
|
|
42
50
|
re-inits an app without reloading the code itself, the upgrade check would miss the fact that
|
|
43
51
|
the client remained on an older version.
|
|
44
|
-
*
|
|
52
|
+
* ⚠️ NOTE that a misconfigured build - where the client version is not set to the same value
|
|
45
53
|
as the server - would result in a false positive for an upgrade. The two should always match.
|
|
46
54
|
* Calls to `Promise.track()` that are rejected with an exception will be tracked with new
|
|
47
55
|
severity level of `TrackSeverity.ERROR`
|
|
@@ -119,6 +127,10 @@ and new properties on `TrackLog`.
|
|
|
119
127
|
|
|
120
128
|
### 🎁 New Features
|
|
121
129
|
|
|
130
|
+
* Improvements to Grid columns `HeaderFilter` component:
|
|
131
|
+
* `GridFilterModel` `commitOnChage` now set to `false` by default
|
|
132
|
+
* Addition of ability to append terms to active filter **only** when `commitOnChage:false`
|
|
133
|
+
* Column header filtering functionality now similar to Excel on Windows
|
|
122
134
|
* Introduced a new "JSON Search" feature to the Hoist Admin Console, accessible from the Config,
|
|
123
135
|
User Preference, and JSON Blob tabs. Supports searching JSON values stored within these objects
|
|
124
136
|
to filter and match data using JSON Path expressions.
|
package/admin/columns/Rest.ts
CHANGED
|
@@ -13,8 +13,6 @@ import {fmtDate, fmtSpan, numberRenderer} from '@xh/hoist/format';
|
|
|
13
13
|
import {Icon} from '@xh/hoist/icon';
|
|
14
14
|
import {ReactElement} from 'react';
|
|
15
15
|
|
|
16
|
-
const autosizeMaxWidth = 400;
|
|
17
|
-
|
|
18
16
|
export const appBuild: ColumnSpec = {
|
|
19
17
|
field: {
|
|
20
18
|
name: 'appBuild',
|
|
@@ -76,7 +74,7 @@ export const correlationId: ColumnSpec = {
|
|
|
76
74
|
export const data: ColumnSpec = {
|
|
77
75
|
field: {name: 'data', type: 'json'},
|
|
78
76
|
width: 250,
|
|
79
|
-
autosizeMaxWidth
|
|
77
|
+
autosizeMaxWidth: 400
|
|
80
78
|
};
|
|
81
79
|
|
|
82
80
|
export const day: ColumnSpec = {
|
|
@@ -181,7 +179,7 @@ export const error: ColumnSpec = {
|
|
|
181
179
|
type: 'string'
|
|
182
180
|
},
|
|
183
181
|
width: 250,
|
|
184
|
-
autosizeMaxWidth,
|
|
182
|
+
autosizeMaxWidth: 400,
|
|
185
183
|
renderer: e => fmtSpan(e, {className: 'xh-font-family-mono xh-font-size-small'})
|
|
186
184
|
};
|
|
187
185
|
|
|
@@ -213,7 +211,7 @@ export const msg: ColumnSpec = {
|
|
|
213
211
|
aggregator: 'UNIQUE'
|
|
214
212
|
},
|
|
215
213
|
width: 250,
|
|
216
|
-
autosizeMaxWidth
|
|
214
|
+
autosizeMaxWidth: 400
|
|
217
215
|
};
|
|
218
216
|
|
|
219
217
|
export const severity: ColumnSpec = {
|
|
@@ -269,13 +267,13 @@ export const url: ColumnSpec = {
|
|
|
269
267
|
displayName: 'URL'
|
|
270
268
|
},
|
|
271
269
|
width: 250,
|
|
272
|
-
autosizeMaxWidth
|
|
270
|
+
autosizeMaxWidth: 400
|
|
273
271
|
};
|
|
274
272
|
|
|
275
273
|
export const urlPathOnly: ColumnSpec = {
|
|
276
274
|
field: url.field,
|
|
277
275
|
width: 250,
|
|
278
|
-
autosizeMaxWidth,
|
|
276
|
+
autosizeMaxWidth: 400,
|
|
279
277
|
tooltip: true,
|
|
280
278
|
renderer: v => {
|
|
281
279
|
if (!v) return null;
|
|
@@ -295,8 +293,7 @@ export const userAgent: ColumnSpec = {
|
|
|
295
293
|
isDimension: true,
|
|
296
294
|
aggregator: 'UNIQUE'
|
|
297
295
|
},
|
|
298
|
-
width: 130
|
|
299
|
-
autosizeMaxWidth
|
|
296
|
+
width: 130
|
|
300
297
|
};
|
|
301
298
|
|
|
302
299
|
export const userAlertedFlag: ColumnSpec = {
|
|
@@ -15,15 +15,14 @@ import {FormModel} from '@xh/hoist/cmp/form';
|
|
|
15
15
|
import {ColumnRenderer, ColumnSpec, GridModel, TreeStyle} from '@xh/hoist/cmp/grid';
|
|
16
16
|
import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
|
|
17
17
|
import {HoistModel, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
|
|
18
|
-
import {Cube, CubeFieldSpec, FieldSpec
|
|
18
|
+
import {Cube, CubeFieldSpec, FieldSpec} from '@xh/hoist/data';
|
|
19
19
|
import {dateRenderer, dateTimeSecRenderer, fmtNumber, numberRenderer} from '@xh/hoist/format';
|
|
20
20
|
import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
21
21
|
import {LocalDate} from '@xh/hoist/utils/datetime';
|
|
22
22
|
import {compact, get, isEmpty, isEqual, round} from 'lodash';
|
|
23
23
|
import moment from 'moment';
|
|
24
|
-
import {ActivityDetailProvider} from './detail/ActivityDetailModel';
|
|
25
24
|
|
|
26
|
-
export class ActivityTrackingModel extends HoistModel
|
|
25
|
+
export class ActivityTrackingModel extends HoistModel {
|
|
27
26
|
/** FormModel for server-side querying controls. */
|
|
28
27
|
@managed formModel: FormModel;
|
|
29
28
|
|
|
@@ -40,17 +39,6 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
40
39
|
*/
|
|
41
40
|
@observable.ref dataFields: ActivityTrackingDataFieldSpec[] = [];
|
|
42
41
|
|
|
43
|
-
// TODO - process two collections - one for agg grid with _agg fields left as-is, another for
|
|
44
|
-
// detail grid and filter that replaces (potentially multiple) agg fields with a single
|
|
45
|
-
// underlying field.
|
|
46
|
-
get dataFieldCols(): ColumnSpec[] {
|
|
47
|
-
return this.dataFields.map(df => ({
|
|
48
|
-
field: df,
|
|
49
|
-
renderer: this.getDfRenderer(df),
|
|
50
|
-
appData: {showInAggGrid: !!df.aggregator}
|
|
51
|
-
}));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
42
|
@observable showFilterChooser: boolean = false;
|
|
55
43
|
|
|
56
44
|
get enabled(): boolean {
|
|
@@ -89,18 +77,21 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
89
77
|
return this.maxRows === this.cube.store.allCount;
|
|
90
78
|
}
|
|
91
79
|
|
|
80
|
+
// TODO - process two collections - one for agg grid with _agg fields left as-is, another for
|
|
81
|
+
// detail grid and filter that replaces (potentially multiple) agg fields with a single
|
|
82
|
+
// underlying field.
|
|
83
|
+
get dataFieldCols(): ColumnSpec[] {
|
|
84
|
+
return this.dataFields.map(df => ({
|
|
85
|
+
field: df,
|
|
86
|
+
renderer: this.getDfRenderer(df),
|
|
87
|
+
appData: {showInAggGrid: !!df.aggregator}
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
92
91
|
get viewManagerModel() {
|
|
93
92
|
return getAppModel().viewManagerModels.activityTracking;
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
//-----------------------
|
|
97
|
-
// ActivityDetailProvider
|
|
98
|
-
//-----------------------
|
|
99
|
-
readonly isActivityDetailProvider = true;
|
|
100
|
-
|
|
101
|
-
/** Raw leaf-level log entries for the selected aggregate record, for detail. */
|
|
102
|
-
@observable.ref trackLogs: PlainObject[] = [];
|
|
103
|
-
|
|
104
95
|
private _monthFormat = 'MMM YYYY';
|
|
105
96
|
|
|
106
97
|
constructor() {
|
|
@@ -130,11 +121,6 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
130
121
|
track: () => [this.cube.records, this.dimensions],
|
|
131
122
|
run: () => this.loadGridAsync(),
|
|
132
123
|
debounce: 100
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
track: () => this.gridModel.selectedRecords,
|
|
136
|
-
run: recs => (this.trackLogs = this.getAllLeafRows(recs)),
|
|
137
|
-
debounce: 100
|
|
138
124
|
}
|
|
139
125
|
);
|
|
140
126
|
}
|
|
@@ -287,23 +273,6 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
287
273
|
};
|
|
288
274
|
}
|
|
289
275
|
|
|
290
|
-
// Extract all leaf, track-entry-level rows from an aggregate record (at any level).
|
|
291
|
-
private getAllLeafRows(aggRecs: StoreRecord[], ret = []): PlainObject[] {
|
|
292
|
-
if (isEmpty(aggRecs)) return [];
|
|
293
|
-
|
|
294
|
-
aggRecs.forEach(aggRec => {
|
|
295
|
-
if (aggRec.children.length) {
|
|
296
|
-
this.getAllLeafRows(aggRec.children, ret);
|
|
297
|
-
} else if (aggRec.raw.leafRows) {
|
|
298
|
-
aggRec.raw.leafRows.forEach(leaf => {
|
|
299
|
-
ret.push({...leaf});
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
return ret;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
276
|
//------------------------
|
|
308
277
|
// Impl - core data models
|
|
309
278
|
//------------------------
|
|
@@ -413,12 +382,11 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
413
382
|
const hidden = true;
|
|
414
383
|
return new GridModel({
|
|
415
384
|
persistWith: {...this.persistWith, path: 'aggGrid'},
|
|
416
|
-
selModel: 'multiple',
|
|
417
385
|
enableExport: true,
|
|
418
386
|
colChooserModel: true,
|
|
419
387
|
treeMode: true,
|
|
420
388
|
treeStyle: TreeStyle.HIGHLIGHTS_AND_BORDERS,
|
|
421
|
-
autosizeOptions: {mode: 'managed'
|
|
389
|
+
autosizeOptions: {mode: 'managed'},
|
|
422
390
|
exportOptions: {filename: exportFilename('activity-summary')},
|
|
423
391
|
emptyText: 'No activity reported...',
|
|
424
392
|
sortBy: ['cubeLabel'],
|
|
@@ -430,7 +398,6 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
430
398
|
displayName: 'Group'
|
|
431
399
|
},
|
|
432
400
|
minWidth: 100,
|
|
433
|
-
autosizeMaxWidth: 400,
|
|
434
401
|
isTreeColumn: true,
|
|
435
402
|
comparator: this.cubeLabelComparator.bind(this)
|
|
436
403
|
},
|
|
@@ -155,7 +155,7 @@ const aggregateView = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
|
|
|
155
155
|
persistWith: {...model.persistWith, path: 'aggPanel'}
|
|
156
156
|
},
|
|
157
157
|
tbar: toolbar({
|
|
158
|
-
compact: true,
|
|
158
|
+
// compact: true,
|
|
159
159
|
items: [
|
|
160
160
|
groupingChooser({flex: 10, maxWidth: 300}),
|
|
161
161
|
filler(),
|
|
@@ -6,32 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {exportFilename} from '@xh/hoist/admin/AdminUtils';
|
|
8
8
|
import * as Col from '@xh/hoist/admin/columns';
|
|
9
|
-
import {ActivityTrackingDataFieldSpec} from '@xh/hoist/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel';
|
|
10
9
|
import {FormModel} from '@xh/hoist/cmp/form';
|
|
11
|
-
import {
|
|
12
|
-
import {HoistModel, lookup, managed
|
|
10
|
+
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
11
|
+
import {HoistModel, lookup, managed} from '@xh/hoist/core';
|
|
13
12
|
import {StoreRecord} from '@xh/hoist/data';
|
|
14
13
|
import {timestampReplacer} from '@xh/hoist/format';
|
|
15
14
|
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
16
|
-
import {get
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Interface to cover the two usages of this component - {@link ActivityTrackingModel} and {@link ClientDetailModel}
|
|
20
|
-
*/
|
|
21
|
-
export interface ActivityDetailProvider {
|
|
22
|
-
isActivityDetailProvider: true;
|
|
23
|
-
trackLogs: PlainObject[];
|
|
24
|
-
persistWith?: PersistOptions;
|
|
25
|
-
colDefaults?: Record<string, Partial<ColumnSpec>>;
|
|
26
|
-
dataFields?: ActivityTrackingDataFieldSpec[];
|
|
27
|
-
dataFieldCols?: ColumnSpec[];
|
|
28
|
-
}
|
|
15
|
+
import {get} from 'lodash';
|
|
16
|
+
import {ActivityTrackingModel} from '../ActivityTrackingModel';
|
|
29
17
|
|
|
30
18
|
export class ActivityDetailModel extends HoistModel {
|
|
31
|
-
@lookup(
|
|
32
|
-
return model.isActivityDetailProvider ?? false;
|
|
33
|
-
})
|
|
34
|
-
parentModel: ActivityDetailProvider;
|
|
19
|
+
@lookup(ActivityTrackingModel) activityTrackingModel: ActivityTrackingModel;
|
|
35
20
|
|
|
36
21
|
@managed @observable.ref gridModel: GridModel;
|
|
37
22
|
@managed @observable.ref formModel: FormModel;
|
|
@@ -46,22 +31,14 @@ export class ActivityDetailModel extends HoistModel {
|
|
|
46
31
|
/** Stringified, pretty-printed, optionally path-filtered `data` payload. */
|
|
47
32
|
@observable formattedData: string;
|
|
48
33
|
|
|
49
|
-
get dataFields(): ActivityTrackingDataFieldSpec[] {
|
|
50
|
-
return this.parentModel?.dataFields ?? [];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
get dataFieldCols(): ColumnSpec[] {
|
|
54
|
-
return this.parentModel?.dataFieldCols ?? [];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
34
|
@computed
|
|
58
35
|
get hasExtraTrackData(): boolean {
|
|
59
|
-
return this.gridModel
|
|
36
|
+
return this.gridModel.selectedRecord?.data.data != null;
|
|
60
37
|
}
|
|
61
38
|
|
|
62
39
|
@computed
|
|
63
40
|
get hasSelection() {
|
|
64
|
-
return this.gridModel
|
|
41
|
+
return this.gridModel.selectedRecord != null;
|
|
65
42
|
}
|
|
66
43
|
|
|
67
44
|
constructor() {
|
|
@@ -70,22 +47,17 @@ export class ActivityDetailModel extends HoistModel {
|
|
|
70
47
|
}
|
|
71
48
|
|
|
72
49
|
override onLinked() {
|
|
73
|
-
|
|
74
|
-
this.persistWith = {...this.parentModel.persistWith, path: 'activityDetail'};
|
|
75
|
-
this.markPersist('formattedDataFilterPath', {
|
|
76
|
-
path: `${this.persistWith.path}.formattedDataFilterPath`
|
|
77
|
-
});
|
|
78
|
-
}
|
|
50
|
+
this.markPersist('formattedDataFilterPath', this.activityTrackingModel.persistWith);
|
|
79
51
|
|
|
80
52
|
this.addReaction(
|
|
81
53
|
{
|
|
82
|
-
track: () => this.dataFields,
|
|
54
|
+
track: () => this.activityTrackingModel.dataFields,
|
|
83
55
|
run: () => this.createAndSetCoreModels(),
|
|
84
56
|
fireImmediately: true
|
|
85
57
|
},
|
|
86
58
|
{
|
|
87
|
-
track: () => this.
|
|
88
|
-
run:
|
|
59
|
+
track: () => this.activityTrackingModel.gridModel.selectedRecord,
|
|
60
|
+
run: aggRec => this.showActivityEntriesAsync(aggRec)
|
|
89
61
|
},
|
|
90
62
|
{
|
|
91
63
|
track: () => this.gridModel.selectedRecord,
|
|
@@ -101,12 +73,29 @@ export class ActivityDetailModel extends HoistModel {
|
|
|
101
73
|
//------------------
|
|
102
74
|
// Implementation
|
|
103
75
|
//------------------
|
|
104
|
-
private async
|
|
105
|
-
const {gridModel} = this
|
|
106
|
-
|
|
76
|
+
private async showActivityEntriesAsync(aggRec: StoreRecord) {
|
|
77
|
+
const {gridModel} = this,
|
|
78
|
+
leaves = this.getAllLeafRows(aggRec);
|
|
79
|
+
|
|
80
|
+
gridModel.loadData(leaves);
|
|
107
81
|
await gridModel.preSelectFirstAsync();
|
|
108
82
|
}
|
|
109
83
|
|
|
84
|
+
// Extract all leaf, track-entry-level rows from an aggregate record (at any level).
|
|
85
|
+
private getAllLeafRows(aggRec: StoreRecord, ret = []) {
|
|
86
|
+
if (!aggRec) return [];
|
|
87
|
+
|
|
88
|
+
if (aggRec.children.length) {
|
|
89
|
+
aggRec.children.forEach(childRec => this.getAllLeafRows(childRec, ret));
|
|
90
|
+
} else if (aggRec.raw.leafRows) {
|
|
91
|
+
aggRec.raw.leafRows.forEach(leaf => {
|
|
92
|
+
ret.push({...leaf});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return ret;
|
|
97
|
+
}
|
|
98
|
+
|
|
110
99
|
/** Extract data from a (detail) grid record and flush it into our form for display. */
|
|
111
100
|
@action
|
|
112
101
|
private showEntryDetail(detailRec: StoreRecord) {
|
|
@@ -150,13 +139,11 @@ export class ActivityDetailModel extends HoistModel {
|
|
|
150
139
|
}
|
|
151
140
|
|
|
152
141
|
private createGridModel(): GridModel {
|
|
153
|
-
const
|
|
154
|
-
colDefaults = parentModel.colDefaults ?? {},
|
|
155
|
-
hidden = true,
|
|
142
|
+
const hidden = true,
|
|
156
143
|
pinned = true;
|
|
157
144
|
|
|
158
145
|
return new GridModel({
|
|
159
|
-
persistWith:
|
|
146
|
+
persistWith: {...this.activityTrackingModel.persistWith, path: 'detailGrid'},
|
|
160
147
|
sortBy: 'dateCreated|desc',
|
|
161
148
|
colChooserModel: true,
|
|
162
149
|
enableExport: true,
|
|
@@ -187,11 +174,8 @@ export class ActivityDetailModel extends HoistModel {
|
|
|
187
174
|
{...Col.urlPathOnly},
|
|
188
175
|
{...Col.data, hidden},
|
|
189
176
|
{...Col.dateCreatedNoYear, displayName: 'Timestamp'},
|
|
190
|
-
...dataFieldCols
|
|
191
|
-
]
|
|
192
|
-
const fieldName = isString(it.field) ? it.field : it.field.name;
|
|
193
|
-
return {...it, ...colDefaults[fieldName]};
|
|
194
|
-
})
|
|
177
|
+
...this.activityTrackingModel.dataFieldCols
|
|
178
|
+
]
|
|
195
179
|
});
|
|
196
180
|
}
|
|
197
181
|
|
|
@@ -37,12 +37,11 @@ export const activityDetailView = hoistCmp.factory({
|
|
|
37
37
|
const tbar = hoistCmp.factory<ActivityDetailModel>(({model}) => {
|
|
38
38
|
const {gridModel} = model;
|
|
39
39
|
return toolbar({
|
|
40
|
-
compact: true,
|
|
41
40
|
items: [
|
|
42
41
|
filler(),
|
|
43
42
|
gridCountLabel({unit: 'entry'}),
|
|
44
43
|
'-',
|
|
45
|
-
gridFindField({gridModel, key: gridModel.xhId
|
|
44
|
+
gridFindField({gridModel, key: gridModel.xhId}),
|
|
46
45
|
colChooserButton({gridModel}),
|
|
47
46
|
exportButton()
|
|
48
47
|
]
|
|
@@ -51,8 +50,6 @@ const tbar = hoistCmp.factory<ActivityDetailModel>(({model}) => {
|
|
|
51
50
|
|
|
52
51
|
// Discrete outer panel to retain sizing across master/detail selection changes.
|
|
53
52
|
const detailRecPanel = hoistCmp.factory<ActivityDetailModel>(({model}) => {
|
|
54
|
-
const {persistWith} = model;
|
|
55
|
-
|
|
56
53
|
return panel({
|
|
57
54
|
collapsedTitle: 'Activity Details',
|
|
58
55
|
collapsedIcon: Icon.info(),
|
|
@@ -60,9 +57,10 @@ const detailRecPanel = hoistCmp.factory<ActivityDetailModel>(({model}) => {
|
|
|
60
57
|
modelConfig: {
|
|
61
58
|
side: 'bottom',
|
|
62
59
|
defaultSize: 400,
|
|
63
|
-
persistWith:
|
|
64
|
-
|
|
65
|
-
:
|
|
60
|
+
persistWith: {
|
|
61
|
+
...model.activityTrackingModel.persistWith,
|
|
62
|
+
path: 'singleActivityDetailPanel'
|
|
63
|
+
}
|
|
66
64
|
},
|
|
67
65
|
item: detailRecForm()
|
|
68
66
|
});
|
|
@@ -73,8 +73,6 @@ export class ClientsModel extends BaseAdminTabModel {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
override async doLoadAsync(loadSpec: LoadSpec) {
|
|
76
|
-
const {gridModel} = this;
|
|
77
|
-
|
|
78
76
|
try {
|
|
79
77
|
const data = await XH.fetchJson({
|
|
80
78
|
url: 'clientAdmin/allClients',
|
|
@@ -82,15 +80,12 @@ export class ClientsModel extends BaseAdminTabModel {
|
|
|
82
80
|
});
|
|
83
81
|
if (loadSpec.isStale) return;
|
|
84
82
|
|
|
85
|
-
gridModel.loadData(data);
|
|
86
|
-
gridModel.preSelectFirstAsync();
|
|
83
|
+
this.gridModel.loadData(data);
|
|
87
84
|
runInAction(() => {
|
|
88
85
|
this.lastRefresh = Date.now();
|
|
89
86
|
});
|
|
90
87
|
} catch (e) {
|
|
91
|
-
if (loadSpec.isStale
|
|
92
|
-
|
|
93
|
-
gridModel.clear();
|
|
88
|
+
if (loadSpec.isStale) return;
|
|
94
89
|
XH.handleException(e, {alertType: 'toast'});
|
|
95
90
|
}
|
|
96
91
|
}
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {clientDetailPanel} from '@xh/hoist/admin/tabs/client/clients/activity/ClientDetailPanel';
|
|
8
7
|
import {errorMessage} from '@xh/hoist/cmp/error';
|
|
9
8
|
import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
|
|
10
|
-
import {filler, fragment,
|
|
9
|
+
import {filler, fragment, p} from '@xh/hoist/cmp/layout';
|
|
11
10
|
import {relativeTimestamp} from '@xh/hoist/cmp/relativetimestamp';
|
|
12
11
|
import {storeFilterField} from '@xh/hoist/cmp/store';
|
|
13
12
|
import {creates, hoistCmp, XH} from '@xh/hoist/core';
|
|
@@ -49,7 +48,7 @@ export const clientsPanel = hoistCmp.factory<ClientsModel>({
|
|
|
49
48
|
colChooserButton(),
|
|
50
49
|
exportButton()
|
|
51
50
|
],
|
|
52
|
-
|
|
51
|
+
item: grid(),
|
|
53
52
|
mask: 'onLoad',
|
|
54
53
|
ref: model.viewRef
|
|
55
54
|
});
|
|
@@ -6,8 +6,7 @@ import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
|
|
|
6
6
|
import { HoistModel, LoadSpec, PlainObject } from '@xh/hoist/core';
|
|
7
7
|
import { Cube } from '@xh/hoist/data';
|
|
8
8
|
import { LocalDate } from '@xh/hoist/utils/datetime';
|
|
9
|
-
|
|
10
|
-
export declare class ActivityTrackingModel extends HoistModel implements ActivityDetailProvider {
|
|
9
|
+
export declare class ActivityTrackingModel extends HoistModel {
|
|
11
10
|
/** FormModel for server-side querying controls. */
|
|
12
11
|
formModel: FormModel;
|
|
13
12
|
/** Models for data-handling components - can be rebuilt due to change in dataFields. */
|
|
@@ -21,7 +20,6 @@ export declare class ActivityTrackingModel extends HoistModel implements Activit
|
|
|
21
20
|
* and promoted to top-level columns in the grids. Supports dot-delimited paths as names.
|
|
22
21
|
*/
|
|
23
22
|
dataFields: ActivityTrackingDataFieldSpec[];
|
|
24
|
-
get dataFieldCols(): ColumnSpec[];
|
|
25
23
|
showFilterChooser: boolean;
|
|
26
24
|
get enabled(): boolean;
|
|
27
25
|
get dimensions(): string[];
|
|
@@ -34,10 +32,8 @@ export declare class ActivityTrackingModel extends HoistModel implements Activit
|
|
|
34
32
|
get maxRows(): number;
|
|
35
33
|
/** True if data loaded from the server has been topped by maxRows. */
|
|
36
34
|
get maxRowsReached(): boolean;
|
|
35
|
+
get dataFieldCols(): ColumnSpec[];
|
|
37
36
|
get viewManagerModel(): import("../../../../cmp/viewmanager").ViewManagerModel<PlainObject>;
|
|
38
|
-
readonly isActivityDetailProvider = true;
|
|
39
|
-
/** Raw leaf-level log entries for the selected aggregate record, for detail. */
|
|
40
|
-
trackLogs: PlainObject[];
|
|
41
37
|
private _monthFormat;
|
|
42
38
|
constructor();
|
|
43
39
|
doLoadAsync(loadSpec: LoadSpec): Promise<void>;
|
|
@@ -53,7 +49,6 @@ export declare class ActivityTrackingModel extends HoistModel implements Activit
|
|
|
53
49
|
private cubeLabelComparator;
|
|
54
50
|
private getComparableValForDim;
|
|
55
51
|
private get query();
|
|
56
|
-
private getAllLeafRows;
|
|
57
52
|
private createAndSetCoreModels;
|
|
58
53
|
private createCube;
|
|
59
54
|
private createFilterChooserModel;
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import { ActivityTrackingDataFieldSpec } from '@xh/hoist/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel';
|
|
2
1
|
import { FormModel } from '@xh/hoist/cmp/form';
|
|
3
|
-
import {
|
|
4
|
-
import { HoistModel
|
|
5
|
-
|
|
6
|
-
* Interface to cover the two usages of this component - {@link ActivityTrackingModel} and {@link ClientDetailModel}
|
|
7
|
-
*/
|
|
8
|
-
export interface ActivityDetailProvider {
|
|
9
|
-
isActivityDetailProvider: true;
|
|
10
|
-
trackLogs: PlainObject[];
|
|
11
|
-
persistWith?: PersistOptions;
|
|
12
|
-
colDefaults?: Record<string, Partial<ColumnSpec>>;
|
|
13
|
-
dataFields?: ActivityTrackingDataFieldSpec[];
|
|
14
|
-
dataFieldCols?: ColumnSpec[];
|
|
15
|
-
}
|
|
2
|
+
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
3
|
+
import { HoistModel } from '@xh/hoist/core';
|
|
4
|
+
import { ActivityTrackingModel } from '../ActivityTrackingModel';
|
|
16
5
|
export declare class ActivityDetailModel extends HoistModel {
|
|
17
|
-
|
|
6
|
+
activityTrackingModel: ActivityTrackingModel;
|
|
18
7
|
gridModel: GridModel;
|
|
19
8
|
formModel: FormModel;
|
|
20
9
|
/**
|
|
@@ -25,13 +14,12 @@ export declare class ActivityDetailModel extends HoistModel {
|
|
|
25
14
|
formattedDataFilterPath: string;
|
|
26
15
|
/** Stringified, pretty-printed, optionally path-filtered `data` payload. */
|
|
27
16
|
formattedData: string;
|
|
28
|
-
get dataFields(): ActivityTrackingDataFieldSpec[];
|
|
29
|
-
get dataFieldCols(): ColumnSpec[];
|
|
30
17
|
get hasExtraTrackData(): boolean;
|
|
31
18
|
get hasSelection(): boolean;
|
|
32
19
|
constructor();
|
|
33
20
|
onLinked(): void;
|
|
34
|
-
private
|
|
21
|
+
private showActivityEntriesAsync;
|
|
22
|
+
private getAllLeafRows;
|
|
35
23
|
/** Extract data from a (detail) grid record and flush it into our form for display. */
|
|
36
24
|
private showEntryDetail;
|
|
37
25
|
private updateFormattedData;
|
|
@@ -60,7 +60,10 @@ export interface GridFilterModelConfig {
|
|
|
60
60
|
* gridModel's store.
|
|
61
61
|
*/
|
|
62
62
|
bind?: Store | View;
|
|
63
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* True to update filters immediately after each change made in the column-based filter UI.
|
|
65
|
+
* Defaults to False.
|
|
66
|
+
*/
|
|
64
67
|
commitOnChange?: boolean;
|
|
65
68
|
/**
|
|
66
69
|
* Specifies the fields this model supports for filtering. Should be configs for
|
|
@@ -11,6 +11,7 @@ export declare class ValuesTabModel extends HoistModel {
|
|
|
11
11
|
pendingValues: any[];
|
|
12
12
|
/** Bound search term for `StoreFilterField` */
|
|
13
13
|
filterText: string;
|
|
14
|
+
combineCurrentFilters: boolean;
|
|
14
15
|
/** FieldFilter output by this model. */
|
|
15
16
|
get filter(): FieldFilterSpec;
|
|
16
17
|
get allVisibleRecsChecked(): boolean;
|
|
@@ -26,6 +27,7 @@ export declare class ValuesTabModel extends HoistModel {
|
|
|
26
27
|
reset(): void;
|
|
27
28
|
setRecsChecked(isChecked: boolean, values: any[]): void;
|
|
28
29
|
toggleAllRecsChecked(): void;
|
|
30
|
+
setPendingValues(): void;
|
|
29
31
|
private getFilter;
|
|
30
32
|
private doSyncWithFilter;
|
|
31
33
|
private syncGrid;
|
|
@@ -2,10 +2,11 @@ import { PopperBoundary, PopperModifierOverrides } from '@blueprintjs/core';
|
|
|
2
2
|
import { TimePickerProps } from '@blueprintjs/datetime';
|
|
3
3
|
import { ReactDayPickerSingleProps } from '@blueprintjs/datetime2/src/common/reactDayPickerProps';
|
|
4
4
|
import { HoistInputProps } from '@xh/hoist/cmp/input';
|
|
5
|
-
import { HoistProps,
|
|
5
|
+
import { HoistProps, LayoutProps, Some } from '@xh/hoist/core';
|
|
6
6
|
import '@xh/hoist/desktop/register';
|
|
7
7
|
import { Position } from '@xh/hoist/kit/blueprint';
|
|
8
8
|
import { LocalDate } from '@xh/hoist/utils/datetime';
|
|
9
|
+
import type { Property } from 'csstype';
|
|
9
10
|
import { ReactElement, ReactNode } from 'react';
|
|
10
11
|
import './DateInput.scss';
|
|
11
12
|
export interface DateInputProps extends HoistProps, LayoutProps, HoistInputProps {
|
|
@@ -88,7 +89,7 @@ export interface DateInputProps extends HoistProps, LayoutProps, HoistInputProps
|
|
|
88
89
|
*/
|
|
89
90
|
strictInputParsing?: boolean;
|
|
90
91
|
/** Alignment of entry text within control, default 'left'. */
|
|
91
|
-
textAlign?:
|
|
92
|
+
textAlign?: Property.TextAlign;
|
|
92
93
|
/**
|
|
93
94
|
* Props passed to the TimePicker, as per Blueprint docs.
|
|
94
95
|
* @see https://blueprintjs.com/docs/#datetime/dateinput
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { HoistInputProps } from '@xh/hoist/cmp/input';
|
|
2
|
-
import { HoistProps,
|
|
2
|
+
import { HoistProps, LayoutProps, StyleProps } from '@xh/hoist/core';
|
|
3
3
|
import '@xh/hoist/desktop/register';
|
|
4
4
|
import { NumericPrecision, ZeroPad } from '@xh/hoist/format';
|
|
5
|
+
import type { Property } from 'csstype';
|
|
5
6
|
import { KeyboardEventHandler, ReactElement, ReactNode, Ref } from 'react';
|
|
6
7
|
export interface NumberInputProps extends HoistProps, LayoutProps, StyleProps, HoistInputProps {
|
|
7
8
|
value?: number;
|
|
@@ -51,7 +52,7 @@ export interface NumberInputProps extends HoistProps, LayoutProps, StyleProps, H
|
|
|
51
52
|
/** Standard step size for increment/decrement handling. */
|
|
52
53
|
stepSize?: number;
|
|
53
54
|
/** Alignment of entry text within control, default 'right'. */
|
|
54
|
-
textAlign?:
|
|
55
|
+
textAlign?: Property.TextAlign;
|
|
55
56
|
/**
|
|
56
57
|
* Text appended to the rendered value within control when not editing.
|
|
57
58
|
* Can be used to append e.g. "%" or a unit without need for an external right label.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HoistInputModel, HoistInputProps } from '@xh/hoist/cmp/input';
|
|
2
|
-
import { HoistProps,
|
|
2
|
+
import { HoistProps, LayoutProps, StyleProps } from '@xh/hoist/core';
|
|
3
3
|
import '@xh/hoist/desktop/register';
|
|
4
|
+
import type { Property } from 'csstype';
|
|
4
5
|
import { FocusEvent, KeyboardEventHandler, ReactElement, ReactNode, Ref } from 'react';
|
|
5
6
|
export interface TextInputProps extends HoistProps, HoistInputProps, LayoutProps, StyleProps {
|
|
6
7
|
value?: string;
|
|
@@ -36,7 +37,7 @@ export interface TextInputProps extends HoistProps, HoistInputProps, LayoutProps
|
|
|
36
37
|
/** True to select contents when control receives focus. */
|
|
37
38
|
selectOnFocus?: boolean;
|
|
38
39
|
/** Alignment of entry text within control, default 'left'. */
|
|
39
|
-
textAlign?:
|
|
40
|
+
textAlign?: Property.TextAlign;
|
|
40
41
|
/** True to allow browser spell check, default false. */
|
|
41
42
|
spellCheck?: boolean;
|
|
42
43
|
/** Underlying HTML <input> element type. */
|