@onehat/ui 0.4.77 → 0.4.79
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/Editor/AttachmentDirectoriesEditor.js +51 -0
- package/src/Components/Editor/AttachmentsEditor.js +81 -0
- package/src/Components/Form/Field/Combo/AttachmentDirectoriesCombo.js +20 -0
- package/src/Components/Form/Field/Combo/AttachmentDirectoriesComboEditor.js +22 -0
- package/src/Components/Form/Field/Combo/AttachmentsCombo.js +20 -0
- package/src/Components/Form/Field/Combo/AttachmentsComboEditor.js +22 -0
- package/src/Components/Form/Field/Tag/AttachmentDirectoriesTag.js +22 -0
- package/src/Components/Form/Field/Tag/AttachmentDirectoriesTagEditor.js +22 -0
- package/src/Components/Form/Field/Tag/AttachmentsTag.js +22 -0
- package/src/Components/Form/Field/Tag/AttachmentsTagEditor.js +22 -0
- package/src/Components/Form/Form.js +17 -17
- package/src/Components/Grid/AttachmentDirectoriesFilteredGrid.js +17 -0
- package/src/Components/Grid/AttachmentDirectoriesFilteredGridEditor.js +17 -0
- package/src/Components/Grid/AttachmentDirectoriesFilteredInlineGridEditor.js +17 -0
- package/src/Components/Grid/AttachmentDirectoriesFilteredSideGridEditor.js +17 -0
- package/src/Components/Grid/AttachmentDirectoriesGrid.js +20 -0
- package/src/Components/Grid/AttachmentDirectoriesGridEditor.js +27 -0
- package/src/Components/Grid/AttachmentDirectoriesInlineGridEditor.js +25 -0
- package/src/Components/Grid/AttachmentDirectoriesSideGridEditor.js +24 -0
- package/src/Components/Grid/AttachmentsFilteredGrid.js +17 -0
- package/src/Components/Grid/AttachmentsFilteredGridEditor.js +17 -0
- package/src/Components/Grid/AttachmentsFilteredInlineGridEditor.js +17 -0
- package/src/Components/Grid/AttachmentsFilteredSideGridEditor.js +17 -0
- package/src/Components/Grid/AttachmentsGrid.js +20 -0
- package/src/Components/Grid/AttachmentsGridEditor.js +27 -0
- package/src/Components/Grid/AttachmentsInlineGridEditor.js +25 -0
- package/src/Components/Grid/AttachmentsSideGridEditor.js +24 -0
- package/src/Components/Grid/Columns/AttachmentDirectoriesGridColumns.js +32 -0
- package/src/Components/Grid/Columns/AttachmentsGridColumns.js +133 -0
- package/src/Components/Grid/Grid.js +194 -21
- package/src/Components/Grid/GridHeaderRow.js +10 -17
- package/src/Components/Grid/GridRow.js +49 -22
- package/src/Components/Grid/RowHandle.js +8 -6
- package/src/Components/Hoc/withEditor.js +18 -1
- package/src/Components/Hoc/withModal.js +4 -0
- package/src/Components/Hoc/withPdfButtons.js +1 -1
- package/src/Components/Hoc/withSelection.js +26 -4
- package/src/Components/Layout/AsyncOperation.js +299 -195
- package/src/Components/Messages/GlobalModals.js +1 -2
- package/src/Components/Panel/Panel.js +14 -2
- package/src/Components/Panel/TabPanel.js +1 -1
- package/src/Components/Panel/TreePanel.js +1 -1
- package/src/Components/Report/Report.js +106 -17
- package/src/Components/Toolbar/PaginationToolbar.js +4 -3
- package/src/Components/Toolbar/Toolbar.js +6 -3
- package/src/Components/Tree/Tree.js +219 -148
- package/src/Components/Tree/TreeNode.js +20 -13
- package/src/Components/Window/AttachmentDirectoriesEditorWindow.js +34 -0
- package/src/Components/Window/AttachmentsEditorWindow.js +34 -0
- package/src/Components/index.js +92 -1
- package/src/Constants/Attachments.js +2 -0
- package/src/Constants/Dates.js +2 -2
- package/src/Constants/Progress.js +5 -1
- package/src/Models/Schemas/AttachmentDirectories.js +66 -0
- package/src/Models/Schemas/Attachments.js +88 -0
- package/src/Models/Slices/SystemSlice.js +220 -0
- package/src/PlatformImports/Web/Attachments.js +855 -161
- package/src/Styles/Global.css +7 -2
|
@@ -6,14 +6,24 @@ import {
|
|
|
6
6
|
VStack,
|
|
7
7
|
} from '@project-components/Gluestack';
|
|
8
8
|
import clsx from 'clsx';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
9
|
+
import * as Progress from 'react-native-progress';
|
|
10
|
+
import useForceUpdate from '@onehat/ui/src/Hooks/useForceUpdate';
|
|
11
|
+
import {
|
|
12
|
+
PROGRESS__NONE_FOUND,
|
|
13
|
+
PROGRESS__IN_PROCESS,
|
|
14
|
+
PROGRESS__COMPLETED,
|
|
15
|
+
PROGRESS__FAILED,
|
|
16
|
+
PROGRESS__STUCK,
|
|
17
|
+
} from '../../Constants/Progress.js';
|
|
18
|
+
import {
|
|
19
|
+
MOMENT_DATE_FORMAT_2,
|
|
20
|
+
} from '../../Constants/Dates.js';
|
|
12
21
|
import isJson from '../../Functions/isJson.js';
|
|
13
22
|
import Form from '../Form/Form.js';
|
|
14
23
|
import Button from '../Buttons/Button.js';
|
|
15
24
|
import withComponent from '../Hoc/withComponent.js';
|
|
16
25
|
import withAlert from '../Hoc/withAlert.js';
|
|
26
|
+
import Loading from '../Messages/Loading.js';
|
|
17
27
|
import ChevronLeft from '../Icons/ChevronLeft.js';
|
|
18
28
|
import ChevronRight from '../Icons/ChevronRight.js';
|
|
19
29
|
import Play from '../Icons/Play.js';
|
|
@@ -22,31 +32,35 @@ import Stop from '../Icons/Stop.js';
|
|
|
22
32
|
import TabBar from '../Tab/TabBar.js';
|
|
23
33
|
import Panel from '../Panel/Panel.js';
|
|
24
34
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
35
|
+
import moment from 'moment';
|
|
25
36
|
import _ from 'lodash';
|
|
26
37
|
|
|
27
38
|
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
INIT = 'INIT', // no footer shown; used when component initially loads, to see if operation is already in progress
|
|
40
|
+
START = 'START', // shows the start form
|
|
41
|
+
PROCESSING = 'PROCESSING', // shows the loading indicator while starting the operation
|
|
42
|
+
RESULTS = 'RESULTS'; // shows the results of the operation, or any in-progress updates
|
|
31
43
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
44
|
+
// If getProgressUpdates is false, the component will show the start form initially.
|
|
45
|
+
// If getProgressUpdates is true, the component will initially query the server to see if
|
|
46
|
+
// an operation is already in progress.
|
|
47
|
+
// If so, it will automatically poll the server for progress updates
|
|
48
|
+
// If not, it will show the start form.
|
|
34
49
|
|
|
35
50
|
function AsyncOperation(props) {
|
|
36
51
|
|
|
37
|
-
if (!props.Repository || !props.
|
|
38
|
-
throw Error('AsyncOperation: Repository and
|
|
52
|
+
if (!props.Repository || !props.process) {
|
|
53
|
+
throw Error('AsyncOperation: Repository and process are required!');
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
const {
|
|
42
|
-
|
|
57
|
+
process,
|
|
43
58
|
Repository,
|
|
44
59
|
formItems = [],
|
|
45
60
|
formStartingValues = {},
|
|
46
61
|
_form = {},
|
|
47
62
|
getProgressUpdates = false,
|
|
48
|
-
parseProgress, // optional fn, accepts 'response' as arg and returns
|
|
49
|
-
progressStuckThreshold = null, // e.g. 3, if left blank, doesn't check for stuck state
|
|
63
|
+
parseProgress, // optional fn, accepts 'response' as arg and returns an object like this: { status, errors, started, lastUpdated, timeElapsed, count, current, total, percentage }
|
|
50
64
|
updateInterval = 10000, // ms
|
|
51
65
|
|
|
52
66
|
// withComponent
|
|
@@ -55,7 +69,7 @@ function AsyncOperation(props) {
|
|
|
55
69
|
// withAlert
|
|
56
70
|
alert,
|
|
57
71
|
} = props,
|
|
58
|
-
|
|
72
|
+
forceUpdate = useForceUpdate(),
|
|
59
73
|
isValid = useRef(true),
|
|
60
74
|
setIsValid = (valid) => {
|
|
61
75
|
isValid.current = valid;
|
|
@@ -63,33 +77,105 @@ function AsyncOperation(props) {
|
|
|
63
77
|
getIsValid = () => {
|
|
64
78
|
return isValid.current;
|
|
65
79
|
},
|
|
66
|
-
mode = useRef(
|
|
80
|
+
mode = useRef(INIT),
|
|
67
81
|
setMode = (newMode) => {
|
|
68
82
|
mode.current = newMode;
|
|
69
83
|
},
|
|
70
84
|
getMode = () => {
|
|
71
85
|
return mode.current;
|
|
72
86
|
},
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
isInProcess = getMode() === PROCESSING,
|
|
88
|
+
currentTabIx = (getMode() === PROCESSING ? 1 : (getMode() === RESULTS ? 2 : 0)),
|
|
89
|
+
intervalRef = useRef(null),
|
|
90
|
+
getInterval = () => {
|
|
91
|
+
return intervalRef.current;
|
|
92
|
+
},
|
|
93
|
+
setIntervalRef = (interval) => { // 'setInterval' is a reserved name
|
|
94
|
+
intervalRef.current = interval;
|
|
95
|
+
},
|
|
96
|
+
formValuesRef = useRef(null),
|
|
97
|
+
getFormValues = () => {
|
|
98
|
+
return formValuesRef.current;
|
|
99
|
+
},
|
|
100
|
+
setFormValues = (values) => {
|
|
101
|
+
formValuesRef.current = values;
|
|
102
|
+
},
|
|
103
|
+
getFooter = () => {
|
|
104
|
+
switch(getMode()) {
|
|
105
|
+
case INIT:
|
|
106
|
+
return null;
|
|
107
|
+
case START:
|
|
108
|
+
return <Toolbar>
|
|
109
|
+
<Button
|
|
110
|
+
text="Start"
|
|
111
|
+
rightIcon={ChevronRight}
|
|
112
|
+
onPress={() => startProcess()}
|
|
113
|
+
isDisabled={!getIsValid()}
|
|
114
|
+
/>
|
|
115
|
+
</Toolbar>;
|
|
116
|
+
case PROCESSING:
|
|
117
|
+
// TODO: Add a cancellation option to the command.
|
|
118
|
+
// would require a backend controller action to support it
|
|
119
|
+
return null;
|
|
120
|
+
// return <Toolbar>
|
|
121
|
+
// <Button
|
|
122
|
+
// text="Please wait"
|
|
123
|
+
// isLoading={true}
|
|
124
|
+
// variant="link"
|
|
125
|
+
// />
|
|
126
|
+
// </Toolbar>;
|
|
127
|
+
case RESULTS:
|
|
128
|
+
return <Toolbar>
|
|
129
|
+
<Button
|
|
130
|
+
text="Reset"
|
|
131
|
+
icon={ChevronLeft}
|
|
132
|
+
onPress={() => resetToInitialState()}
|
|
133
|
+
/>
|
|
134
|
+
</Toolbar>;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
[footer, setFooter] = useState(getFooter()),
|
|
138
|
+
[results, setResults] = useState(null),
|
|
139
|
+
[progress, setProgress] = useState(null),
|
|
140
|
+
[isStuck, setIsStuck] = useState(false),
|
|
141
|
+
[isReady, setIsReady] = useState(false),
|
|
142
|
+
showResults = (results) => {
|
|
143
|
+
setMode(RESULTS);
|
|
144
|
+
setFooter(getFooter());
|
|
145
|
+
setResults(results);
|
|
146
|
+
},
|
|
147
|
+
startProcess = async () => {
|
|
148
|
+
stopGettingProgress();
|
|
76
149
|
setMode(PROCESSING);
|
|
77
150
|
setFooter(getFooter());
|
|
78
|
-
setIsInProgress(true);
|
|
79
151
|
|
|
80
152
|
const
|
|
81
153
|
method = Repository.methods.edit,
|
|
82
|
-
uri = Repository.getModel() + '/'
|
|
83
|
-
formValues = self?.children?.form?.formGetValues() || {}
|
|
154
|
+
uri = Repository.getModel() + '/startProcess',
|
|
155
|
+
formValues = self?.children?.form?.formGetValues() || {};
|
|
156
|
+
formValues.process = process;
|
|
157
|
+
const
|
|
84
158
|
result = await Repository._send(method, uri, formValues);
|
|
85
159
|
|
|
86
160
|
setFormValues(formValues);
|
|
87
161
|
|
|
88
162
|
const response = Repository._processServerResponse(result);
|
|
89
|
-
if (!response
|
|
163
|
+
if (!response?.success) {
|
|
164
|
+
alert(response.message || 'Error starting process on server.');
|
|
90
165
|
resetToInitialState();
|
|
91
166
|
return;
|
|
92
167
|
}
|
|
168
|
+
|
|
169
|
+
if (getProgressUpdates) {
|
|
170
|
+
setProgress(<VStack className="p-4">
|
|
171
|
+
<Text className="text-lg" key="status">
|
|
172
|
+
Process has started. Progress updates will appear here momentarily.
|
|
173
|
+
</Text>
|
|
174
|
+
<Loading />
|
|
175
|
+
</VStack>);
|
|
176
|
+
getProgress();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
93
179
|
|
|
94
180
|
let results = <Text>Success</Text>;
|
|
95
181
|
if (response.message) {
|
|
@@ -106,205 +192,223 @@ function AsyncOperation(props) {
|
|
|
106
192
|
<Text>{message}</Text>;
|
|
107
193
|
}
|
|
108
194
|
showResults(results);
|
|
109
|
-
|
|
110
|
-
getFooter = (which = getMode()) => {
|
|
111
|
-
switch(which) {
|
|
112
|
-
case INITIATE:
|
|
113
|
-
return <Toolbar>
|
|
114
|
-
<Button
|
|
115
|
-
text="Start"
|
|
116
|
-
rightIcon={ChevronRight}
|
|
117
|
-
onPress={() => initiate()}
|
|
118
|
-
isDisabled={!getIsValid()}
|
|
119
|
-
/>
|
|
120
|
-
</Toolbar>;
|
|
121
|
-
case PROCESSING:
|
|
122
|
-
return <Toolbar>
|
|
123
|
-
<Button
|
|
124
|
-
text="Please wait"
|
|
125
|
-
isLoading={true}
|
|
126
|
-
variant="link"
|
|
127
|
-
/>
|
|
128
|
-
</Toolbar>;
|
|
129
|
-
case RESULTS:
|
|
130
|
-
return <Toolbar>
|
|
131
|
-
<Button
|
|
132
|
-
text="Reset"
|
|
133
|
-
icon={ChevronLeft}
|
|
134
|
-
onPress={() => resetToInitialState()}
|
|
135
|
-
/>
|
|
136
|
-
</Toolbar>;
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
operationsInProgress = useSelector((state) => state.app.operationsInProgress),
|
|
140
|
-
isInProgress = operationsInProgress.includes(action),
|
|
141
|
-
forceUpdate = useForceUpdate(),
|
|
142
|
-
[footer, setFooter] = useState(getFooter()),
|
|
143
|
-
[results, setResults] = useState(isInProgress ? 'Checking progress...' : null),
|
|
144
|
-
[progress, setProgress] = useState(null),
|
|
145
|
-
[isStuck, setIsStuck] = useState(false),
|
|
146
|
-
[currentTabIx, setCurrentTab] = useState(isInProgress ? 1 : 0),
|
|
147
|
-
previousProgressRef = useRef(null),
|
|
148
|
-
unchangedProgressCountRef = useRef(0),
|
|
149
|
-
intervalRef = useRef(null),
|
|
150
|
-
formValuesRef = useRef(null),
|
|
151
|
-
getPreviousProgress = () => {
|
|
152
|
-
return previousProgressRef.current;
|
|
153
|
-
},
|
|
154
|
-
setPreviousProgress = (progress) => {
|
|
155
|
-
previousProgressRef.current = progress;
|
|
156
|
-
},
|
|
157
|
-
getUnchangedProgressCount = () => {
|
|
158
|
-
return unchangedProgressCountRef.current;
|
|
159
|
-
},
|
|
160
|
-
setUnchangedProgressCount = (count) => {
|
|
161
|
-
unchangedProgressCountRef.current = count;
|
|
162
|
-
forceUpdate();
|
|
163
|
-
},
|
|
164
|
-
getInterval = () => {
|
|
165
|
-
return intervalRef.current;
|
|
166
|
-
},
|
|
167
|
-
setIntervalRef = (interval) => { // 'setInterval' is a reserved name
|
|
168
|
-
intervalRef.current = interval;
|
|
169
|
-
},
|
|
170
|
-
getFormValues = () => {
|
|
171
|
-
return formValuesRef.current;
|
|
172
|
-
},
|
|
173
|
-
setFormValues = (values) => {
|
|
174
|
-
formValuesRef.current = values;
|
|
175
|
-
},
|
|
176
|
-
showResults = (results) => {
|
|
177
|
-
setCurrentTab(1);
|
|
178
|
-
setMode(RESULTS);
|
|
179
|
-
setFooter(getFooter());
|
|
180
|
-
setResults(results);
|
|
181
|
-
getProgress();
|
|
195
|
+
|
|
182
196
|
},
|
|
183
197
|
getProgress = (immediately = false) => {
|
|
184
|
-
if (getProgressUpdates) {
|
|
198
|
+
if (!getProgressUpdates) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function fetchProgress(isInitial = false) {
|
|
203
|
+
|
|
204
|
+
setIsStuck(false);
|
|
205
|
+
|
|
206
|
+
const
|
|
207
|
+
method = Repository.methods.edit,
|
|
208
|
+
uri = Repository.getModel() + '/getProcessProgress',
|
|
209
|
+
data = {
|
|
210
|
+
process,
|
|
211
|
+
...getFormValues(), // in case options submitted when starting the process affect the progress updates
|
|
212
|
+
},
|
|
213
|
+
result = await Repository._send(method, uri, data);
|
|
214
|
+
|
|
215
|
+
const response = Repository._processServerResponse(result);
|
|
216
|
+
if (!response.success) {
|
|
217
|
+
alert(response.message || 'Error getting progress info from server.');
|
|
218
|
+
stopGettingProgress();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
185
221
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
222
|
+
const
|
|
223
|
+
progress = parseProgress ? parseProgress(response.root) : response.root,
|
|
224
|
+
{
|
|
225
|
+
status,
|
|
226
|
+
errors,
|
|
227
|
+
started,
|
|
228
|
+
lastUpdated,
|
|
229
|
+
timeElapsed,
|
|
230
|
+
count,
|
|
231
|
+
current,
|
|
232
|
+
total,
|
|
233
|
+
percentage,
|
|
234
|
+
message,
|
|
235
|
+
} = progress || {},
|
|
236
|
+
renderItems = [];
|
|
237
|
+
if (status === PROGRESS__NONE_FOUND) {
|
|
238
|
+
resetToInitialState();
|
|
239
|
+
setIsReady(true);
|
|
240
|
+
forceUpdate();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let color = 'text-black',
|
|
245
|
+
statusMessage = '',
|
|
246
|
+
errorMessage = null;
|
|
247
|
+
if (status === PROGRESS__IN_PROCESS) {
|
|
248
|
+
setMode(PROCESSING);
|
|
249
|
+
color = 'text-green-600';
|
|
250
|
+
statusMessage = 'In process...';
|
|
251
|
+
} else {
|
|
252
|
+
setMode(RESULTS);
|
|
253
|
+
stopGettingProgress();
|
|
254
|
+
if (status === PROGRESS__COMPLETED) {
|
|
255
|
+
statusMessage = 'Completed';
|
|
256
|
+
} else if (status === PROGRESS__FAILED) {
|
|
257
|
+
color = 'text-red-400 font-bold';
|
|
258
|
+
statusMessage = 'Failed';
|
|
259
|
+
} else if (status === PROGRESS__STUCK) {
|
|
260
|
+
color = 'text-red-400 font-bold';
|
|
261
|
+
setIsStuck(true);
|
|
262
|
+
statusMessage = 'Stuck';
|
|
198
263
|
}
|
|
264
|
+
}
|
|
199
265
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
setUnchangedProgressCount(0);
|
|
219
|
-
}
|
|
266
|
+
const className = 'text-lg';
|
|
267
|
+
renderItems.push(<Text className={className + ' ' + color} key="status">Status: {statusMessage}</Text>);
|
|
268
|
+
if (!_.isNil(percentage)) {
|
|
269
|
+
renderItems.push(<HStack className="mb-2" key="progress">
|
|
270
|
+
<Progress.Bar
|
|
271
|
+
animated={true}
|
|
272
|
+
progress={percentage / 100}
|
|
273
|
+
width={175}
|
|
274
|
+
height={20}
|
|
275
|
+
color="#666"
|
|
276
|
+
/>
|
|
277
|
+
<Text className={className + ' pl-1'}>{percentage}%</Text>
|
|
278
|
+
</HStack>);
|
|
279
|
+
}
|
|
280
|
+
if (started) {
|
|
281
|
+
const startedMoment = moment(started);
|
|
282
|
+
if (startedMoment.isValid()) {
|
|
283
|
+
renderItems.push(<Text className={className} key="started">Started: {startedMoment.format(MOMENT_DATE_FORMAT_2)}</Text>);
|
|
220
284
|
}
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
if (immediately) {
|
|
224
|
-
fetchProgress();
|
|
225
285
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
286
|
+
if (lastUpdated) {
|
|
287
|
+
const updatedMoment = moment(lastUpdated);
|
|
288
|
+
if (updatedMoment.isValid()) {
|
|
289
|
+
renderItems.push(<Text className={className} key="lastUpdated">Last Updated: {updatedMoment.format(MOMENT_DATE_FORMAT_2)}</Text>);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (timeElapsed) {
|
|
293
|
+
renderItems.push(<Text className={className} key="timeElapsed">Time Elapsed: {timeElapsed}</Text>);
|
|
294
|
+
}
|
|
295
|
+
if (!_.isNil(count) && count !== 0) {
|
|
296
|
+
renderItems.push(<Text className={className} key="count">Count: {count}</Text>);
|
|
297
|
+
}
|
|
298
|
+
if (!_.isNil(current) && !_.isNil(total) && current !== 0 && total !== 0) {
|
|
299
|
+
renderItems.push(<Text className={className} key="currentTotal">Current/Total: {current} / {total}</Text>);
|
|
300
|
+
}
|
|
301
|
+
if (!_.isNil(message)) {
|
|
302
|
+
renderItems.push(<Text className={className} key="message">{message}</Text>);
|
|
303
|
+
}
|
|
304
|
+
if (!_.isNil(errors)) {
|
|
305
|
+
renderItems.push(<VStack key="errors">
|
|
306
|
+
<Text className="text-red-400 font-bold">Errors:</Text>
|
|
307
|
+
{errors?.map((line, ix)=> {
|
|
308
|
+
return <Text key={ix}>{line}</Text>;
|
|
309
|
+
})}
|
|
310
|
+
</VStack>);
|
|
311
|
+
}
|
|
312
|
+
if (getMode() === PROCESSING) {
|
|
313
|
+
setProgress(renderItems);
|
|
314
|
+
} else {
|
|
315
|
+
setResults(renderItems);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
setIsReady(true);
|
|
319
|
+
setFooter(getFooter());
|
|
320
|
+
forceUpdate();
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
let interval = getInterval();
|
|
324
|
+
if (interval) {
|
|
325
|
+
clearInterval(interval);
|
|
326
|
+
}
|
|
327
|
+
setIntervalRef(setInterval(fetchProgress, updateInterval));
|
|
328
|
+
|
|
329
|
+
if (immediately) {
|
|
330
|
+
fetchProgress(true); // isInitial
|
|
229
331
|
}
|
|
230
332
|
},
|
|
231
333
|
resetToInitialState = () => {
|
|
232
|
-
|
|
233
|
-
setMode(INITIATE);
|
|
334
|
+
setMode(START);
|
|
234
335
|
setFooter(getFooter());
|
|
235
|
-
clearProgress();
|
|
236
|
-
},
|
|
237
|
-
clearProgress = () => {
|
|
238
|
-
setIsInProgress(false);
|
|
239
336
|
setIsStuck(false);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
337
|
+
stopGettingProgress();
|
|
338
|
+
},
|
|
339
|
+
stopGettingProgress = () => {
|
|
243
340
|
clearInterval(getInterval());
|
|
244
341
|
setIntervalRef(null);
|
|
245
342
|
},
|
|
246
|
-
setIsInProgress = (isInProgress) => {
|
|
247
|
-
dispatch({
|
|
248
|
-
type: 'app/setOperationsInProgress',
|
|
249
|
-
payload: {
|
|
250
|
-
operation: action,
|
|
251
|
-
isInProgress,
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
|
-
},
|
|
255
343
|
onValidityChange = (isValid) => {
|
|
256
344
|
setIsValid(isValid);
|
|
257
345
|
setFooter(getFooter());
|
|
258
|
-
}
|
|
259
|
-
unchangedProgressCount = getUnchangedProgressCount();
|
|
346
|
+
};
|
|
260
347
|
|
|
261
348
|
useEffect(() => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
349
|
+
if (getProgressUpdates) {
|
|
350
|
+
getProgress(true);
|
|
351
|
+
} else {
|
|
352
|
+
setMode(START);
|
|
353
|
+
setIsReady(true);
|
|
265
354
|
}
|
|
266
|
-
|
|
267
355
|
return () => {
|
|
268
356
|
// clear the interval when the component unmounts
|
|
269
|
-
|
|
357
|
+
const interval = getInterval();
|
|
358
|
+
if (interval) {
|
|
359
|
+
clearInterval(interval);
|
|
360
|
+
}
|
|
270
361
|
};
|
|
271
362
|
}, []);
|
|
272
363
|
|
|
273
|
-
return <Panel
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
364
|
+
return <Panel
|
|
365
|
+
{...props}
|
|
366
|
+
footer={footer}
|
|
367
|
+
>
|
|
368
|
+
{!isReady && <Loading />}
|
|
369
|
+
{isReady &&
|
|
370
|
+
<TabBar
|
|
371
|
+
tabs={[
|
|
372
|
+
{
|
|
373
|
+
title: 'Start',
|
|
374
|
+
icon: Play,
|
|
375
|
+
isDisabled: currentTabIx !== 0,
|
|
376
|
+
content: getMode() === INIT ?
|
|
377
|
+
<Loading /> :
|
|
378
|
+
<ScrollView className="ScrollView h-full w-full">
|
|
379
|
+
<Form
|
|
380
|
+
reference="form"
|
|
381
|
+
parent={self}
|
|
382
|
+
className="w-full h-full flex-1"
|
|
383
|
+
disableFooter={true}
|
|
384
|
+
items={formItems}
|
|
385
|
+
startingValues={formStartingValues}
|
|
386
|
+
onValidityChange={onValidityChange}
|
|
387
|
+
{..._form}
|
|
388
|
+
/>
|
|
389
|
+
</ScrollView>,
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
title: 'Progress',
|
|
393
|
+
icon: EllipsisHorizontal,
|
|
394
|
+
isDisabled: currentTabIx !== 1,
|
|
395
|
+
content: <ScrollView className="ScrollView h-full w-full p-4">
|
|
396
|
+
{progress}
|
|
397
|
+
</ScrollView>,
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
title: 'Results',
|
|
401
|
+
icon: Stop,
|
|
402
|
+
isDisabled: currentTabIx !== 2,
|
|
403
|
+
content: <ScrollView className="ScrollView h-full w-full p-4">
|
|
404
|
+
{results}
|
|
405
|
+
</ScrollView>,
|
|
406
|
+
},
|
|
407
|
+
]}
|
|
408
|
+
currentTabIx={currentTabIx}
|
|
409
|
+
canToggleCollapse={false}
|
|
410
|
+
tabsAreButtons={false}
|
|
411
|
+
/>}
|
|
308
412
|
</Panel>;
|
|
309
413
|
}
|
|
310
414
|
|
|
@@ -11,13 +11,12 @@ import {
|
|
|
11
11
|
setInfoMessage,
|
|
12
12
|
selectProgressMessage,
|
|
13
13
|
selectProgressPercentage,
|
|
14
|
-
} from '
|
|
14
|
+
} from '../../Models/Slices/SystemSlice';
|
|
15
15
|
import WaitMessage from './WaitMessage';
|
|
16
16
|
import ErrorMessage from './ErrorMessage';
|
|
17
17
|
import ProgressModal from './ProgressModal';
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
export default function GlobalModals(props) {
|
|
22
21
|
const {
|
|
23
22
|
progressColor = '#666',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState, } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
Box,
|
|
3
4
|
ScrollView,
|
|
4
5
|
VStack,
|
|
5
6
|
VStackNative,
|
|
@@ -110,11 +111,22 @@ function Panel(props) {
|
|
|
110
111
|
if (maxHeight) {
|
|
111
112
|
style.maxHeight = maxHeight;
|
|
112
113
|
}
|
|
114
|
+
|
|
115
|
+
// Handle collapsed state and filter conflicting classes
|
|
116
|
+
let filteredClassName = props.className;
|
|
113
117
|
if (isCollapsed) {
|
|
114
118
|
if (collapseDirection === HORIZONTAL) {
|
|
119
|
+
if (filteredClassName) {
|
|
120
|
+
// Remove any width-related classes from props.className to prevent conflicts
|
|
121
|
+
filteredClassName = filteredClassName.replace(/\b(w-\S+|width-\S+|min-w-\S+|max-w-\S+)\b/g, '').trim();
|
|
122
|
+
}
|
|
115
123
|
className += ' w-[33px]';
|
|
116
124
|
delete style.width;
|
|
117
125
|
} else {
|
|
126
|
+
if (filteredClassName) {
|
|
127
|
+
// Remove any height-related classes from props.className to prevent conflicts
|
|
128
|
+
filteredClassName = filteredClassName.replace(/\b(h-\S+|height-\S+|min-h-\S+|max-h-\S+)\b/g, '').trim();
|
|
129
|
+
}
|
|
118
130
|
className += ' h-[33px]';
|
|
119
131
|
delete style.height;
|
|
120
132
|
}
|
|
@@ -123,8 +135,8 @@ function Panel(props) {
|
|
|
123
135
|
// frame
|
|
124
136
|
className += ' border-grey-300' + (isWindow ? ' rounded-lg shadow-lg ' : '') + (frame ? ' border-2' : ' border-none');
|
|
125
137
|
|
|
126
|
-
if (
|
|
127
|
-
className += ' ' +
|
|
138
|
+
if (filteredClassName) {
|
|
139
|
+
className += ' ' + filteredClassName;
|
|
128
140
|
}
|
|
129
141
|
|
|
130
142
|
return <VStackNative
|