@xh/hoist 73.0.0-SNAPSHOT.1745689122610 → 73.0.0-SNAPSHOT.1745973083869

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.
Files changed (39) hide show
  1. package/admin/AdminUtils.ts +5 -0
  2. package/admin/AppModel.ts +17 -5
  3. package/admin/tabs/activity/tracking/ActivityTracking.scss +18 -0
  4. package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +296 -199
  5. package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +81 -51
  6. package/admin/tabs/activity/tracking/chart/AggChartModel.ts +218 -0
  7. package/admin/tabs/activity/tracking/chart/AggChartPanel.ts +61 -0
  8. package/admin/tabs/activity/tracking/datafields/DataFieldsEditor.ts +147 -0
  9. package/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel.ts +133 -0
  10. package/admin/tabs/activity/tracking/detail/ActivityDetailModel.ts +114 -59
  11. package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +61 -30
  12. package/admin/tabs/cluster/instances/memory/MemoryMonitorModel.ts +1 -2
  13. package/build/types/admin/AdminUtils.d.ts +2 -0
  14. package/build/types/admin/AppModel.d.ts +4 -1
  15. package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +30 -26
  16. package/build/types/admin/tabs/activity/tracking/chart/AggChartModel.d.ts +33 -0
  17. package/build/types/admin/tabs/activity/tracking/chart/AggChartPanel.d.ts +2 -0
  18. package/build/types/admin/tabs/activity/tracking/datafields/DataFieldsEditor.d.ts +2 -0
  19. package/build/types/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel.d.ts +46 -0
  20. package/build/types/admin/tabs/activity/tracking/detail/ActivityDetailModel.d.ts +13 -1
  21. package/build/types/cmp/form/FormModel.d.ts +15 -27
  22. package/build/types/cmp/form/field/SubformsFieldModel.d.ts +20 -18
  23. package/build/types/core/HoistBase.d.ts +2 -2
  24. package/build/types/data/cube/CubeField.d.ts +4 -5
  25. package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +3 -3
  26. package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +3 -3
  27. package/cmp/error/ErrorBoundaryModel.ts +1 -1
  28. package/cmp/form/FormModel.ts +18 -28
  29. package/cmp/form/field/SubformsFieldModel.ts +28 -22
  30. package/cmp/grid/impl/GridHScrollbar.ts +1 -2
  31. package/core/HoistBase.ts +12 -12
  32. package/data/cube/CubeField.ts +17 -18
  33. package/package.json +1 -1
  34. package/promise/Promise.ts +8 -6
  35. package/tsconfig.tsbuildinfo +1 -1
  36. package/admin/tabs/activity/tracking/charts/ChartsModel.ts +0 -218
  37. package/admin/tabs/activity/tracking/charts/ChartsPanel.ts +0 -76
  38. package/build/types/admin/tabs/activity/tracking/charts/ChartsModel.d.ts +0 -34
  39. package/build/types/admin/tabs/activity/tracking/charts/ChartsPanel.d.ts +0 -2
@@ -0,0 +1,133 @@
1
+ import {ActivityTrackingModel} from '@xh/hoist/admin/tabs/activity/tracking/ActivityTrackingModel';
2
+ import {FormModel, SubformsFieldModel} from '@xh/hoist/cmp/form';
3
+ import {HoistModel, managed} from '@xh/hoist/core';
4
+ import {AggregatorToken, FieldType, genDisplayName, required} from '@xh/hoist/data';
5
+ import {action, observable, makeObservable} from '@xh/hoist/mobx';
6
+ import {computed} from '@xh/hoist/mobx';
7
+ import {last, uniqBy} from 'lodash';
8
+
9
+ /**
10
+ * Slimmed down {@link CubeFieldSpec} for persisted specs of fields to be extracted from the `data`
11
+ * block of loaded track statements and promoted to top-level columns in the grids. These are the
12
+ * entities (stored on parent `ActivityTrackingModel`) that are edited by the this component.
13
+ */
14
+ export interface ActivityTrackingDataFieldSpec {
15
+ /**
16
+ * Path to field data within the `data` block of each track log entry. Can be dot-delimited for
17
+ * nested data (e.g. `timings.preAuth`). See {@link ActivityTrackingModel.processRawTrackLog}.
18
+ */
19
+ path: string;
20
+ /**
21
+ * Normalized name for the field for use in Cube/Grid - adds `df_` prefix to avoid conflicts
22
+ * and strips out dot-delimiters. See {@link ActivityTrackingModel.setDataFields}.
23
+ */
24
+ name: string;
25
+ displayName?: string;
26
+ type?: FieldType;
27
+ isDimension?: boolean;
28
+ aggregator?: AggregatorToken;
29
+ }
30
+
31
+ export class DataFieldsEditorModel extends HoistModel {
32
+ @observable showEditor = false;
33
+
34
+ @managed formModel: FormModel;
35
+ private parentModel: ActivityTrackingModel;
36
+
37
+ aggTokens: AggregatorToken[] = ['AVG', 'MAX', 'MIN', 'SINGLE', 'SUM', 'UNIQUE'];
38
+
39
+ get dataFields(): SubformsFieldModel {
40
+ return this.formModel.fields.dataFields as SubformsFieldModel;
41
+ }
42
+
43
+ @computed
44
+ get appliedDataFieldCount(): number {
45
+ return this.parentModel.dataFields.length;
46
+ }
47
+
48
+ get hasAppliedDataFields(): boolean {
49
+ return this.appliedDataFieldCount > 0;
50
+ }
51
+
52
+ constructor(parentModel: ActivityTrackingModel) {
53
+ super();
54
+ makeObservable(this);
55
+
56
+ this.parentModel = parentModel;
57
+
58
+ this.formModel = new FormModel({
59
+ fields: [
60
+ {
61
+ name: 'dataFields',
62
+ subforms: {
63
+ fields: [
64
+ {name: 'path', rules: [required]},
65
+ {name: 'displayName'},
66
+ {name: 'type', initialValue: 'auto'},
67
+ {name: 'isDimension'},
68
+ {name: 'aggregator'}
69
+ ]
70
+ }
71
+ }
72
+ ]
73
+ });
74
+
75
+ this.addReaction({
76
+ track: () => this.parentModel.dataFields,
77
+ run: () => this.syncFromParent(),
78
+ fireImmediately: true
79
+ });
80
+ }
81
+
82
+ @action
83
+ show() {
84
+ this.syncFromParent();
85
+ this.showEditor = true;
86
+ }
87
+
88
+ @action
89
+ applyAndClose() {
90
+ this.syncToParent();
91
+ this.showEditor = false;
92
+ }
93
+
94
+ @action
95
+ close() {
96
+ this.showEditor = false;
97
+ }
98
+
99
+ addField() {
100
+ this.dataFields.add();
101
+ }
102
+
103
+ cloneField(formModel: FormModel) {
104
+ const {dataFields} = this,
105
+ srcIdx = dataFields.value.indexOf(formModel);
106
+
107
+ dataFields.add({initialValues: formModel.getData(), index: srcIdx + 1});
108
+ }
109
+
110
+ private syncFromParent() {
111
+ this.formModel.init({
112
+ dataFields: this.parentModel.dataFields
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Normalize field specs and set onto parent.
118
+ * Note, will de-dupe fields by name w/o any alert to user.
119
+ */
120
+ private syncToParent() {
121
+ const raw = this.formModel.getData().dataFields,
122
+ specs: ActivityTrackingDataFieldSpec[] = raw.map(it => {
123
+ const {displayName, path, aggregator: agg} = it;
124
+ return {
125
+ ...it,
126
+ name: 'df_' + path.replaceAll('.', '') + (agg ? `_${agg}` : ''),
127
+ displayName: displayName || genDisplayName(last(path.split('.')))
128
+ };
129
+ });
130
+
131
+ this.parentModel.setDataFields(uniqBy(specs, 'name'));
132
+ }
133
+ }
@@ -9,15 +9,27 @@ import * as Col from '@xh/hoist/admin/columns';
9
9
  import {FormModel} from '@xh/hoist/cmp/form';
10
10
  import {GridModel} from '@xh/hoist/cmp/grid';
11
11
  import {HoistModel, lookup, managed} from '@xh/hoist/core';
12
- import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
12
+ import {StoreRecord} from '@xh/hoist/data';
13
+ import {timestampReplacer} from '@xh/hoist/format';
14
+ import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
15
+ import {get} from 'lodash';
13
16
  import {ActivityTrackingModel} from '../ActivityTrackingModel';
14
- import {fmtJson, timestampReplacer} from '@xh/hoist/format';
15
17
 
16
18
  export class ActivityDetailModel extends HoistModel {
17
19
  @lookup(ActivityTrackingModel) activityTrackingModel: ActivityTrackingModel;
18
- @managed gridModel: GridModel;
19
- @managed formModel: FormModel;
20
- @observable formattedData;
20
+
21
+ @managed @observable.ref gridModel: GridModel;
22
+ @managed @observable.ref formModel: FormModel;
23
+
24
+ /**
25
+ * Optional dot-delimited path(s) to filter the displayed `data` payload down to a particular
26
+ * node or nodes, for easier browsing of records with a large data payload. Multiple paths
27
+ * can be separated with `|`.
28
+ */
29
+ @bindable formattedDataFilterPath: string;
30
+
31
+ /** Stringified, pretty-printed, optionally path-filtered `data` payload. */
32
+ @observable formattedData: string;
21
33
 
22
34
  @computed
23
35
  get hasSelection() {
@@ -30,48 +42,14 @@ export class ActivityDetailModel extends HoistModel {
30
42
  }
31
43
 
32
44
  override onLinked() {
33
- const hidden = true;
34
-
35
- this.gridModel = new GridModel({
36
- sortBy: 'dateCreated|desc',
37
- colChooserModel: true,
38
- enableExport: true,
39
- filterModel: false,
40
- exportOptions: {
41
- columns: 'ALL',
42
- filename: exportFilename('activity-detail')
43
- },
44
- emptyText: 'Select a group on the left to see detailed tracking logs.',
45
- columns: [
46
- {...Col.impersonatingFlag},
47
- {...Col.entryId, hidden},
48
- {...Col.username},
49
- {...Col.impersonating, hidden},
50
- {...Col.category},
51
- {...Col.msg},
52
- {...Col.browser},
53
- {...Col.device},
54
- {...Col.userAgent, hidden},
55
- {...Col.appVersion},
56
- {...Col.appEnvironment, hidden},
57
- {...Col.data, hidden},
58
- {...Col.url},
59
- {...Col.correlationId},
60
- {...Col.instance, hidden},
61
- {...Col.severity, hidden},
62
- {...Col.elapsed},
63
- {...Col.dateCreatedWithSec, displayName: 'Timestamp'}
64
- ]
65
- });
66
-
67
- this.formModel = new FormModel({
68
- readonly: true,
69
- fields: this.gridModel
70
- .getLeafColumns()
71
- .map(it => ({name: it.field, displayName: it.headerName as string}))
72
- });
45
+ this.markPersist('formattedDataFilterPath', this.activityTrackingModel.persistWith);
73
46
 
74
47
  this.addReaction(
48
+ {
49
+ track: () => this.activityTrackingModel.dataFields,
50
+ run: () => this.createAndSetCoreModels(),
51
+ fireImmediately: true
52
+ },
75
53
  {
76
54
  track: () => this.activityTrackingModel.gridModel.selectedRecord,
77
55
  run: aggRec => this.showActivityEntriesAsync(aggRec)
@@ -79,11 +57,18 @@ export class ActivityDetailModel extends HoistModel {
79
57
  {
80
58
  track: () => this.gridModel.selectedRecord,
81
59
  run: detailRec => this.showEntryDetail(detailRec)
60
+ },
61
+ {
62
+ track: () => this.formattedDataFilterPath,
63
+ run: () => this.updateFormattedData()
82
64
  }
83
65
  );
84
66
  }
85
67
 
86
- private async showActivityEntriesAsync(aggRec) {
68
+ //------------------
69
+ // Implementation
70
+ //------------------
71
+ private async showActivityEntriesAsync(aggRec: StoreRecord) {
87
72
  const {gridModel} = this,
88
73
  leaves = this.getAllLeafRows(aggRec);
89
74
 
@@ -92,7 +77,7 @@ export class ActivityDetailModel extends HoistModel {
92
77
  }
93
78
 
94
79
  // Extract all leaf, track-entry-level rows from an aggregate record (at any level).
95
- private getAllLeafRows(aggRec, ret = []) {
80
+ private getAllLeafRows(aggRec: StoreRecord, ret = []) {
96
81
  if (!aggRec) return [];
97
82
 
98
83
  if (aggRec.children.length) {
@@ -106,22 +91,92 @@ export class ActivityDetailModel extends HoistModel {
106
91
  return ret;
107
92
  }
108
93
 
109
- // Extract data from a (detail) grid record and flush it into our form for display.
110
- // Also parse/format any additional data (as JSON) if provided.
94
+ /** Extract data from a (detail) grid record and flush it into our form for display. */
95
+ @action
96
+ private showEntryDetail(detailRec: StoreRecord) {
97
+ this.formModel.init(detailRec?.data ?? {});
98
+ this.updateFormattedData();
99
+ }
100
+
111
101
  @action
112
- private showEntryDetail(detailRec) {
113
- const recData = detailRec?.data ?? {},
114
- trackData = recData.data;
102
+ private updateFormattedData() {
103
+ const {gridModel, formattedDataFilterPath} = this,
104
+ trackData = gridModel.selectedRecord?.data.data;
105
+
106
+ if (!trackData) {
107
+ this.formattedData = '';
108
+ return;
109
+ }
115
110
 
116
- this.formModel.init(recData);
111
+ let parsed = JSON.parse(trackData),
112
+ toFormat = parsed;
117
113
 
118
- let formattedTrackData = trackData;
119
- if (formattedTrackData) {
120
- try {
121
- formattedTrackData = fmtJson(trackData, {replacer: timestampReplacer()});
122
- } catch (ignored) {}
114
+ if (formattedDataFilterPath) {
115
+ const paths = formattedDataFilterPath.split('|');
116
+ if (paths.length > 1) {
117
+ toFormat = {};
118
+ paths.forEach(path => (toFormat[path.trim()] = get(parsed, path.trim())));
119
+ } else {
120
+ toFormat = get(parsed, formattedDataFilterPath.trim());
121
+ }
123
122
  }
124
123
 
125
- this.formattedData = formattedTrackData;
124
+ this.formattedData = JSON.stringify(toFormat, timestampReplacer(), 2);
125
+ }
126
+
127
+ //------------------------
128
+ // Core data-handling models
129
+ //------------------------
130
+ @action
131
+ private createAndSetCoreModels() {
132
+ this.gridModel = this.createGridModel();
133
+ this.formModel = this.createSingleEntryFormModel();
134
+ }
135
+
136
+ private createGridModel(): GridModel {
137
+ const hidden = true;
138
+ return new GridModel({
139
+ persistWith: {...this.activityTrackingModel.persistWith, path: 'detailGrid'},
140
+ sortBy: 'dateCreated|desc',
141
+ colChooserModel: true,
142
+ enableExport: true,
143
+ filterModel: false,
144
+ exportOptions: {
145
+ columns: 'ALL',
146
+ filename: exportFilename('activity-detail')
147
+ },
148
+ emptyText: 'Select a group on the left to see detailed tracking logs.',
149
+ columns: [
150
+ {...Col.impersonatingFlag},
151
+ {...Col.entryId, hidden},
152
+ {...Col.username},
153
+ {...Col.impersonating, hidden},
154
+ {...Col.category},
155
+ {...Col.msg},
156
+ {...Col.browser},
157
+ {...Col.device},
158
+ {...Col.userAgent, hidden},
159
+ {...Col.appVersion},
160
+ {...Col.appEnvironment, hidden},
161
+ {...Col.data, hidden},
162
+ {...Col.url},
163
+ {...Col.correlationId},
164
+ {...Col.instance, hidden},
165
+ {...Col.severity, hidden},
166
+ {...Col.elapsed},
167
+ {...Col.dateCreatedWithSec, displayName: 'Timestamp'},
168
+ ...this.activityTrackingModel.dataFieldCols
169
+ ]
170
+ });
171
+ }
172
+
173
+ // TODO - don't base on grid cols
174
+ private createSingleEntryFormModel(): FormModel {
175
+ return new FormModel({
176
+ readonly: true,
177
+ fields: this.gridModel
178
+ .getLeafColumns()
179
+ .map(it => ({name: it.field, displayName: it.headerName as string}))
180
+ });
126
181
  }
127
182
  }
@@ -7,16 +7,17 @@
7
7
  import {correlationId, instance} from '@xh/hoist/admin/columns';
8
8
  import {form} from '@xh/hoist/cmp/form';
9
9
  import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
10
- import {a, div, filler, h3, hframe, placeholder, span} from '@xh/hoist/cmp/layout';
11
- import {hoistCmp, creates} from '@xh/hoist/core';
10
+ import {a, br, div, filler, h3, hframe, placeholder, span} from '@xh/hoist/cmp/layout';
11
+ import {creates, hoistCmp} from '@xh/hoist/core';
12
12
  import {colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button';
13
13
  import {formField} from '@xh/hoist/desktop/cmp/form';
14
14
  import {gridFindField} from '@xh/hoist/desktop/cmp/grid';
15
- import {jsonInput} from '@xh/hoist/desktop/cmp/input';
15
+ import {jsonInput, textInput} from '@xh/hoist/desktop/cmp/input';
16
16
  import {panel} from '@xh/hoist/desktop/cmp/panel';
17
17
  import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
18
18
  import {dateTimeSecRenderer, numberRenderer} from '@xh/hoist/format';
19
19
  import {Icon} from '@xh/hoist/icon/Icon';
20
+ import {tooltip} from '@xh/hoist/kit/blueprint';
20
21
  import {ActivityDetailModel} from './ActivityDetailModel';
21
22
 
22
23
  export const activityDetailView = hoistCmp.factory({
@@ -24,26 +25,28 @@ export const activityDetailView = hoistCmp.factory({
24
25
 
25
26
  render({model, ...props}) {
26
27
  return panel({
27
- title: 'Track Log Entries',
28
- icon: Icon.list(),
29
28
  className: 'xh-admin-activity-detail',
30
- compactHeader: true,
31
- items: [grid({flex: 1}), detailRecPanel()],
32
29
  tbar: tbar(),
30
+ items: [grid({flex: 1}), detailRecPanel()],
33
31
  ...props
34
32
  });
35
33
  }
36
34
  });
37
35
 
38
- const tbar = hoistCmp.factory(({model}) => {
39
- return toolbar(
40
- filler(),
41
- gridCountLabel({unit: 'entry'}),
42
- '-',
43
- gridFindField(),
44
- colChooserButton(),
45
- exportButton()
46
- );
36
+ const tbar = hoistCmp.factory<ActivityDetailModel>(({model}) => {
37
+ const {gridModel} = model;
38
+ return toolbar({
39
+ compact: true,
40
+ items: [
41
+ filler(),
42
+ gridCountLabel({unit: 'entry'}),
43
+ '-',
44
+ // TODO - these don't react properly to swapping out grid model
45
+ gridFindField({gridModel}),
46
+ colChooserButton({gridModel}),
47
+ exportButton()
48
+ ]
49
+ });
47
50
  });
48
51
 
49
52
  // Discrete outer panel to retain sizing across master/detail selection changes.
@@ -54,7 +57,11 @@ const detailRecPanel = hoistCmp.factory<ActivityDetailModel>(({model}) => {
54
57
  compactHeader: true,
55
58
  modelConfig: {
56
59
  side: 'bottom',
57
- defaultSize: 400
60
+ defaultSize: 400,
61
+ persistWith: {
62
+ ...model.activityTrackingModel.persistWith,
63
+ path: 'singleActivityDetailPanel'
64
+ }
58
65
  },
59
66
  item: detailRecForm()
60
67
  });
@@ -125,23 +132,47 @@ const detailRecForm = hoistCmp.factory<ActivityDetailModel>(({model}) => {
125
132
  formField({field: 'userAgent'})
126
133
  ]
127
134
  }),
128
- panel({
129
- flex: 1,
130
- className: 'xh-border-left',
131
- items: [h3(Icon.json(), 'Additional Data'), additionalDataJsonInput()]
132
- })
135
+ additionalDataPanel()
133
136
  )
134
137
  })
135
- : placeholder('Select an activity tracking record to view details.');
138
+ : placeholder(Icon.detail(), 'Select an activity tracking record to view details.');
136
139
  });
137
140
 
138
- const additionalDataJsonInput = hoistCmp.factory<ActivityDetailModel>(({model}) => {
139
- return jsonInput({
140
- readonly: true,
141
- width: '100%',
142
- height: '100%',
143
- showCopyButton: true,
144
- value: model.formattedData
141
+ const additionalDataPanel = hoistCmp.factory<ActivityDetailModel>(({model}) => {
142
+ return panel({
143
+ flex: 1,
144
+ className: 'xh-border-left',
145
+ items: [
146
+ h3(Icon.json(), 'Additional Data'),
147
+ jsonInput({
148
+ readonly: true,
149
+ width: '100%',
150
+ height: '100%',
151
+ showCopyButton: true,
152
+ value: model.formattedData
153
+ }),
154
+ toolbar({
155
+ compact: true,
156
+ items: [
157
+ textInput({
158
+ placeholder: 'Path filter(s)...',
159
+ leftIcon: Icon.filter(),
160
+ commitOnChange: true,
161
+ enableClear: true,
162
+ flex: 1,
163
+ bind: 'formattedDataFilterPath'
164
+ }),
165
+ tooltip({
166
+ item: Icon.questionCircle({className: 'xh-margin-right'}),
167
+ content: span(
168
+ 'Specify one or more dot-delimited paths to filter the JSON data displayed above.',
169
+ br(),
170
+ 'Separate multiple paths that you wish to include with a | character.'
171
+ )
172
+ })
173
+ ]
174
+ })
175
+ ]
145
176
  });
146
177
  });
147
178
 
@@ -13,9 +13,8 @@ import {LoadSpec, managed, XH} from '@xh/hoist/core';
13
13
  import {lengthIs, required} from '@xh/hoist/data';
14
14
  import {fmtTime, numberRenderer} from '@xh/hoist/format';
15
15
  import {Icon} from '@xh/hoist/icon';
16
- import {bindable, makeObservable} from '@xh/hoist/mobx';
16
+ import {bindable, makeObservable, observable, runInAction} from '@xh/hoist/mobx';
17
17
  import {forOwn, orderBy, sortBy} from 'lodash';
18
- import {observable, runInAction} from 'mobx';
19
18
 
20
19
  export interface PastInstance {
21
20
  name: string;
@@ -1,3 +1,4 @@
1
+ import { AppModel } from '@xh/hoist/admin/AppModel';
1
2
  /**
2
3
  * Generate a standardized filename for an Admin module grid export, without datestamp.
3
4
  */
@@ -7,3 +8,4 @@ export declare function exportFilename(moduleName: string): string;
7
8
  * Returned as a closure to ensure current date is evaluated at export time.
8
9
  */
9
10
  export declare function exportFilenameWithDate(moduleName: string): () => string;
11
+ export declare function getAppModel<T extends AppModel>(): T;
@@ -1,11 +1,13 @@
1
1
  import { TabConfig, TabContainerModel } from '@xh/hoist/cmp/tab';
2
+ import { ViewManagerModel } from '@xh/hoist/cmp/viewmanager';
2
3
  import { HoistAppModel } from '@xh/hoist/core';
3
4
  import { Route } from 'router5';
4
5
  export declare class AppModel extends HoistAppModel {
5
- static instance: AppModel;
6
6
  tabModel: TabContainerModel;
7
+ viewManagerModels: Record<string, ViewManagerModel>;
7
8
  static get readonly(): boolean;
8
9
  constructor();
10
+ initAsync(): Promise<void>;
9
11
  getRoutes(): Route[];
10
12
  getAppMenuButtonExtraItems(): any[];
11
13
  getTabRoutes(): Route[];
@@ -13,4 +15,5 @@ export declare class AppModel extends HoistAppModel {
13
15
  /** Open the primary business-facing application, typically 'app'. */
14
16
  openPrimaryApp(): void;
15
17
  getPrimaryAppCode(): string;
18
+ initViewManagerModelsAsync(): Promise<void>;
16
19
  }
@@ -1,31 +1,30 @@
1
+ import { ActivityTrackingDataFieldSpec, DataFieldsEditorModel } from '@xh/hoist/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel';
1
2
  import { FilterChooserModel } from '@xh/hoist/cmp/filter';
2
3
  import { FormModel } from '@xh/hoist/cmp/form';
3
- import { GridModel } from '@xh/hoist/cmp/grid';
4
+ import { ColumnSpec, GridModel } from '@xh/hoist/cmp/grid';
4
5
  import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
5
- import { HoistModel, LoadSpec } from '@xh/hoist/core';
6
+ import { HoistModel, LoadSpec, PlainObject } from '@xh/hoist/core';
6
7
  import { Cube } from '@xh/hoist/data';
7
8
  import { LocalDate } from '@xh/hoist/utils/datetime';
8
- export declare const PERSIST_ACTIVITY: {
9
- localStorageKey: string;
10
- };
11
9
  export declare class ActivityTrackingModel extends HoistModel {
12
- persistWith: {
13
- localStorageKey: string;
14
- };
10
+ /** FormModel for server-side querying controls. */
15
11
  formModel: FormModel;
12
+ /** Models for data-handling components - can be rebuilt due to change in dataFields. */
16
13
  groupingChooserModel: GroupingChooserModel;
17
14
  cube: Cube;
18
15
  filterChooserModel: FilterChooserModel;
19
16
  gridModel: GridModel;
20
- get enabled(): boolean;
21
- get dimensions(): string[];
17
+ dataFieldsEditorModel: DataFieldsEditorModel;
22
18
  /**
23
- * Summary of currently active query / filters.
24
- * TODO - include new local filters if feasible, or drop this altogether.
25
- * Formerly summarized server-side filters, but was misleading w/new filtering.
19
+ * Optional spec for fields to be extracted from additional `data` returned by track entries
20
+ * and promoted to top-level columns in the grids. Supports dot-delimited paths as names.
26
21
  */
27
- get queryDisplayString(): string;
22
+ dataFields: ActivityTrackingDataFieldSpec[];
23
+ showFilterChooser: boolean;
24
+ get enabled(): boolean;
25
+ get dimensions(): string[];
28
26
  get endDay(): LocalDate;
27
+ get hasFilter(): boolean;
29
28
  get maxRowOptions(): {
30
29
  value: number;
31
30
  label: string;
@@ -33,24 +32,29 @@ export declare class ActivityTrackingModel extends HoistModel {
33
32
  get maxRows(): number;
34
33
  /** True if data loaded from the server has been topped by maxRows. */
35
34
  get maxRowsReached(): boolean;
35
+ get dataFieldCols(): ColumnSpec[];
36
+ get viewManagerModel(): import("../../../../cmp/viewmanager").ViewManagerModel<PlainObject>;
36
37
  private _monthFormat;
37
- private _defaultDims;
38
38
  constructor();
39
39
  doLoadAsync(loadSpec: LoadSpec): Promise<void>;
40
- loadGridAsync(): Promise<void>;
41
- separateLeafRows(node: any): void;
42
- resetQuery(): void;
43
- adjustDates(dir: any): void;
40
+ setDataFields(dataFields: ActivityTrackingDataFieldSpec[]): void;
41
+ toggleFilterChooser(): void;
42
+ adjustDates(dir: 'add' | 'subtract'): void;
44
43
  adjustStartDate(value: any, unit: any): void;
45
44
  isInterval(value: any, unit: any): boolean;
46
- cubeLabelComparator(valA: any, valB: any, sortDir: any, abs: any, { recordA, recordB, defaultComparator }: {
47
- recordA: any;
48
- recordB: any;
49
- defaultComparator: any;
50
- }): any;
51
- getComparableValForDim(raw: any, dim: any): any;
45
+ getDisplayName(fieldName: string): string;
46
+ private loadGridAsync;
47
+ private separateLeafRows;
48
+ private cubeLabelComparator;
49
+ private getComparableValForDim;
52
50
  private get defaultStartDay();
53
51
  private get defaultEndDay();
54
- private loadLookupsAsync;
55
52
  private get query();
53
+ private createAndSetCoreModels;
54
+ private createCube;
55
+ private createFilterChooserModel;
56
+ private createGroupingChooserModel;
57
+ private createGridModel;
58
+ private processRawTrackLog;
59
+ private getDfRenderer;
56
60
  }
@@ -0,0 +1,33 @@
1
+ import { ChartModel } from '@xh/hoist/cmp/chart';
2
+ import { HoistModel, SelectOption } from '@xh/hoist/core';
3
+ import { ActivityTrackingModel } from '../ActivityTrackingModel';
4
+ export declare class AggChartModel extends HoistModel {
5
+ activityTrackingModel: ActivityTrackingModel;
6
+ /**
7
+ * Metric to chart on Y axis - one of:
8
+ * - entryCount - count of total track log entries within the primary dim group.
9
+ * - count - count of unique secondary dim values within the primary dim group.
10
+ * - elapsed - avg elapsed time in ms for the primary dim group.
11
+ * - any other numeric, aggregated custom data field metrics, if so configured
12
+ */
13
+ metric: string;
14
+ get metricLabel(): string;
15
+ incWeekends: boolean;
16
+ chartModel: ChartModel;
17
+ get showAsTimeseries(): boolean;
18
+ get selectableMetrics(): SelectOption[];
19
+ constructor();
20
+ onLinked(): void;
21
+ private get cube();
22
+ private get dimensions();
23
+ private get primaryDim();
24
+ private get primaryDimLabel();
25
+ private get secondaryDim();
26
+ private get secondaryDimLabel();
27
+ private get data();
28
+ private createChartModel;
29
+ private selectRow;
30
+ private loadChart;
31
+ private getSeriesData;
32
+ private getDisplayName;
33
+ }
@@ -0,0 +1,2 @@
1
+ import { AggChartModel } from './AggChartModel';
2
+ export declare const aggChartPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<AggChartModel>>;
@@ -0,0 +1,2 @@
1
+ import { DataFieldsEditorModel } from '@xh/hoist/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel';
2
+ export declare const dataFieldsEditor: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<DataFieldsEditorModel>>;