@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
|
@@ -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
|
|
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 (
|
|
66
|
-
const extensionRegex = new RegExp(
|
|
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:
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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}>
|