@xh/hoist 59.0.2 → 59.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/admin/AdminUtils.ts +23 -0
  3. package/admin/tabs/activity/clienterrors/ClientErrorsModel.ts +2 -1
  4. package/admin/tabs/activity/feedback/FeedbackPanel.ts +3 -3
  5. package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +2 -1
  6. package/admin/tabs/activity/tracking/detail/ActivityDetailModel.ts +3 -2
  7. package/admin/tabs/general/config/ConfigPanelModel.ts +2 -2
  8. package/admin/tabs/general/users/UserModel.ts +2 -2
  9. package/admin/tabs/monitor/MonitorResultsModel.ts +48 -11
  10. package/admin/tabs/monitor/MonitorResultsPanel.ts +71 -8
  11. package/admin/tabs/server/connectionpool/ConnPoolMonitorModel.ts +2 -2
  12. package/admin/tabs/server/ehcache/EhCacheModel.ts +2 -2
  13. package/admin/tabs/server/environment/ServerEnvModel.ts +3 -3
  14. package/admin/tabs/server/logLevel/LogLevelPanel.ts +3 -3
  15. package/admin/tabs/server/logViewer/LogViewerModel.ts +2 -2
  16. package/admin/tabs/server/memory/MemoryMonitorModel.ts +2 -2
  17. package/admin/tabs/server/services/ServiceModel.ts +3 -3
  18. package/admin/tabs/server/websocket/WebSocketModel.ts +3 -2
  19. package/admin/tabs/userData/jsonblob/JsonBlobModel.ts +2 -2
  20. package/admin/tabs/userData/prefs/PreferenceModel.ts +2 -2
  21. package/admin/tabs/userData/prefs/UserPreferencePanel.ts +3 -3
  22. package/appcontainer/AppContainerModel.ts +5 -0
  23. package/appcontainer/ExceptionDialogModel.ts +6 -0
  24. package/cmp/error/ErrorBoundary.ts +68 -0
  25. package/cmp/error/ErrorBoundaryModel.ts +77 -0
  26. package/cmp/markdown/Markdown.ts +32 -0
  27. package/cmp/markdown/index.ts +1 -0
  28. package/core/XH.ts +21 -9
  29. package/core/exception/ExceptionHandler.ts +15 -0
  30. package/data/cube/View.ts +14 -2
  31. package/desktop/appcontainer/AppContainer.ts +17 -3
  32. package/desktop/appcontainer/Banner.scss +25 -0
  33. package/desktop/appcontainer/Banner.ts +2 -1
  34. package/desktop/cmp/dash/canvas/impl/DashCanvasView.ts +4 -1
  35. package/desktop/cmp/dash/container/impl/DashContainerView.ts +2 -1
  36. package/desktop/cmp/dock/impl/DockView.ts +2 -1
  37. package/desktop/cmp/error/ErrorMessage.scss +1 -0
  38. package/desktop/cmp/error/ErrorMessage.ts +61 -23
  39. package/desktop/cmp/input/Checkbox.scss +13 -0
  40. package/desktop/cmp/input/Checkbox.ts +2 -0
  41. package/desktop/cmp/modalsupport/ModalSupport.scss +2 -0
  42. package/desktop/cmp/panel/Panel.ts +37 -14
  43. package/desktop/cmp/panel/PanelModel.ts +35 -7
  44. package/desktop/cmp/panel/impl/PanelHeader.scss +5 -0
  45. package/desktop/cmp/panel/impl/PanelHeader.ts +53 -38
  46. package/desktop/cmp/tab/impl/Tab.ts +2 -1
  47. package/dynamics/desktop.ts +15 -17
  48. package/dynamics/mobile.ts +10 -8
  49. package/inspector/Inspector.scss +5 -10
  50. package/inspector/InspectorPanel.ts +2 -0
  51. package/kit/react-markdown/index.ts +11 -0
  52. package/mobile/appcontainer/AppContainer.ts +17 -3
  53. package/mobile/appcontainer/Banner.scss +25 -0
  54. package/mobile/appcontainer/Banner.ts +2 -1
  55. package/mobile/cmp/error/ErrorMessage.ts +58 -18
  56. package/mobile/cmp/navigator/PageModel.ts +1 -0
  57. package/mobile/cmp/navigator/impl/Page.ts +2 -1
  58. package/mobile/cmp/panel/Panel.ts +5 -1
  59. package/mobile/cmp/tab/impl/Tab.ts +2 -1
  60. package/package.json +5 -3
  61. package/styles/vars.scss +2 -0
  62. package/svc/AlertBannerService.ts +2 -2
  63. package/svc/GridExportService.ts +1 -1
  64. package/admin/tabs/monitor/MonitorResultsToolbar.ts +0 -66
  65. package/appcontainer/ErrorBoundary.ts +0 -36
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 59.1.0 - 2023-09-20
4
+
5
+ ### 🎁 New Features
6
+
7
+ * Introduced new `ErrorBoundary` component for finer-grained application handling of React Errors.
8
+ * Hoist now wraps `Tab`, `DashCanvasView`, `DashContainerView`, `DockView`, and `Page` in an
9
+ `ErrorBoundary`. This provides better isolation of application content, minimizing the chance
10
+ that any individual component can crash the entire app.
11
+ * A new `PanelModel.errorBoundary` prop allows developers to opt-in to an `ErrorBoundary`
12
+ wrapper around the contents of any panel.
13
+ * `ErrorMessage` component now provides an ability to show additional exception details.
14
+ * Added new `Markdown` component for rendering Markdown formatted strings as markup. This includes
15
+ bundling `react-markdown` in Hoist.
16
+ * If your app already uses `react-markdown` or similar, we recommend updating to use the
17
+ new `Markdown` component exported by Hoist to benefit from future upgrades.
18
+ * Admin-managed alert banners leverage the new markdown component to support bold, italics and
19
+ links within alert messages.
20
+ * Improved and fixed up `Panel` headers, including:
21
+ * Added new `Panel.headerClassName` prop for easier CSS manipulation of panel's header.
22
+ * Improved `Panel.collapsedTitle` prop and added `Panel.collapsedIcon` prop. These two props now
23
+ fully govern header display when collapsed.
24
+ * Improved styling for disabled `checkbox` inputs.
25
+
26
+ ### ⚙️ Technical
27
+ * `XH.showException` has been deprecated. Use similar methods on `XH.exceptionHandler` instead.
28
+
29
+ ### 📚 Libraries
30
+
31
+ * numbro `2.3 -> 2.4`
32
+ * react-markdown `added @ 8.0`
33
+ * remark-breaks `added @ 3.0`
34
+
35
+ ## 59.0.3 - 2023-08-25
36
+
37
+ ### ⚙️ Technical
38
+
39
+ * New `XH.flags` property to govern experimental, hotfix, or otherwise provisional features.
40
+
41
+ * Provide temporary workaround to chromium bug effecting BigNumber. Enabled via flag
42
+ `applyBigNumberWorkaround`. See https://github.com/MikeMcl/bignumber.js/issues/354.
43
+
3
44
  ## 59.0.2 - 2023-08-24
4
45
 
5
46
  ### 🐞 Bug Fixes
@@ -0,0 +1,23 @@
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 © 2023 Extremely Heavy Industries Inc.
6
+ */
7
+ import {XH} from '@xh/hoist/core';
8
+ import {LocalDate} from '@xh/hoist/utils/datetime';
9
+
10
+ /**
11
+ * Generate a standardized filename for an Admin module grid export, without datestamp.
12
+ */
13
+ export function exportFilename(moduleName: string): string {
14
+ return `${XH.appCode}-${moduleName}`;
15
+ }
16
+
17
+ /**
18
+ * Generate a standardized filename for an Admin module grid export, with current datestamp.
19
+ * Returned as a closure to ensure current date is evaluated at export time.
20
+ */
21
+ export function exportFilenameWithDate(moduleName: string): () => string {
22
+ return () => `${XH.appCode}-${moduleName}-${LocalDate.today()}`;
23
+ }
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilename} from '@xh/hoist/admin/AdminUtils';
7
8
  import {FilterChooserModel} from '@xh/hoist/cmp/filter';
8
9
  import {FormModel} from '@xh/hoist/cmp/form';
9
10
  import {GridModel} from '@xh/hoist/cmp/grid';
@@ -43,7 +44,7 @@ export class ClientErrorsModel extends HoistModel {
43
44
  colChooserModel: true,
44
45
  enableExport: true,
45
46
  exportOptions: {
46
- filename: `${XH.appCode}-client-errors`,
47
+ filename: exportFilename('client-errors'),
47
48
  columns: 'ALL'
48
49
  },
49
50
  emptyText: 'No errors reported...',
@@ -4,13 +4,13 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {AppModel} from '@xh/hoist/admin/AppModel';
8
9
  import * as Col from '@xh/hoist/admin/columns';
9
- import {hoistCmp, XH} from '@xh/hoist/core';
10
+ import {hoistCmp} from '@xh/hoist/core';
10
11
  import {FieldSpec} from '@xh/hoist/data';
11
12
  import {textArea} from '@xh/hoist/desktop/cmp/input';
12
13
  import {deleteAction, restGrid, RestGridConfig} from '@xh/hoist/desktop/cmp/rest';
13
- import {LocalDate} from '@xh/hoist/utils/datetime';
14
14
 
15
15
  export const feedbackPanel = hoistCmp.factory(() =>
16
16
  restGrid({modelConfig: {...modelSpec, readonly: AppModel.readonly}})
@@ -20,7 +20,7 @@ const modelSpec: RestGridConfig = {
20
20
  persistWith: {localStorageKey: 'xhAdminFeedbackState'},
21
21
  colChooserModel: true,
22
22
  enableExport: true,
23
- exportOptions: {filename: `${XH.appCode}-feedback-${LocalDate.today()}`},
23
+ exportOptions: {filename: exportFilenameWithDate('feedback')},
24
24
  emptyText: 'No feedback reported...',
25
25
  store: {
26
26
  url: 'rest/feedbackAdmin',
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilename} from '@xh/hoist/admin/AdminUtils';
7
8
  import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
8
9
  import {FilterChooserModel} from '@xh/hoist/cmp/filter';
9
10
  import {FormModel} from '@xh/hoist/cmp/form';
@@ -159,7 +160,7 @@ export class ActivityTrackingModel extends HoistModel {
159
160
  },
160
161
  colChooserModel: true,
161
162
  enableExport: true,
162
- exportOptions: {filename: `${XH.appCode}-activity-summary`},
163
+ exportOptions: {filename: exportFilename('activity-summary')},
163
164
  emptyText: 'No activity reported...',
164
165
  sortBy: ['cubeLabel'],
165
166
  columns: [
@@ -4,9 +4,10 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilename} from '@xh/hoist/admin/AdminUtils';
7
8
  import {FormModel} from '@xh/hoist/cmp/form';
8
9
  import {GridModel} from '@xh/hoist/cmp/grid';
9
- import {managed, HoistModel, XH, lookup} from '@xh/hoist/core';
10
+ import {managed, HoistModel, lookup} from '@xh/hoist/core';
10
11
  import {action, observable, makeObservable} from '@xh/hoist/mobx';
11
12
  import {fmtJson} from '@xh/hoist/format';
12
13
  import * as Col from '@xh/hoist/admin/columns';
@@ -34,7 +35,7 @@ export class ActivityDetailModel extends HoistModel {
34
35
  filterModel: true,
35
36
  exportOptions: {
36
37
  columns: 'ALL',
37
- filename: `${XH.appCode}-activity-detail`
38
+ filename: exportFilename('activity-detail')
38
39
  },
39
40
  emptyText: 'Select a group on the left to see detailed tracking logs.',
40
41
  columns: [
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {AppModel} from '@xh/hoist/admin/AppModel';
8
9
  import * as Col from '@xh/hoist/admin/columns';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
@@ -18,7 +19,6 @@ import {
18
19
  RestStore
19
20
  } from '@xh/hoist/desktop/cmp/rest';
20
21
  import {action, makeObservable, observable} from '@xh/hoist/mobx';
21
- import {LocalDate} from '@xh/hoist/utils/datetime';
22
22
  import {isNil, truncate} from 'lodash';
23
23
  import {DifferModel} from '../../../differ/DifferModel';
24
24
  import {RegroupDialogModel} from '../../../regroup/RegroupDialogModel';
@@ -49,7 +49,7 @@ export class ConfigPanelModel extends HoistModel {
49
49
  persistWith: this.persistWith,
50
50
  colChooserModel: true,
51
51
  enableExport: true,
52
- exportOptions: {filename: `${XH.appCode}-configs-${LocalDate.today()}`},
52
+ exportOptions: {filename: exportFilenameWithDate('configs')},
53
53
  selModel: 'multiple',
54
54
  store: new RestStore({
55
55
  url: 'rest/configAdmin',
@@ -4,11 +4,11 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import * as Col from '@xh/hoist/admin/columns';
8
9
  import {GridModel} from '@xh/hoist/cmp/grid';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
10
11
  import {bindable, makeObservable} from '@xh/hoist/mobx';
11
- import {LocalDate} from '@xh/hoist/utils/datetime';
12
12
  import {keyBy, keys} from 'lodash';
13
13
 
14
14
  export class UserModel extends HoistModel {
@@ -28,7 +28,7 @@ export class UserModel extends HoistModel {
28
28
  persistWith: this.persistWith,
29
29
  colChooserModel: true,
30
30
  enableExport: true,
31
- exportOptions: {filename: `${XH.appCode}-users-${LocalDate.today()}`},
31
+ exportOptions: {filename: exportFilenameWithDate('users')},
32
32
  store: {idSpec: 'username'},
33
33
  sortBy: 'username',
34
34
  columns: [
@@ -5,18 +5,17 @@
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
8
- import {action, computed, observable, makeObservable} from '@xh/hoist/mobx';
8
+ import {Icon} from '@xh/hoist/icon';
9
+ import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
9
10
  import {Timer} from '@xh/hoist/utils/async';
10
11
  import {SECONDS} from '@xh/hoist/utils/datetime';
11
- import {isDisplayed} from '@xh/hoist/utils/js';
12
- import {createObservableRef} from '@xh/hoist/utils/react';
13
- import {min, sortBy} from 'lodash';
12
+ import {pluralize} from '@xh/hoist/utils/js';
13
+ import {isEqual, min, sortBy} from 'lodash';
14
14
 
15
15
  export class MonitorResultsModel extends HoistModel {
16
16
  @observable.ref results = [];
17
17
  @observable lastRun = null;
18
18
  @managed timer = null;
19
- viewRef = createObservableRef<HTMLElement>();
20
19
 
21
20
  @computed
22
21
  get passed(): number {
@@ -38,6 +37,18 @@ export class MonitorResultsModel extends HoistModel {
38
37
  return this.results.filter(monitor => monitor.status === 'INACTIVE').length;
39
38
  }
40
39
 
40
+ get countsByStatus() {
41
+ const {passed, warned, failed, inactive} = this;
42
+ return {OK: passed, WARN: warned, FAIL: failed, INACTIVE: inactive};
43
+ }
44
+
45
+ get worstStatus() {
46
+ if (this.failed) return 'FAIL';
47
+ if (this.warned) return 'WARN';
48
+ if (this.passed) return 'OK';
49
+ return 'INACTIVE';
50
+ }
51
+
41
52
  constructor() {
42
53
  super();
43
54
  makeObservable(this);
@@ -50,7 +61,7 @@ export class MonitorResultsModel extends HoistModel {
50
61
  }
51
62
 
52
63
  override async doLoadAsync(loadSpec: LoadSpec) {
53
- if (!isDisplayed(this.viewRef.current)) return;
64
+ if (!XH.pageIsVisible) return;
54
65
 
55
66
  return XH.fetchJson({url: 'monitorAdmin/results', loadSpec})
56
67
  .then(rows => {
@@ -76,16 +87,42 @@ export class MonitorResultsModel extends HoistModel {
76
87
  //-------------------
77
88
  @action
78
89
  private completeLoad(vals) {
90
+ const prevCounts = this.countsByStatus;
79
91
  this.results = sortBy(Object.values(vals), 'sortOrder');
80
- this.getLastRun();
81
- }
82
92
 
83
- @action
84
- private getLastRun() {
93
+ const counts = this.countsByStatus,
94
+ worst = this.worstStatus;
95
+
96
+ let intent = null,
97
+ icon = null;
98
+ switch (worst) {
99
+ case 'FAIL':
100
+ intent = 'danger';
101
+ icon = Icon.error();
102
+ break;
103
+ case 'WARN':
104
+ intent = 'warning';
105
+ icon = Icon.warning();
106
+ break;
107
+ case 'OK':
108
+ intent = 'success';
109
+ icon = Icon.checkCircle();
110
+ }
111
+
112
+ // This is imperfect, in that e.g. two monitors could swap their status and we would not
113
+ // alert, but this approach is easy and seemed reasonable enough for this low-stakes toast.
114
+ if (!isEqual(prevCounts, counts) && worst !== 'INACTIVE') {
115
+ XH.toast({
116
+ message: `Status update: ${pluralize('monitor', counts[worst], true)} @ ${worst}`,
117
+ timeout: 6000,
118
+ icon,
119
+ intent
120
+ });
121
+ }
122
+
85
123
  const lastRun = min(
86
124
  this.results.filter(monitor => monitor.status !== 'UNKNOWN').map(it => it.date)
87
125
  );
88
-
89
126
  this.lastRun = lastRun ? new Date(lastRun) : null;
90
127
  }
91
128
  }
@@ -4,27 +4,37 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {AppModel} from '@xh/hoist/admin/AppModel';
8
+ import {filler, hbox, label, placeholder, tileFrame} from '@xh/hoist/cmp/layout';
9
+ import {relativeTimestamp} from '@xh/hoist/cmp/relativetimestamp';
7
10
  import {creates, hoistCmp} from '@xh/hoist/core';
8
- import {placeholder, tileFrame} from '@xh/hoist/cmp/layout';
11
+ import {button, modalToggleButton} from '@xh/hoist/desktop/cmp/button';
9
12
  import {panel} from '@xh/hoist/desktop/cmp/panel';
13
+ import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
14
+ import {Icon} from '@xh/hoist/icon';
10
15
  import {isEmpty} from 'lodash';
11
-
12
- import './MonitorResultsPanel.scss';
16
+ import {errorMessage} from '../../../desktop/cmp/error';
13
17
  import {MonitorResultsModel} from './MonitorResultsModel';
14
- import {monitorResultsToolbar} from './MonitorResultsToolbar';
18
+ import './MonitorResultsPanel.scss';
15
19
  import {tile} from './Tile';
16
- import {errorMessage} from '../../../desktop/cmp/error';
17
20
 
18
21
  export const monitorResultsPanel = hoistCmp.factory({
19
22
  model: creates(MonitorResultsModel),
20
23
 
21
24
  render({model}) {
22
25
  return panel({
23
- ref: model.viewRef,
24
26
  mask: 'onLoad',
25
27
  className: 'xh-monitor-results-panel',
26
- tbar: monitorResultsToolbar(),
27
- item: body()
28
+ tbar: tbar(),
29
+ item: body(),
30
+ modelConfig: {
31
+ modalSupport: {
32
+ width: '100vw',
33
+ height: '100vh'
34
+ },
35
+ collapsible: false,
36
+ resizable: false
37
+ }
28
38
  });
29
39
  }
30
40
  });
@@ -48,3 +58,56 @@ const body = hoistCmp.factory<MonitorResultsModel>(({model}) => {
48
58
  items: results.map(check => tile({check}))
49
59
  });
50
60
  });
61
+
62
+ const tbar = hoistCmp.factory<MonitorResultsModel>(({model}) => {
63
+ const {passed, warned, failed, inactive, results} = model,
64
+ getClassName = hide => {
65
+ return `xh-monitor-result-count ${hide ? 'xh-monitor-result-count--hidden' : ''}`;
66
+ };
67
+
68
+ return toolbar(
69
+ button({
70
+ icon: Icon.refresh(),
71
+ text: 'Run all now',
72
+ disabled: isEmpty(results),
73
+ omit: AppModel.readonly,
74
+ onClick: () => model.forceRunAllMonitorsAsync()
75
+ }),
76
+ hbox({
77
+ className: getClassName(!failed),
78
+ items: [
79
+ toolbarSep(),
80
+ Icon.error({prefix: 'fas', className: 'xh-red'}),
81
+ label(`${failed} failed`)
82
+ ]
83
+ }),
84
+ hbox({
85
+ className: getClassName(!warned),
86
+ items: [
87
+ toolbarSep(),
88
+ Icon.warning({prefix: 'fas', className: 'xh-orange'}),
89
+ label(`${warned} warned`)
90
+ ]
91
+ }),
92
+ hbox({
93
+ className: getClassName(!passed),
94
+ items: [
95
+ toolbarSep(),
96
+ Icon.checkCircle({prefix: 'fas', className: 'xh-green'}),
97
+ label(`${passed} passed`)
98
+ ]
99
+ }),
100
+ hbox({
101
+ className: getClassName(!inactive),
102
+ items: [
103
+ toolbarSep(),
104
+ Icon.disabled({prefix: 'fas', className: 'xh-gray'}),
105
+ label(`${inactive} inactive`)
106
+ ]
107
+ }),
108
+ filler(),
109
+ relativeTimestamp({bind: 'lastRun'}),
110
+ '-',
111
+ modalToggleButton()
112
+ );
113
+ });
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {ChartModel} from '@xh/hoist/cmp/chart';
8
9
  import {GridModel} from '@xh/hoist/cmp/grid';
9
10
  import {HoistModel, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
10
11
  import {fmtTime} from '@xh/hoist/format';
11
12
  import {bindable} from '@xh/hoist/mobx';
12
- import {LocalDate} from '@xh/hoist/utils/datetime';
13
13
  import {forOwn, sortBy} from 'lodash';
14
14
  import * as MCol from '../../monitor/MonitorColumns';
15
15
 
@@ -35,7 +35,7 @@ export class ConnPoolMonitorModel extends HoistModel {
35
35
 
36
36
  this.gridModel = new GridModel({
37
37
  enableExport: true,
38
- exportOptions: {filename: `${XH.appCode}-conn-pool-monitor-${LocalDate.today()}`},
38
+ exportOptions: {filename: exportFilenameWithDate('conn-pool-monitor')},
39
39
  filterModel: true,
40
40
  sortBy: 'timestamp|desc',
41
41
  store: {idSpec: 'timestamp'},
@@ -4,11 +4,11 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {GridModel} from '@xh/hoist/cmp/grid';
8
9
  import * as Col from '@xh/hoist/cmp/grid/columns';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
10
11
  import {UrlStore} from '@xh/hoist/data';
11
- import {LocalDate} from '@xh/hoist/utils/datetime';
12
12
  import {trimEnd} from 'lodash';
13
13
 
14
14
  export class EhCacheModel extends HoistModel {
@@ -19,7 +19,7 @@ export class EhCacheModel extends HoistModel {
19
19
  persistWith: this.persistWith,
20
20
  colChooserModel: true,
21
21
  enableExport: true,
22
- exportOptions: {filename: `${XH.appCode}-eh-caches-${LocalDate.today()}`},
22
+ exportOptions: {filename: exportFilenameWithDate('eh-caches')},
23
23
  store: new UrlStore({
24
24
  url: 'ehCacheAdmin/listCaches',
25
25
  fields: [
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {GridModel} from '@xh/hoist/cmp/grid';
8
9
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
9
- import {LocalDate} from '@xh/hoist/utils/datetime';
10
10
  import {forOwn} from 'lodash';
11
11
 
12
12
  /**
@@ -23,8 +23,8 @@ export class ServerEnvModel extends HoistModel {
23
23
  groupBy: 'type',
24
24
  sortBy: 'name',
25
25
  enableExport: true,
26
- exportOptions: {filename: `${XH.appCode}-env-${LocalDate.today()}`},
27
- store: {idSpec: XH.genId},
26
+ exportOptions: {filename: exportFilenameWithDate('env')},
27
+ store: {idSpec: data => `${data.type}-${data.name}`},
28
28
  columns: [
29
29
  {
30
30
  field: {name: 'type', type: 'string'},
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {AppModel} from '@xh/hoist/admin/AppModel';
8
9
  import * as Col from '@xh/hoist/admin/columns';
9
- import {hoistCmp, XH} from '@xh/hoist/core';
10
+ import {hoistCmp} from '@xh/hoist/core';
10
11
  import {FieldSpec} from '@xh/hoist/data';
11
12
  import {restGrid, RestGridConfig} from '@xh/hoist/desktop/cmp/rest';
12
- import {LocalDate} from '@xh/hoist/utils/datetime';
13
13
  import * as LogLevelCol from './LogLevelColumns';
14
14
 
15
15
  export const logLevelPanel = hoistCmp.factory(() =>
@@ -20,7 +20,7 @@ const modelSpec: RestGridConfig = {
20
20
  persistWith: {localStorageKey: 'xhAdminLogLevelState'},
21
21
  colChooserModel: true,
22
22
  enableExport: true,
23
- exportOptions: {filename: `${XH.appCode}-log-levels-${LocalDate.today()}`},
23
+ exportOptions: {filename: exportFilenameWithDate('log-levels')},
24
24
  store: {
25
25
  url: 'rest/logLevelAdmin',
26
26
  fieldDefaults: {disableXssProtection: true},
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {AppModel} from '@xh/hoist/admin/AppModel';
8
9
  import {GridModel} from '@xh/hoist/cmp/grid';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
@@ -11,7 +12,6 @@ import {RecordActionSpec} from '@xh/hoist/data';
11
12
  import {compactDateRenderer, fmtNumber} from '@xh/hoist/format';
12
13
  import {Icon} from '@xh/hoist/icon';
13
14
  import {makeObservable, observable} from '@xh/hoist/mobx';
14
- import {LocalDate} from '@xh/hoist/utils/datetime';
15
15
  import download from 'downloadjs';
16
16
  import {createRef} from 'react';
17
17
  import {LogDisplayModel} from './LogDisplayModel';
@@ -149,7 +149,7 @@ export class LogViewerModel extends HoistModel {
149
149
  private createGridModel() {
150
150
  return new GridModel({
151
151
  enableExport: true,
152
- exportOptions: {filename: `${XH.appCode}-logs-${LocalDate.today()}`},
152
+ exportOptions: {filename: exportFilenameWithDate('logs')},
153
153
  selModel: 'multiple',
154
154
  store: {
155
155
  idSpec: 'filename',
@@ -4,13 +4,13 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {ChartModel} from '@xh/hoist/cmp/chart';
8
9
  import {GridModel} from '@xh/hoist/cmp/grid';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
10
11
  import {lengthIs, required} from '@xh/hoist/data';
11
12
  import {fmtTime} from '@xh/hoist/format';
12
13
  import {Icon} from '@xh/hoist/icon';
13
- import {LocalDate} from '@xh/hoist/utils/datetime';
14
14
  import {forOwn, sortBy} from 'lodash';
15
15
  import * as MCol from '../../monitor/MonitorColumns';
16
16
 
@@ -31,7 +31,7 @@ export class MemoryMonitorModel extends HoistModel {
31
31
 
32
32
  this.gridModel = new GridModel({
33
33
  enableExport: true,
34
- exportOptions: {filename: `${XH.appCode}-memory-monitor-${LocalDate.today()}`},
34
+ exportOptions: {filename: exportFilenameWithDate('memory-monitor')},
35
35
  filterModel: true,
36
36
  sortBy: 'timestamp|desc',
37
37
  store: {idSpec: 'timestamp'},
@@ -4,21 +4,21 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {GridModel} from '@xh/hoist/cmp/grid';
8
9
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
9
10
  import {UrlStore} from '@xh/hoist/data';
10
- import {LocalDate} from '@xh/hoist/utils/datetime';
11
11
  import {isEmpty, lowerFirst} from 'lodash';
12
12
 
13
13
  export class ServiceModel extends HoistModel {
14
14
  @managed
15
15
  gridModel: GridModel = new GridModel({
16
16
  enableExport: true,
17
- exportOptions: {filename: `${XH.appCode}-services-${LocalDate.today()}`},
17
+ exportOptions: {filename: exportFilenameWithDate('services')},
18
18
  hideHeaders: true,
19
19
  store: new UrlStore({
20
20
  url: 'serviceAdmin/listServices',
21
- idSpec: XH.genId,
21
+ idSpec: data => `${data.provider}-${data.name}`,
22
22
  processRawData: this.processRawData,
23
23
  fields: [
24
24
  {name: 'provider', type: 'string'},
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import * as Col from '@xh/hoist/admin/columns';
8
9
  import {GridModel} from '@xh/hoist/cmp/grid';
9
10
  import {div, p} from '@xh/hoist/cmp/layout';
@@ -12,7 +13,7 @@ import {textInput} from '@xh/hoist/desktop/cmp/input';
12
13
  import {Icon} from '@xh/hoist/icon';
13
14
  import {makeObservable, observable, runInAction} from '@xh/hoist/mobx';
14
15
  import {Timer} from '@xh/hoist/utils/async';
15
- import {LocalDate, SECONDS} from '@xh/hoist/utils/datetime';
16
+ import {SECONDS} from '@xh/hoist/utils/datetime';
16
17
  import {isDisplayed} from '@xh/hoist/utils/js';
17
18
  import {isEmpty} from 'lodash';
18
19
  import {createRef} from 'react';
@@ -37,7 +38,7 @@ export class WebSocketModel extends HoistModel {
37
38
  this.gridModel = new GridModel({
38
39
  emptyText: 'No clients connected.',
39
40
  enableExport: true,
40
- exportOptions: {filename: `${XH.appCode}-ws-connections-${LocalDate.today()}`},
41
+ exportOptions: {filename: exportFilenameWithDate('ws-connections')},
41
42
  selModel: 'multiple',
42
43
  store: {
43
44
  idSpec: 'key',
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {AppModel} from '@xh/hoist/admin/AppModel';
8
9
  import * as Col from '@xh/hoist/admin/columns';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
@@ -18,7 +19,6 @@ import {
18
19
  } from '@xh/hoist/desktop/cmp/rest';
19
20
  import {fmtDateTime} from '@xh/hoist/format';
20
21
  import {action, makeObservable, observable} from '@xh/hoist/mobx';
21
- import {LocalDate} from '@xh/hoist/utils/datetime';
22
22
  import {isDate} from 'lodash';
23
23
  import {DifferModel} from '../../../differ/DifferModel';
24
24
  import * as JBCol from './JsonBlobColumns';
@@ -46,7 +46,7 @@ export class JsonBlobModel extends HoistModel {
46
46
  persistWith: this.persistWith,
47
47
  colChooserModel: true,
48
48
  enableExport: true,
49
- exportOptions: {filename: `${XH.appCode}-json-blobs-${LocalDate.today()}`},
49
+ exportOptions: {filename: exportFilenameWithDate('json-blobs')},
50
50
  selModel: 'multiple',
51
51
  store: {
52
52
  url: 'rest/jsonBlobAdmin',
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2023 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {exportFilenameWithDate} from '@xh/hoist/admin/AdminUtils';
7
8
  import {AppModel} from '@xh/hoist/admin/AppModel';
8
9
  import * as Col from '@xh/hoist/admin/columns';
9
10
  import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
@@ -11,7 +12,6 @@ import {FieldSpec} from '@xh/hoist/data';
11
12
  import {textArea} from '@xh/hoist/desktop/cmp/input';
12
13
  import {addAction, deleteAction, editAction, RestGridModel} from '@xh/hoist/desktop/cmp/rest';
13
14
  import {action, makeObservable, observable} from '@xh/hoist/mobx';
14
- import {LocalDate} from '@xh/hoist/utils/datetime';
15
15
  import {DifferModel} from '../../../differ/DifferModel';
16
16
  import {RegroupDialogModel} from '../../../regroup/RegroupDialogModel';
17
17
 
@@ -41,7 +41,7 @@ export class PreferenceModel extends HoistModel {
41
41
  persistWith: this.persistWith,
42
42
  colChooserModel: true,
43
43
  enableExport: true,
44
- exportOptions: {filename: `${XH.appCode}-prefs-${LocalDate.today()}`},
44
+ exportOptions: {filename: exportFilenameWithDate('prefs')},
45
45
  selModel: 'multiple',
46
46
  store: {
47
47
  url: 'rest/preferenceAdmin',