@onehat/ui 0.4.102 → 0.4.103
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/Accordion/Accordion.js +65 -6
- package/src/Components/Container/Container.js +10 -4
- package/src/Components/Form/Form.js +8 -3
- package/src/Components/Grid/Grid.js +230 -154
- package/src/Components/Hoc/withPresetButtons.js +18 -6
- package/src/Components/Icons/ArrowsLeftRight.js +10 -0
- package/src/Components/Icons/Bar.js +10 -0
- package/src/Components/Icons/Box.js +11 -0
- package/src/Components/Icons/BoxOpen.js +11 -0
- package/src/Components/Icons/Bucket.js +10 -0
- package/src/Components/Icons/Bump.js +21 -0
- package/src/Components/Icons/Calculator.js +12 -0
- package/src/Components/Icons/Dots.js +20 -0
- package/src/Components/Icons/Fleets.js +26 -0
- package/src/Components/Icons/Microchip.js +12 -0
- package/src/Components/Icons/Num1.js +10 -0
- package/src/Components/Icons/Num2.js +10 -0
- package/src/Components/Icons/Num3.js +10 -0
- package/src/Components/Icons/Num4.js +10 -0
- package/src/Components/Icons/OilCan.js +11 -0
- package/src/Components/Icons/Operations.js +10 -0
- package/src/Components/Icons/OverduePms.js +10 -0
- package/src/Components/Icons/SackDollar.js +11 -0
- package/src/Components/Icons/ShortBar.js +15 -0
- package/src/Components/Icons/Tower.js +10 -0
- package/src/Components/Icons/UpcomingPms.js +10 -0
- package/src/Components/Layout/ScreenHeader.js +35 -3
- package/src/Components/Layout/SetupButton.js +31 -0
- package/src/Components/Layout/UserIndicator.js +35 -0
- package/src/Components/Pms/Editor/BumpPmsEditor.js +9 -0
- package/src/Components/Pms/Editor/MetersEditor.js +173 -0
- package/src/Components/Pms/Editor/PmEventsEditor.js +291 -0
- package/src/Components/Pms/Grid/UpcomingPmsGrid.js +569 -0
- package/src/Components/Pms/Layout/TreeSpecific/MakeTreeSelection.js +11 -0
- package/src/Components/Pms/Layout/TreeSpecific/TreeSpecific.js +30 -0
- package/src/Components/Pms/Modals/BulkAssignTechnician.js +104 -0
- package/src/Components/Pms/Screens/PmsManager.js +136 -0
- package/src/Components/Pms/Window/BumpPmsEditorWindow.js +25 -0
- package/src/Components/Screens/Manager.js +3 -0
- package/src/Components/Tree/Tree.js +15 -6
- package/src/Components/Viewer/PmCalcDebugViewer.js +164 -146
- package/src/Components/Viewer/TextWithLinks.js +9 -1
- package/src/Components/Viewer/Viewer.js +38 -30
- package/src/Functions/flatten.js +39 -0
- package/src/Functions/verifyCanCrudPmEvents.js +33 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { useState, useEffect, } from 'react';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
import { useIsFocused } from '@react-navigation/native';
|
|
4
|
+
import {
|
|
5
|
+
selectTreeSelection,
|
|
6
|
+
} from '@src/Models/Slices/AppSlice';
|
|
7
|
+
import {
|
|
8
|
+
NODE_TYPES__FLEETS,
|
|
9
|
+
NODE_TYPES__EQUIPMENT,
|
|
10
|
+
} from '@src/Constants/NodeTypes.js';
|
|
11
|
+
import oneHatData from '@onehat/data';
|
|
12
|
+
import testProps from '../../../Functions/testProps.js';
|
|
13
|
+
import verifyCanCrud from '../../../Functions/verifyCanCrudPmEvents.js';
|
|
14
|
+
import ManagerScreen from '../../Screens/Manager.js';
|
|
15
|
+
import Bell from '../../Icons/Bell.js';
|
|
16
|
+
import ClockRegular from '../../Icons/ClockRegular.js';
|
|
17
|
+
import OilCan from '../../Icons/OilCan.js';
|
|
18
|
+
import TabBar from '../../Tab/TabBar.js';
|
|
19
|
+
import TreeSpecific from '../Layout/TreeSpecific/TreeSpecific.js';
|
|
20
|
+
import UpcomingPmsGrid from '../Grid/UpcomingPmsGrid.js';
|
|
21
|
+
import PmEventsFilteredGridEditor from '@src/Components/Grid/PmEventsFilteredGridEditor.js';
|
|
22
|
+
import PmEventsFilteredSideGridEditor from '@src/Components/Grid/PmEventsFilteredSideGridEditor.js';
|
|
23
|
+
import _ from 'lodash';
|
|
24
|
+
|
|
25
|
+
const EmptyWrapper = ({ children }) => {
|
|
26
|
+
return <>{children}</>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default function PmsManager(props) {
|
|
30
|
+
const {
|
|
31
|
+
Wrapper = EmptyWrapper,
|
|
32
|
+
} = props,
|
|
33
|
+
treeSelection = useSelector(selectTreeSelection),
|
|
34
|
+
isActive = useIsFocused(),
|
|
35
|
+
[defaultMeterId, setDefaultMeterId] = useState(null),
|
|
36
|
+
[Equipment] = useState(() => oneHatData.getRepository('Equipment', true)),
|
|
37
|
+
treeNode = treeSelection?.[0],
|
|
38
|
+
isFleet = treeNode?.nodeType === NODE_TYPES__FLEETS,
|
|
39
|
+
isEquipment = treeNode?.nodeType === NODE_TYPES__EQUIPMENT;
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (isEquipment) {
|
|
43
|
+
(async () => {
|
|
44
|
+
// Fetch the primary_meter_id for this Equipment
|
|
45
|
+
const idToUse = treeNode?.actualId || treeNode?.id;
|
|
46
|
+
let entity = Equipment.getById(idToUse);
|
|
47
|
+
if (!entity) {
|
|
48
|
+
entity = await Equipment.loadOneAdditionalEntity(idToUse);
|
|
49
|
+
}
|
|
50
|
+
let defaultMeterId = null;
|
|
51
|
+
if (entity && !entity.equipment__has_multiple_meters) {
|
|
52
|
+
defaultMeterId = entity.equipment__primary_meter_id;
|
|
53
|
+
}
|
|
54
|
+
setDefaultMeterId(defaultMeterId);
|
|
55
|
+
})();
|
|
56
|
+
} else {
|
|
57
|
+
setDefaultMeterId(null);
|
|
58
|
+
}
|
|
59
|
+
}, [treeNode]);
|
|
60
|
+
|
|
61
|
+
if (!isActive) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let fleetId = null,
|
|
66
|
+
selectorId = null,
|
|
67
|
+
selectorSelectedField = undefined; // so the default prop assignment will work in Grid (won't work with null)
|
|
68
|
+
if (isFleet) {
|
|
69
|
+
selectorId = 'pm_events__fleet_id';
|
|
70
|
+
selectorSelectedField = 'actualId';
|
|
71
|
+
fleetId = treeNode.actualId;
|
|
72
|
+
} else if (isEquipment) {
|
|
73
|
+
selectorId = 'pm_events__equipment_id';
|
|
74
|
+
selectorSelectedField = 'actualId';
|
|
75
|
+
fleetId = treeNode.equipment__fleet_id;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const
|
|
79
|
+
gridProps = {
|
|
80
|
+
reference: 'PmEventsGridEditor',
|
|
81
|
+
canRecordBeEdited: verifyCanCrud,
|
|
82
|
+
canRecordBeDeleted: verifyCanCrud,
|
|
83
|
+
canRecordBeDuplicated: verifyCanCrud,
|
|
84
|
+
selectorId,
|
|
85
|
+
selectorSelected: treeSelection[0],
|
|
86
|
+
selectorSelectedField,
|
|
87
|
+
defaultValues: defaultMeterId ? {
|
|
88
|
+
pm_events__meter_id: defaultMeterId,
|
|
89
|
+
} : undefined,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return <TabBar
|
|
93
|
+
{...testProps('PmsManager')}
|
|
94
|
+
reference="PmsManager"
|
|
95
|
+
tabs={[
|
|
96
|
+
{
|
|
97
|
+
title: 'Upcoming PMs',
|
|
98
|
+
icon: Bell,
|
|
99
|
+
...testProps('UpcomingPmsGrid'),
|
|
100
|
+
content: <Wrapper>
|
|
101
|
+
<TreeSpecific>
|
|
102
|
+
<UpcomingPmsGrid
|
|
103
|
+
reference="UpcomingPmsGrid"
|
|
104
|
+
nodeType={treeNode?.nodeType}
|
|
105
|
+
nodeId={treeNode?.[selectorSelectedField]}
|
|
106
|
+
/>
|
|
107
|
+
</TreeSpecific>
|
|
108
|
+
</Wrapper>,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
title: 'PM Events',
|
|
112
|
+
icon: ClockRegular,
|
|
113
|
+
...testProps('PmsManager'),
|
|
114
|
+
content: <ManagerScreen
|
|
115
|
+
title="PmEvents"
|
|
116
|
+
icon={OilCan}
|
|
117
|
+
reference="PmEventsManager"
|
|
118
|
+
fullModeComponent={<Wrapper>
|
|
119
|
+
<TreeSpecific>
|
|
120
|
+
<PmEventsFilteredGridEditor
|
|
121
|
+
{...gridProps}
|
|
122
|
+
/>
|
|
123
|
+
</TreeSpecific>
|
|
124
|
+
</Wrapper>}
|
|
125
|
+
sideModeComponent={<Wrapper>
|
|
126
|
+
<TreeSpecific>
|
|
127
|
+
<PmEventsFilteredSideGridEditor
|
|
128
|
+
{...gridProps}
|
|
129
|
+
/>
|
|
130
|
+
</TreeSpecific>
|
|
131
|
+
</Wrapper>}
|
|
132
|
+
/>,
|
|
133
|
+
},
|
|
134
|
+
]}
|
|
135
|
+
/>;
|
|
136
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import UiGlobals from '../../../UiGlobals.js';
|
|
2
|
+
import Panel from '../../Panel/Panel.js';
|
|
3
|
+
import useAdjustedWindowSize from '../../../Hooks/useAdjustedWindowSize.js';
|
|
4
|
+
import BumpPmsEditor from '../Editor/BumpPmsEditor.js';
|
|
5
|
+
|
|
6
|
+
export default function BumpPmsEditorWindow(props) {
|
|
7
|
+
const
|
|
8
|
+
styles = UiGlobals.styles,
|
|
9
|
+
[width, height] = useAdjustedWindowSize(500, 600);
|
|
10
|
+
|
|
11
|
+
return <Panel
|
|
12
|
+
reference="BumpPmsEditorWindow"
|
|
13
|
+
isCollapsible={false}
|
|
14
|
+
model="PmEvents"
|
|
15
|
+
title={'Add "Bump" PM Event'}
|
|
16
|
+
bg="#fff"
|
|
17
|
+
{...props}
|
|
18
|
+
w={width}
|
|
19
|
+
h={height}
|
|
20
|
+
flex={null}
|
|
21
|
+
>
|
|
22
|
+
<BumpPmsEditor {...props} />
|
|
23
|
+
</Panel>;
|
|
24
|
+
}
|
|
25
|
+
|
|
@@ -18,6 +18,8 @@ function ManagerScreen(props) {
|
|
|
18
18
|
const {
|
|
19
19
|
title,
|
|
20
20
|
icon,
|
|
21
|
+
info,
|
|
22
|
+
_info,
|
|
21
23
|
sideModeComponent,
|
|
22
24
|
fullModeComponent,
|
|
23
25
|
onChangeMode,
|
|
@@ -88,6 +90,7 @@ function ManagerScreen(props) {
|
|
|
88
90
|
<ScreenHeader
|
|
89
91
|
title={title}
|
|
90
92
|
icon={icon}
|
|
93
|
+
info={info}
|
|
91
94
|
useModeIcons={true}
|
|
92
95
|
actualMode={actualMode}
|
|
93
96
|
allowSideBySide={allowSideBySide}
|
|
@@ -401,7 +401,7 @@ function TreeComponent(props) {
|
|
|
401
401
|
} else {
|
|
402
402
|
// closing
|
|
403
403
|
if (datumContainsSelection(datum)) {
|
|
404
|
-
if (forceSelectionOnCollapse) {
|
|
404
|
+
if (forceSelectionOnCollapse || !allowToggleSelection) {
|
|
405
405
|
// Select the node being collapsed instead of deselecting all
|
|
406
406
|
setSelection([datum.item]);
|
|
407
407
|
} else {
|
|
@@ -1068,13 +1068,15 @@ function TreeComponent(props) {
|
|
|
1068
1068
|
return items;
|
|
1069
1069
|
},
|
|
1070
1070
|
getFooterToolbarItems = () => {
|
|
1071
|
-
// Process additionalToolbarButtons to evaluate
|
|
1071
|
+
// Process additionalToolbarButtons to evaluate functions
|
|
1072
1072
|
const processedButtons = _.map(additionalToolbarButtons, (config) => {
|
|
1073
1073
|
const processedConfig = { ...config };
|
|
1074
|
-
// If the button has an getIsButtonDisabled function, evaluate it with current selection
|
|
1075
1074
|
if (_.isFunction(config.getIsButtonDisabled)) {
|
|
1076
1075
|
processedConfig.isDisabled = config.getIsButtonDisabled(selection);
|
|
1077
1076
|
}
|
|
1077
|
+
if (_.isFunction(config.getText)) {
|
|
1078
|
+
processedConfig.text = config.getText(selection);
|
|
1079
|
+
}
|
|
1078
1080
|
return processedConfig;
|
|
1079
1081
|
});
|
|
1080
1082
|
return _.map(processedButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
@@ -1483,6 +1485,16 @@ function TreeComponent(props) {
|
|
|
1483
1485
|
}
|
|
1484
1486
|
}, [selectorId, selectorSelected]);
|
|
1485
1487
|
|
|
1488
|
+
const
|
|
1489
|
+
hasDynamicFooterToolbarItems = useMemo(() => _.some(additionalToolbarButtons, (config) => {
|
|
1490
|
+
return _.isFunction(config.getIsButtonDisabled) || _.isFunction(config.getText);
|
|
1491
|
+
}), [additionalToolbarButtons]),
|
|
1492
|
+
memoizedFooterToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [Repository?.hash, additionalToolbarButtons, getTreeNodeData()]),
|
|
1493
|
+
headerToolbarItemComponents = showHeaderToolbar ? useMemo(() => getHeaderToolbarItems(), [Repository?.hash, treeSearchValue, getTreeNodeData()]) : null,
|
|
1494
|
+
footerToolbarItemComponents = hasDynamicFooterToolbarItems
|
|
1495
|
+
? getFooterToolbarItems()
|
|
1496
|
+
: memoizedFooterToolbarItemComponents;
|
|
1497
|
+
|
|
1486
1498
|
if (canUser && !canUser('view')) {
|
|
1487
1499
|
return <CenterBox>
|
|
1488
1500
|
<Unauthorized />
|
|
@@ -1510,9 +1522,6 @@ function TreeComponent(props) {
|
|
|
1510
1522
|
self.forceUpdate = forceUpdate;
|
|
1511
1523
|
}
|
|
1512
1524
|
|
|
1513
|
-
const
|
|
1514
|
-
headerToolbarItemComponents = showHeaderToolbar ? useMemo(() => getHeaderToolbarItems(), [Repository?.hash, treeSearchValue, getTreeNodeData()]) : null,
|
|
1515
|
-
footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [Repository?.hash, additionalToolbarButtons, getTreeNodeData()]);
|
|
1516
1525
|
|
|
1517
1526
|
if (!isReady) {
|
|
1518
1527
|
return <CenterBox>
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
METER_TYPES__MILES_UNITS,
|
|
30
30
|
METER_TYPES__MILES_TEXT,
|
|
31
31
|
} from '../../Constants/MeterTypes.js';
|
|
32
|
+
import flatten from '../../Functions/flatten.js';
|
|
32
33
|
import Button from '../Buttons/Button.js';
|
|
33
34
|
import Json from '../Form/Field/Json.js';
|
|
34
35
|
import Panel from '../Panel/Panel.js';
|
|
@@ -38,6 +39,7 @@ import testProps from '../../Functions/testProps.js';
|
|
|
38
39
|
import moment from 'moment';
|
|
39
40
|
import _ from 'lodash';
|
|
40
41
|
|
|
42
|
+
|
|
41
43
|
export default function PmCalcDebugViewer(props) {
|
|
42
44
|
|
|
43
45
|
const {
|
|
@@ -45,34 +47,7 @@ export default function PmCalcDebugViewer(props) {
|
|
|
45
47
|
onClose,
|
|
46
48
|
} = props,
|
|
47
49
|
json = metersPmSchedule?.properties.meters_pm_schedules__debug_json.json,
|
|
48
|
-
|
|
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
|
-
},
|
|
50
|
+
record = flatten(json),
|
|
76
51
|
ucfirst = (string) => {
|
|
77
52
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
78
53
|
},
|
|
@@ -90,37 +65,85 @@ export default function PmCalcDebugViewer(props) {
|
|
|
90
65
|
return 'Unknown Mode';
|
|
91
66
|
}
|
|
92
67
|
},
|
|
93
|
-
|
|
68
|
+
formatBooleanValue = (value, record, self) => {
|
|
69
|
+
if (value === null || value === undefined) {
|
|
70
|
+
return 'N/A';
|
|
71
|
+
}
|
|
72
|
+
return value ? 'Yes' : 'No';
|
|
73
|
+
},
|
|
94
74
|
formatDaysValue = (value, record, self) => {
|
|
95
75
|
if (value === null || value === undefined) {
|
|
96
|
-
return
|
|
76
|
+
return 'N/A';
|
|
97
77
|
}
|
|
98
78
|
return parseInt(_.round(value), 10);
|
|
99
79
|
},
|
|
100
80
|
formatMeterValue = (value, record, self) => {
|
|
101
81
|
if (value === null || value === undefined) {
|
|
102
|
-
return
|
|
82
|
+
return 'N/A';
|
|
103
83
|
}
|
|
104
84
|
let ret;
|
|
105
85
|
const meterType = parseInt(record?.meter_type, 10);
|
|
86
|
+
let units = meterType === METER_TYPES__HOURS ? METER_TYPES__HOURS_UNITS : meterType === METER_TYPES__MILES ? METER_TYPES__MILES_UNITS : '';
|
|
87
|
+
if (value === 1) {
|
|
88
|
+
units = units.replace(/s$/, ''); // remove plural 's' if value is 1
|
|
89
|
+
}
|
|
106
90
|
switch(meterType) {
|
|
107
91
|
case METER_TYPES__HOURS:
|
|
108
|
-
ret = `${value} ${
|
|
92
|
+
ret = `${value} ${units}`;
|
|
109
93
|
break;
|
|
110
94
|
case METER_TYPES__MILES:
|
|
111
|
-
ret = `${value} ${
|
|
95
|
+
ret = `${value} ${units}`;
|
|
112
96
|
break;
|
|
113
97
|
}
|
|
114
98
|
return ret;
|
|
115
99
|
},
|
|
116
100
|
formatDateValue = (value, record, self) => {
|
|
117
101
|
if (value === null || value === undefined) {
|
|
118
|
-
return
|
|
102
|
+
return 'N/A';
|
|
119
103
|
}
|
|
120
104
|
|
|
121
105
|
// convert from datetime to pretty-printed date
|
|
122
106
|
return moment(value).format(MOMENT_DATE_FORMAT_6);
|
|
123
107
|
},
|
|
108
|
+
formatStatusValue = (value, record, self) => {
|
|
109
|
+
let ret = value,
|
|
110
|
+
classNames = null;
|
|
111
|
+
|
|
112
|
+
switch(record['pm_status_id']) {
|
|
113
|
+
case PM_STATUSES__OVERDUE:
|
|
114
|
+
classNames = [
|
|
115
|
+
'text-red-500',
|
|
116
|
+
'font-bold',
|
|
117
|
+
];
|
|
118
|
+
break;
|
|
119
|
+
case PM_STATUSES__PM_DUE:
|
|
120
|
+
classNames = [
|
|
121
|
+
'text-[#c89903]',
|
|
122
|
+
];
|
|
123
|
+
break;
|
|
124
|
+
case PM_STATUSES__DELAYED:
|
|
125
|
+
classNames = [
|
|
126
|
+
'text-green-500',
|
|
127
|
+
];
|
|
128
|
+
break;
|
|
129
|
+
case PM_STATUSES__WILL_CALL:
|
|
130
|
+
classNames = [
|
|
131
|
+
'text-blue-500',
|
|
132
|
+
];
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
if (classNames) {
|
|
136
|
+
// special formatting
|
|
137
|
+
ret = <Text
|
|
138
|
+
className={clsx(
|
|
139
|
+
classNames,
|
|
140
|
+
'px-3',
|
|
141
|
+
'py-2',
|
|
142
|
+
)}
|
|
143
|
+
>{value}</Text>;
|
|
144
|
+
}
|
|
145
|
+
return ret;
|
|
146
|
+
},
|
|
124
147
|
items = [
|
|
125
148
|
{
|
|
126
149
|
"type": "Column",
|
|
@@ -131,133 +154,138 @@ export default function PmCalcDebugViewer(props) {
|
|
|
131
154
|
"items": [
|
|
132
155
|
{
|
|
133
156
|
"type": "FieldSet",
|
|
134
|
-
"title": "
|
|
135
|
-
"reference": "
|
|
157
|
+
"title": "Overview",
|
|
158
|
+
"reference": "overview",
|
|
136
159
|
"defaults": {},
|
|
137
160
|
"items": [
|
|
138
|
-
{
|
|
161
|
+
record['pmSchedule.name'] && {
|
|
139
162
|
label: 'PM Schedule',
|
|
140
163
|
name: 'pmSchedule.name',
|
|
141
164
|
},
|
|
142
|
-
{
|
|
165
|
+
record['pm_status_name'] && {
|
|
143
166
|
label: 'Status',
|
|
144
167
|
name: 'pm_status_name',
|
|
168
|
+
viewerFormatter: formatStatusValue,
|
|
145
169
|
},
|
|
146
|
-
{
|
|
170
|
+
record['nextPmDate'] && {
|
|
147
171
|
label: 'Next PM Date',
|
|
148
172
|
name: 'nextPmDate',
|
|
149
173
|
viewerFormatter: formatDateValue,
|
|
150
174
|
},
|
|
151
|
-
|
|
152
|
-
label: '
|
|
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',
|
|
175
|
+
record['pm_status_id'] && record['pm_status_id'] === PM_STATUSES__OVERDUE && {
|
|
176
|
+
label: 'Grace Period Ends',
|
|
171
177
|
name: 'maxGracePeriodDateTime',
|
|
172
178
|
viewerFormatter: formatDateValue,
|
|
173
179
|
},
|
|
174
|
-
{
|
|
175
|
-
label: '
|
|
176
|
-
name: '
|
|
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,
|
|
180
|
+
record['pm_status_id'] && record['pm_status_id'] === PM_STATUSES__OVERDUE && {
|
|
181
|
+
label: 'Overdue by # Cycles',
|
|
182
|
+
name: 'overduePms',
|
|
208
183
|
},
|
|
209
|
-
|
|
210
|
-
// label: 'Is Delayed',
|
|
211
|
-
// name: 'isDelayed',
|
|
212
|
-
// },
|
|
213
|
-
// json?.isOverride && {
|
|
214
|
-
// label: 'Is Override',
|
|
215
|
-
// name: 'isOverride',
|
|
216
|
-
// },
|
|
217
|
-
]
|
|
184
|
+
],
|
|
218
185
|
},
|
|
219
|
-
{
|
|
186
|
+
record['calculationMode'] && {
|
|
220
187
|
"type": "FieldSet",
|
|
221
|
-
"title": "
|
|
222
|
-
"reference": "
|
|
188
|
+
"title": "How it was calculated",
|
|
189
|
+
"reference": "calcDetails",
|
|
223
190
|
"defaults": {},
|
|
224
191
|
"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
192
|
{
|
|
235
|
-
label: 'Mode',
|
|
236
|
-
name: '
|
|
193
|
+
label: 'Calculation Mode',
|
|
194
|
+
name: 'calculationMode',
|
|
237
195
|
},
|
|
238
|
-
]
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
"type": "FieldSet",
|
|
242
|
-
"title": "Equipment",
|
|
243
|
-
"reference": "equipment",
|
|
244
|
-
"defaults": {},
|
|
245
|
-
"items": [
|
|
246
196
|
{
|
|
247
197
|
label: 'In Service Date',
|
|
248
198
|
name: 'inServiceDate',
|
|
249
199
|
viewerFormatter: formatDateValue,
|
|
250
200
|
},
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
201
|
+
...(record['isOverride'] ? [
|
|
202
|
+
// these items are only for overrides
|
|
203
|
+
{
|
|
204
|
+
label: 'Is Override',
|
|
205
|
+
name: 'isOverride',
|
|
206
|
+
viewerFormatter: formatBooleanValue,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
label: 'Override Event Date',
|
|
210
|
+
name: 'overrideEventDate',
|
|
211
|
+
viewerFormatter: formatDateValue,
|
|
212
|
+
},
|
|
213
|
+
] : []),
|
|
214
|
+
...(!record['isOverride'] ? [
|
|
215
|
+
// these items are only for non-overrides
|
|
216
|
+
{
|
|
217
|
+
label: 'Last Reset Date',
|
|
218
|
+
name: 'resetDate',
|
|
219
|
+
viewerFormatter: formatDateValue,
|
|
220
|
+
},
|
|
221
|
+
record['workOrder.title'] && { // Gingerich
|
|
222
|
+
label: 'Work Order',
|
|
223
|
+
name: 'workOrder.title',
|
|
224
|
+
},
|
|
225
|
+
record['workOrder.service_order'] && { // MH
|
|
226
|
+
label: 'Service Order',
|
|
227
|
+
name: 'workOrder.service_order',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
label: 'Interval Days',
|
|
231
|
+
name: 'intervalDays',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
label: 'Days Left Until Next PM',
|
|
235
|
+
name: 'daysLeft',
|
|
236
|
+
viewerFormatter: formatDaysValue,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
label: 'Interval Meter',
|
|
240
|
+
name: 'intervalMeter',
|
|
241
|
+
viewerFormatter: formatMeterValue,
|
|
242
|
+
},
|
|
243
|
+
typeof record['latestMeterReading.value'] !== 'undefined' && { // typeof so it allows 0
|
|
244
|
+
label: 'Latest Meter Reading',
|
|
245
|
+
name: 'latestMeterReading',
|
|
246
|
+
viewerFormatter: (value, record) => {
|
|
247
|
+
const
|
|
248
|
+
meterValue = formatMeterValue(record['latestMeterReading.value'], record),
|
|
249
|
+
meterDate = formatDateValue(record['latestMeterReading.date'], record);
|
|
250
|
+
return `${meterValue} on ${meterDate}`;
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
label: 'Meter Accrued Since Latest PM',
|
|
255
|
+
name: 'meterAccruedSinceLatestPm',
|
|
256
|
+
viewerFormatter: formatMeterValue,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
label: 'Avg Daily Meter',
|
|
260
|
+
name: 'avgDailyMeter',
|
|
261
|
+
viewerFormatter: formatMeterValue,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
label: 'Meter Until Next PM',
|
|
265
|
+
name: 'meterRemainingUntilNextPm',
|
|
266
|
+
viewerFormatter: formatMeterValue,
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
label: 'Controlling Method',
|
|
270
|
+
name: 'controllingMethod',
|
|
271
|
+
tooltip: 'Indicates whether the calculation was based on days or usage (meter). ' +
|
|
272
|
+
'If both methods are applicable, the one resulting in the earlier PM date is chosen.',
|
|
273
|
+
},
|
|
274
|
+
] : []),
|
|
275
|
+
|
|
276
|
+
...(record['isOverride'] ? [
|
|
277
|
+
// these items are only for delays
|
|
278
|
+
{
|
|
279
|
+
label: 'Is Delayed',
|
|
280
|
+
name: 'isDelayed',
|
|
281
|
+
viewerFormatter: formatBooleanValue,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
label: 'Delay Threshold Date',
|
|
285
|
+
name: 'delayThresholdDate',
|
|
286
|
+
viewerFormatter: formatDateValue,
|
|
287
|
+
},
|
|
288
|
+
] : []),
|
|
261
289
|
]
|
|
262
290
|
},
|
|
263
291
|
|
|
@@ -265,7 +293,7 @@ export default function PmCalcDebugViewer(props) {
|
|
|
265
293
|
}
|
|
266
294
|
];
|
|
267
295
|
|
|
268
|
-
|
|
296
|
+
record.pmScheduleMode = getPmScheduleMode(json?.pmSchedule.pm_schedule_mode_id);
|
|
269
297
|
|
|
270
298
|
return <Panel
|
|
271
299
|
title="PM Calculation Info"
|
|
@@ -282,18 +310,8 @@ export default function PmCalcDebugViewer(props) {
|
|
|
282
310
|
</Footer>}
|
|
283
311
|
>
|
|
284
312
|
<Viewer
|
|
285
|
-
record={
|
|
313
|
+
record={record}
|
|
286
314
|
items={items}
|
|
287
315
|
/>
|
|
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
316
|
</Panel>;
|
|
299
317
|
}
|
|
@@ -49,13 +49,21 @@ function TextWithLinksElement(props) {
|
|
|
49
49
|
modifiedText = modifiedText.replace(link, key);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
let
|
|
53
53
|
textClassName = clsx(
|
|
54
54
|
'TextWithLinks-Text',
|
|
55
55
|
'text-base',
|
|
56
56
|
'overflow-hidden',
|
|
57
|
+
styles.FORM_TEXT_CLASSNAME,
|
|
57
58
|
),
|
|
58
59
|
textSegments = modifiedText.split(/(link_\d+)/);
|
|
60
|
+
if (props.className) {
|
|
61
|
+
const
|
|
62
|
+
classes = props.className.split(' '),
|
|
63
|
+
newClasses = _.reject(classes, (c) => c === 'overflow-auto'),
|
|
64
|
+
className = newClasses.join(' ');
|
|
65
|
+
textClassName += ' ' + className;
|
|
66
|
+
}
|
|
59
67
|
if (textSegments.length === 1) {
|
|
60
68
|
return <Text className={textClassName}>{modifiedText}</Text>;
|
|
61
69
|
}
|