@controleonline/ui-common 1.2.83 → 1.2.85

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.83",
3
+ "version": "1.2.85",
4
4
  "author": "ControleOnline",
5
5
  "description": "ControleOnline Common Quasar UI Component",
6
6
  "license": "MIT",
@@ -15,7 +15,42 @@ import DefaultUpload from '@controleonline/ui-default/src/react/components/uploa
15
15
  import { resolveSystemErrorMessage } from '@controleonline/ui-common/src/react/utils/systemErrorMessage';
16
16
  import styles from './AddImportModal.styles';
17
17
 
18
- const AddImportModal = ({ visible, onClose, onSuccess, context = {} }) => {
18
+ const AddImportModal = ({
19
+ visible,
20
+ onClose,
21
+ onSuccess,
22
+ context = {},
23
+ allowedExtensions = [], // default will be set to ['csv'] if empty
24
+ // acceptedTypes will be derived from allowedExtensions for security
25
+ helperLabel,
26
+ modalTitle,
27
+ fileLabel,
28
+ selectFileLabel,
29
+ cancelLabel,
30
+ importSuccessLabel,
31
+ importErrorLabel,
32
+ importType = null,
33
+ }) => {
34
+ const fallbackModalTitle = global.t?.t('imports', 'title', 'new_import');
35
+ const fallbackFileLabel = global.t?.t('imports', 'label', 'file');
36
+ const fallbackSelectFileLabel = global.t?.t('imports', 'button', 'select_file');
37
+ const fallbackCancelLabel = global.t?.t('imports', 'button', 'cancel');
38
+ const fallbackSuccessLabel = global.t?.t('imports', 'success', 'import_sent_successfully');
39
+ const fallbackErrorLabel = global.t?.t('imports', 'error', 'error_sending_import');
40
+ const fallbackInvalidFileLabel = global.t?.t('imports', 'message', 'invalid_file_extension');
41
+ const _modalTitle = modalTitle ?? fallbackModalTitle;
42
+ const _fileLabel = fileLabel ?? fallbackFileLabel;
43
+ const _selectFileLabel = selectFileLabel ?? fallbackSelectFileLabel;
44
+ const _cancelLabel = cancelLabel ?? fallbackCancelLabel;
45
+ const _importSuccessLabel = importSuccessLabel ?? fallbackSuccessLabel;
46
+ const _importErrorLabel = importErrorLabel ?? fallbackErrorLabel;
47
+ const _helperLabel = helperLabel ?? fallbackInvalidFileLabel;
48
+ // Resolve allowed extensions securely – default to CSV only
49
+ const _allowedExtensions = (allowedExtensions && allowedExtensions.length > 0) ? allowedExtensions : ['csv'];
50
+ // Build acceptedTypes string for file input (e.g., ".csv,.xml,.zip")
51
+ const _acceptedTypes = _allowedExtensions.map(ext => `.${ext}`).join(',');
52
+ // Determine import type: prioritize prop, then infer from allowed extensions, default to 'csv'
53
+ const _importType = importType ?? (_allowedExtensions.includes('xml') ? 'xml' : (_allowedExtensions.includes('zip') ? 'zip' : 'csv'));
19
54
  const { showError, showSuccess } = useMessage();
20
55
  const peopleStore = useStore('people');
21
56
  const importsStore = useStore('imports');
@@ -31,32 +66,29 @@ const AddImportModal = ({ visible, onClose, onSuccess, context = {} }) => {
31
66
  buttonIcon: themeColors.buttonIcon || themeColors.buttonText,
32
67
  };
33
68
 
34
- const modalTitle = global.t?.t('imports', 'title', 'new_import');
35
- const csvLabel = global.t?.t('imports', 'label', 'csv_file');
36
- const selectFileLabel = global.t?.t('imports', 'button', 'select_file');
37
- const cancelLabel = global.t?.t('imports', 'button', 'cancel');
38
- const csvOnlyLabel = global.t?.t('imports', 'message', 'only_csv_files_are_allowed');
39
- const importSuccessLabel = global.t?.t('imports', 'success', 'import_sent_successfully');
40
- const importErrorLabel = global.t?.t('imports', 'error', 'error_sending_import');
69
+
41
70
 
42
71
  const handleUploadImportFile = async ({ file }) => {
43
- if (!file?.name?.toLowerCase().endsWith('.csv')) {
44
- throw new Error(csvOnlyLabel);
72
+ if (_allowedExtensions.length > 0) {
73
+ const extensionRegex = new RegExp(`\\.(${_allowedExtensions.join('|')})$`, 'i');
74
+ if (!file?.name?.toLowerCase().match(extensionRegex)) {
75
+ throw new Error(_helperLabel);
76
+ }
45
77
  }
46
78
 
47
79
  try {
48
80
  await importActions.uploadImportFile({
49
81
  file,
50
- importType: context.context,
82
+ importType: _importType,
51
83
  peopleId: currentCompany.id,
52
84
  });
53
- showSuccess(importSuccessLabel);
85
+ showSuccess(_importSuccessLabel);
54
86
  if (onSuccess) onSuccess();
55
87
  handleClose();
56
88
  return file;
57
89
  } catch (error) {
58
90
  const importFeedback =
59
- resolveSystemErrorMessage(error) || importErrorLabel;
91
+ resolveSystemErrorMessage(error) || _importErrorLabel;
60
92
  showError(importFeedback);
61
93
  throw new Error(importFeedback);
62
94
  }
@@ -74,31 +106,30 @@ const AddImportModal = ({ visible, onClose, onSuccess, context = {} }) => {
74
106
  >
75
107
  <View style={styles.sheet}>
76
108
  <View style={styles.header}>
77
- <Text style={styles.title}>{modalTitle}</Text>
109
+ <Text style={styles.title}>{_modalTitle}</Text>
78
110
  <TouchableOpacity onPress={handleClose}>
79
111
  <Icon name="close" size={24} />
80
112
  </TouchableOpacity>
81
113
  </View>
82
114
 
83
115
  <ScrollView style={styles.content}>
84
- <Text style={styles.label}>{csvLabel}</Text>
116
+ <Text style={styles.label}>{_fileLabel}</Text>
85
117
  <DefaultUpload
86
- relationStoreName="imports"
87
118
  relationField="import"
88
119
  relationResource="imports"
89
120
  entityId={context.context || 'import'}
90
121
  companyId={currentCompany.id}
91
122
  context={`imports-${context.context || 'default'}`}
92
123
  libraryContexts={[`imports-${context.context || 'default'}`]}
93
- acceptedTypes="text/csv,.csv"
124
+ acceptedTypes={_acceptedTypes}
94
125
  fileType=""
95
- title={csvLabel}
96
- triggerLabel={selectFileLabel}
97
- managerTitle={modalTitle}
98
- searchPlaceholder={selectFileLabel}
99
- uploadButtonLabel={selectFileLabel}
126
+ title={_fileLabel}
127
+ triggerLabel={_selectFileLabel}
128
+ managerTitle={_modalTitle}
129
+ searchPlaceholder={_selectFileLabel}
130
+ uploadButtonLabel={_selectFileLabel}
100
131
  emptyAttachmentLabel=""
101
- emptyLibraryLabel={selectFileLabel}
132
+ emptyLibraryLabel={_selectFileLabel}
102
133
  showInlineContent={false}
103
134
  uploadResultAlreadyAttached
104
135
  requireEntity={false}
@@ -126,14 +157,12 @@ const AddImportModal = ({ visible, onClose, onSuccess, context = {} }) => {
126
157
  </TouchableOpacity>
127
158
  )}
128
159
  />
129
- <Text style={styles.helperText}>
130
- {csvOnlyLabel}
131
- </Text>
160
+
132
161
  </ScrollView>
133
162
 
134
163
  <View style={styles.footer}>
135
164
  <TouchableOpacity onPress={handleClose} style={styles.secondaryButton}>
136
- <Text>{cancelLabel}</Text>
165
+ <Text>{_cancelLabel}</Text>
137
166
  </TouchableOpacity>
138
167
  </View>
139
168
  </View>