@xh/hoist 87.1.1 → 87.3.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 (270) hide show
  1. package/.npmignore +39 -0
  2. package/CHANGELOG.md +108 -20
  3. package/admin/AdminUtils.ts +64 -0
  4. package/admin/App.scss +88 -0
  5. package/admin/columns/UserData.ts +30 -3
  6. package/admin/detail/RestDetailModel.ts +64 -0
  7. package/admin/detail/RestDetailPanel.ts +106 -0
  8. package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +27 -94
  9. package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +23 -60
  10. package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +1 -2
  11. package/admin/tabs/general/alertBanner/AlertBannerPanel.ts +2 -13
  12. package/admin/tabs/general/config/ConfigDetailPanel.ts +72 -0
  13. package/admin/tabs/general/config/ConfigPanel.ts +41 -53
  14. package/admin/tabs/general/config/ConfigPanelModel.ts +3 -0
  15. package/admin/tabs/general/config/ConfigValue.scss +14 -0
  16. package/admin/tabs/general/config/ConfigValue.ts +104 -65
  17. package/admin/tabs/userData/jsonblob/JsonBlobDetailPanel.ts +93 -0
  18. package/admin/tabs/userData/jsonblob/JsonBlobModel.ts +4 -0
  19. package/admin/tabs/userData/jsonblob/JsonBlobPanel.ts +2 -0
  20. package/admin/tabs/userData/prefs/UserPreferenceDetailPanel.ts +62 -0
  21. package/admin/tabs/userData/prefs/UserPreferenceModel.ts +10 -3
  22. package/admin/tabs/userData/prefs/UserPreferencePanel.ts +3 -1
  23. package/admin/tabs/userData/roles/details/RoleDetails.scss +2 -19
  24. package/admin/tabs/userData/roles/details/RoleDetails.ts +13 -13
  25. package/appcontainer/AboutDialogModel.ts +1 -0
  26. package/appcontainer/AppContainerModel.ts +3 -0
  27. package/appcontainer/AppStateModel.ts +1 -0
  28. package/appcontainer/BannerSourceModel.ts +1 -0
  29. package/appcontainer/ChangelogDialogModel.ts +1 -0
  30. package/appcontainer/ColChooserOptionsModel.ts +1 -0
  31. package/appcontainer/ExceptionDialogModel.ts +1 -0
  32. package/appcontainer/FeedbackDialogModel.ts +1 -0
  33. package/appcontainer/ImpersonationBarModel.ts +1 -0
  34. package/appcontainer/MessageSourceModel.ts +1 -0
  35. package/appcontainer/OptionsDialogModel.ts +1 -0
  36. package/appcontainer/PageStateModel.ts +1 -0
  37. package/appcontainer/RouterModel.ts +2 -0
  38. package/appcontainer/SizingModeModel.ts +1 -0
  39. package/appcontainer/ThemeModel.ts +1 -0
  40. package/appcontainer/ToastSourceModel.ts +1 -0
  41. package/appcontainer/UserAgentModel.ts +1 -0
  42. package/appcontainer/ViewportSizeModel.ts +1 -0
  43. package/build/types/admin/AdminUtils.d.ts +29 -0
  44. package/build/types/admin/columns/UserData.d.ts +3 -0
  45. package/build/types/admin/detail/RestDetailModel.d.ts +20 -0
  46. package/build/types/admin/detail/RestDetailPanel.d.ts +34 -0
  47. package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +5 -17
  48. package/build/types/admin/tabs/general/config/ConfigDetailPanel.d.ts +7 -0
  49. package/build/types/admin/tabs/general/config/ConfigValue.d.ts +10 -4
  50. package/build/types/admin/tabs/userData/jsonblob/JsonBlobDetailPanel.d.ts +3 -0
  51. package/build/types/admin/tabs/userData/prefs/UserPreferenceDetailPanel.d.ts +3 -0
  52. package/build/types/admin/tabs/userData/prefs/UserPreferenceModel.d.ts +3 -0
  53. package/build/types/appcontainer/AboutDialogModel.d.ts +1 -0
  54. package/build/types/appcontainer/AppContainerModel.d.ts +1 -0
  55. package/build/types/appcontainer/AppStateModel.d.ts +1 -0
  56. package/build/types/appcontainer/BannerSourceModel.d.ts +1 -0
  57. package/build/types/appcontainer/ChangelogDialogModel.d.ts +1 -0
  58. package/build/types/appcontainer/ColChooserOptionsModel.d.ts +1 -0
  59. package/build/types/appcontainer/ExceptionDialogModel.d.ts +1 -0
  60. package/build/types/appcontainer/FeedbackDialogModel.d.ts +1 -0
  61. package/build/types/appcontainer/ImpersonationBarModel.d.ts +1 -0
  62. package/build/types/appcontainer/MessageSourceModel.d.ts +1 -0
  63. package/build/types/appcontainer/OptionsDialogModel.d.ts +1 -0
  64. package/build/types/appcontainer/PageStateModel.d.ts +1 -0
  65. package/build/types/appcontainer/RouterModel.d.ts +1 -0
  66. package/build/types/appcontainer/SizingModeModel.d.ts +1 -0
  67. package/build/types/appcontainer/ThemeModel.d.ts +1 -0
  68. package/build/types/appcontainer/ToastSourceModel.d.ts +1 -0
  69. package/build/types/appcontainer/UserAgentModel.d.ts +1 -0
  70. package/build/types/appcontainer/ViewportSizeModel.d.ts +1 -0
  71. package/build/types/cmp/ag-grid/AgGridModel.d.ts +3 -1
  72. package/build/types/cmp/card/Card.d.ts +2 -2
  73. package/build/types/cmp/chart/ChartModel.d.ts +2 -0
  74. package/build/types/cmp/dataview/DataViewModel.d.ts +2 -0
  75. package/build/types/cmp/daterange/DateRangePickerModel.d.ts +266 -0
  76. package/build/types/cmp/daterange/DateRangePresets.d.ts +26 -0
  77. package/build/types/cmp/daterange/DateRangeUtils.d.ts +96 -0
  78. package/build/types/cmp/daterange/Types.d.ts +152 -0
  79. package/build/types/cmp/daterange/index.d.ts +4 -0
  80. package/build/types/cmp/filter/FilterChooserModel.d.ts +3 -1
  81. package/build/types/cmp/form/FormModel.d.ts +3 -1
  82. package/build/types/cmp/grid/GridModel.d.ts +2 -0
  83. package/build/types/cmp/grid/Types.d.ts +2 -0
  84. package/build/types/cmp/grid/filter/GridFilterModel.d.ts +1 -1
  85. package/build/types/cmp/grid/impl/ColumnWidthCalculator.d.ts +1 -1
  86. package/build/types/cmp/grouping/GroupingChooserModel.d.ts +3 -1
  87. package/build/types/cmp/input/HoistInputProps.d.ts +49 -2
  88. package/build/types/cmp/tab/TabContainerModel.d.ts +3 -1
  89. package/build/types/cmp/tab/TabModel.d.ts +3 -1
  90. package/build/types/cmp/treemap/TreeMapModel.d.ts +2 -0
  91. package/build/types/cmp/viewmanager/ViewManagerModel.d.ts +2 -0
  92. package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +2 -0
  93. package/build/types/core/HoistBase.d.ts +7 -0
  94. package/build/types/core/HoistProps.d.ts +26 -1
  95. package/build/types/core/model/RootRefreshContextModel.d.ts +1 -0
  96. package/build/types/core/types/Telemetry.d.ts +1 -1
  97. package/build/types/data/Store.d.ts +3 -1
  98. package/build/types/data/StoreSelectionModel.d.ts +3 -1
  99. package/build/types/data/cube/Cube.d.ts +5 -2
  100. package/build/types/data/cube/View.d.ts +2 -0
  101. package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +3 -0
  102. package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +3 -0
  103. package/build/types/desktop/cmp/button/Button.d.ts +2 -2
  104. package/build/types/desktop/cmp/button/ButtonGroup.d.ts +2 -2
  105. package/build/types/desktop/cmp/dash/DashConfig.d.ts +2 -0
  106. package/build/types/desktop/cmp/dash/DashViewModel.d.ts +3 -1
  107. package/build/types/desktop/cmp/dash/canvas/DashCanvasModel.d.ts +1 -1
  108. package/build/types/desktop/cmp/dash/container/DashContainerModel.d.ts +1 -1
  109. package/build/types/desktop/cmp/daterange/DateRangePicker.d.ts +66 -0
  110. package/build/types/desktop/cmp/daterange/impl/CustomTab.d.ts +3 -0
  111. package/build/types/desktop/cmp/daterange/impl/DateRangePickerLocalModel.d.ts +121 -0
  112. package/build/types/desktop/cmp/daterange/impl/PeriodTab.d.ts +2 -0
  113. package/build/types/desktop/cmp/daterange/impl/PresetsTab.d.ts +3 -0
  114. package/build/types/desktop/cmp/daterange/impl/RelativeTab.d.ts +3 -0
  115. package/build/types/desktop/cmp/daterange/impl/TabUtils.d.ts +21 -0
  116. package/build/types/desktop/cmp/daterange/index.d.ts +2 -0
  117. package/build/types/desktop/cmp/dock/DockContainerModel.d.ts +3 -1
  118. package/build/types/desktop/cmp/dock/DockViewModel.d.ts +3 -1
  119. package/build/types/desktop/cmp/grouping/GroupingChooser.d.ts +4 -1
  120. package/build/types/desktop/cmp/input/IntentInput.d.ts +38 -0
  121. package/build/types/desktop/cmp/input/NumberInput.d.ts +2 -2
  122. package/build/types/desktop/cmp/input/SegmentedControl.d.ts +25 -4
  123. package/build/types/desktop/cmp/input/TextArea.d.ts +2 -2
  124. package/build/types/desktop/cmp/input/TextInput.d.ts +2 -2
  125. package/build/types/desktop/cmp/input/index.d.ts +1 -0
  126. package/build/types/desktop/cmp/leftrightchooser/LeftRightChooserModel.d.ts +3 -1
  127. package/build/types/desktop/cmp/panel/PanelModel.d.ts +3 -1
  128. package/build/types/desktop/cmp/rest/RestGridModel.d.ts +1 -1
  129. package/build/types/desktop/cmp/tab/impl/TabContainer.d.ts +1 -1
  130. package/build/types/desktop/hooks/UseHotkeys.d.ts +4 -3
  131. package/build/types/icon/Icon.d.ts +3 -0
  132. package/build/types/inspector/impl/InspectorUtils.d.ts +3 -0
  133. package/build/types/inspector/instances/InstancesModel.d.ts +4 -0
  134. package/build/types/kit/react-day-picker/index.d.ts +3 -3
  135. package/build/types/mobile/cmp/button/Button.d.ts +2 -2
  136. package/build/types/mobile/cmp/input/NumberInput.d.ts +2 -2
  137. package/build/types/mobile/cmp/input/SegmentedControl.d.ts +18 -4
  138. package/build/types/mobile/cmp/input/TextArea.d.ts +2 -2
  139. package/build/types/mobile/cmp/input/TextInput.d.ts +2 -2
  140. package/build/types/mobile/cmp/navigator/NavigatorModel.d.ts +8 -2
  141. package/build/types/mobile/cmp/navigator/PageModel.d.ts +3 -1
  142. package/build/types/mobile/cmp/tab/impl/TabContainer.d.ts +1 -1
  143. package/build/types/svc/InspectorService.d.ts +1 -0
  144. package/build/types/utils/js/LangUtils.d.ts +24 -5
  145. package/cmp/README.md +1 -0
  146. package/cmp/ag-grid/AgGridModel.ts +5 -0
  147. package/cmp/badge/Badge.ts +5 -3
  148. package/cmp/card/Card.ts +4 -3
  149. package/cmp/chart/ChartModel.ts +11 -1
  150. package/cmp/clock/Clock.ts +1 -1
  151. package/cmp/dataview/DataViewModel.ts +7 -1
  152. package/cmp/daterange/DateRangePickerModel.ts +628 -0
  153. package/cmp/daterange/DateRangePresets.ts +274 -0
  154. package/cmp/daterange/DateRangeUtils.ts +557 -0
  155. package/cmp/daterange/README.md +434 -0
  156. package/cmp/daterange/Types.ts +203 -0
  157. package/cmp/daterange/index.ts +10 -0
  158. package/cmp/filter/FilterChooserModel.ts +7 -4
  159. package/cmp/form/FormModel.ts +6 -0
  160. package/cmp/form/README.md +1 -0
  161. package/cmp/grid/GridModel.ts +24 -4
  162. package/cmp/grid/Types.ts +3 -0
  163. package/cmp/grid/filter/GridFilterModel.ts +3 -1
  164. package/cmp/grid/impl/ColumnWidthCalculator.ts +5 -10
  165. package/cmp/grouping/GroupingChooserModel.ts +6 -1
  166. package/cmp/input/HoistInputProps.ts +50 -2
  167. package/cmp/input/README.md +42 -0
  168. package/cmp/layout/Box.ts +3 -2
  169. package/cmp/tab/TabContainerModel.ts +9 -1
  170. package/cmp/tab/TabModel.ts +5 -0
  171. package/cmp/treemap/SplitTreeMapModel.ts +12 -2
  172. package/cmp/treemap/TreeMapModel.ts +6 -1
  173. package/cmp/viewmanager/ViewManagerModel.ts +6 -1
  174. package/cmp/zoneGrid/ZoneGridModel.ts +7 -0
  175. package/core/HoistBase.ts +11 -0
  176. package/core/HoistProps.ts +27 -0
  177. package/core/README.md +46 -0
  178. package/core/impl/InstallServices.ts +5 -1
  179. package/core/model/RootRefreshContextModel.ts +2 -0
  180. package/core/types/Telemetry.ts +1 -1
  181. package/data/README.md +3 -1
  182. package/data/Store.ts +7 -0
  183. package/data/StoreSelectionModel.ts +5 -1
  184. package/data/cube/Cube.ts +12 -3
  185. package/data/cube/View.ts +5 -1
  186. package/desktop/README.md +2 -0
  187. package/desktop/appcontainer/LoginPanel.ts +2 -0
  188. package/desktop/cmp/button/Button.scss +71 -42
  189. package/desktop/cmp/button/Button.ts +10 -1
  190. package/desktop/cmp/button/ButtonGroup.ts +4 -1
  191. package/desktop/cmp/button/grid/ColChooserButton.ts +9 -1
  192. package/desktop/cmp/button/zoneGrid/ZoneMapperButton.ts +1 -0
  193. package/desktop/cmp/dash/DashConfig.ts +3 -0
  194. package/desktop/cmp/dash/DashViewModel.ts +14 -1
  195. package/desktop/cmp/dash/canvas/DashCanvasModel.ts +3 -1
  196. package/desktop/cmp/dash/container/DashContainerModel.ts +3 -1
  197. package/desktop/cmp/daterange/DateRangePicker.scss +577 -0
  198. package/desktop/cmp/daterange/DateRangePicker.ts +357 -0
  199. package/desktop/cmp/daterange/impl/CustomTab.ts +165 -0
  200. package/desktop/cmp/daterange/impl/DateRangePickerLocalModel.ts +482 -0
  201. package/desktop/cmp/daterange/impl/PeriodTab.ts +138 -0
  202. package/desktop/cmp/daterange/impl/PresetsTab.ts +58 -0
  203. package/desktop/cmp/daterange/impl/RelativeTab.ts +108 -0
  204. package/desktop/cmp/daterange/impl/TabUtils.ts +34 -0
  205. package/desktop/cmp/daterange/index.ts +8 -0
  206. package/desktop/cmp/dock/DockContainerModel.ts +6 -1
  207. package/desktop/cmp/dock/DockViewModel.ts +6 -1
  208. package/desktop/cmp/filechooser/FileChooser.ts +4 -0
  209. package/desktop/cmp/form/FormField.ts +1 -0
  210. package/desktop/cmp/grid/editors/DateEditor.ts +11 -2
  211. package/desktop/cmp/grouping/GroupingChooser.ts +8 -2
  212. package/desktop/cmp/input/Checkbox.ts +1 -0
  213. package/desktop/cmp/input/CodeInput.ts +1 -0
  214. package/desktop/cmp/input/DateInput.scss +1 -1
  215. package/desktop/cmp/input/DateInput.ts +1 -0
  216. package/desktop/cmp/input/IntentInput.scss +202 -0
  217. package/desktop/cmp/input/IntentInput.ts +213 -0
  218. package/desktop/cmp/input/NumberInput.ts +11 -2
  219. package/desktop/cmp/input/Picker.ts +1 -0
  220. package/desktop/cmp/input/RadioInput.ts +2 -1
  221. package/desktop/cmp/input/SegmentedControl.scss +57 -5
  222. package/desktop/cmp/input/SegmentedControl.ts +66 -11
  223. package/desktop/cmp/input/Select.ts +9 -7
  224. package/desktop/cmp/input/Slider.ts +2 -0
  225. package/desktop/cmp/input/SwitchInput.ts +1 -0
  226. package/desktop/cmp/input/TextArea.ts +11 -2
  227. package/desktop/cmp/input/TextInput.ts +11 -2
  228. package/desktop/cmp/input/index.ts +1 -0
  229. package/desktop/cmp/leftrightchooser/LeftRightChooserModel.ts +5 -0
  230. package/desktop/cmp/panel/Panel.ts +8 -5
  231. package/desktop/cmp/panel/PanelModel.ts +8 -0
  232. package/desktop/cmp/panel/impl/ResizeContainer.ts +2 -1
  233. package/desktop/cmp/rest/RestGridModel.ts +4 -0
  234. package/desktop/cmp/tab/impl/TabContainer.ts +2 -0
  235. package/desktop/cmp/toolbar/Toolbar.scss +20 -6
  236. package/desktop/hooks/UseHotkeys.ts +41 -12
  237. package/docs/README.md +2 -0
  238. package/docs/build-and-publish.md +23 -7
  239. package/docs/doc-registry.json +8 -0
  240. package/docs/version-compatibility.md +2 -0
  241. package/icon/Icon.ts +9 -0
  242. package/icon/index.ts +24 -0
  243. package/inspector/README.md +8 -2
  244. package/inspector/impl/InspectorUtils.ts +12 -0
  245. package/inspector/instances/DiagnosticsModel.ts +3 -2
  246. package/inspector/instances/InstancesModel.ts +81 -14
  247. package/inspector/instances/InstancesPanel.ts +6 -0
  248. package/kit/react-day-picker/index.ts +3 -3
  249. package/mobile/appcontainer/LoginPanel.ts +2 -0
  250. package/mobile/cmp/button/Button.ts +16 -3
  251. package/mobile/cmp/form/FormField.ts +1 -0
  252. package/mobile/cmp/input/Checkbox.ts +1 -0
  253. package/mobile/cmp/input/DateInput.ts +1 -0
  254. package/mobile/cmp/input/Label.ts +13 -8
  255. package/mobile/cmp/input/NumberInput.ts +11 -2
  256. package/mobile/cmp/input/SearchInput.ts +1 -0
  257. package/mobile/cmp/input/SegmentedControl.scss +36 -6
  258. package/mobile/cmp/input/SegmentedControl.ts +37 -11
  259. package/mobile/cmp/input/Select.ts +2 -0
  260. package/mobile/cmp/input/SwitchInput.ts +1 -0
  261. package/mobile/cmp/input/TextArea.ts +11 -2
  262. package/mobile/cmp/input/TextInput.ts +11 -2
  263. package/mobile/cmp/navigator/NavigatorModel.ts +20 -8
  264. package/mobile/cmp/navigator/PageModel.ts +6 -1
  265. package/mobile/cmp/tab/impl/TabContainer.ts +9 -1
  266. package/package.json +19 -19
  267. package/styles/vars.scss +32 -2
  268. package/svc/InspectorService.ts +2 -0
  269. package/svc/TraceService.ts +5 -2
  270. package/utils/js/LangUtils.ts +37 -5
@@ -11,21 +11,12 @@ import {
11
11
  ActivityTrackingDataFieldSpec,
12
12
  DataFieldsEditorModel
13
13
  } from '@xh/hoist/admin/tabs/activity/tracking/datafields/DataFieldsEditorModel';
14
+ import {DateRangePickerModel} from '@xh/hoist/cmp/daterange';
14
15
  import {FilterChooserModel} from '@xh/hoist/cmp/filter';
15
16
  import {FormModel} from '@xh/hoist/cmp/form';
16
17
  import {ColumnRenderer, ColumnSpec, GridModel, TreeStyle} from '@xh/hoist/cmp/grid';
17
18
  import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
18
- import {
19
- HoistModel,
20
- LoadSpec,
21
- LocalDateUnit,
22
- managed,
23
- PersistableState,
24
- PersistenceProvider,
25
- persistOptions,
26
- PlainObject,
27
- XH
28
- } from '@xh/hoist/core';
19
+ import {HoistModel, LoadSpec, managed, PlainObject, XH} from '@xh/hoist/core';
29
20
  import {Cube, CubeFieldSpec, FieldSpec, getCubeLeaves, ViewRowData} from '@xh/hoist/data';
30
21
  import {dateRenderer, dateTimeSecRenderer, numberRenderer} from '@xh/hoist/format';
31
22
  import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
@@ -34,29 +25,15 @@ import {compact, get, isEmpty, isEqual, round} from 'lodash';
34
25
  import moment from 'moment';
35
26
  import {ActivityDetailProvider} from './detail/ActivityDetailModel';
36
27
 
37
- /**
38
- * A relative time interval (e.g. 7 days) ending on the current app day. Drives the quick-select
39
- * buttons and the persisted time period, which rolls forward to end on today on restore.
40
- */
41
- export interface ActivityInterval {
42
- value: number;
43
- unit: LocalDateUnit;
44
- }
45
-
46
- /** Quick-select intervals offered in the toolbar - labels are derived as `${value}${unit[0]}`. */
47
- export const INTERVALS: ActivityInterval[] = [
48
- {value: 6, unit: 'months'},
49
- {value: 1, unit: 'months'},
50
- {value: 7, unit: 'days'},
51
- {value: 1, unit: 'days'}
52
- ];
53
-
54
28
  export class ActivityTrackingModel extends HoistModel implements ActivityDetailProvider {
55
29
  override telemetryPrefix = 'xh.client.admin.tracking';
56
30
 
57
31
  /** FormModel for server-side querying controls. */
58
32
  @managed formModel: FormModel;
59
33
 
34
+ /** Period to query, resolved against the current app day. */
35
+ @managed dateRangePickerModel: DateRangePickerModel;
36
+
60
37
  /** Models for data-handling components - can be rebuilt due to change in dataFields. */
61
38
  @managed @observable.ref groupingChooserModel: GroupingChooserModel;
62
39
  @managed @observable.ref cube: Cube;
@@ -92,10 +69,6 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
92
69
  return this.groupingChooserModel.value;
93
70
  }
94
71
 
95
- get endDay(): LocalDate {
96
- return this.formModel.values.endDay;
97
- }
98
-
99
72
  @computed
100
73
  get hasFilter(): boolean {
101
74
  return !!this.filterChooserModel.value;
@@ -142,7 +115,7 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
142
115
  this.markPersist('showFilterChooser');
143
116
 
144
117
  this.formModel = this.createQueryFormModel();
145
- this.setupPeriodPersistence();
118
+ this.dateRangePickerModel = this.createDateRangePickerModel();
146
119
 
147
120
  this.dataFieldsEditorModel = new DataFieldsEditorModel(this);
148
121
  this.markPersist('dataFields');
@@ -211,39 +184,6 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
211
184
  this.showFilterChooser = !this.showFilterChooser;
212
185
  }
213
186
 
214
- adjustDates(dir: 'add' | 'subtract') {
215
- const {startDay, endDay} = this.formModel.fields,
216
- appDay = LocalDate.currentAppDay(),
217
- start: LocalDate = startDay.value,
218
- end: LocalDate = endDay.value,
219
- diff = end.diff(start),
220
- incr = diff + 1;
221
-
222
- let newStart = start[dir](incr),
223
- newEnd = end[dir](incr);
224
-
225
- if (newEnd > appDay) {
226
- newStart = appDay.subtract(Math.abs(diff));
227
- newEnd = appDay;
228
- }
229
-
230
- startDay.setValue(newStart);
231
- endDay.setValue(newEnd);
232
- }
233
-
234
- // Set the start date by taking the end date and pushing back the interval - then pushing
235
- // forward one day as the day range query is inclusive.
236
- adjustStartDate(interval: ActivityInterval) {
237
- this.formModel.setValues({
238
- startDay: this.endDay.subtract(interval.value, interval.unit).nextDay()
239
- });
240
- }
241
-
242
- isInterval(interval: ActivityInterval) {
243
- const {startDay, endDay} = this.formModel.values;
244
- return startDay === endDay.subtract(interval.value, interval.unit).nextDay();
245
- }
246
-
247
187
  getDisplayName(fieldName: string) {
248
188
  return fieldName ? (this.cube.store.getField(fieldName)?.displayName ?? fieldName) : null;
249
189
  }
@@ -254,33 +194,26 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
254
194
  private createQueryFormModel(): FormModel {
255
195
  return new FormModel({
256
196
  persistWith: {...this.persistWith, path: 'queryFormValues', includeFields: ['maxRows']},
257
- fields: [
258
- {name: 'startDay', initialValue: () => LocalDate.currentAppDay()},
259
- {name: 'endDay', initialValue: () => LocalDate.currentAppDay()},
260
- {name: 'maxRows', initialValue: XH.trackService.conf.maxRows?.default}
261
- ]
197
+ fields: [{name: 'maxRows', initialValue: XH.trackService.conf.maxRows?.default}]
262
198
  });
263
199
  }
264
200
 
265
- // Persist the selected time period as part of the active view, if endDay is currentDay
266
- private setupPeriodPersistence() {
267
- PersistenceProvider.create<ActivityInterval>({
268
- persistOptions: persistOptions({path: 'queryPeriod'}, this.persistWith),
269
- owner: this,
270
- target: {
271
- getPersistableState: () => {
272
- const currDay = this.endDay === LocalDate.currentAppDay(),
273
- interval = currDay ? INTERVALS.find(it => this.isInterval(it)) : null;
274
- return new PersistableState(interval ?? null);
275
- },
276
- setPersistableState: ({value}) => {
277
- if (value) {
278
- const endDay = LocalDate.currentAppDay();
279
- this.formModel.setValues({endDay});
280
- this.adjustStartDate(value);
281
- }
282
- }
283
- }
201
+ private createDateRangePickerModel(): DateRangePickerModel {
202
+ return new DateRangePickerModel({
203
+ persistWith: this.persistWith,
204
+ presets: [
205
+ 'anchorDay',
206
+ 'prevDay',
207
+ 'prev7Days',
208
+ 'prev30Days',
209
+ 'prev90Days',
210
+ 'mtd',
211
+ 'prevMonth',
212
+ 'ytd'
213
+ ],
214
+ initialValue: 'anchorDay',
215
+ // Track logs are stamped in the app time zone - follow its day, across midnight too.
216
+ anchorDay: 'appDay'
284
217
  });
285
218
  }
286
219
 
@@ -324,11 +257,11 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
324
257
 
325
258
  @computed
326
259
  private get query() {
327
- const {values} = this.formModel;
260
+ const {start, end} = this.dateRangePickerModel.currentRange;
328
261
  return {
329
- startDay: values.startDay,
330
- endDay: values.endDay,
331
- maxRows: values.maxRows,
262
+ startDay: start,
263
+ endDay: end,
264
+ maxRows: this.formModel.values.maxRows,
332
265
  filters: this.filterChooserModel.value
333
266
  };
334
267
  }
@@ -10,17 +10,17 @@ import {form} from '@xh/hoist/cmp/form';
10
10
  import {grid} from '@xh/hoist/cmp/grid';
11
11
  import {div, filler, hframe} from '@xh/hoist/cmp/layout';
12
12
  import {creates, hoistCmp} from '@xh/hoist/core';
13
- import {button, buttonGroup, colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button';
13
+ import {button, colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button';
14
+ import {dateRangePicker} from '@xh/hoist/desktop/cmp/daterange';
14
15
  import {filterChooser} from '@xh/hoist/desktop/cmp/filter';
15
16
  import {formField} from '@xh/hoist/desktop/cmp/form';
16
17
  import {groupingChooser} from '@xh/hoist/desktop/cmp/grouping';
17
- import {dateInput, DateInputProps, select} from '@xh/hoist/desktop/cmp/input';
18
+ import {select} from '@xh/hoist/desktop/cmp/input';
18
19
  import {panel} from '@xh/hoist/desktop/cmp/panel';
19
20
  import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
20
21
  import {viewManager} from '@xh/hoist/desktop/cmp/viewmanager';
21
22
  import {Icon} from '@xh/hoist/icon';
22
- import {LocalDate} from '@xh/hoist/utils/datetime';
23
- import {INTERVALS, ActivityTrackingModel} from './ActivityTrackingModel';
23
+ import {ActivityTrackingModel} from './ActivityTrackingModel';
24
24
  import {aggChartPanel} from '@xh/hoist/admin/tabs/activity/tracking/chart/AggChartPanel';
25
25
  import {activityDetailView} from './detail/ActivityDetailView';
26
26
  import './ActivityTracking.scss';
@@ -45,64 +45,33 @@ export const activityTrackingPanel = hoistCmp.factory({
45
45
  }
46
46
  });
47
47
 
48
- const tbar = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
49
- const dateBtn = {outlined: true, width: 40} as const;
50
- return toolbar(
48
+ const tbar = hoistCmp.factory<ActivityTrackingModel>(({model}) =>
49
+ toolbar(
51
50
  viewManager({
52
51
  model: model.viewManagerModel,
53
52
  showSaveButton: 'always'
54
53
  }),
55
54
  '-',
55
+ dateRangePicker({showStepButtons: true, testId: 'activity-period'}),
56
+ toolbarSep(),
57
+ filterChooserToggleButton(),
58
+ toolbarSep(),
59
+ dataFieldsEditor(),
60
+ filler(),
56
61
  form({
57
- fieldDefaults: {label: null},
58
- items: [
59
- button({
60
- icon: Icon.angleLeft(),
61
- onClick: () => model.adjustDates('subtract')
62
- }),
63
- formField({
64
- field: 'startDay',
65
- item: dateInput({...dateInputProps})
66
- }),
67
- Icon.caretRight(),
68
- formField({
69
- field: 'endDay',
70
- item: dateInput({...dateInputProps})
71
- }),
72
- button({
73
- icon: Icon.angleRight(),
74
- onClick: () => model.adjustDates('add'),
75
- disabled: model.endDay >= LocalDate.currentAppDay()
76
- }),
77
- buttonGroup({
78
- items: INTERVALS.map(interval =>
79
- button({
80
- text: `${interval.value}${interval.unit[0]}`,
81
- onClick: () => model.adjustStartDate(interval),
82
- active: model.isInterval(interval),
83
- ...dateBtn
84
- })
85
- )
86
- }),
87
- toolbarSep(),
88
- filterChooserToggleButton(),
89
- toolbarSep(),
90
- dataFieldsEditor(),
91
- filler(),
92
- formField({
93
- field: 'maxRows',
94
- label: 'Max rows',
95
- width: 140,
96
- item: select({
97
- enableFilter: false,
98
- hideDropdownIndicator: true,
99
- options: model.maxRowOptions
100
- })
62
+ item: formField({
63
+ field: 'maxRows',
64
+ label: 'Max rows',
65
+ width: 140,
66
+ item: select({
67
+ enableFilter: false,
68
+ hideDropdownIndicator: true,
69
+ options: model.maxRowOptions
101
70
  })
102
- ]
71
+ })
103
72
  })
104
- );
105
- });
73
+ )
74
+ );
106
75
 
107
76
  const filterChooserToggleButton = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
108
77
  const {hasFilter, showFilterChooser} = model;
@@ -161,9 +130,3 @@ const aggregateView = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
161
130
  ]
162
131
  });
163
132
  });
164
-
165
- const dateInputProps: DateInputProps = {
166
- popoverPosition: 'bottom',
167
- valueType: 'localDate',
168
- width: 120
169
- };
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2026 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {naSpan, valOrNa} from '@xh/hoist/admin/AdminUtils';
7
8
  import {badgeRenderer} from '@xh/hoist/admin/columns';
8
9
  import {form} from '@xh/hoist/cmp/form';
9
10
  import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
@@ -205,6 +206,4 @@ const additionalDataPanel = hoistCmp.factory<ActivityDetailModel>(({model}) => {
205
206
  });
206
207
  });
207
208
 
208
- const valOrNa = v => (v != null ? v : naSpan());
209
- const naSpan = () => span({item: 'N/A', className: 'xh-text-color-muted'});
210
209
  const hyperlinkVal = v => (v ? a({href: v, item: v, target: '_blank'}) : '-');
@@ -28,6 +28,7 @@ import {formField} from '@xh/hoist/desktop/cmp/form';
28
28
  import {
29
29
  buttonGroupInput,
30
30
  dateInput,
31
+ intentInput,
31
32
  select,
32
33
  switchInput,
33
34
  textArea
@@ -112,19 +113,7 @@ const formPanel = hoistCmp.factory<AlertBannerModel>(({model}) => {
112
113
  }),
113
114
  formField({
114
115
  field: 'intent',
115
- item: buttonGroupInput({
116
- items: model.intentOptions.map(intent =>
117
- button({
118
- intent,
119
- minimal: false,
120
- // Opacity solution to account for Icon.placeholder() causing label to move when primary deselected
121
- icon: Icon.check({
122
- opacity: formModel.values.intent === intent ? 1 : 0
123
- }),
124
- value: intent
125
- })
126
- )
127
- })
116
+ item: intentInput({intents: model.intentOptions})
128
117
  }),
129
118
  formField({
130
119
  field: 'iconName',
@@ -0,0 +1,72 @@
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 © 2026 Extremely Heavy Industries Inc.
6
+ */
7
+ import {markdownRenderer, yesNoRenderer} from '@xh/hoist/admin/AdminUtils';
8
+ import {valueTypeRenderer} from '@xh/hoist/admin/columns';
9
+ import {restDetailPanel} from '@xh/hoist/admin/detail/RestDetailPanel';
10
+ import {formFieldSet} from '@xh/hoist/cmp/form';
11
+ import {hbox} from '@xh/hoist/cmp/layout';
12
+ import {hoistCmp, uses} from '@xh/hoist/core';
13
+ import {formField} from '@xh/hoist/desktop/cmp/form';
14
+ import {Icon} from '@xh/hoist/icon';
15
+ import {ConfigPanelModel} from './ConfigPanelModel';
16
+ import {configValue} from './ConfigValue';
17
+
18
+ /**
19
+ * Read-only detail view of the config selected in the grid. Shows the config's metadata and every
20
+ * available view of its value - resolved, instance override, database, and typedClass defaults -
21
+ * opening on the most-derived view.
22
+ */
23
+ export const configDetailPanel = hoistCmp.factory<ConfigPanelModel>({
24
+ displayName: 'ConfigDetailPanel',
25
+ model: uses(ConfigPanelModel),
26
+
27
+ render({model}) {
28
+ return restDetailPanel({
29
+ icon: Icon.settings(),
30
+ emptyText: 'Select a config to view its details.',
31
+ persistWith: {...model.persistWith, path: 'detailPanel'},
32
+ renderForm: (record, formModel) => {
33
+ const {note, valueType} = formModel.values;
34
+ return [
35
+ formFieldSet({
36
+ title: 'Config',
37
+ items: [
38
+ hbox(
39
+ formField({field: 'groupName', flex: 1}),
40
+ formField({
41
+ field: 'valueType',
42
+ readonlyRenderer: valueTypeRenderer,
43
+ flex: 1
44
+ }),
45
+ formField({
46
+ field: 'clientVisible',
47
+ readonlyRenderer: yesNoRenderer,
48
+ flex: 1
49
+ })
50
+ ),
51
+ formField({
52
+ field: 'note',
53
+ readonlyRenderer: markdownRenderer,
54
+ omit: !note
55
+ }),
56
+ hbox(
57
+ formField({field: 'lastUpdatedBy', label: 'Updated By', flex: 1}),
58
+ formField({field: 'lastUpdated', label: 'Updated', flex: 2})
59
+ )
60
+ ]
61
+ }),
62
+ formFieldSet({
63
+ title: 'Value',
64
+ // JSON values stretch to fill the remaining height - scalars sit compactly.
65
+ className: valueType === 'json' ? 'xh-admin-readonly-form__fill' : null,
66
+ item: configValue({formModel})
67
+ })
68
+ ];
69
+ }
70
+ });
71
+ }
72
+ });
@@ -7,8 +7,8 @@
7
7
  import * as AdminCol from '@xh/hoist/admin/columns';
8
8
  import * as Col from '@xh/hoist/admin/columns/Rest';
9
9
  import {jsonSearchButton} from '@xh/hoist/admin/jsonsearch/JsonSearch';
10
- import {filler, fragment} from '@xh/hoist/cmp/layout';
11
- import {creates, hoistCmp, XH} from '@xh/hoist/core';
10
+ import {filler, fragment, hframe} from '@xh/hoist/cmp/layout';
11
+ import {creates, hoistCmp} from '@xh/hoist/core';
12
12
  import {button} from '@xh/hoist/desktop/cmp/button';
13
13
  import {recordActionBar} from '@xh/hoist/desktop/cmp/record';
14
14
  import {restGrid} from '@xh/hoist/desktop/cmp/rest';
@@ -17,6 +17,7 @@ import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
17
17
  import {Icon} from '@xh/hoist/icon';
18
18
  import {differ} from '../../../differ/Differ';
19
19
  import {regroupDialog} from '../../../regroup/RegroupDialog';
20
+ import {configDetailPanel} from './ConfigDetailPanel';
20
21
  import {ConfigPanelModel} from './ConfigPanelModel';
21
22
 
22
23
  export const configPanel = hoistCmp.factory({
@@ -24,46 +25,49 @@ export const configPanel = hoistCmp.factory({
24
25
 
25
26
  render({model}) {
26
27
  return fragment(
27
- restGrid({
28
- testId: 'config',
29
- formBbar: configFormBbar(),
30
- extraToolbarItems: () => [
31
- button({
32
- icon: Icon.diff(),
33
- text: 'Compare w/ Remote',
34
- onClick: () => model.openDiffer()
35
- }),
36
- toolbarSep(),
37
- jsonSearchButton({
38
- subjectName: 'Config',
39
- docSearchUrl: 'jsonSearch/searchConfigs',
40
- gridModelConfig: {
41
- sortBy: ['groupName', 'name'],
42
- columns: [
43
- {...AdminCol.groupName},
44
- {...AdminCol.name},
45
- {
46
- field: {name: 'json', type: 'string'},
47
- hidden: true
48
- },
49
- {...Col.lastUpdated}
50
- ]
51
- },
52
- groupByOptions: ['groupName']
53
- })
54
- ]
55
- }),
28
+ hframe(
29
+ restGrid({
30
+ testId: 'config',
31
+ formBbar: configFormBbar(),
32
+ extraToolbarItems: () => [
33
+ button({
34
+ icon: Icon.diff(),
35
+ text: 'Compare w/ Remote',
36
+ onClick: () => model.openDiffer()
37
+ }),
38
+ toolbarSep(),
39
+ jsonSearchButton({
40
+ subjectName: 'Config',
41
+ docSearchUrl: 'jsonSearch/searchConfigs',
42
+ gridModelConfig: {
43
+ sortBy: ['groupName', 'name'],
44
+ columns: [
45
+ {...AdminCol.groupName},
46
+ {...AdminCol.name},
47
+ {
48
+ field: {name: 'json', type: 'string'},
49
+ hidden: true
50
+ },
51
+ {...Col.lastUpdated}
52
+ ]
53
+ },
54
+ groupByOptions: ['groupName']
55
+ })
56
+ ]
57
+ }),
58
+ configDetailPanel()
59
+ ),
56
60
  differ({omit: !model.differModel}),
57
61
  regroupDialog()
58
62
  );
59
63
  }
60
64
  });
61
65
 
62
- // Custom toolbar supports reverting the form, and leaving it open after change
66
+ // Custom toolbar adds a Revert button to the standard record actions and Cancel/Save. The dialog
67
+ // is edit-only - viewing is handled by the docked detail panel.
63
68
  const configFormBbar = hoistCmp.factory<RestFormModel>(({model}) => {
64
69
  const {formModel, actions, currentRecord, gridModel} = model,
65
- {isDirty, isValid, readonly} = formModel,
66
- containsResolved = formModel.values.resolvedValue != null;
70
+ {isDirty, isValid} = formModel;
67
71
  return toolbar(
68
72
  recordActionBar({
69
73
  actions,
@@ -74,11 +78,11 @@ const configFormBbar = hoistCmp.factory<RestFormModel>(({model}) => {
74
78
  text: 'Revert',
75
79
  icon: Icon.reset(),
76
80
  onClick: () => formModel.reset(),
77
- omit: readonly || !isDirty
81
+ omit: !isDirty
78
82
  }),
79
83
  filler(),
80
84
  button({
81
- text: readonly || (containsResolved && !isDirty) ? 'Close' : 'Cancel',
85
+ text: 'Cancel',
82
86
  onClick: () => model.close()
83
87
  }),
84
88
  button({
@@ -87,23 +91,7 @@ const configFormBbar = hoistCmp.factory<RestFormModel>(({model}) => {
87
91
  intent: 'success',
88
92
  outlined: true,
89
93
  disabled: (!model.isAdd && !isDirty) || !isValid,
90
- onClick: async () => {
91
- const {isAdd} = model,
92
- {id} = model.currentRecord,
93
- reopen = !isAdd && containsResolved && formModel.getField('value')?.isDirty;
94
-
95
- await model.validateAndSaveAsync();
96
- if (reopen && !model.isOpen) {
97
- const record = model.store.getById(id);
98
- if (record) {
99
- model.parent.editRecord(record);
100
- XH.successToast(
101
- 'The edit has been saved and the resolved value has been updated.'
102
- );
103
- }
104
- }
105
- },
106
- omit: readonly
94
+ onClick: () => model.validateAndSaveAsync()
107
95
  })
108
96
  );
109
97
  });
@@ -62,6 +62,9 @@ export class ConfigPanelModel extends HoistModel {
62
62
  selModel: 'multiple',
63
63
  sortBy: 'name',
64
64
  unit: 'config',
65
+ onRowDoubleClicked: ({data}) => {
66
+ if (data && !this.gridModel.readonly) this.gridModel.editRecord(data);
67
+ },
65
68
  // Store + fields
66
69
  store: {
67
70
  url: 'rest/configAdmin',
@@ -77,3 +77,17 @@
77
77
  padding: 0;
78
78
  }
79
79
  }
80
+
81
+ // Fill mode (no fixed `height`) - the tab set / single field stretches to its container, with
82
+ // the nested layers passing the flex down to the JSON editor.
83
+ .xh-config-value--fill {
84
+ display: flex;
85
+ flex: 1;
86
+ flex-direction: column;
87
+ min-height: 0;
88
+
89
+ .xh-form-field__inner {
90
+ flex: 1;
91
+ min-height: 0;
92
+ }
93
+ }