@onehat/ui 0.4.96 → 0.4.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Components/Container/Container.js +47 -43
- package/src/Components/Form/Form.js +14 -6
- package/src/Components/Grid/Grid.js +71 -37
- package/src/Components/Hoc/withEditor.js +16 -4
- package/src/Components/Panel/TabPanel.js +1 -1
- package/src/Components/Panel/TreePanel.js +1 -1
- package/src/Components/Tree/Tree.js +26 -3
- package/src/Components/Tree/TreeNode.js +3 -0
- package/src/Components/Viewer/DateTimeViewer.js +25 -0
- package/src/Components/Viewer/DateViewer.js +25 -0
- package/src/Components/Viewer/PmCalcDebugViewer.js +299 -0
- package/src/Components/Viewer/PmStatusesViewer.js +51 -0
- package/src/Components/Viewer/TimeViewer.js +25 -0
- package/src/Components/Viewer/Viewer.js +8 -1
- package/src/Components/index.js +10 -0
- package/src/Constants/Dates.js +1 -0
- package/src/Constants/EditorModes.js +2 -0
- package/src/Constants/MeterSources.js +5 -0
- package/src/Constants/MeterTypes.js +4 -2
- package/src/Constants/PmEventTypes.js +11 -0
- package/src/Constants/PmScheduleModes.js +4 -0
- package/src/Constants/PmStatuses.js +7 -0
- package/src/PlatformImports/Web/Attachments.js +1 -0
package/package.json
CHANGED
|
@@ -118,77 +118,81 @@ function Container(props) {
|
|
|
118
118
|
westWidthRef = useRef(westInitialWidth),
|
|
119
119
|
[isReady, setIsReady] = useState(false),
|
|
120
120
|
[isComponentsDisabled, setIsComponentsDisabled] = useState(false),
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
localNorthIsCollapsedRef = useRef(northInitialIsCollapsed),
|
|
122
|
+
localSouthIsCollapsedRef = useRef(southInitialIsCollapsed),
|
|
123
|
+
localEastIsCollapsedRef = useRef(eastInitialIsCollapsed),
|
|
124
|
+
localWestIsCollapsedRef = useRef(westInitialIsCollapsed),
|
|
125
125
|
setNorthIsCollapsed = (bool) => {
|
|
126
126
|
if (setExternalNorthIsCollapsed) {
|
|
127
127
|
setExternalNorthIsCollapsed(bool);
|
|
128
128
|
} else {
|
|
129
|
-
|
|
129
|
+
localNorthIsCollapsedRef.current = bool;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
if (id) {
|
|
133
133
|
setSaved(id + '-northIsCollapsed', bool);
|
|
134
134
|
}
|
|
135
|
+
forceUpdate();
|
|
135
136
|
},
|
|
136
137
|
getNorthIsCollapsed = () => {
|
|
137
138
|
if (setExternalNorthIsCollapsed) {
|
|
138
139
|
return northIsCollapsed;
|
|
139
140
|
}
|
|
140
|
-
return
|
|
141
|
+
return localNorthIsCollapsedRef.current;
|
|
141
142
|
},
|
|
142
143
|
setSouthIsCollapsed = (bool) => {
|
|
143
144
|
if (setExternalSouthIsCollapsed) {
|
|
144
145
|
setExternalSouthIsCollapsed(bool);
|
|
145
146
|
} else {
|
|
146
|
-
|
|
147
|
+
localSouthIsCollapsedRef.current = bool;
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
if (id) {
|
|
150
151
|
setSaved(id + '-southIsCollapsed', bool);
|
|
151
152
|
}
|
|
153
|
+
forceUpdate();
|
|
152
154
|
},
|
|
153
155
|
getSouthIsCollapsed = () => {
|
|
154
156
|
if (setExternalSouthIsCollapsed) {
|
|
155
157
|
return southIsCollapsed;
|
|
156
158
|
}
|
|
157
|
-
return
|
|
159
|
+
return localSouthIsCollapsedRef.current;
|
|
158
160
|
},
|
|
159
161
|
setEastIsCollapsed = (bool) => {
|
|
160
162
|
if (setExternalEastIsCollapsed) {
|
|
161
163
|
setExternalEastIsCollapsed(bool);
|
|
162
164
|
} else {
|
|
163
|
-
|
|
165
|
+
localEastIsCollapsedRef.current = bool;
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
if (id) {
|
|
167
169
|
setSaved(id + '-eastIsCollapsed', bool);
|
|
168
170
|
}
|
|
171
|
+
forceUpdate();
|
|
169
172
|
},
|
|
170
173
|
getEastIsCollapsed = () => {
|
|
171
174
|
if (setExternalEastIsCollapsed) {
|
|
172
175
|
return eastIsCollapsed;
|
|
173
176
|
}
|
|
174
|
-
return
|
|
177
|
+
return localEastIsCollapsedRef.current;
|
|
175
178
|
},
|
|
176
179
|
setWestIsCollapsed = (bool) => {
|
|
177
180
|
if (setExternalWestIsCollapsed) {
|
|
178
181
|
setExternalWestIsCollapsed(bool);
|
|
179
182
|
} else {
|
|
180
|
-
|
|
183
|
+
localWestIsCollapsedRef.current = bool;
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
if (id) {
|
|
184
187
|
setSaved(id + '-westIsCollapsed', bool);
|
|
185
188
|
}
|
|
189
|
+
forceUpdate();
|
|
186
190
|
},
|
|
187
191
|
getWestIsCollapsed = () => {
|
|
188
192
|
if (setExternalWestIsCollapsed) {
|
|
189
193
|
return westIsCollapsed;
|
|
190
194
|
}
|
|
191
|
-
return
|
|
195
|
+
return localWestIsCollapsedRef.current;
|
|
192
196
|
},
|
|
193
197
|
setNorthHeight = (height) => {
|
|
194
198
|
if (!getNorthIsCollapsed()) {
|
|
@@ -348,7 +352,7 @@ function Container(props) {
|
|
|
348
352
|
return null;
|
|
349
353
|
}
|
|
350
354
|
|
|
351
|
-
let componentProps = {},
|
|
355
|
+
let componentProps = { _panel: { ...center?.props?._panel }, },
|
|
352
356
|
wrapperProps = null,
|
|
353
357
|
centerComponent = null,
|
|
354
358
|
northComponent = null,
|
|
@@ -360,15 +364,15 @@ function Container(props) {
|
|
|
360
364
|
westComponent = null,
|
|
361
365
|
westSplitter = null;
|
|
362
366
|
|
|
363
|
-
componentProps.isCollapsible = false;
|
|
364
|
-
componentProps.isDisabled = isDisabled || isComponentsDisabled;
|
|
367
|
+
componentProps._panel.isCollapsible = false;
|
|
368
|
+
componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
|
|
365
369
|
centerComponent = cloneElement(center, componentProps);
|
|
366
370
|
if (north) {
|
|
367
|
-
componentProps = {};
|
|
371
|
+
componentProps = { _panel: { ...north.props?._panel }, };
|
|
368
372
|
wrapperProps = {};
|
|
369
373
|
|
|
370
|
-
componentProps.isDisabled = isDisabled || isComponentsDisabled;
|
|
371
|
-
componentProps.className = 'h-full w-full ' + (north.props.className || '');
|
|
374
|
+
componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
|
|
375
|
+
componentProps._panel.className = 'h-full w-full ' + (north.props.className || '');
|
|
372
376
|
wrapperProps.onLayout = (e) => {
|
|
373
377
|
const height = parseFloat(e.nativeEvent.layout.height);
|
|
374
378
|
if (height && height !== northHeight) {
|
|
@@ -387,9 +391,9 @@ function Container(props) {
|
|
|
387
391
|
wrapperProps.style = { height: northHeight, };
|
|
388
392
|
}
|
|
389
393
|
}
|
|
390
|
-
componentProps.collapseDirection = VERTICAL;
|
|
391
|
-
componentProps.isCollapsed = getNorthIsCollapsed();
|
|
392
|
-
componentProps.setIsCollapsed = setNorthIsCollapsed;
|
|
394
|
+
componentProps._panel.collapseDirection = VERTICAL;
|
|
395
|
+
componentProps._panel.isCollapsed = getNorthIsCollapsed();
|
|
396
|
+
componentProps._panel.setIsCollapsed = setNorthIsCollapsed;
|
|
393
397
|
if (isWeb && northIsResizable) {
|
|
394
398
|
northSplitter = <Splitter
|
|
395
399
|
mode={VERTICAL}
|
|
@@ -402,11 +406,11 @@ function Container(props) {
|
|
|
402
406
|
</BoxNative>;
|
|
403
407
|
}
|
|
404
408
|
if (south) {
|
|
405
|
-
componentProps = {};
|
|
409
|
+
componentProps = { _panel: { ...south.props?._panel }, };
|
|
406
410
|
wrapperProps = {};
|
|
407
411
|
|
|
408
|
-
componentProps.isDisabled = isDisabled || isComponentsDisabled;
|
|
409
|
-
componentProps.className = 'h-full w-full ' + (south.props.className || '');
|
|
412
|
+
componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
|
|
413
|
+
componentProps._panel.className = 'h-full w-full ' + (south.props.className || '');
|
|
410
414
|
wrapperProps.onLayout = (e) => {
|
|
411
415
|
const height = parseFloat(e.nativeEvent.layout.height);
|
|
412
416
|
if (height && height !== getSouthHeight()) {
|
|
@@ -425,9 +429,9 @@ function Container(props) {
|
|
|
425
429
|
wrapperProps.style = { height: southHeight, };
|
|
426
430
|
}
|
|
427
431
|
}
|
|
428
|
-
componentProps.collapseDirection = VERTICAL;
|
|
429
|
-
componentProps.isCollapsed = getSouthIsCollapsed();
|
|
430
|
-
componentProps.setIsCollapsed = setSouthIsCollapsed;
|
|
432
|
+
componentProps._panel.collapseDirection = VERTICAL;
|
|
433
|
+
componentProps._panel.isCollapsed = getSouthIsCollapsed();
|
|
434
|
+
componentProps._panel.setIsCollapsed = setSouthIsCollapsed;
|
|
431
435
|
if (isWeb && southIsResizable) {
|
|
432
436
|
southSplitter = <Splitter
|
|
433
437
|
mode={VERTICAL}
|
|
@@ -440,11 +444,11 @@ function Container(props) {
|
|
|
440
444
|
</BoxNative>;
|
|
441
445
|
}
|
|
442
446
|
if (east) {
|
|
443
|
-
componentProps = {};
|
|
447
|
+
componentProps = { _panel: { ...east.props?._panel }, };
|
|
444
448
|
wrapperProps = {};
|
|
445
449
|
|
|
446
|
-
componentProps.isDisabled = isDisabled || isComponentsDisabled;
|
|
447
|
-
componentProps.className = 'h-full w-full ' + (east.props.className || '');
|
|
450
|
+
componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
|
|
451
|
+
componentProps._panel.className = 'h-full w-full ' + (east.props.className || '');
|
|
448
452
|
wrapperProps.onLayout = (e) => {
|
|
449
453
|
const width = parseFloat(e.nativeEvent.layout.width);
|
|
450
454
|
if (width && width !== getEastWidth()) {
|
|
@@ -463,9 +467,9 @@ function Container(props) {
|
|
|
463
467
|
wrapperProps.style = { width: eastWidth, };
|
|
464
468
|
}
|
|
465
469
|
}
|
|
466
|
-
componentProps.collapseDirection = HORIZONTAL;
|
|
467
|
-
componentProps.isCollapsed = getEastIsCollapsed();
|
|
468
|
-
componentProps.setIsCollapsed = setEastIsCollapsed;
|
|
470
|
+
componentProps._panel.collapseDirection = HORIZONTAL;
|
|
471
|
+
componentProps._panel.isCollapsed = getEastIsCollapsed();
|
|
472
|
+
componentProps._panel.setIsCollapsed = setEastIsCollapsed;
|
|
469
473
|
if (isWeb && eastIsResizable) {
|
|
470
474
|
eastSplitter = <Splitter
|
|
471
475
|
mode={HORIZONTAL}
|
|
@@ -478,11 +482,11 @@ function Container(props) {
|
|
|
478
482
|
</BoxNative>;
|
|
479
483
|
}
|
|
480
484
|
if (west) {
|
|
481
|
-
componentProps = {};
|
|
485
|
+
componentProps = { _panel: { ...west.props?._panel }, };
|
|
482
486
|
wrapperProps = {};
|
|
483
487
|
|
|
484
|
-
componentProps.isDisabled = isDisabled || isComponentsDisabled;
|
|
485
|
-
componentProps.className = 'h-full w-full ' + (west.props.className || '');
|
|
488
|
+
componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
|
|
489
|
+
componentProps._panel.className = 'h-full w-full ' + (west.props.className || '');
|
|
486
490
|
wrapperProps.onLayout = (e) => {
|
|
487
491
|
const width = parseFloat(e.nativeEvent.layout.width);
|
|
488
492
|
if (width && width !== getWestWidth()) {
|
|
@@ -501,9 +505,9 @@ function Container(props) {
|
|
|
501
505
|
wrapperProps.style = { width: westWidth, };
|
|
502
506
|
}
|
|
503
507
|
}
|
|
504
|
-
componentProps.collapseDirection = HORIZONTAL;
|
|
505
|
-
componentProps.isCollapsed = getWestIsCollapsed();
|
|
506
|
-
componentProps.setIsCollapsed = setWestIsCollapsed;
|
|
508
|
+
componentProps._panel.collapseDirection = HORIZONTAL;
|
|
509
|
+
componentProps._panel.isCollapsed = getWestIsCollapsed();
|
|
510
|
+
componentProps._panel.setIsCollapsed = setWestIsCollapsed;
|
|
507
511
|
if (isWeb && westIsResizable) {
|
|
508
512
|
westSplitter = <Splitter
|
|
509
513
|
mode={HORIZONTAL}
|
|
@@ -517,17 +521,17 @@ function Container(props) {
|
|
|
517
521
|
}
|
|
518
522
|
return <VStack className="Container-all w-full flex-1">
|
|
519
523
|
{northComponent}
|
|
520
|
-
{!
|
|
524
|
+
{!getNorthIsCollapsed() && northSplitter}
|
|
521
525
|
<HStack className="Container-mid w-full flex-[100]">
|
|
522
526
|
{westComponent}
|
|
523
|
-
{!
|
|
527
|
+
{!getWestIsCollapsed() && westSplitter}
|
|
524
528
|
<VStack className="Container-center h-full overflow-auto flex-[100]">
|
|
525
529
|
{centerComponent}
|
|
526
530
|
</VStack>
|
|
527
|
-
{!
|
|
531
|
+
{!getEastIsCollapsed() && eastSplitter}
|
|
528
532
|
{eastComponent}
|
|
529
533
|
</HStack>
|
|
530
|
-
{!
|
|
534
|
+
{!getSouthIsCollapsed() && southSplitter}
|
|
531
535
|
{southComponent}
|
|
532
536
|
</VStack>;
|
|
533
537
|
}
|
|
@@ -522,6 +522,8 @@ function Form(props) {
|
|
|
522
522
|
}
|
|
523
523
|
let {
|
|
524
524
|
type,
|
|
525
|
+
editorType: itemEditorType,
|
|
526
|
+
viewerType,
|
|
525
527
|
title,
|
|
526
528
|
name,
|
|
527
529
|
isEditable = true,
|
|
@@ -560,12 +562,18 @@ function Form(props) {
|
|
|
560
562
|
}
|
|
561
563
|
if (!type) {
|
|
562
564
|
if (isEditable) {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
565
|
+
if (itemEditorType) {
|
|
566
|
+
type = itemEditorType;
|
|
567
|
+
} else {
|
|
568
|
+
const {
|
|
569
|
+
type: t,
|
|
570
|
+
...p
|
|
571
|
+
} = propertyDef?.editorType;
|
|
572
|
+
type = t;
|
|
573
|
+
editorTypeProps = p;
|
|
574
|
+
}
|
|
575
|
+
} else if (viewerType) {
|
|
576
|
+
type = viewerType;
|
|
569
577
|
} else if (propertyDef?.viewerType) {
|
|
570
578
|
const {
|
|
571
579
|
type: t,
|
|
@@ -423,7 +423,16 @@ function GridComponent(props) {
|
|
|
423
423
|
}
|
|
424
424
|
},
|
|
425
425
|
getFooterToolbarItems = () => {
|
|
426
|
-
|
|
426
|
+
// Process additionalToolbarButtons to evaluate getIsButtonDisabled functions
|
|
427
|
+
const processedButtons = _.map(additionalToolbarButtons, (config) => {
|
|
428
|
+
const processedConfig = { ...config };
|
|
429
|
+
// If the button has an getIsButtonDisabled function, evaluate it with current selection
|
|
430
|
+
if (_.isFunction(config.getIsButtonDisabled)) {
|
|
431
|
+
processedConfig.isDisabled = config.getIsButtonDisabled(selection);
|
|
432
|
+
}
|
|
433
|
+
return processedConfig;
|
|
434
|
+
});
|
|
435
|
+
const items = _.map(processedButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
427
436
|
if (canRowsReorder && CURRENT_MODE === UI_MODE_WEB) { // DND is currently web-only TODO: implement for RN
|
|
428
437
|
items.unshift(<IconButton
|
|
429
438
|
{...testProps('reorderBtn')}
|
|
@@ -476,33 +485,41 @@ function GridComponent(props) {
|
|
|
476
485
|
}
|
|
477
486
|
break;
|
|
478
487
|
case DOUBLE_CLICK:
|
|
479
|
-
if (
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
|
|
484
|
-
if (onView) {
|
|
485
|
-
if (canUser && !canUser(VIEW)) { // permissions
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
onView(!props.isEditorViewOnly);
|
|
488
|
+
if (editorType === EDITOR_TYPE__SIDE) {
|
|
489
|
+
// For side-editors, a double-click just acts like a single click
|
|
490
|
+
if (!getIsEditorShown()) {
|
|
491
|
+
onRowClick(item, e); // sets selection
|
|
489
492
|
}
|
|
490
493
|
} else {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit && !isEditorViewOnly) {
|
|
494
|
-
canDoEdit = true;
|
|
495
|
-
} else
|
|
496
|
-
if (onView && canUser && canUser(VIEW) && !props.disableView) {
|
|
497
|
-
canDoView = true;
|
|
494
|
+
if (!isSelected) { // If a row was already selected when double-clicked, the first click will deselect it,
|
|
495
|
+
onRowClick(item, e); // so reselect it
|
|
498
496
|
}
|
|
497
|
+
if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
|
|
498
|
+
if (onView) {
|
|
499
|
+
if (canUser && !canUser(VIEW)) { // permissions
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
onView(!props.isEditorViewOnly);
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
let canDoEdit = false,
|
|
506
|
+
canDoView = false;
|
|
507
|
+
if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit && !isEditorViewOnly) {
|
|
508
|
+
canDoEdit = true;
|
|
509
|
+
} else
|
|
510
|
+
if (onView && canUser && canUser(VIEW) && !props.disableView) {
|
|
511
|
+
canDoView = true;
|
|
512
|
+
}
|
|
499
513
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
514
|
+
if (canDoEdit) {
|
|
515
|
+
onEdit();
|
|
516
|
+
} else if (canDoView) {
|
|
517
|
+
onView();
|
|
518
|
+
}
|
|
504
519
|
}
|
|
520
|
+
|
|
505
521
|
}
|
|
522
|
+
|
|
506
523
|
break;
|
|
507
524
|
case TRIPLE_CLICK:
|
|
508
525
|
break;
|
|
@@ -1562,10 +1579,19 @@ function GridComponent(props) {
|
|
|
1562
1579
|
const
|
|
1563
1580
|
currentLength = entities?.length || 0,
|
|
1564
1581
|
wasEmpty = previousEntitiesLength.current === 0,
|
|
1565
|
-
isNowPopulated = currentLength > 0
|
|
1582
|
+
isNowPopulated = currentLength > 0,
|
|
1583
|
+
hasPhantomRecord = entities?.some(entity => entity?.isPhantom);
|
|
1584
|
+
|
|
1585
|
+
// NOTE: The Repository was reloading when a phantom record was added,
|
|
1586
|
+
// and this broke the Editor because selection was being reset to zero.
|
|
1587
|
+
// This is because adjustPageSizeToHeight calls setPageSize,
|
|
1588
|
+
// which calls _onChangePagination, which calls reload.
|
|
1589
|
+
// The reloaded repository doesn’t get the new phantom record,
|
|
1590
|
+
// so it’s not found, and selection goes to zero.
|
|
1591
|
+
// So we skip this adjustment when there is a phantom record.
|
|
1566
1592
|
|
|
1567
1593
|
// Only remeasure the FIRST time rows appear after being empty
|
|
1568
|
-
if (autoAdjustPageSizeToHeight && wasEmpty && isNowPopulated && !hasRemeasuredAfterRowsAppeared.current) {
|
|
1594
|
+
if (!hasPhantomRecord && autoAdjustPageSizeToHeight && wasEmpty && isNowPopulated && !hasRemeasuredAfterRowsAppeared.current) {
|
|
1569
1595
|
// Rows just appeared for the first time - restart measurement cycle to use actual heights
|
|
1570
1596
|
if (DEBUG) {
|
|
1571
1597
|
console.log(`${getMeasurementPhase()}, useEffect 5 - rows appeared for first time, restarting measurement cycle`);
|
|
@@ -1602,13 +1628,24 @@ function GridComponent(props) {
|
|
|
1602
1628
|
// first time through, render a placeholder so we can get container dimensions
|
|
1603
1629
|
return <VStackNative
|
|
1604
1630
|
onLayout={(e) => {
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1631
|
+
const hasPhantomRecord = entities?.some(entity => entity?.isPhantom);
|
|
1632
|
+
if (!hasPhantomRecord) {
|
|
1633
|
+
// NOTE: The Repository was reloading when a phantom record was added,
|
|
1634
|
+
// and this broke the Editor because selection was being reset to zero.
|
|
1635
|
+
// This is because adjustPageSizeToHeight calls setPageSize,
|
|
1636
|
+
// which calls _onChangePagination, which calls reload.
|
|
1637
|
+
// The reloaded repository doesn’t get the new phantom record,
|
|
1638
|
+
// so it’s not found, and selection goes to zero.
|
|
1639
|
+
// So we skip this adjustment when there is a phantom record.
|
|
1640
|
+
|
|
1641
|
+
if (DEBUG) {
|
|
1642
|
+
console.log(`${getMeasurementPhase()}, placeholder onLayout, call adjustPageSizeToHeight()`);
|
|
1643
|
+
}
|
|
1644
|
+
const containerHeight = e.nativeEvent.layout.height;
|
|
1645
|
+
adjustPageSizeToHeight(containerHeight);
|
|
1646
|
+
if (DEBUG) {
|
|
1647
|
+
console.log(`${getMeasurementPhase()}, placeholder onLayout, call setIsInited(true)`);
|
|
1648
|
+
}
|
|
1612
1649
|
}
|
|
1613
1650
|
setIsInited(true);
|
|
1614
1651
|
}}
|
|
@@ -1785,14 +1822,11 @@ function GridComponent(props) {
|
|
|
1785
1822
|
)}
|
|
1786
1823
|
>
|
|
1787
1824
|
{grid}
|
|
1788
|
-
{/*
|
|
1789
|
-
|
|
1790
|
-
(getMeasurementPhase() === PHASES__INITIAL || getMeasurementPhase() === PHASES__MEASURING) &&
|
|
1791
|
-
entities?.length > 0 && (
|
|
1825
|
+
{/* Load overlay during initial phase to prevent visual flashing * /
|
|
1826
|
+
autoAdjustPageSizeToHeight && getMeasurementPhase() === PHASES__INITIAL &&
|
|
1792
1827
|
<VStack className="absolute inset-0 z-10 bg-white">
|
|
1793
1828
|
<Loading />
|
|
1794
|
-
</VStack
|
|
1795
|
-
)}
|
|
1829
|
+
</VStack>*/}
|
|
1796
1830
|
</VStack>
|
|
1797
1831
|
|
|
1798
1832
|
{listFooterComponent}
|
|
@@ -38,6 +38,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
38
38
|
disableDuplicate = false,
|
|
39
39
|
disableView = false,
|
|
40
40
|
useRemoteDuplicate = false, // call specific copyToNew function on server, rather than simple duplicate on client
|
|
41
|
+
getDuplicateValues, // fn(entity) to get default values for duplication
|
|
41
42
|
getRecordIdentifier = (selection) => {
|
|
42
43
|
if (selection.length > 1) {
|
|
43
44
|
return 'records?';
|
|
@@ -89,6 +90,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
89
90
|
editorModeRef = useRef(initialEditorMode),
|
|
90
91
|
isIgnoreNextSelectionChangeRef = useRef(false),
|
|
91
92
|
isEditorShownRef = useRef(false),
|
|
93
|
+
defaultValuesRef = useRef(defaultValues),
|
|
92
94
|
canEditorBeInEditModeRef = useRef(true), // whether the editor is allowed to be in edit mode based on canRecordBeEdited
|
|
93
95
|
[currentRecord, setCurrentRecord] = useState(null),
|
|
94
96
|
[isAdding, setIsAdding] = useState(false),
|
|
@@ -111,6 +113,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
111
113
|
getIsEditorShown = () => {
|
|
112
114
|
return isEditorShownRef.current;
|
|
113
115
|
},
|
|
116
|
+
getDefaultValues = () => {
|
|
117
|
+
return defaultValuesRef.current;
|
|
118
|
+
},
|
|
119
|
+
setDefaultValues = (vals) => {
|
|
120
|
+
defaultValuesRef.current = vals;
|
|
121
|
+
},
|
|
114
122
|
setCanEditorBeInEditMode = (bool) => {
|
|
115
123
|
canEditorBeInEditModeRef.current = bool;
|
|
116
124
|
forceUpdate();
|
|
@@ -164,6 +172,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
164
172
|
getNewEntityDisplayValue = () => {
|
|
165
173
|
return newEntityDisplayValueRef.current;
|
|
166
174
|
},
|
|
175
|
+
setNewEntityDisplayValue = (val) => {
|
|
176
|
+
newEntityDisplayValueRef.current = val;
|
|
177
|
+
},
|
|
167
178
|
doAdd = async (e, values) => {
|
|
168
179
|
if (canUser && !canUser(ADD)) {
|
|
169
180
|
showPermissionsError(ADD);
|
|
@@ -191,8 +202,8 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
191
202
|
// 2. Use the repository's default values (defined on each property as 'defaultValue'), or
|
|
192
203
|
// 3. Individually override the repository's default values with submitted 'defaultValues' (given as a prop to this HOC)
|
|
193
204
|
let defaultValuesToUse = Repository.getSchema().getDefaultValues();
|
|
194
|
-
if (
|
|
195
|
-
_.merge(defaultValuesToUse,
|
|
205
|
+
if (getDefaultValues()) {
|
|
206
|
+
_.merge(defaultValuesToUse, getDefaultValues());
|
|
196
207
|
}
|
|
197
208
|
addValues = {...defaultValuesToUse};
|
|
198
209
|
}
|
|
@@ -458,7 +469,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
458
469
|
const
|
|
459
470
|
entity = selection[0],
|
|
460
471
|
idProperty = Repository.getSchema().model.idProperty,
|
|
461
|
-
rawValues = _.omit(entity.getOriginalData(), idProperty);
|
|
472
|
+
rawValues = getDuplicateValues ? getDuplicateValues(entity) : _.omit(entity.getOriginalData(), idProperty);
|
|
462
473
|
rawValues.id = null; // unset the id of the duplicate
|
|
463
474
|
|
|
464
475
|
setIsWaitModalShown(true);
|
|
@@ -738,7 +749,8 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
738
749
|
self.duplicate = doDuplicate;
|
|
739
750
|
self.setIsEditorShown = setIsEditorShown;
|
|
740
751
|
}
|
|
741
|
-
|
|
752
|
+
setNewEntityDisplayValue(newEntityDisplayValue);
|
|
753
|
+
setDefaultValues(defaultValues);
|
|
742
754
|
|
|
743
755
|
if (lastSelection !== selection) {
|
|
744
756
|
// NOTE: If I don't calculate this on the fly for selection changes,
|
|
@@ -654,9 +654,23 @@ function TreeComponent(props) {
|
|
|
654
654
|
return;
|
|
655
655
|
}
|
|
656
656
|
|
|
657
|
-
// Create datum for the new entity and add it to parent's children
|
|
658
657
|
const newDatum = buildTreeNodeDatum(entity);
|
|
659
|
-
|
|
658
|
+
|
|
659
|
+
let isInserted = false;
|
|
660
|
+
if (entity._originalData?.previousSiblingId) {
|
|
661
|
+
// Insert datum in the correct position among parent's children
|
|
662
|
+
const siblingDatum = getDatumById(entity._originalData.previousSiblingId);
|
|
663
|
+
if (siblingDatum) {
|
|
664
|
+
const siblingIndex = parentDatum.children.findIndex(child => child.item.id === siblingDatum.item.id);
|
|
665
|
+
parentDatum.children.splice(siblingIndex + 1, 0, newDatum);
|
|
666
|
+
isInserted = true;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (!isInserted) {
|
|
670
|
+
// Insert datum at end of parent's children
|
|
671
|
+
parentDatum.children.push(newDatum);
|
|
672
|
+
}
|
|
673
|
+
|
|
660
674
|
|
|
661
675
|
// Update parent to show it has children and expand if needed
|
|
662
676
|
if (!entity.parent.hasChildren) {
|
|
@@ -1052,7 +1066,16 @@ function TreeComponent(props) {
|
|
|
1052
1066
|
return items;
|
|
1053
1067
|
},
|
|
1054
1068
|
getFooterToolbarItems = () => {
|
|
1055
|
-
|
|
1069
|
+
// Process additionalToolbarButtons to evaluate getIsButtonDisabled functions
|
|
1070
|
+
const processedButtons = _.map(additionalToolbarButtons, (config) => {
|
|
1071
|
+
const processedConfig = { ...config };
|
|
1072
|
+
// If the button has an getIsButtonDisabled function, evaluate it with current selection
|
|
1073
|
+
if (_.isFunction(config.getIsButtonDisabled)) {
|
|
1074
|
+
processedConfig.isDisabled = config.getIsButtonDisabled(selection);
|
|
1075
|
+
}
|
|
1076
|
+
return processedConfig;
|
|
1077
|
+
});
|
|
1078
|
+
return _.map(processedButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
1056
1079
|
},
|
|
1057
1080
|
renderTreeNode = (datum) => {
|
|
1058
1081
|
if (!datum.isVisible) {
|
|
@@ -148,6 +148,9 @@ export default function TreeNode(props) {
|
|
|
148
148
|
if (props.className) {
|
|
149
149
|
className += ' ' + props.className;
|
|
150
150
|
}
|
|
151
|
+
if (nodeProps.className) {
|
|
152
|
+
className += ' ' + nodeProps.className;
|
|
153
|
+
}
|
|
151
154
|
|
|
152
155
|
return <HStackNative
|
|
153
156
|
{...testProps('node' + (isSelected ? '-selected' : ''))}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import DisplayField from '../Form/Field/DisplayField.js';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
MOMENT_DATE_FORMAT_1,
|
|
5
|
+
} from '../../Constants/Dates';
|
|
6
|
+
|
|
7
|
+
export default function DateTimeViewer(props) {
|
|
8
|
+
const {
|
|
9
|
+
moment,
|
|
10
|
+
} = props;
|
|
11
|
+
if (!moment || !moment.isValid()) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
let className = clsx(
|
|
15
|
+
'flex-none',
|
|
16
|
+
);
|
|
17
|
+
if (props.className) {
|
|
18
|
+
className += ' ' + props.className;
|
|
19
|
+
}
|
|
20
|
+
return <DisplayField
|
|
21
|
+
text={moment.format(MOMENT_DATE_FORMAT_1)}
|
|
22
|
+
{...props}
|
|
23
|
+
className={className}
|
|
24
|
+
/>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import DisplayField from '../Form/Field/DisplayField.js';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
MOMENT_DATE_FORMAT_4,
|
|
5
|
+
} from '../../Constants/Dates';
|
|
6
|
+
|
|
7
|
+
export default function DateViewer(props) {
|
|
8
|
+
const {
|
|
9
|
+
moment,
|
|
10
|
+
} = props;
|
|
11
|
+
if (!moment || !moment.isValid()) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
let className = clsx(
|
|
15
|
+
'flex-none',
|
|
16
|
+
);
|
|
17
|
+
if (props.className) {
|
|
18
|
+
className += ' ' + props.className;
|
|
19
|
+
}
|
|
20
|
+
return <DisplayField
|
|
21
|
+
text={moment.format(MOMENT_DATE_FORMAT_4)}
|
|
22
|
+
{...props}
|
|
23
|
+
className={className}
|
|
24
|
+
/>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Text,
|
|
3
|
+
VStack,
|
|
4
|
+
} from '@project-components/Gluestack';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import {
|
|
7
|
+
MOMENT_DATE_FORMAT_6,
|
|
8
|
+
} from '../../Constants/Dates.js';
|
|
9
|
+
import {
|
|
10
|
+
PM_SCHEDULE_MODES__HISTORICAL_USAGE,
|
|
11
|
+
PM_SCHEDULE_MODES__EXPECTED_USAGE,
|
|
12
|
+
PM_SCHEDULE_MODES__NO_ESTIMATION,
|
|
13
|
+
PM_SCHEDULE_MODES__WILL_CALL,
|
|
14
|
+
} from '../../Constants/PmScheduleModes.js';
|
|
15
|
+
import {
|
|
16
|
+
PM_STATUSES__OK,
|
|
17
|
+
PM_STATUSES__PM_DUE,
|
|
18
|
+
PM_STATUSES__DELAYED,
|
|
19
|
+
PM_STATUSES__WILL_CALL,
|
|
20
|
+
PM_STATUSES__SCHEDULED,
|
|
21
|
+
PM_STATUSES__OVERDUE,
|
|
22
|
+
PM_STATUSES__COMPLETED,
|
|
23
|
+
} from '../../Constants/PmStatuses.js';
|
|
24
|
+
import {
|
|
25
|
+
METER_TYPES__HOURS,
|
|
26
|
+
METER_TYPES__HOURS_UNITS,
|
|
27
|
+
METER_TYPES__HOURS_TEXT,
|
|
28
|
+
METER_TYPES__MILES,
|
|
29
|
+
METER_TYPES__MILES_UNITS,
|
|
30
|
+
METER_TYPES__MILES_TEXT,
|
|
31
|
+
} from '../../Constants/MeterTypes.js';
|
|
32
|
+
import Button from '../Buttons/Button.js';
|
|
33
|
+
import Json from '../Form/Field/Json.js';
|
|
34
|
+
import Panel from '../Panel/Panel.js';
|
|
35
|
+
import Footer from '../Layout/Footer.js';
|
|
36
|
+
import Viewer from '../Viewer/Viewer.js';
|
|
37
|
+
import testProps from '../../Functions/testProps.js';
|
|
38
|
+
import moment from 'moment';
|
|
39
|
+
import _ from 'lodash';
|
|
40
|
+
|
|
41
|
+
export default function PmCalcDebugViewer(props) {
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
metersPmSchedule,
|
|
45
|
+
onClose,
|
|
46
|
+
} = props,
|
|
47
|
+
json = metersPmSchedule?.properties.meters_pm_schedules__debug_json.json,
|
|
48
|
+
flatten = (obj, prefix = '', result = {}) => {
|
|
49
|
+
for (const key in obj) {
|
|
50
|
+
if (obj.hasOwnProperty(key)) {
|
|
51
|
+
const
|
|
52
|
+
newKey = prefix ? `${prefix}.${key}` : key,
|
|
53
|
+
value = obj[key];
|
|
54
|
+
|
|
55
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
56
|
+
// Recursively flatten nested objects
|
|
57
|
+
flatten(value, newKey, result);
|
|
58
|
+
} else if (Array.isArray(value)) {
|
|
59
|
+
// Flatten arrays using index as key
|
|
60
|
+
value.forEach((item, index) => {
|
|
61
|
+
const arrayKey = `${newKey}.${index}`;
|
|
62
|
+
if (item !== null && typeof item === 'object') {
|
|
63
|
+
flatten(item, arrayKey, result);
|
|
64
|
+
} else {
|
|
65
|
+
result[arrayKey] = item;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
// Assign primitive values and null
|
|
70
|
+
result[newKey] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
},
|
|
76
|
+
ucfirst = (string) => {
|
|
77
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
78
|
+
},
|
|
79
|
+
getPmScheduleMode = (pm_schedule_mode_id) => {
|
|
80
|
+
switch(pm_schedule_mode_id) {
|
|
81
|
+
case PM_SCHEDULE_MODES__HISTORICAL_USAGE:
|
|
82
|
+
return 'Historical Usage';
|
|
83
|
+
case PM_SCHEDULE_MODES__EXPECTED_USAGE:
|
|
84
|
+
return 'Expected Usage';
|
|
85
|
+
case PM_SCHEDULE_MODES__NO_ESTIMATION:
|
|
86
|
+
return 'No Estimation';
|
|
87
|
+
case PM_SCHEDULE_MODES__WILL_CALL:
|
|
88
|
+
return 'Will Call';
|
|
89
|
+
default:
|
|
90
|
+
return 'Unknown Mode';
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
flattenedJson = flatten(json),
|
|
94
|
+
formatDaysValue = (value, record, self) => {
|
|
95
|
+
if (value === null || value === undefined) {
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
return parseInt(_.round(value), 10);
|
|
99
|
+
},
|
|
100
|
+
formatMeterValue = (value, record, self) => {
|
|
101
|
+
if (value === null || value === undefined) {
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
let ret;
|
|
105
|
+
const meterType = parseInt(record?.meter_type, 10);
|
|
106
|
+
switch(meterType) {
|
|
107
|
+
case METER_TYPES__HOURS:
|
|
108
|
+
ret = `${value} ${METER_TYPES__HOURS_UNITS}`;
|
|
109
|
+
break;
|
|
110
|
+
case METER_TYPES__MILES:
|
|
111
|
+
ret = `${value} ${METER_TYPES__MILES_UNITS}`;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
return ret;
|
|
115
|
+
},
|
|
116
|
+
formatDateValue = (value, record, self) => {
|
|
117
|
+
if (value === null || value === undefined) {
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// convert from datetime to pretty-printed date
|
|
122
|
+
return moment(value).format(MOMENT_DATE_FORMAT_6);
|
|
123
|
+
},
|
|
124
|
+
items = [
|
|
125
|
+
{
|
|
126
|
+
"type": "Column",
|
|
127
|
+
"flex": 1,
|
|
128
|
+
"defaults": {
|
|
129
|
+
labelWidth: 300,
|
|
130
|
+
},
|
|
131
|
+
"items": [
|
|
132
|
+
{
|
|
133
|
+
"type": "FieldSet",
|
|
134
|
+
"title": "General",
|
|
135
|
+
"reference": "general",
|
|
136
|
+
"defaults": {},
|
|
137
|
+
"items": [
|
|
138
|
+
{
|
|
139
|
+
label: 'PM Schedule',
|
|
140
|
+
name: 'pmSchedule.name',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: 'Status',
|
|
144
|
+
name: 'pm_status_name',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
label: 'Next PM Date',
|
|
148
|
+
name: 'nextPmDate',
|
|
149
|
+
viewerFormatter: formatDateValue,
|
|
150
|
+
},
|
|
151
|
+
json?.overduePms > 0 && {
|
|
152
|
+
label: 'Overdue PMs',
|
|
153
|
+
name: 'overduePms',
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"type": "FieldSet",
|
|
159
|
+
"title": "Calculation Details",
|
|
160
|
+
"reference": "calcDetails",
|
|
161
|
+
"defaults": {},
|
|
162
|
+
"items": [
|
|
163
|
+
{
|
|
164
|
+
label: 'Controlling Method',
|
|
165
|
+
name: 'controllingMethod',
|
|
166
|
+
tooltip: 'Indicates whether the calculation was based on days or usage (meter). ' +
|
|
167
|
+
'If both methods are applicable, the one resulting in the earlier PM date is chosen.',
|
|
168
|
+
},
|
|
169
|
+
(json?.pm_status_id === PM_STATUSES__OVERDUE || json?.pm_status_id === PM_STATUSES__PM_DUE) && {
|
|
170
|
+
label: 'Grace Period End Date',
|
|
171
|
+
name: 'maxGracePeriodDateTime',
|
|
172
|
+
viewerFormatter: formatDateValue,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
label: 'Last Reset Date',
|
|
176
|
+
name: 'resetDate',
|
|
177
|
+
tooltip: 'Indicates whether the calculation was based on days or usage (meter). ' +
|
|
178
|
+
'If both methods are applicable, the one resulting in the earlier PM date is chosen.',
|
|
179
|
+
viewerFormatter: formatDateValue,
|
|
180
|
+
},
|
|
181
|
+
json?.workOrder?.title && {
|
|
182
|
+
label: 'Work Order',
|
|
183
|
+
name: 'workOrder.title',
|
|
184
|
+
},
|
|
185
|
+
json?.workOrder?.service_order && {
|
|
186
|
+
label: 'Work Order',
|
|
187
|
+
name: 'workOrder.service_order',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
label: 'Meter Accrued Since Last PM',
|
|
191
|
+
name: 'meterAccruedSinceLatestPm',
|
|
192
|
+
viewerFormatter: formatMeterValue,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
label: 'Meter Remaining Until Next PM',
|
|
196
|
+
name: 'meterRemainingUntilNextPm',
|
|
197
|
+
viewerFormatter: formatMeterValue,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
label: 'Days Since Last PM',
|
|
201
|
+
name: 'daysSinceLatestPm',
|
|
202
|
+
viewerFormatter: formatDaysValue,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
label: 'Days Left Until Next PM',
|
|
206
|
+
name: 'daysLeft',
|
|
207
|
+
viewerFormatter: formatDaysValue,
|
|
208
|
+
},
|
|
209
|
+
// json?.isDelayed && {
|
|
210
|
+
// label: 'Is Delayed',
|
|
211
|
+
// name: 'isDelayed',
|
|
212
|
+
// },
|
|
213
|
+
// json?.isOverride && {
|
|
214
|
+
// label: 'Is Override',
|
|
215
|
+
// name: 'isOverride',
|
|
216
|
+
// },
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"type": "FieldSet",
|
|
221
|
+
"title": "PM Schedule Config",
|
|
222
|
+
"reference": "pmSchedule",
|
|
223
|
+
"defaults": {},
|
|
224
|
+
"items": [
|
|
225
|
+
json?.pmSchedule?.interval_days && {
|
|
226
|
+
label: 'Interval Days',
|
|
227
|
+
name: 'pmSchedule.interval_days',
|
|
228
|
+
},
|
|
229
|
+
json?.pmSchedule?.interval_meter && {
|
|
230
|
+
label: 'Interval Meter',
|
|
231
|
+
name: 'pmSchedule.interval_meter',
|
|
232
|
+
viewerFormatter: formatMeterValue,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
label: 'Mode',
|
|
236
|
+
name: 'pmScheduleMode',
|
|
237
|
+
},
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"type": "FieldSet",
|
|
242
|
+
"title": "Equipment",
|
|
243
|
+
"reference": "equipment",
|
|
244
|
+
"defaults": {},
|
|
245
|
+
"items": [
|
|
246
|
+
{
|
|
247
|
+
label: 'In Service Date',
|
|
248
|
+
name: 'inServiceDate',
|
|
249
|
+
viewerFormatter: formatDateValue,
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
label: 'Latest Meter Reading',
|
|
253
|
+
name: 'latestMeterReading',
|
|
254
|
+
viewerFormatter: formatMeterValue,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: 'Avg Daily Meter',
|
|
258
|
+
name: 'avgDailyMeter',
|
|
259
|
+
viewerFormatter: formatMeterValue,
|
|
260
|
+
},
|
|
261
|
+
]
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
flattenedJson.pmScheduleMode = getPmScheduleMode(json?.pmSchedule.pm_schedule_mode_id);
|
|
269
|
+
|
|
270
|
+
return <Panel
|
|
271
|
+
title="PM Calculation Info"
|
|
272
|
+
className="PmCalcViewer-Panel"
|
|
273
|
+
isScrollable={true}
|
|
274
|
+
footer={<Footer className="justify-end">
|
|
275
|
+
<Button
|
|
276
|
+
{...testProps('closeBtn')}
|
|
277
|
+
key="closeBtn"
|
|
278
|
+
onPress={onClose}
|
|
279
|
+
className="text-white"
|
|
280
|
+
text="Close"
|
|
281
|
+
/>
|
|
282
|
+
</Footer>}
|
|
283
|
+
>
|
|
284
|
+
<Viewer
|
|
285
|
+
record={flattenedJson}
|
|
286
|
+
items={items}
|
|
287
|
+
/>
|
|
288
|
+
{/* <VStack className="PmCalcDebugViewer-VStack p-4">
|
|
289
|
+
|
|
290
|
+
<Text>Equipment: {metersPmSchedule.meters__nickname}</Text>
|
|
291
|
+
<Json
|
|
292
|
+
value={metersPmSchedule.meters_pm_schedules__debug_json}
|
|
293
|
+
displaySize="expanded"
|
|
294
|
+
editable={false}
|
|
295
|
+
collapsed={2}
|
|
296
|
+
/>
|
|
297
|
+
</VStack> */}
|
|
298
|
+
</Panel>;
|
|
299
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import DisplayField from '../Form/Field/DisplayField.js';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
PM_STATUSES__OK,
|
|
5
|
+
PM_STATUSES__PM_DUE,
|
|
6
|
+
PM_STATUSES__DELAYED,
|
|
7
|
+
PM_STATUSES__WILL_CALL,
|
|
8
|
+
PM_STATUSES__SCHEDULED,
|
|
9
|
+
PM_STATUSES__OVERDUE,
|
|
10
|
+
PM_STATUSES__COMPLETED,
|
|
11
|
+
} from '../../Constants/PmStatuses.js';
|
|
12
|
+
|
|
13
|
+
export default function PmStatusesViewer(props) {
|
|
14
|
+
let text = '';
|
|
15
|
+
switch(props.id) {
|
|
16
|
+
case PM_STATUSES__OK:
|
|
17
|
+
text = 'OK';
|
|
18
|
+
break;
|
|
19
|
+
case PM_STATUSES__PM_DUE:
|
|
20
|
+
text = 'Due';
|
|
21
|
+
break;
|
|
22
|
+
case PM_STATUSES__DELAYED:
|
|
23
|
+
text = 'Delayed';
|
|
24
|
+
break;
|
|
25
|
+
case PM_STATUSES__WILL_CALL:
|
|
26
|
+
text = 'Will Call';
|
|
27
|
+
break;
|
|
28
|
+
case PM_STATUSES__SCHEDULED:
|
|
29
|
+
text = 'Scheduled';
|
|
30
|
+
break;
|
|
31
|
+
case PM_STATUSES__OVERDUE:
|
|
32
|
+
text = 'Overdue';
|
|
33
|
+
break;
|
|
34
|
+
case PM_STATUSES__COMPLETED:
|
|
35
|
+
text = 'Completed';
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
text = 'Unknown';
|
|
39
|
+
}
|
|
40
|
+
let className = clsx(
|
|
41
|
+
'flex-none',
|
|
42
|
+
);
|
|
43
|
+
if (props.className) {
|
|
44
|
+
className += ' ' + props.className;
|
|
45
|
+
}
|
|
46
|
+
return <DisplayField
|
|
47
|
+
text={text}
|
|
48
|
+
{...props}
|
|
49
|
+
className={className}
|
|
50
|
+
/>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import DisplayField from '../Form/Field/DisplayField.js';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
MOMENT_DATE_FORMAT_5,
|
|
5
|
+
} from '../../Constants/Dates';
|
|
6
|
+
|
|
7
|
+
export default function TimeViewer(props) {
|
|
8
|
+
const {
|
|
9
|
+
moment,
|
|
10
|
+
} = props;
|
|
11
|
+
if (!moment || !moment.isValid()) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
let className = clsx(
|
|
15
|
+
'flex-none',
|
|
16
|
+
);
|
|
17
|
+
if (props.className) {
|
|
18
|
+
className += ' ' + props.className;
|
|
19
|
+
}
|
|
20
|
+
return <DisplayField
|
|
21
|
+
text={moment.format(MOMENT_DATE_FORMAT_5)}
|
|
22
|
+
{...props}
|
|
23
|
+
className={className}
|
|
24
|
+
/>;
|
|
25
|
+
}
|
|
@@ -114,6 +114,7 @@ function Viewer(props) {
|
|
|
114
114
|
}
|
|
115
115
|
let {
|
|
116
116
|
type,
|
|
117
|
+
viewerType,
|
|
117
118
|
title,
|
|
118
119
|
name,
|
|
119
120
|
label,
|
|
@@ -122,6 +123,7 @@ function Viewer(props) {
|
|
|
122
123
|
isHidden = false,
|
|
123
124
|
isHiddenInViewMode = false,
|
|
124
125
|
getDynamicProps,
|
|
126
|
+
viewerFormatter = null,
|
|
125
127
|
...itemPropsToPass
|
|
126
128
|
} = item,
|
|
127
129
|
viewerTypeProps = {};
|
|
@@ -137,7 +139,9 @@ function Viewer(props) {
|
|
|
137
139
|
}
|
|
138
140
|
const propertyDef = name && Repository?.getSchema().getPropertyDefinition(name);
|
|
139
141
|
if (!type) {
|
|
140
|
-
if (
|
|
142
|
+
if (viewerType) {
|
|
143
|
+
type = viewerType;
|
|
144
|
+
} else if (propertyDef?.viewerType?.type) {
|
|
141
145
|
const
|
|
142
146
|
{
|
|
143
147
|
type: t,
|
|
@@ -274,6 +278,9 @@ function Viewer(props) {
|
|
|
274
278
|
value = record.properties[fkDisplayField].displayValue;
|
|
275
279
|
}
|
|
276
280
|
}
|
|
281
|
+
if (viewerFormatter) {
|
|
282
|
+
value = viewerFormatter(value, record, self);
|
|
283
|
+
}
|
|
277
284
|
|
|
278
285
|
let elementClassName = clsx(
|
|
279
286
|
'Viewer-field',
|
package/src/Components/index.js
CHANGED
|
@@ -255,6 +255,8 @@ import Container from './Container/Container.js';
|
|
|
255
255
|
import ContainerColumn from './Container/ContainerColumn.js';
|
|
256
256
|
import DataMgt from './Screens/DataMgt.js';
|
|
257
257
|
import Date from './Form/Field/Date.js';
|
|
258
|
+
import DateViewer from './Viewer/DateViewer.js';
|
|
259
|
+
import DateTimeViewer from './Viewer/DateTimeViewer.js';
|
|
258
260
|
import DateRange from './Filter/DateRange.js';
|
|
259
261
|
import DisplayField from './Form/Field/DisplayField.js';
|
|
260
262
|
import ExpandButton from './Buttons/ExpandButton.js';
|
|
@@ -278,6 +280,8 @@ import NumberRange from './Filter/NumberRange.js';
|
|
|
278
280
|
import Panel from './Panel/Panel.js';
|
|
279
281
|
// import Picker from './Panel/Picker.js';
|
|
280
282
|
import PlusMinusButton from './Buttons/PlusMinusButton.js';
|
|
283
|
+
import PmCalcDebugViewer from './Viewer/PmCalcDebugViewer.js';
|
|
284
|
+
import PmStatusesViewer from './Viewer/PmStatusesViewer.js';
|
|
281
285
|
import RadioGroup from './Form/Field/RadioGroup/RadioGroup.js';
|
|
282
286
|
// import Slider from './Form/Field/Slider.js'; // Currently, Slider is not compatible with the new React architecture. Temporarily remove it from index.js to prevent issues.
|
|
283
287
|
import SquareButton from './Buttons/SquareButton.js';
|
|
@@ -286,6 +290,7 @@ import Tag from './Form/Field/Tag/Tag.js';
|
|
|
286
290
|
import TextArea from './Form/Field/TextArea.js';
|
|
287
291
|
import Text from './Form/Field/Text.js';
|
|
288
292
|
import TextWithLinks from './Viewer/TextWithLinks.js';
|
|
293
|
+
import TimeViewer from './Viewer/TimeViewer.js';
|
|
289
294
|
import TimezonesCombo from './Form/Field/Combo/TimezonesCombo.js';
|
|
290
295
|
import Toggle from './Form/Field/Toggle.js';
|
|
291
296
|
import Toolbar from './Toolbar/Toolbar.js';
|
|
@@ -550,6 +555,8 @@ const components = {
|
|
|
550
555
|
ContainerColumn,
|
|
551
556
|
DataMgt,
|
|
552
557
|
Date,
|
|
558
|
+
DateViewer,
|
|
559
|
+
DateTimeViewer,
|
|
553
560
|
DateRange,
|
|
554
561
|
DisplayField,
|
|
555
562
|
ExpandButton,
|
|
@@ -573,6 +580,8 @@ const components = {
|
|
|
573
580
|
Panel,
|
|
574
581
|
// Picker,
|
|
575
582
|
PlusMinusButton,
|
|
583
|
+
PmCalcDebugViewer,
|
|
584
|
+
PmStatusesViewer,
|
|
576
585
|
RadioGroup,
|
|
577
586
|
// Slider,
|
|
578
587
|
SquareButton,
|
|
@@ -581,6 +590,7 @@ const components = {
|
|
|
581
590
|
Text,
|
|
582
591
|
TextArea,
|
|
583
592
|
TextWithLinks,
|
|
593
|
+
TimeViewer,
|
|
584
594
|
TimezonesCombo,
|
|
585
595
|
Toggle,
|
|
586
596
|
Toolbar,
|
package/src/Constants/Dates.js
CHANGED
|
@@ -18,3 +18,4 @@ export const MOMENT_DATE_FORMAT_2 = 'MMM D YYYY, h:mm:ssa'; // pretty datetime
|
|
|
18
18
|
export const MOMENT_DATE_FORMAT_3 = 'h:mma'; // pretty time
|
|
19
19
|
export const MOMENT_DATE_FORMAT_4 = 'YYYY-MM-DD';
|
|
20
20
|
export const MOMENT_DATE_FORMAT_5 = 'HH:mm:ss';
|
|
21
|
+
export const MOMENT_DATE_FORMAT_6 = 'MMM D, YYYY'; // pretty date
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const METER_TYPES__HOURS = 1;
|
|
2
|
+
export const METER_TYPES__HOURS_UNITS = 'hrs';
|
|
3
|
+
export const METER_TYPES__HOURS_TEXT = 'Time (' + METER_TYPES__HOURS_UNITS + ')';
|
|
2
4
|
export const METER_TYPES__MILES = 2;
|
|
3
|
-
export const
|
|
4
|
-
export const METER_TYPES__MILES_TEXT = 'Distance (
|
|
5
|
+
export const METER_TYPES__MILES_UNITS = 'mi-km';
|
|
6
|
+
export const METER_TYPES__MILES_TEXT = 'Distance (' + METER_TYPES__MILES_UNITS + ')';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const PM_EVENT_TYPES__INITIAL = 1;
|
|
2
|
+
export const PM_EVENT_TYPES__WORK_ORDER = 2;
|
|
3
|
+
export const PM_EVENT_TYPES__ALERT = 3;
|
|
4
|
+
export const PM_EVENT_TYPES__COMPLETE = 4;
|
|
5
|
+
export const PM_EVENT_TYPES__RESET = 5;
|
|
6
|
+
export const PM_EVENT_TYPES__DELAY_BY_DAYS = 6;
|
|
7
|
+
export const PM_EVENT_TYPES__DELAY_BY_METER = 7;
|
|
8
|
+
export const PM_EVENT_TYPES__SCHEDULE_PM = 8;
|
|
9
|
+
export const PM_EVENT_TYPES__WILL_CALL = 9;
|
|
10
|
+
export const PM_EVENT_TYPES__ASSIGN_TECHNICIAN = 10;
|
|
11
|
+
export const PM_EVENT_TYPES__COMMENT = 11;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const PM_STATUSES__OK = 1;
|
|
2
|
+
export const PM_STATUSES__PM_DUE = 2;
|
|
3
|
+
export const PM_STATUSES__DELAYED = 3;
|
|
4
|
+
export const PM_STATUSES__WILL_CALL = 4;
|
|
5
|
+
export const PM_STATUSES__SCHEDULED = 5;
|
|
6
|
+
export const PM_STATUSES__OVERDUE = 6;
|
|
7
|
+
export const PM_STATUSES__COMPLETED = 7;
|