@onehat/ui 0.4.62 → 0.4.65

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.4.62",
3
+ "version": "0.4.65",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -0,0 +1,21 @@
1
+ import {
2
+ VStack,
3
+ } from '@project-components/Gluestack';
4
+ import _ from 'lodash';
5
+
6
+ // This component allows us to stack multiple children in a Container slot (e.g. east)
7
+ // such that the ContainerColumn can be passed props like isResizable,
8
+ // which the Container will translate into classNames for the VStack component.
9
+
10
+ export default function ContainerColumn(props) {
11
+ let className = `
12
+ ContainerColumn
13
+ `;
14
+ if (props.className) {
15
+ className += ` ${props.className}`;
16
+ }
17
+
18
+ return <VStack className={className}>
19
+ {props.children}
20
+ </VStack>;
21
+ }
@@ -767,6 +767,7 @@ export const ComboComponent = forwardRef((props, ref) => {
767
767
  newEntityDisplayProperty={newEntityDisplayProperty}
768
768
  disablePresetButtons={!isEditor}
769
769
  alternateRowBackgrounds={false}
770
+ showSelectHandle={false}
770
771
  onChangeSelection={(selection) => {
771
772
 
772
773
  if (Repository && selection[0]?.isPhantom) {
@@ -46,12 +46,12 @@ export function JsonElement(props) {
46
46
  ${testID}
47
47
  `;
48
48
  if (props.className) {
49
- className += ' ' + props.className;
49
+ className += ' ' + propsToPass.className;
50
50
  }
51
51
  // if (UiGlobals.mode === UI_MODE_WEB) {
52
52
  const src = value ? JSON.parse(value) : {};
53
53
  assembledComponents =
54
- <HStack style={props.style} className={className}>
54
+ <HStack style={propsToPass.style} className={className}>
55
55
  <JsonEditor
56
56
  width="100%"
57
57
  editable={!isViewOnly}
@@ -61,7 +61,7 @@ export function JsonElement(props) {
61
61
  onEdit={(obj) => {
62
62
  setValue(JSON.stringify(obj.updated_src));
63
63
  }}
64
- {...props}
64
+ {...propsToPass}
65
65
  />
66
66
  </HStack>;
67
67
  // }
@@ -61,8 +61,8 @@ function TagComponent(props) {
61
61
  await repository.waitUntilDoneLoading();
62
62
  }
63
63
  let record = repository.getById(id); // first try to get from entities in memory
64
- if (!record && repository.getSingleEntityFromServer) {
65
- record = await repository.getSingleEntityFromServer(id);
64
+ if (!record && repository.loadOneAdditionalEntity) {
65
+ record = await repository.loadOneAdditionalEntity(id);
66
66
  }
67
67
 
68
68
  if (!record) {
@@ -83,6 +83,7 @@ function Form(props) {
83
83
  editorType = EDITOR_TYPE__WINDOWED, // EDITOR_TYPE__INLINE | EDITOR_TYPE__WINDOWED | EDITOR_TYPE__SIDE | EDITOR_TYPE__SMART | EDITOR_TYPE__PLAIN
84
84
  startingValues = {},
85
85
  items = [], // Columns, FieldSets, Fields, etc to define the form
86
+ isItemsCustomLayout = false,
86
87
  ancillaryItems = [], // additional items which are not controllable form elements, but should appear in the form
87
88
  showAncillaryButtons = false,
88
89
  columnDefaults = {}, // defaults for each Column defined in items (above)
@@ -1187,8 +1188,8 @@ function Form(props) {
1187
1188
  formComponents = buildFromItems();
1188
1189
  const formAncillaryComponents = buildAncillary();
1189
1190
  editor = <>
1190
- {containerWidth >= styles.FORM_ONE_COLUMN_THRESHOLD ? <HStack className="Form-formComponents-HStack p-4 gap-4 justify-center">{formComponents}</HStack> : null}
1191
- {containerWidth < styles.FORM_ONE_COLUMN_THRESHOLD ? <VStack className="Form-formComponents-VStack p-4">{formComponents}</VStack> : null}
1191
+ {containerWidth >= styles.FORM_ONE_COLUMN_THRESHOLD && !isItemsCustomLayout ? <HStack className="Form-formComponents-HStack p-4 gap-4 justify-center">{formComponents}</HStack> : null}
1192
+ {containerWidth < styles.FORM_ONE_COLUMN_THRESHOLD || isItemsCustomLayout ? <VStack className="Form-formComponents-VStack p-4">{formComponents}</VStack> : null}
1192
1193
  {formAncillaryComponents.length ? <VStack className="Form-AncillaryComponents m-2 pt-4 px-2">{formAncillaryComponents}</VStack> : null}
1193
1194
  </>;
1194
1195
 
@@ -132,6 +132,7 @@ function GridComponent(props) {
132
132
  getExpandedRowContent,
133
133
  showHeaders = true,
134
134
  showHovers = true,
135
+ showSelectHandle = true,
135
136
  canColumnsSort = true,
136
137
  canColumnsReorder = true,
137
138
  canColumnsResize = true,
@@ -424,10 +425,10 @@ function GridComponent(props) {
424
425
  } else {
425
426
  let canDoEdit = false,
426
427
  canDoView = false;
427
- if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection))) {
428
+ if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit) {
428
429
  canDoEdit = true;
429
430
  } else
430
- if (onView && canUser && canUser(VIEW)) {
431
+ if (onView && canUser && canUser(VIEW) && !props.disableView) {
431
432
  canDoView = true;
432
433
  }
433
434
 
@@ -493,6 +494,7 @@ function GridComponent(props) {
493
494
  isInlineEditorShown={isInlineEditorShown}
494
495
  areRowsDragSource={areRowsDragSource}
495
496
  showColumnsSelector={showColumnsSelector}
497
+ showSelectHandle={showSelectHandle}
496
498
  />;
497
499
  if (showRowExpander) {
498
500
  // align the header row to content rows by adding a spacer that matches the width of the Grid-rowExpander-expandBtn
@@ -570,6 +572,7 @@ function GridComponent(props) {
570
572
  isSelected={isSelected}
571
573
  isHovered={hovered}
572
574
  showHovers={showHovers}
575
+ showSelectHandle={showSelectHandle}
573
576
  index={index}
574
577
  alternatingInterval={alternatingInterval}
575
578
  alternateRowBackgrounds={alternateRowBackgrounds}
@@ -20,6 +20,7 @@ import UiGlobals from '../../UiGlobals.js';
20
20
  import useBlocking from '../../Hooks/useBlocking.js';
21
21
  import testProps from '../../Functions/testProps.js';
22
22
  import AngleRight from '../Icons/AngleRight.js';
23
+ import ArrowPointer from '../Icons/ArrowPointer.js';
23
24
  import HeaderReorderHandle from './HeaderReorderHandle.js';
24
25
  import HeaderResizeHandle from './HeaderResizeHandle.js';
25
26
  import HeaderColumnSelectorHandle from './HeaderColumnSelectorHandle.js';
@@ -46,6 +47,7 @@ export default function GridHeaderRow(props) {
46
47
  isInlineEditorShown,
47
48
  areRowsDragSource,
48
49
  showColumnsSelector,
50
+ showSelectHandle,
49
51
  } = props,
50
52
  styles = UiGlobals.styles,
51
53
  sortFn = Repository && Repository.getSortFn(),
@@ -462,6 +464,14 @@ export default function GridHeaderRow(props) {
462
464
  />}
463
465
  </Pressable>;
464
466
  });
467
+ if (showSelectHandle) {
468
+ headerColumns.unshift(<Box
469
+ key="RowSelectHandle"
470
+ className="Spacer-RowSelectHandle px-2 items-center justify-center flex-none w-[40px]"
471
+ >
472
+ <Icon as={ArrowPointer} className={`ArrowPointer w-[20px] h-[20px] text-[#aaa]`} />
473
+ </Box>);
474
+ }
465
475
  if (areRowsDragSource) {
466
476
  headerColumns.unshift(<Box
467
477
  key="spacer"
@@ -16,6 +16,7 @@ import { withDragSource, withDropTarget } from '../Hoc/withDnd.js';
16
16
  import testProps from '../../Functions/testProps.js';
17
17
  import AngleRight from '../Icons/AngleRight.js';
18
18
  import RowDragHandle from './RowDragHandle.js';
19
+ import RowSelectHandle from './RowSelectHandle.js';
19
20
  import _ from 'lodash';
20
21
 
21
22
  // This was broken out from Grid simply so we can memoize it
@@ -27,6 +28,7 @@ function GridRow(props) {
27
28
  fields,
28
29
  rowProps,
29
30
  hideNavColumn,
31
+ showSelectHandle,
30
32
  isSelected,
31
33
  isHovered,
32
34
  bg,
@@ -265,6 +267,8 @@ function GridRow(props) {
265
267
 
266
268
  let rowContents = <>
267
269
  {(isDragSource || isDraggable) && <RowDragHandle />}
270
+ {showSelectHandle && <RowSelectHandle />}
271
+
268
272
  {isPhantom &&
269
273
  <Box
270
274
  className={`
@@ -5,11 +5,10 @@ import {
5
5
  import styles from '../../Styles/StyleSheets.js';
6
6
  import GripVertical from '../Icons/GripVertical.js';
7
7
 
8
- function RowDragHandle(props) {
9
- return <VStack
10
- style={styles.ewResize}
11
- className="HeaderReorderHandle bg-grey-100 w-[3px] items-center justify-center"
12
- >
8
+ function RowDragHandle(props) { return <VStack
9
+ style={styles.ewResize}
10
+ className="RowDragHandle bg-grey-100 w-[3px] items-center justify-center select-none"
11
+ >
13
12
  <Icon
14
13
  as={GripVertical}
15
14
  size="xs"
@@ -0,0 +1,18 @@
1
+ import {
2
+ Icon,
3
+ VStack,
4
+ } from '@project-components/Gluestack';
5
+ import ArrowPointer from '../Icons/ArrowPointer.js';
6
+
7
+ function RowSelectHandle(props) {
8
+ return <VStack
9
+ className="RowSelectHandle w-[40px] px-2 items-center justify-center select-none cursor-grab"
10
+ >
11
+ <Icon
12
+ as={ArrowPointer}
13
+ size="xs"
14
+ className="w-[20px] h-[20px] text-[#ddd]" />
15
+ </VStack>;
16
+ }
17
+
18
+ export default RowSelectHandle;
@@ -29,6 +29,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
29
29
  userCanEdit = true, // not permissions, but capability
30
30
  userCanView = true,
31
31
  canEditorViewOnly = false, // whether the editor can *ever* change state out of 'View' mode
32
+ canProceedWithCrud, // fn returns bool on if the CRUD operation can proceed
32
33
  disableAdd = false,
33
34
  disableEdit = false,
34
35
  disableDelete = false,
@@ -153,6 +154,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
153
154
  showPermissionsError(ADD);
154
155
  return;
155
156
  }
157
+ if (canProceedWithCrud && !canProceedWithCrud()) {
158
+ return;
159
+ }
156
160
 
157
161
  const selection = getSelection();
158
162
  let addValues = values;
@@ -252,6 +256,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
252
256
  showPermissionsError(EDIT);
253
257
  return;
254
258
  }
259
+ if (canProceedWithCrud && !canProceedWithCrud()) {
260
+ return;
261
+ }
255
262
  const selection = getSelection();
256
263
  if (_.isEmpty(selection) || (_.isArray(selection) && (selection.length > 1 || selection[0]?.isDestroyed))) {
257
264
  return;
@@ -271,6 +278,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
271
278
  showPermissionsError(DELETE);
272
279
  return;
273
280
  }
281
+ if (canProceedWithCrud && !canProceedWithCrud()) {
282
+ return;
283
+ }
274
284
  let cb = null;
275
285
  if (_.isFunction(args)) {
276
286
  cb = args;
@@ -368,6 +378,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
368
378
  showPermissionsError(VIEW);
369
379
  return;
370
380
  }
381
+ if (canProceedWithCrud && !canProceedWithCrud()) {
382
+ return;
383
+ }
371
384
  if (editorType === EDITOR_TYPE__INLINE) {
372
385
  alert('Cannot view in inline editor.');
373
386
  return; // inline editor doesn't have a view mode
@@ -395,6 +408,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
395
408
  showPermissionsError(DUPLICATE);
396
409
  return;
397
410
  }
411
+ if (canProceedWithCrud && !canProceedWithCrud()) {
412
+ return;
413
+ }
398
414
 
399
415
  const selection = getSelection();
400
416
  if (selection.length !== 1) {
@@ -8,6 +8,7 @@ import {
8
8
  DUPLICATE,
9
9
  PRINT,
10
10
  UPLOAD_DOWNLOAD,
11
+ DOWNLOAD,
11
12
  } from '../../Constants/Commands.js';
12
13
  import Clipboard from '../Icons/Clipboard.js';
13
14
  import Duplicate from '../Icons/Duplicate.js';
@@ -17,6 +18,7 @@ import Trash from '../Icons/Trash.js';
17
18
  import Plus from '../Icons/Plus.js';
18
19
  import Print from '../Icons/Print.js';
19
20
  import UploadDownload from '../Icons/UploadDownload.js';
21
+ import Download from '../Icons/Download.js';
20
22
  import inArray from '../../Functions/inArray.js';
21
23
  import UploadsDownloadsWindow from '../Window/UploadsDownloadsWindow.js';
22
24
  import _ from 'lodash';
@@ -33,6 +35,7 @@ const presetButtons = [
33
35
  DUPLICATE,
34
36
  // PRINT,
35
37
  UPLOAD_DOWNLOAD,
38
+ DOWNLOAD,
36
39
  ];
37
40
 
38
41
  export default function withPresetButtons(WrappedComponent, isGrid = false) {
@@ -48,6 +51,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
48
51
  contextMenuItems = [],
49
52
  additionalToolbarButtons = [],
50
53
  useUploadDownload = false,
54
+ useDownload = false,
51
55
  uploadHeaders,
52
56
  uploadParams,
53
57
  onUpload,
@@ -57,7 +61,6 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
57
61
  canRecordBeEdited,
58
62
  canRecordBeDeleted,
59
63
  canRecordBeDuplicated,
60
- canProceedWithCrud, // fn returns bool on if the CRUD operation can proceed
61
64
  ...propsToPass
62
65
  } = props,
63
66
  {
@@ -177,6 +180,13 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
177
180
  isDisabled = true;
178
181
  }
179
182
  break;
183
+ case DOWNLOAD:
184
+ if (!useDownload) {
185
+ isDisabled = true;
186
+ } else if (canUser && !(canUser(DOWNLOAD) || canUser(UPLOAD_DOWNLOAD))) { // check Permissions
187
+ isDisabled = true;
188
+ }
189
+ break;
180
190
  default:
181
191
  }
182
192
  return isDisabled;
@@ -211,9 +221,6 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
211
221
  key = 'addBtn';
212
222
  text = 'Add';
213
223
  handler = (parent, e) => {
214
- if (canProceedWithCrud && !canProceedWithCrud()) {
215
- return;
216
- }
217
224
  onAdd();
218
225
  };
219
226
  icon = Plus;
@@ -227,9 +234,6 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
227
234
  key = 'editBtn';
228
235
  text = 'Edit';
229
236
  handler = (parent, e) => {
230
- if (canProceedWithCrud && !canProceedWithCrud()) {
231
- return;
232
- }
233
237
  onEdit();
234
238
  };
235
239
  icon = Edit;
@@ -247,9 +251,6 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
247
251
  text = 'Delete';
248
252
  handler = onDelete;
249
253
  handler = (parent, e) => {
250
- if (canProceedWithCrud && !canProceedWithCrud()) {
251
- return;
252
- }
253
254
  onDelete();
254
255
  };
255
256
  icon = Trash;
@@ -297,9 +298,6 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
297
298
  key = 'duplicateBtn';
298
299
  text = 'Duplicate';
299
300
  handler = (parent, e) => {
300
- if (canProceedWithCrud && !canProceedWithCrud()) {
301
- return;
302
- }
303
301
  onDuplicate();
304
302
  };
305
303
  icon = Duplicate;
@@ -323,6 +321,12 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
323
321
  handler = (parent, e) => onUploadDownload();
324
322
  icon = UploadDownload;
325
323
  break;
324
+ case DOWNLOAD:
325
+ key = 'downloadBtn';
326
+ text = 'Download';
327
+ handler = (parent, e) => onDownload();
328
+ icon = Download;
329
+ break;
326
330
  default:
327
331
  }
328
332
  return {
@@ -397,6 +401,20 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
397
401
  />,
398
402
  onCancel: hideModal,
399
403
  });
404
+ },
405
+ onDownload = () => {
406
+ showModal({
407
+ body: <UploadsDownloadsWindow
408
+ reference="downloads"
409
+ onClose={hideModal}
410
+ isDownloadOnly={true}
411
+ Repository={Repository}
412
+ columnsConfig={props.columnsConfig}
413
+ downloadHeaders={downloadHeaders}
414
+ downloadParams={downloadParams}
415
+ />,
416
+ onCancel: hideModal,
417
+ });
400
418
  };
401
419
  // onPrint = () => {
402
420
  // debugger;
@@ -0,0 +1,11 @@
1
+ import { createIcon } from "../Gluestack/icon";
2
+ // Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.
3
+ import Svg, { Path } from "react-native-svg"
4
+
5
+ const SvgComponent = createIcon({
6
+ Root: Svg,
7
+ viewBox: '0 0 320 512',
8
+ path: <Path d="M0 55.2V426c0 12.2 9.9 22 22 22 6.3 0 12.4-2.7 16.6-7.5l82.6-94.5 58.1 116.3c7.9 15.8 27.1 22.2 42.9 14.3s22.2-27.1 14.3-42.9L179.8 320h118.1c12.2 0 22.1-9.9 22.1-22.1 0-6.3-2.7-12.3-7.4-16.5L38.6 37.9c-4.3-3.8-9.7-5.9-15.4-5.9C10.4 32 0 42.4 0 55.2z" />,
9
+ });
10
+
11
+ export default SvgComponent
@@ -23,6 +23,11 @@ import Panel from '../Panel/Panel.js';
23
23
  import Toolbar from '../Toolbar/Toolbar.js';
24
24
  import _ from 'lodash';
25
25
 
26
+ const
27
+ INITIATE = 'INITIATE',
28
+ PROCESSING = 'PROCESSING',
29
+ RESULTS = 'RESULTS';
30
+
26
31
  // NOTE: This component assumes you have an AppSlice, that has
27
32
  // an 'operationsInProgress' state var and a 'setOperationsInProgress' action.
28
33
 
@@ -37,6 +42,7 @@ function AsyncOperation(props) {
37
42
  Repository,
38
43
  formItems = [],
39
44
  formStartingValues = {},
45
+ _form = {},
40
46
  getProgressUpdates = false,
41
47
  parseProgress, // optional fn, accepts 'response' as arg and returns progress string
42
48
  progressStuckThreshold = null, // e.g. 3, if left blank, doesn't check for stuck state
@@ -49,10 +55,25 @@ function AsyncOperation(props) {
49
55
  alert,
50
56
  } = props,
51
57
  dispatch = useDispatch(),
58
+ isValid = useRef(true),
59
+ setIsValid = (valid) => {
60
+ isValid.current = valid;
61
+ },
62
+ getIsValid = () => {
63
+ return isValid.current;
64
+ },
65
+ mode = useRef(INITIATE),
66
+ setMode = (newMode) => {
67
+ mode.current = newMode;
68
+ },
69
+ getMode = () => {
70
+ return mode.current;
71
+ },
52
72
  initiate = async () => {
53
73
 
54
74
  clearProgress();
55
- setFooter(getFooter('processing'));
75
+ setMode(PROCESSING);
76
+ setFooter(getFooter());
56
77
  setIsInProgress(true);
57
78
 
58
79
  const
@@ -85,17 +106,18 @@ function AsyncOperation(props) {
85
106
  }
86
107
  showResults(results);
87
108
  },
88
- getFooter = (which = 'initiate') => {
109
+ getFooter = (which = getMode()) => {
89
110
  switch(which) {
90
- case 'initiate':
111
+ case INITIATE:
91
112
  return <Toolbar>
92
113
  <Button
93
114
  text="Start"
94
115
  rightIcon={ChevronRight}
95
116
  onPress={() => initiate()}
117
+ isDisabled={!getIsValid()}
96
118
  />
97
119
  </Toolbar>;
98
- case 'processing':
120
+ case PROCESSING:
99
121
  return <Toolbar>
100
122
  <Button
101
123
  text="Please wait"
@@ -103,7 +125,7 @@ function AsyncOperation(props) {
103
125
  variant="link"
104
126
  />
105
127
  </Toolbar>;
106
- case 'results':
128
+ case RESULTS:
107
129
  return <Toolbar>
108
130
  <Button
109
131
  text="Reset"
@@ -152,7 +174,8 @@ function AsyncOperation(props) {
152
174
  },
153
175
  showResults = (results) => {
154
176
  setCurrentTab(1);
155
- setFooter(getFooter('results'));
177
+ setMode(RESULTS);
178
+ setFooter(getFooter());
156
179
  setResults(results);
157
180
  getProgress();
158
181
  },
@@ -206,6 +229,7 @@ function AsyncOperation(props) {
206
229
  },
207
230
  resetToInitialState = () => {
208
231
  setCurrentTab(0);
232
+ setMode(INITIATE);
209
233
  setFooter(getFooter());
210
234
  clearProgress();
211
235
  },
@@ -227,6 +251,10 @@ function AsyncOperation(props) {
227
251
  },
228
252
  });
229
253
  },
254
+ onValidityChange = (isValid) => {
255
+ setIsValid(isValid);
256
+ setFooter(getFooter());
257
+ },
230
258
  unchangedProgressCount = getUnchangedProgressCount();
231
259
 
232
260
  useEffect(() => {
@@ -255,6 +283,8 @@ function AsyncOperation(props) {
255
283
  disableFooter={true}
256
284
  items={formItems}
257
285
  startingValues={formStartingValues}
286
+ onValidityChange={onValidityChange}
287
+ {..._form}
258
288
  />,
259
289
  },
260
290
  {
@@ -3,6 +3,7 @@ import {
3
3
  Box,
4
4
  HStack,
5
5
  Icon,
6
+ Pressable,
6
7
  Text,
7
8
  VStack,
8
9
  VStackNative,
@@ -17,6 +18,7 @@ import {
17
18
  REPORT_TYPES__PDF,
18
19
  } from '../../Constants/ReportTypes.js';
19
20
  import Form from '../Form/Form.js';
21
+ import IconButton from '../Buttons/IconButton.js';
20
22
  import withComponent from '../Hoc/withComponent.js';
21
23
  import withAlert from '../Hoc/withAlert.js';
22
24
  import testProps from '../../Functions/testProps.js';
@@ -37,9 +39,19 @@ function Report(props) {
37
39
  disablePdf = false,
38
40
  disableExcel = false,
39
41
  showReportHeaders = true,
42
+ isQuickReport = false,
43
+ quickReportData = {},
40
44
  alert,
41
45
  } = props,
42
46
  buttons = [],
47
+ onPressQuickReport = () => {
48
+ downloadReport({
49
+ reportId,
50
+ reportType: REPORT_TYPES__EXCEL,
51
+ showReportHeaders,
52
+ data: quickReportData,
53
+ });
54
+ },
43
55
  downloadReport = (args) => {
44
56
  getReport(args);
45
57
  alert('Download started');
@@ -59,6 +71,48 @@ function Report(props) {
59
71
  icon = <Icon as={icon} {...propsIcon} />;
60
72
  }
61
73
 
74
+ if (isQuickReport) {
75
+ let className = `
76
+ Report
77
+ max-w-[100px]
78
+ m-2
79
+ `;
80
+ if (props.className) {
81
+ className += ' ' + props.className;
82
+ }
83
+ return <VStackNative
84
+ {...testProps('QuickReport-' + reportId)}
85
+ className={className}
86
+ >
87
+ <Pressable
88
+ onPress={onPressQuickReport}
89
+ className={`
90
+ flex-1
91
+ items-center
92
+ justify-center
93
+ flex-col
94
+ bg-white
95
+ p-3
96
+ rounded-lg
97
+ border
98
+ border-primary-300
99
+ hover:bg-primary-300
100
+ `}
101
+ >
102
+ {icon}
103
+ <Text
104
+ className={`
105
+ text-black
106
+ text-center
107
+ text-[17px]
108
+ leading-tight
109
+ mt-2
110
+ `}
111
+ >{title}</Text>
112
+ </Pressable>
113
+ </VStackNative>;
114
+ }
115
+
62
116
  if (!disableExcel) {
63
117
  buttons.push({
64
118
  ...testProps('excelBtn'),
@@ -25,6 +25,7 @@ function UploadsDownloadsWindow(props) {
25
25
  downloadHeaders,
26
26
  uploadParams = {},
27
27
  downloadParams = {},
28
+ isDownloadOnly = false,
28
29
  onUpload,
29
30
 
30
31
  // withComponent
@@ -124,6 +125,61 @@ function UploadsDownloadsWindow(props) {
124
125
  }
125
126
  }
126
127
  };
128
+
129
+ const items = [
130
+ {
131
+ type: 'DisplayField',
132
+ text: 'Download an Excel file of the current grid contents.',
133
+ },
134
+ {
135
+ type: 'Button',
136
+ text: 'Download',
137
+ isEditable: false,
138
+ icon: Excel,
139
+ _icon: {
140
+ size: 'md',
141
+ },
142
+ onPress: () => onDownload(),
143
+ className: 'mb-5',
144
+ },
145
+ ];
146
+ if (!isDownloadOnly) {
147
+ items.push({
148
+ type: 'DisplayField',
149
+ text: 'Upload an Excel file to the current grid.',
150
+ });
151
+ items.push({
152
+ type: 'File',
153
+ name: 'file',
154
+ onChangeValue: setImportFile,
155
+ accept: '.xlsx',
156
+ });
157
+ items.push({
158
+ type: 'Row',
159
+ className: 'mt-2',
160
+ items: [
161
+ {
162
+ type: 'Button',
163
+ text: 'Upload',
164
+ isEditable: false,
165
+ icon: Upload,
166
+ _icon: {
167
+ size: 'md',
168
+ },
169
+ isDisabled: !importFile,
170
+ onPress: onUploadLocal,
171
+ },
172
+ {
173
+ type: 'Button',
174
+ text: 'Get Template',
175
+ icon: Download,
176
+ isEditable: false,
177
+ onPress: onDownloadTemplate,
178
+ },
179
+
180
+ ],
181
+ });
182
+ }
127
183
 
128
184
  return <Panel
129
185
  {...props}
@@ -151,58 +207,7 @@ function UploadsDownloadsWindow(props) {
151
207
  "type": "Column",
152
208
  "flex": 1,
153
209
  "defaults": {},
154
- "items": [
155
- {
156
- type: 'DisplayField',
157
- text: 'Download an Excel file of the current grid contents.',
158
- },
159
- {
160
- type: 'Button',
161
- text: 'Download',
162
- isEditable: false,
163
- icon: Excel,
164
- _icon: {
165
- size: 'md',
166
- },
167
- onPress: () => onDownload(),
168
- className: 'mb-5',
169
- },
170
- {
171
- type: 'DisplayField',
172
- text: 'Upload an Excel file to the current grid.',
173
- },
174
- {
175
- type: 'File',
176
- name: 'file',
177
- onChangeValue: setImportFile,
178
- accept: '.xlsx',
179
- },
180
- {
181
- type: 'Row',
182
- className: 'mt-2',
183
- items: [
184
- {
185
- type: 'Button',
186
- text: 'Upload',
187
- isEditable: false,
188
- icon: Upload,
189
- _icon: {
190
- size: 'md',
191
- },
192
- isDisabled: !importFile,
193
- onPress: onUploadLocal,
194
- },
195
- {
196
- type: 'Button',
197
- text: 'Get Template',
198
- icon: Download,
199
- isEditable: false,
200
- onPress: onDownloadTemplate,
201
- },
202
-
203
- ],
204
- },
205
- ]
210
+ "items": items,
206
211
  },
207
212
  ]}
208
213
  // record={selection}
@@ -9,6 +9,7 @@ import AngleRight from './Icons/AngleRight.js';
9
9
  import AnglesLeft from './Icons/AnglesLeft.js';
10
10
  import AnglesRight from './Icons/AnglesRight.js';
11
11
  import Asterisk from './Icons/Asterisk.js';
12
+ import ArrowPointer from './Icons/ArrowPointer.js';
12
13
  import ArrowUp from './Icons/ArrowUp.js';
13
14
  import Ban from './Icons/Ban.js';
14
15
  import Bars from './Icons/Bars.js';
@@ -204,6 +205,7 @@ import Color from './Form/Field/Color.js';
204
205
  import Combo from './Form/Field/Combo/Combo.js';
205
206
  // import { ComboEditor } from './Form/Field/Combo/Combo.js';
206
207
  import Container from './Container/Container.js';
208
+ import ContainerColumn from './Container/ContainerColumn.js';
207
209
  import DataMgt from './Screens/DataMgt.js';
208
210
  import Date from './Form/Field/Date.js';
209
211
  import DateRange from './Filter/DateRange.js';
@@ -254,6 +256,7 @@ const components = {
254
256
  AnglesLeft,
255
257
  AnglesRight,
256
258
  Asterisk,
259
+ ArrowPointer,
257
260
  ArrowUp,
258
261
  Ban,
259
262
  Bars,
@@ -449,6 +452,7 @@ const components = {
449
452
  Combo,
450
453
  // ComboEditor,
451
454
  Container,
455
+ ContainerColumn,
452
456
  DataMgt,
453
457
  Date,
454
458
  DateRange,
@@ -6,3 +6,4 @@ export const COPY = 'copy';
6
6
  export const DUPLICATE = 'duplicate';
7
7
  export const PRINT = 'print';
8
8
  export const UPLOAD_DOWNLOAD = 'uploadDownload';
9
+ export const DOWNLOAD = 'download';