@controleonline/ui-common 1.2.84 → 1.2.86

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": "@controleonline/ui-common",
3
- "version": "1.2.84",
3
+ "version": "1.2.86",
4
4
  "author": "ControleOnline",
5
5
  "description": "ControleOnline Common Quasar UI Component",
6
6
  "license": "MIT",
@@ -11,7 +11,8 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
11
11
  import { useStore } from '@store';
12
12
  import AnimatedModal from './AnimatedModal';
13
13
  import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
14
- import DefaultUpload from '@controleonline/ui-default/src/react/components/upload/DefaultUpload';
14
+ import ImportsPage from '@controleonline/ui-common/src/react/pages/Imports';
15
+ import { useState } from 'react';
15
16
  import { resolveSystemErrorMessage } from '@controleonline/ui-common/src/react/utils/systemErrorMessage';
16
17
  import styles from './AddImportModal.styles';
17
18
 
@@ -20,8 +21,8 @@ const AddImportModal = ({
20
21
  onClose,
21
22
  onSuccess,
22
23
  context = {},
23
- allowedExtensions = [],
24
- acceptedTypes = '*/*',
24
+ allowedExtensions = [], // default will be set to ['csv'] if empty
25
+ // acceptedTypes will be derived from allowedExtensions for security
25
26
  helperLabel,
26
27
  modalTitle,
27
28
  fileLabel,
@@ -29,6 +30,7 @@ const AddImportModal = ({
29
30
  cancelLabel,
30
31
  importSuccessLabel,
31
32
  importErrorLabel,
33
+ importType = null,
32
34
  }) => {
33
35
  const fallbackModalTitle = global.t?.t('imports', 'title', 'new_import');
34
36
  const fallbackFileLabel = global.t?.t('imports', 'label', 'file');
@@ -44,6 +46,12 @@ const AddImportModal = ({
44
46
  const _importSuccessLabel = importSuccessLabel ?? fallbackSuccessLabel;
45
47
  const _importErrorLabel = importErrorLabel ?? fallbackErrorLabel;
46
48
  const _helperLabel = helperLabel ?? fallbackInvalidFileLabel;
49
+ // Resolve allowed extensions securely – default to CSV only
50
+ const _allowedExtensions = (allowedExtensions && allowedExtensions.length > 0) ? allowedExtensions : ['csv'];
51
+ // Build acceptedTypes string for file input (e.g., ".csv,.xml,.zip")
52
+ const _acceptedTypes = _allowedExtensions.map(ext => `.${ext}`).join(',');
53
+ // Determine import type: prioritize prop, then infer from allowed extensions, default to 'csv'
54
+ const _importType = importType ?? (_allowedExtensions.includes('xml') ? 'xml' : (_allowedExtensions.includes('zip') ? 'zip' : 'csv'));
47
55
  const { showError, showSuccess } = useMessage();
48
56
  const peopleStore = useStore('people');
49
57
  const importsStore = useStore('imports');
@@ -59,35 +67,45 @@ const AddImportModal = ({
59
67
  buttonIcon: themeColors.buttonIcon || themeColors.buttonText,
60
68
  };
61
69
 
62
-
70
+ const [showImportList, setShowImportList] = useState(false);
71
+ const [importResult, setImportResult] = useState(null); // true = success, false = error
72
+ const [uploading, setUploading] = useState(false);
63
73
 
64
74
  const handleUploadImportFile = async ({ file }) => {
65
- if (allowedExtensions.length > 0) {
66
- const extensionRegex = new RegExp(`\\.(${allowedExtensions.join('|')})$`, 'i');
75
+ if (_allowedExtensions.length > 0) {
76
+ const extensionRegex = new RegExp(`\\\\.(${_allowedExtensions.join('|')})$`, 'i');
67
77
  if (!file?.name?.toLowerCase().match(extensionRegex)) {
68
78
  throw new Error(_helperLabel);
69
79
  }
70
80
  }
71
81
 
82
+ setUploading(true);
72
83
  try {
73
84
  await importActions.uploadImportFile({
74
85
  file,
75
- importType: context.context,
86
+ importType: _importType,
76
87
  peopleId: currentCompany.id,
77
88
  });
78
89
  showSuccess(_importSuccessLabel);
90
+ // Indicate successful import and open import list
91
+ setImportResult(true);
92
+ setShowImportList(true);
79
93
  if (onSuccess) onSuccess();
80
- handleClose();
81
94
  return file;
82
95
  } catch (error) {
83
96
  const importFeedback =
84
97
  resolveSystemErrorMessage(error) || _importErrorLabel;
85
98
  showError(importFeedback);
99
+ setImportResult(false);
86
100
  throw new Error(importFeedback);
101
+ } finally {
102
+ setUploading(false);
87
103
  }
88
104
  };
89
105
 
90
106
  const handleClose = () => {
107
+ setShowImportList(false);
108
+ setImportResult(null);
91
109
  onClose();
92
110
  };
93
111
 
@@ -105,53 +123,26 @@ const AddImportModal = ({
105
123
  </TouchableOpacity>
106
124
  </View>
107
125
 
108
- <ScrollView style={styles.content}>
109
- <Text style={styles.label>{_fileLabel}</Text>
110
- <DefaultUpload
111
- relationField="import"
112
- relationResource="imports"
113
- entityId={context.context || 'import'}
114
- companyId={currentCompany.id}
115
- context={`imports-${context.context || 'default'}`}
116
- libraryContexts={[`imports-${context.context || 'default'}`]}
117
- acceptedTypes={acceptedTypes}
118
- fileType=""
119
- title={_fileLabel}
120
- triggerLabel={_selectFileLabel}
121
- managerTitle={_modalTitle}
122
- searchPlaceholder={_selectFileLabel}
123
- uploadButtonLabel={_selectFileLabel}
124
- emptyAttachmentLabel=""
125
- emptyLibraryLabel={_selectFileLabel}
126
- showInlineContent={false}
127
- uploadResultAlreadyAttached
128
- requireEntity={false}
129
- onUploadFile={handleUploadImportFile}
130
- renderTrigger={({openManager, uploading}) => (
131
- <TouchableOpacity
132
- onPress={openManager}
133
- disabled={uploading}
134
- style={[
135
- styles.filePicker,
136
- {
137
- backgroundColor: buttonPalette.buttonBackground,
138
- borderColor: buttonPalette.buttonBorder,
139
- },
140
- ]}
141
- >
142
- <Text numberOfLines={1} style={[styles.fileName, { color: buttonPalette.buttonText }]}>
143
- {selectFileLabel}
144
- </Text>
145
- {uploading ? (
146
- <ActivityIndicator color={buttonPalette.buttonIcon} />
147
- ) : (
148
- <Icon name="upload-file" size={22} color={buttonPalette.buttonIcon} />
149
- )}
150
- </TouchableOpacity>
151
- )}
152
- />
126
+ <ScrollView style={styles.content} keyboardShouldPersistTaps="always">
127
+ <TouchableOpacity
128
+ onPress={handleUploadImportFile}
129
+ style={[styles.filePicker, { backgroundColor: buttonPalette.buttonBackground, borderColor: buttonPalette.buttonBorder }]}
130
+ >
131
+ <Text numberOfLines={1} style={[styles.fileName, { color: buttonPalette.buttonText }]}>{_selectFileLabel}</Text>
132
+ {uploading ? (
133
+ <ActivityIndicator color={buttonPalette.buttonIcon} />
134
+ ) : (
135
+ <Icon name="upload-file" size={22} color={buttonPalette.buttonIcon} />
136
+ )}
137
+ </TouchableOpacity>
138
+ <TouchableOpacity
139
+ onPress={() => setShowImportList(true)}
140
+ style={[styles.filePicker, { marginTop: 10, backgroundColor: buttonPalette.buttonBackground, borderColor: buttonPalette.buttonBorder }]}
141
+ >
142
+ <Text style={[styles.fileName, { color: buttonPalette.buttonText }]}>{global.t?.t('imports', 'button', 'view_imports') || 'View Imports'}</Text>
143
+ </TouchableOpacity>
144
+ </ScrollView>
153
145
 
154
- </ScrollView>
155
146
 
156
147
  <View style={styles.footer}>
157
148
  <TouchableOpacity onPress={handleClose} style={styles.secondaryButton}>
@@ -400,7 +400,9 @@ const Imports = ({ context = {}, onClose }) => {
400
400
  onSuccess={() => {
401
401
  setCurrentPage(1);
402
402
  fetchImports(searchQuery, 1);
403
+ if (onImportComplete) onImportComplete();
403
404
  }}
405
+ onImportComplete={onImportComplete}
404
406
  />
405
407
  </View>
406
408
  );