@onehat/ui 0.4.76 → 0.4.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/package.json +1 -1
  2. package/src/Components/Editor/AttachmentDirectoriesEditor.js +51 -0
  3. package/src/Components/Editor/AttachmentsEditor.js +81 -0
  4. package/src/Components/Form/Field/Combo/AttachmentDirectoriesCombo.js +20 -0
  5. package/src/Components/Form/Field/Combo/AttachmentDirectoriesComboEditor.js +22 -0
  6. package/src/Components/Form/Field/Combo/AttachmentsCombo.js +20 -0
  7. package/src/Components/Form/Field/Combo/AttachmentsComboEditor.js +22 -0
  8. package/src/Components/Form/Field/Tag/AttachmentDirectoriesTag.js +22 -0
  9. package/src/Components/Form/Field/Tag/AttachmentDirectoriesTagEditor.js +22 -0
  10. package/src/Components/Form/Field/Tag/AttachmentsTag.js +22 -0
  11. package/src/Components/Form/Field/Tag/AttachmentsTagEditor.js +22 -0
  12. package/src/Components/Form/Form.js +52 -18
  13. package/src/Components/Grid/AttachmentDirectoriesFilteredGrid.js +17 -0
  14. package/src/Components/Grid/AttachmentDirectoriesFilteredGridEditor.js +17 -0
  15. package/src/Components/Grid/AttachmentDirectoriesFilteredInlineGridEditor.js +17 -0
  16. package/src/Components/Grid/AttachmentDirectoriesFilteredSideGridEditor.js +17 -0
  17. package/src/Components/Grid/AttachmentDirectoriesGrid.js +20 -0
  18. package/src/Components/Grid/AttachmentDirectoriesGridEditor.js +27 -0
  19. package/src/Components/Grid/AttachmentDirectoriesInlineGridEditor.js +25 -0
  20. package/src/Components/Grid/AttachmentDirectoriesSideGridEditor.js +24 -0
  21. package/src/Components/Grid/AttachmentsFilteredGrid.js +17 -0
  22. package/src/Components/Grid/AttachmentsFilteredGridEditor.js +17 -0
  23. package/src/Components/Grid/AttachmentsFilteredInlineGridEditor.js +17 -0
  24. package/src/Components/Grid/AttachmentsFilteredSideGridEditor.js +17 -0
  25. package/src/Components/Grid/AttachmentsGrid.js +20 -0
  26. package/src/Components/Grid/AttachmentsGridEditor.js +27 -0
  27. package/src/Components/Grid/AttachmentsInlineGridEditor.js +25 -0
  28. package/src/Components/Grid/AttachmentsSideGridEditor.js +24 -0
  29. package/src/Components/Grid/Columns/AttachmentDirectoriesGridColumns.js +32 -0
  30. package/src/Components/Grid/Columns/AttachmentsGridColumns.js +133 -0
  31. package/src/Components/Grid/Grid.js +193 -20
  32. package/src/Components/Grid/GridHeaderRow.js +10 -17
  33. package/src/Components/Grid/GridRow.js +49 -22
  34. package/src/Components/Grid/RowHandle.js +8 -6
  35. package/src/Components/Hoc/withEditor.js +1 -1
  36. package/src/Components/Hoc/withPdfButtons.js +1 -1
  37. package/src/Components/Hoc/withSelection.js +26 -4
  38. package/src/Components/Layout/AsyncOperation.js +299 -195
  39. package/src/Components/Messages/GlobalModals.js +1 -2
  40. package/src/Components/Panel/Panel.js +14 -2
  41. package/src/Components/Panel/TabPanel.js +1 -1
  42. package/src/Components/Panel/TreePanel.js +1 -1
  43. package/src/Components/Report/Report.js +106 -17
  44. package/src/Components/Toolbar/PaginationToolbar.js +4 -3
  45. package/src/Components/Toolbar/Toolbar.js +6 -3
  46. package/src/Components/Tree/Tree.js +218 -147
  47. package/src/Components/Tree/TreeNode.js +20 -13
  48. package/src/Components/Window/AttachmentDirectoriesEditorWindow.js +34 -0
  49. package/src/Components/Window/AttachmentsEditorWindow.js +34 -0
  50. package/src/Components/index.js +92 -1
  51. package/src/Constants/Attachments.js +2 -0
  52. package/src/Constants/Dates.js +2 -2
  53. package/src/Constants/Progress.js +5 -1
  54. package/src/Models/Schemas/AttachmentDirectories.js +66 -0
  55. package/src/Models/Schemas/Attachments.js +88 -0
  56. package/src/Models/Slices/SystemSlice.js +220 -0
  57. package/src/PlatformImports/Web/Attachments.js +783 -145
  58. package/src/Styles/Global.css +7 -2
@@ -2,7 +2,6 @@ import {
2
2
  HStackNative,
3
3
  VStackNative,
4
4
  } from '@project-components/Gluestack';
5
- import clsx from 'clsx';
6
5
  import AddressBook from './Icons/AddressBook.js';
7
6
  import Alt from './Icons/Alt.js';
8
7
  import AngleLeft from './Icons/AngleLeft.js';
@@ -191,6 +190,52 @@ import Video from './Icons/Video.js';
191
190
  import X from './Icons/X.js';
192
191
  import Xmark from './Icons/Xmark.js';
193
192
 
193
+ // import AttachmentsCombo from './Form/Field/Combo/AttachmentsCombo.js';
194
+ // import AttachmentsComboEditor from './Form/Field/Combo/AttachmentsComboEditor.js';
195
+ // import AttachmentsTag from './Form/Field/Tag/AttachmentsTag.js';
196
+ // import AttachmentsTagEditor from './Form/Field/Tag/AttachmentsTagEditor.js';
197
+ // import AttachmentsGrid from './Grid/AttachmentsGrid.js';
198
+ // import AttachmentsGridEditor from './Grid/AttachmentsGridEditor.js';
199
+ // import AttachmentsSideGridEditor from './Grid/AttachmentsSideGridEditor.js';
200
+ // import AttachmentsInlineGridEditor from './Grid/AttachmentsInlineGridEditor.js';
201
+ // import AttachmentsFilteredGrid from './Grid/AttachmentsFilteredGrid.js';
202
+ // import AttachmentsFilteredGridEditor from './Grid/AttachmentsFilteredGridEditor.js';
203
+ // import AttachmentsFilteredSideGridEditor from './Grid/AttachmentsFilteredSideGridEditor.js';
204
+ // import AttachmentsFilteredInlineGridEditor from './Grid/AttachmentsFilteredInlineGridEditor.js';
205
+ // import AttachmentsGridPanel from './Panel/Grid/AttachmentsGrid.js';
206
+ // import AttachmentsGridEditorPanel from './Panel/Grid/AttachmentsGridEditor.js';
207
+ // import AttachmentsSideGridEditorPanel from './Panel/Grid/AttachmentsSideGridEditor.js';
208
+ // import AttachmentsInlineGridEditorPanel from './Panel/Grid/AttachmentsInlineGridEditor.js';
209
+ // import AttachmentsFilteredGridPanel from './Panel/Grid/AttachmentsFilteredGrid.js';
210
+ // import AttachmentsFilteredGridEditorPanel from './Panel/Grid/AttachmentsFilteredGridEditor.js';
211
+ // import AttachmentsFilteredSideGridEditorPanel from './Panel/Grid/AttachmentsFilteredSideGridEditor.js';
212
+ // import AttachmentsFilteredInlineGridEditorPanel from './Panel/Grid/AttachmentsFilteredInlineGridEditor.js';
213
+ // import AttachmentsEditor from './Editor/AttachmentsEditor.js';
214
+ // import AttachmentsEditorWindow from './Window/AttachmentsEditorWindow.js';
215
+
216
+ // import AttachmentDirectoriesCombo from './Form/Field/Combo/AttachmentDirectoriesCombo.js';
217
+ // import AttachmentDirectoriesComboEditor from './Form/Field/Combo/AttachmentDirectoriesComboEditor.js';
218
+ // import AttachmentDirectoriesTag from './Form/Field/Tag/AttachmentDirectoriesTag.js';
219
+ // import AttachmentDirectoriesTagEditor from './Form/Field/Tag/AttachmentDirectoriesTagEditor.js';
220
+ // import AttachmentDirectoriesGrid from './Grid/AttachmentDirectoriesGrid.js';
221
+ // import AttachmentDirectoriesGridEditor from './Grid/AttachmentDirectoriesGridEditor.js';
222
+ // import AttachmentDirectoriesSideGridEditor from './Grid/AttachmentDirectoriesSideGridEditor.js';
223
+ // import AttachmentDirectoriesInlineGridEditor from './Grid/AttachmentDirectoriesInlineGridEditor.js';
224
+ // import AttachmentDirectoriesFilteredGrid from './Grid/AttachmentDirectoriesFilteredGrid.js';
225
+ // import AttachmentDirectoriesFilteredGridEditor from './Grid/AttachmentDirectoriesFilteredGridEditor.js';
226
+ // import AttachmentDirectoriesFilteredSideGridEditor from './Grid/AttachmentDirectoriesFilteredSideGridEditor.js';
227
+ // import AttachmentDirectoriesFilteredInlineGridEditor from './Grid/AttachmentDirectoriesFilteredInlineGridEditor.js';
228
+ // import AttachmentDirectoriesGridPanel from './Panel/Grid/AttachmentDirectoriesGrid.js';
229
+ // import AttachmentDirectoriesGridEditorPanel from './Panel/Grid/AttachmentDirectoriesGridEditor.js';
230
+ // import AttachmentDirectoriesSideGridEditorPanel from './Panel/Grid/AttachmentDirectoriesSideGridEditor.js';
231
+ // import AttachmentDirectoriesInlineGridEditorPanel from './Panel/Grid/AttachmentDirectoriesInlineGridEditor.js';
232
+ // import AttachmentDirectoriesFilteredGridPanel from './Panel/Grid/AttachmentDirectoriesFilteredGrid.js';
233
+ // import AttachmentDirectoriesFilteredGridEditorPanel from './Panel/Grid/AttachmentDirectoriesFilteredGridEditor.js';
234
+ // import AttachmentDirectoriesFilteredSideGridEditorPanel from './Panel/Grid/AttachmentDirectoriesFilteredSideGridEditor.js';
235
+ // import AttachmentDirectoriesFilteredInlineGridEditorPanel from './Panel/Grid/AttachmentDirectoriesFilteredInlineGridEditor.js';
236
+ // import AttachmentDirectoriesEditor from './Editor/AttachmentDirectoriesEditor.js';
237
+ // import AttachmentDirectoriesEditorWindow from './Window/AttachmentDirectoriesEditorWindow.js';
238
+
194
239
  // import AccordionGridPanel from './Panel/AccordionGridPanel.js';
195
240
  import ArrayCheckboxGroup from './Form/Field/Checkbox/ArrayCheckboxGroup.js';
196
241
  import ArrayCombo from './Form/Field/Combo/ArrayCombo.js';
@@ -439,6 +484,52 @@ const components = {
439
484
  X,
440
485
  Xmark,
441
486
 
487
+ // AttachmentsCombo,
488
+ // AttachmentsComboEditor,
489
+ // AttachmentsTag,
490
+ // AttachmentsTagEditor,
491
+ // AttachmentsGrid,
492
+ // AttachmentsGridEditor,
493
+ // AttachmentsSideGridEditor,
494
+ // AttachmentsInlineGridEditor,
495
+ // AttachmentsFilteredGrid,
496
+ // AttachmentsFilteredGridEditor,
497
+ // AttachmentsFilteredSideGridEditor,
498
+ // AttachmentsFilteredInlineGridEditor,
499
+ // AttachmentsGridPanel,
500
+ // AttachmentsGridEditorPanel,
501
+ // AttachmentsSideGridEditorPanel,
502
+ // AttachmentsInlineGridEditorPanel,
503
+ // AttachmentsFilteredGridPanel,
504
+ // AttachmentsFilteredGridEditorPanel,
505
+ // AttachmentsFilteredSideGridEditorPanel,
506
+ // AttachmentsFilteredInlineGridEditorPanel,
507
+ // AttachmentsEditor,
508
+ // AttachmentsEditorWindow,
509
+
510
+ // AttachmentDirectoriesCombo,
511
+ // AttachmentDirectoriesComboEditor,
512
+ // AttachmentDirectoriesTag,
513
+ // AttachmentDirectoriesTagEditor,
514
+ // AttachmentDirectoriesGrid,
515
+ // AttachmentDirectoriesGridEditor,
516
+ // AttachmentDirectoriesSideGridEditor,
517
+ // AttachmentDirectoriesInlineGridEditor,
518
+ // AttachmentDirectoriesFilteredGrid,
519
+ // AttachmentDirectoriesFilteredGridEditor,
520
+ // AttachmentDirectoriesFilteredSideGridEditor,
521
+ // AttachmentDirectoriesFilteredInlineGridEditor,
522
+ // AttachmentDirectoriesGridPanel,
523
+ // AttachmentDirectoriesGridEditorPanel,
524
+ // AttachmentDirectoriesSideGridEditorPanel,
525
+ // AttachmentDirectoriesInlineGridEditorPanel,
526
+ // AttachmentDirectoriesFilteredGridPanel,
527
+ // AttachmentDirectoriesFilteredGridEditorPanel,
528
+ // AttachmentDirectoriesFilteredSideGridEditorPanel,
529
+ // AttachmentDirectoriesFilteredInlineGridEditorPanel,
530
+ // AttachmentDirectoriesEditor,
531
+ // AttachmentDirectoriesEditorWindow,
532
+
442
533
  // AccordionGridPanel,
443
534
  ArrayCheckboxGroup,
444
535
  ArrayCombo,
@@ -0,0 +1,2 @@
1
+ export const ATTACHMENTS_VIEW_MODES__ICON = 'ATTACHMENTS_VIEW_MODES__ICON';
2
+ export const ATTACHMENTS_VIEW_MODES__LIST = 'ATTACHMENTS_VIEW_MODES__LIST';
@@ -14,7 +14,7 @@ export const START_OF_THIS_MONTH = moment().startOf('months');
14
14
  export const END_OF_LAST_MONTH = moment().subtract(1, 'months').endOf('month');
15
15
  export const ONE_YEAR_AGO = moment().add(-1, 'years');
16
16
  export const MOMENT_DATE_FORMAT_1 = 'YYYY-MM-DD HH:mm:ss';
17
- export const MOMENT_DATE_FORMAT_2 = 'MMMM Do YYYY, h:mm:ss a'; // pretty datetime
18
- export const MOMENT_DATE_FORMAT_3 = 'h:mm A'; // pretty time
17
+ export const MOMENT_DATE_FORMAT_2 = 'MMM D YYYY, h:mm:ssa'; // pretty datetime
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';
@@ -1 +1,5 @@
1
- export const PROGRESS_COMPLETED = 'Completed';
1
+ export const PROGRESS__NONE_FOUND = 'NONE_FOUND';
2
+ export const PROGRESS__IN_PROCESS = 'IN_PROCESS';
3
+ export const PROGRESS__COMPLETED = 'COMPLETED';
4
+ export const PROGRESS__FAILED = 'FAILED';
5
+ export const PROGRESS__STUCK = 'STUCK';
@@ -0,0 +1,66 @@
1
+ import * as yup from 'yup'; // https://github.com/jquense/yup#string
2
+
3
+ const AttachmentDirectories = {
4
+
5
+ name: 'AttachmentDirectories',
6
+
7
+ model: {
8
+
9
+ idProperty: 'attachment_directories__id',
10
+ displayProperty: 'attachment_directories__name',
11
+ sortProperty: 'attachment_directories__name',
12
+
13
+ sorters: null,
14
+
15
+ validator: yup.object({
16
+ attachment_directories__name: yup.string().required(),
17
+ attachment_directories__model: yup.string().max(50).required(),
18
+ attachment_directories__modelid: yup.number().integer().required()
19
+ }),
20
+
21
+ properties: [
22
+ { name: 'attachment_directories__id', mapping: 'id', title: 'Id', type: 'int', isFilteringDisabled: true, fieldGroup: 'General', },
23
+ { name: 'attachment_directories__name', mapping: 'name', title: 'Name', isFilteringDisabled: true, editorType: {"type":"Input"}, fieldGroup: 'General', },
24
+ { name: 'attachment_directories__model', mapping: 'model', title: 'Model', isFilteringDisabled: true, editorType: {"type":"Input"}, fieldGroup: 'General', },
25
+ { name: 'attachment_directories__modelid', mapping: 'modelid', title: 'Modelid', type: 'int', filterType: {"type":"NumberRange"}, editorType: {"type":"Number"}, fieldGroup: 'General', }
26
+ ],
27
+
28
+ associations: {
29
+ hasMany: [
30
+ 'Attachments'
31
+ ],
32
+
33
+ },
34
+
35
+ ancillaryFilters: [],
36
+
37
+ defaultFilters: [
38
+ // 'attachment_directories__modelid'
39
+ ],
40
+
41
+ },
42
+
43
+ entity: {
44
+ methods: {
45
+
46
+ getAttachments: function() {
47
+ const Attachments = this.getAssociatedRepository('Attachments');
48
+ return Attachments.getBy((entity) => {
49
+ return entity.attachments__attachment_directory_id === this.attachment_directories__id;
50
+ });
51
+ },
52
+
53
+ },
54
+ },
55
+
56
+
57
+ repository: {
58
+ type: 'tree',
59
+ isRemotePhantomMode: false,
60
+ isAutoLoad: true,
61
+ isAutoSave: true,
62
+ },
63
+
64
+ };
65
+
66
+ export default AttachmentDirectories;
@@ -0,0 +1,88 @@
1
+ import * as yup from 'yup'; // https://github.com/jquense/yup#string
2
+
3
+ const Attachments = {
4
+
5
+ name: 'Attachments',
6
+
7
+ model: {
8
+
9
+ idProperty: 'attachments__id',
10
+ displayProperty: 'attachments__id',
11
+ sortProperty: 'attachments__filename',
12
+
13
+ sorters: null,
14
+
15
+ validator: yup.object({
16
+ attachments__attachment_directory_id: yup.number().integer().nullable(),
17
+ attachments__model: yup.string().oneOf([]).required(),
18
+ attachments__modelid: yup.number().integer().required(),
19
+ attachments__uuid: yup.string().max(40).required(),
20
+ attachments__path: yup.string().nullable(),
21
+ attachments__filename: yup.string().max(100).required(),
22
+ attachments__mimetype: yup.string().max(100).required(),
23
+ attachments__size: yup.number().integer().nullable()
24
+ }),
25
+
26
+ properties: [
27
+ { name: 'attachments__model_display', mapping: 'model_display', title: 'Model Display', isVirtual: true, isFilteringDisabled: true, isEditingDisabled: true, fieldGroup: 'General', },
28
+ { name: 'attachments__size_formatted', mapping: 'size_formatted', title: 'Size Formatted', isVirtual: true, isFilteringDisabled: true, isEditingDisabled: true, fieldGroup: 'General', },
29
+ { name: 'attachments__info', mapping: 'info', title: 'Info', isVirtual: true, isFilteringDisabled: true, isEditingDisabled: true, fieldGroup: 'General', },
30
+ { name: 'attachments__uri', mapping: 'uri', title: 'Uri', isVirtual: true, isFilteringDisabled: true, isEditingDisabled: true, fieldGroup: 'General', },
31
+ { name: 'attachments__attachment_directory_id', mapping: 'attachment_directory_id', title: 'Attachment Directory', type: 'int', isFk: true, fkIdField: 'attachment_directories__id', fkDisplayField: 'attachment_directories__name', filterType: {"type":"AttachmentDirectoriesCombo","loadAfterRender":!1}, editorType: {"type":"AttachmentDirectoriesComboEditor","loadAfterRender":!1}, fieldGroup: 'General', },
32
+ { name: 'attachments__abs_path', mapping: 'abs_path', title: 'Abs Path', isVirtual: true, isFilteringDisabled: true, isEditingDisabled: true, fieldGroup: 'General', },
33
+ { name: 'attachments__id', mapping: 'id', title: 'Id', type: 'int', isFilteringDisabled: true, fieldGroup: 'General', },
34
+ { name: 'attachments__model', mapping: 'model', title: 'Model', isFilteringDisabled: true, editorType: {"type":"ArrayCombo"}, fieldGroup: 'General', defaultValue: 'INSPECTIONS', },
35
+ { name: 'attachments__modelid', mapping: 'modelid', title: 'Modelid', type: 'int', filterType: {"type":"NumberRange"}, editorType: {"type":"Number"}, fieldGroup: 'General', },
36
+ { name: 'attachments__uuid', mapping: 'uuid', title: 'Uuid', isFilteringDisabled: true, editorType: {"type":"Input"}, fieldGroup: 'General', },
37
+ { name: 'attachments__path', mapping: 'path', title: 'Path', isFilteringDisabled: true, editorType: {"type":"TextArea"}, fieldGroup: 'General', },
38
+ { name: 'attachments__filename', mapping: 'filename', title: 'Filename', isFilteringDisabled: true, editorType: {"type":"Input"}, fieldGroup: 'General', },
39
+ { name: 'attachments__mimetype', mapping: 'mimetype', title: 'Mimetype', isFilteringDisabled: true, editorType: {"type":"Input"}, fieldGroup: 'General', },
40
+ { name: 'attachments__size', mapping: 'size', title: 'Size', type: 'int', filterType: {"type":"NumberRange"}, editorType: {"type":"Number"}, fieldGroup: 'General', },
41
+ { name: 'attachment_directories__id', mapping: 'attachment_directory.id', title: 'Id', type: 'int', isForeignModel: true, isEditingDisabled: true, isFilteringDisabled: true, },
42
+ { name: 'attachment_directories__name', mapping: 'attachment_directory.name', title: 'Name', isForeignModel: true, isEditingDisabled: true, isFilteringDisabled: true, },
43
+ { name: 'attachment_directories__model', mapping: 'attachment_directory.model', title: 'Model', isForeignModel: true, isEditingDisabled: true, isFilteringDisabled: true, },
44
+ { name: 'attachment_directories__modelid', mapping: 'attachment_directory.modelid', title: 'Modelid', type: 'int', isForeignModel: true, isEditingDisabled: true, isFilteringDisabled: true, }
45
+ ],
46
+
47
+ associations: {
48
+ belongsTo: [
49
+ 'AttachmentDirectories'
50
+ ],
51
+
52
+ },
53
+
54
+ ancillaryFilters: [],
55
+
56
+ defaultFilters: [
57
+ // 'attachments__attachment_directory_id',
58
+ // 'attachments__modelid',
59
+ // 'attachments__size'
60
+ ],
61
+
62
+ },
63
+
64
+ entity: {
65
+ methods: {
66
+
67
+ getAttachmentDirectory: async function() {
68
+ const AttachmentDirectories = this.getAssociatedRepository('AttachmentDirectories');
69
+ let entity = AttachmentDirectories.getById(this.attachments__attachment_directory_id);
70
+ if (!entity) {
71
+ entity = await AttachmentDirectories.getSingleEntityFromServer(this.attachments__attachment_directory_id);
72
+ }
73
+ return entity;
74
+ },
75
+
76
+ },
77
+ },
78
+
79
+
80
+ repository: {
81
+ "type": "onebuild",
82
+ "isRemotePhantomMode": false,
83
+ "isAutoLoad": false
84
+ },
85
+
86
+ };
87
+
88
+ export default Attachments;
@@ -0,0 +1,220 @@
1
+ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
2
+ import _ from 'lodash';
3
+
4
+
5
+ // synchronous thunks
6
+ export function setIsWaitModalShown(bool, name = 'default') {
7
+ return (dispatch, getState) => {
8
+ // NOTE: the waitStack is a dictionary of processes and their counts.
9
+ // Normally, this just uses the 'default' name.
10
+ // Optionally, the user can tell us which name is to be shown.
11
+ // If not using the default, the name name is added as the waitMessage.
12
+ // If multiple processes are being awaited, the most recently added name
13
+ // controls the waitMessage, even if other earlier added namees have been
14
+ // incremented.
15
+
16
+ const stack = _.clone(selectWaitStack(getState()));
17
+ if (bool) {
18
+ // Show it
19
+ if (_.isEmpty(stack)) {
20
+ dispatch(setIsWaitModalShownAction(true));
21
+ }
22
+ if (stack[name]) {
23
+ stack[name]++;
24
+ } else {
25
+ stack[name] = 1;
26
+ }
27
+ } else {
28
+ // Hide it
29
+ if (stack[name] !== 1) {
30
+ stack[name]--;
31
+ } else {
32
+ delete stack[name];
33
+ }
34
+ if (_.isEmpty(stack)) {
35
+ dispatch(setIsWaitModalShownAction(false));
36
+ }
37
+
38
+ const
39
+ keys = Object.keys(stack),
40
+ lastKey = keys[keys.length - 1];
41
+ name = lastKey;
42
+ }
43
+ dispatch(setWaitStack(stack));
44
+ dispatch(setWaitMessage(name === 'default' ? null : name));
45
+ };
46
+ }
47
+ export function startOperation(name, totalItems = 0) {
48
+ return (dispatch) => {
49
+ dispatch(_setOperationInProgress({
50
+ name,
51
+ currentItem: 0,
52
+ totalItems,
53
+ progressPercentage: 0,
54
+ }));
55
+ };
56
+ }
57
+ export function updateOperation(name, args = {}) {
58
+ return (dispatch, getState) => {
59
+ let {
60
+ currentItem,
61
+ totalItems,
62
+ progressPercentage,
63
+ } = args;
64
+ const
65
+ state = getState().system,
66
+ operations = selectOperationsInProgress(getState()),
67
+ operation = _.clone(operations.find(op => op.name === name)); // clone so we don't mutate state directly
68
+
69
+ if (!operation) {
70
+ console.warn(`Operation "${name}" not found. Use startOperation() first.`);
71
+ return;
72
+ }
73
+
74
+ // if totalItems is set, use it
75
+ if (totalItems) {
76
+ operation.totalItems = totalItems;
77
+ } else {
78
+ totalItems = operation.totalItems;
79
+ }
80
+ if (progressPercentage) {
81
+ operation.progressPercentage = progressPercentage;
82
+ operation.currentItem = totalItems > 0 ? Math.round((progressPercentage / 100) * totalItems) : 0;
83
+ }
84
+ if (currentItem) {
85
+ operation.currentItem = currentItem;
86
+ operation.progressPercentage = totalItems > 0 ? Math.round((currentItem / totalItems) * 100) : 0;
87
+ }
88
+
89
+ dispatch(_setOperationInProgress(operation));
90
+ };
91
+ }
92
+ export function completeOperation(name) {
93
+ return (dispatch) => {
94
+ dispatch(_setOperationInProgress({
95
+ name,
96
+ progressPercentage: 100,
97
+ }));
98
+ };
99
+ }
100
+
101
+
102
+ // slice
103
+ export const systemSlice = createSlice({
104
+ name: 'system',
105
+ initialState: {
106
+ debugMessage: null,
107
+ debugStack: [],
108
+ alertMessage: null,
109
+ infoMessage: null,
110
+ isWaitModalShown: false,
111
+ waitMessage: null,
112
+ waitStack: {},
113
+ progressMessage: null,
114
+ progressPercentage: 100,
115
+ operationsInProgress: [], // array of { name, totalItems, currentItem, progressPercentage }
116
+ },
117
+ reducers: {
118
+ setDebugMessage: (state, action) => {
119
+ state.debugMessage = action.payload;
120
+ },
121
+ setDebugStack: (state, action) => {
122
+ state.debugStack = action.payload;
123
+ },
124
+ setAlertMessage: (state, action) => {
125
+ state.alertMessage = action.payload;
126
+ },
127
+ setInfoMessage: (state, action) => {
128
+ state.infoMessage = action.payload;
129
+ },
130
+ setIsWaitModalShownAction: (state, action) => {
131
+ state.isWaitModalShown = action.payload;
132
+ },
133
+ setWaitMessage: (state, action) => {
134
+ state.waitMessage = action.payload;
135
+ },
136
+ setWaitStack: (state, action) => {
137
+ state.waitStack = action.payload;
138
+ },
139
+ setProgressMessage: (state, action) => {
140
+ state.progressMessage = action.payload;
141
+ },
142
+ setProgressPercentage: (state, action) => {
143
+ state.progressPercentage = action.payload;
144
+ },
145
+ _setOperationInProgress: (state, action) => {
146
+ // This method sets or updates one particular operation in progress.
147
+ // This is a private function and should not be used directly;
148
+ // use helper thunks (startOperation, updateOperation, completeOperation) instead.
149
+
150
+ const {
151
+ name,
152
+ totalItems,
153
+ currentItem,
154
+ progressPercentage,
155
+ } = action.payload;
156
+
157
+ if (progressPercentage < 100) {
158
+ // Add or update operation
159
+ const
160
+ existingIndex = state.operationsInProgress.findIndex(op => op.name === name),
161
+ operation = {
162
+ name,
163
+ totalItems: totalItems || 0,
164
+ currentItem: currentItem || 0,
165
+ progressPercentage: progressPercentage || 0
166
+ };
167
+
168
+ if (existingIndex >= 0) {
169
+ state.operationsInProgress[existingIndex] = operation;
170
+ } else {
171
+ state.operationsInProgress.push(operation);
172
+ }
173
+ } else {
174
+ // Remove operation
175
+ state.operationsInProgress = state.operationsInProgress.filter(op => op.name !== name);
176
+ }
177
+ },
178
+ clearAllOperations: (state) => {
179
+ state.operationsInProgress = [];
180
+ },
181
+ }
182
+ });
183
+
184
+
185
+ // action definitions
186
+ export const {
187
+ setDebugMessage,
188
+ setDebugStack,
189
+ setAlertMessage,
190
+ setInfoMessage,
191
+ setIsWaitModalShownAction,
192
+ setWaitMessage,
193
+ setWaitStack,
194
+ setProgressMessage,
195
+ setProgressPercentage,
196
+ clearAllOperations,
197
+ } = systemSlice.actions;
198
+ const { // private, do not export
199
+ _setOperationInProgress
200
+ } = systemSlice.actions;
201
+
202
+
203
+
204
+ // selectors
205
+ export const selectDebugMessage = state => state.system.debugMessage;
206
+ export const selectDebugStack = state => state.system.debugStack;
207
+ export const selectAlertMessage = state => state.system.alertMessage;
208
+ export const selectInfoMessage = state => state.system.infoMessage;
209
+ export const selectIsWaitModalShown = state => state.system.isWaitModalShown;
210
+ export const selectWaitMessage = state => state.system.waitMessage;
211
+ export const selectWaitStack = state => state.system.waitStack;
212
+ export const selectProgressMessage = state => state.system.progressMessage;
213
+ export const selectProgressPercentage = state => state.system.progressPercentage;
214
+ export const selectOperationsInProgress = state => state.system.operationsInProgress;
215
+ export const selectOperationByName = (name) => state => state.system.operationsInProgress.find(op => op.name === name);
216
+ export const selectOperationCount = state => state.system.operationsInProgress.length;
217
+ export const selectHasOperationsInProgress = state => state.system.operationsInProgress.length > 0;
218
+
219
+
220
+ export default systemSlice.reducer;