@capillarytech/creatives-library 7.9.13 → 7.9.15
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/assets/group.png +0 -0
- package/components/Card/index.js +1 -1
- package/components/Card/tests/__snapshots__/index.test.js.snap +22 -0
- package/components/Card/tests/index.test.js +19 -7
- package/containers/App/constants.js +2 -1
- package/helpers/intl-enzym-test-helpers.js +40 -0
- package/index.js +6 -0
- package/package.json +3 -2
- package/routes.js +5 -0
- package/services/api.js +9 -15
- package/utils/common.js +17 -4
- package/v2Components/CapTagList/index.js +38 -9
- package/v2Components/CapTagList/messages.js +4 -0
- package/v2Containers/Cap/constants.js +2 -0
- package/v2Containers/Cap/index.js +9 -2
- package/v2Containers/Cap/reducer.js +5 -1
- package/v2Containers/Cap/selectors.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +10 -4
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +6 -1
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/FTP/index.js +14 -4
- package/v2Containers/Sms/Create/index.js +5 -6
- package/v2Containers/Sms/Edit/sagas.js +1 -1
- package/v2Containers/SmsTrai/Create/actions.js +25 -0
- package/v2Containers/SmsTrai/Create/constants.js +74 -0
- package/v2Containers/SmsTrai/Create/index.js +681 -0
- package/v2Containers/SmsTrai/Create/index.scss +92 -0
- package/v2Containers/SmsTrai/Create/messages.js +116 -0
- package/v2Containers/SmsTrai/Create/reducer.js +43 -0
- package/v2Containers/SmsTrai/Create/sagas.js +36 -0
- package/v2Containers/SmsTrai/Create/selectors.js +26 -0
- package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +10351 -0
- package/v2Containers/SmsTrai/Create/tests/index.test.js +77 -0
- package/v2Containers/SmsTrai/Create/tests/mockData.js +58 -0
- package/v2Containers/SmsTrai/Edit/constants.js +16 -0
- package/v2Containers/SmsTrai/Edit/index.js +483 -0
- package/v2Containers/SmsTrai/Edit/messages.js +58 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +16103 -0
- package/v2Containers/SmsTrai/Edit/tests/index.test.js +61 -0
- package/v2Containers/SmsTrai/Edit/tests/mockData.js +36 -0
- package/v2Containers/SmsWrapper/index.js +75 -0
- package/v2Containers/SmsWrapper/tests/index.test.js +10 -0
- package/v2Containers/Templates/_templates.scss +2 -0
- package/v2Containers/Templates/index.js +89 -27
- package/v2Containers/Templates/messages.js +16 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { FormattedMessage, injectIntl } from 'react-intl';
|
|
4
|
+
import { createStructuredSelector } from 'reselect';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import Dropzone from 'react-dropzone';
|
|
7
|
+
import { bindActionCreators } from 'redux';
|
|
8
|
+
import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
|
|
9
|
+
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
|
|
10
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
11
|
+
import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
|
|
12
|
+
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
13
|
+
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
14
|
+
import CapError from '@capillarytech/cap-ui-library/CapError';
|
|
15
|
+
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
|
|
16
|
+
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
17
|
+
import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
18
|
+
import Papa from 'papaparse';
|
|
19
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
20
|
+
import {
|
|
21
|
+
CAP_SPACE_12,
|
|
22
|
+
CAP_SPACE_16,
|
|
23
|
+
CAP_SPACE_24,
|
|
24
|
+
CAP_SPACE_32,
|
|
25
|
+
CAP_WHITE,
|
|
26
|
+
CAP_RED01,
|
|
27
|
+
CAP_PRIMARY,
|
|
28
|
+
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
29
|
+
import './index.scss';
|
|
30
|
+
import dragdropImage from '../../../assets/group.png';
|
|
31
|
+
import * as smsTraiActions from './actions';
|
|
32
|
+
import {
|
|
33
|
+
MANDATORY_COLUMNS,
|
|
34
|
+
HEADER_ALIASES,
|
|
35
|
+
SAMPLE_CSV_DATA,
|
|
36
|
+
DOWNLOAD_ISSUES_CSV,
|
|
37
|
+
SAMPLE_CSV,
|
|
38
|
+
CAPILLARY_REJECTION_REASON,
|
|
39
|
+
APPROVAL_STATUS,
|
|
40
|
+
APPROVED,
|
|
41
|
+
TEMPLATE_ID_ALIASES,
|
|
42
|
+
TEMPLATE_MESSAGE_ALIASES,
|
|
43
|
+
SAVED_COLUMNS,
|
|
44
|
+
MAPPED_SAVED_COLUMN,
|
|
45
|
+
SMS,
|
|
46
|
+
} from './constants';
|
|
47
|
+
import messages from './messages';
|
|
48
|
+
import withCreatives from '../../../hoc/withCreatives';
|
|
49
|
+
|
|
50
|
+
export const SmsTraiCreate = (props) => {
|
|
51
|
+
const { intl, actions, onCreateComplete } = props;
|
|
52
|
+
const { formatMessage } = intl;
|
|
53
|
+
const [files, setFiles] = useState([]);
|
|
54
|
+
const [errorText, setErrorText] = useState('');
|
|
55
|
+
const [dropZone, setDropZone] = useState(true);
|
|
56
|
+
const [previewFileIndex, setPreviewFileIndex] = useState(null);
|
|
57
|
+
const [isFileDropped, setIsFileDropped] = useState(false);
|
|
58
|
+
const [totalFailCount, setTotalFailCount] = useState(0);
|
|
59
|
+
const [saveTemplate, updateSaveTemplate] = useState(false);
|
|
60
|
+
|
|
61
|
+
const SMSTraiFooter = styled.div`
|
|
62
|
+
background-color: ${CAP_WHITE};
|
|
63
|
+
position: fixed;
|
|
64
|
+
bottom: 0;
|
|
65
|
+
width: 100%;
|
|
66
|
+
margin-left: -32px;
|
|
67
|
+
padding: ${CAP_SPACE_32} ${CAP_SPACE_24};
|
|
68
|
+
.ant-btn {
|
|
69
|
+
margin-right: ${CAP_SPACE_16};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
let uploadValidated = false;
|
|
75
|
+
|
|
76
|
+
const getIndexofColumn = (allColumnNames, columnName) =>
|
|
77
|
+
allColumnNames.findIndex(
|
|
78
|
+
(column) => column.toLowerCase().trim() === columnName,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const checkCSVRowsValidation = (transformedResults, selectedHeaderAlias) => {
|
|
82
|
+
const headerIndex = getIndexofColumn(
|
|
83
|
+
transformedResults[0],
|
|
84
|
+
selectedHeaderAlias,
|
|
85
|
+
);
|
|
86
|
+
const approvalIndex = getIndexofColumn(
|
|
87
|
+
transformedResults[0],
|
|
88
|
+
APPROVAL_STATUS,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
for (let row = 1; row < transformedResults.length; row++) {
|
|
92
|
+
const errorArray = [];
|
|
93
|
+
const headerErrorArray = [];
|
|
94
|
+
const approvalErrorArray = [];
|
|
95
|
+
let rejectionReasons = '';
|
|
96
|
+
//adding rejection reasons
|
|
97
|
+
transformedResults[row].forEach((cell, index) => {
|
|
98
|
+
if (index === headerIndex && cell.length < 6) {
|
|
99
|
+
headerErrorArray.push(
|
|
100
|
+
`${selectedHeaderAlias.toUpperCase()} is mandatory and it should be 6 characters. `,
|
|
101
|
+
);
|
|
102
|
+
} else if (index === approvalIndex && cell.toLowerCase() !== APPROVED) {
|
|
103
|
+
approvalErrorArray.push(`Template is not approved. `);
|
|
104
|
+
} else if (!cell && index !== headerIndex && index !== approvalIndex) {
|
|
105
|
+
errorArray.push(`"${transformedResults[0][index]}"`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (errorArray.length !== 0) {
|
|
110
|
+
rejectionReasons = `${rejectionReasons} Please include these mandatory fields: ${errorArray.join(
|
|
111
|
+
' ',
|
|
112
|
+
)}.`;
|
|
113
|
+
}
|
|
114
|
+
if (headerErrorArray.length !== 0 || approvalErrorArray.length !== 0) {
|
|
115
|
+
rejectionReasons = `${rejectionReasons} ${headerErrorArray.toString()} ${approvalErrorArray.toString()}`;
|
|
116
|
+
}
|
|
117
|
+
transformedResults[row].push(rejectionReasons);
|
|
118
|
+
//if there are rejected reasons present in this row at column 6(where we store rejected reasons), consider it as failure
|
|
119
|
+
if (transformedResults[row][6]) {
|
|
120
|
+
setTotalFailCount((prev) => prev + 1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
uploadValidated = true;
|
|
124
|
+
return transformedResults;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const checkCSVValidation = (results, file) => {
|
|
128
|
+
if (file?.name?.split('.').pop() === 'csv') {
|
|
129
|
+
const mandatoryColNotPresent = [];
|
|
130
|
+
const headerAliasPresent = [];
|
|
131
|
+
const templateIdAliasPresent = [];
|
|
132
|
+
const templateMessageAliasPresent = [];
|
|
133
|
+
const colCounts = {};
|
|
134
|
+
//removing last empty row if each cell is empty
|
|
135
|
+
if (results?.data[results.data.length - 1]?.every((cell) => !cell)) {
|
|
136
|
+
results.data.pop();
|
|
137
|
+
}
|
|
138
|
+
const fileLen = results?.data?.length || 0;
|
|
139
|
+
// at least 1 records should be available
|
|
140
|
+
if (fileLen < 2) {
|
|
141
|
+
setErrorText(formatMessage(messages.fileMinlengthError));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// more then 500 records are not available.
|
|
145
|
+
if (fileLen > 501) {
|
|
146
|
+
setErrorText(formatMessage(messages.fileMaxlengthError));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// mandatory 3 header name should be available.
|
|
150
|
+
const cols = getAllColumnNames(results.data[0]);
|
|
151
|
+
|
|
152
|
+
for (let i = 0; i < MANDATORY_COLUMNS.length; i++) {
|
|
153
|
+
if (!cols.includes(MANDATORY_COLUMNS[i])) {
|
|
154
|
+
mandatoryColNotPresent.push(MANDATORY_COLUMNS[i]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (mandatoryColNotPresent.length > 0) {
|
|
158
|
+
setErrorText(
|
|
159
|
+
formatMessage(messages.invalidFileErrorDescription, {
|
|
160
|
+
columns: mandatoryColNotPresent.join(','),
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
//one of the mandatory header alias should be available.
|
|
167
|
+
for (let i = 0; i < HEADER_ALIASES.length; i++) {
|
|
168
|
+
if (cols.includes(HEADER_ALIASES[i])) {
|
|
169
|
+
headerAliasPresent.push(HEADER_ALIASES[i]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (headerAliasPresent.length === 0) {
|
|
173
|
+
setErrorText(
|
|
174
|
+
formatMessage(messages.headerAliasErrorDescription, {
|
|
175
|
+
headerAliases: HEADER_ALIASES.join(' / '),
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
//one of the mandatory template id alias should be available.
|
|
181
|
+
for (let i = 0; i < TEMPLATE_ID_ALIASES.length; i++) {
|
|
182
|
+
if (cols.includes(TEMPLATE_ID_ALIASES[i])) {
|
|
183
|
+
templateIdAliasPresent.push(TEMPLATE_ID_ALIASES[i]);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (templateIdAliasPresent.length === 0) {
|
|
187
|
+
setErrorText(
|
|
188
|
+
formatMessage(messages.templateIdAliasErrorDescription, {
|
|
189
|
+
templateIdAliases: TEMPLATE_ID_ALIASES.join(' / '),
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
//one of the mandatory template message alias should be available.
|
|
195
|
+
for (let i = 0; i < TEMPLATE_MESSAGE_ALIASES.length; i++) {
|
|
196
|
+
if (cols.includes(TEMPLATE_MESSAGE_ALIASES[i])) {
|
|
197
|
+
templateMessageAliasPresent.push(TEMPLATE_MESSAGE_ALIASES[i]);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (templateMessageAliasPresent.length === 0) {
|
|
201
|
+
setErrorText(
|
|
202
|
+
formatMessage(messages.templateMessageAliasErrorDescription, {
|
|
203
|
+
templateMessageAliases: TEMPLATE_MESSAGE_ALIASES.join(' / '),
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// count of each col to validate blank and repetitive cols
|
|
209
|
+
for (const num of cols) {
|
|
210
|
+
colCounts[num] = colCounts[num] ? colCounts[num] + 1 : 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const repetitiveColsArray = Object.keys(
|
|
214
|
+
Object.fromEntries(
|
|
215
|
+
Object.entries(colCounts).filter(
|
|
216
|
+
([key, value]) => key !== '' && value > 1,
|
|
217
|
+
),
|
|
218
|
+
),
|
|
219
|
+
);
|
|
220
|
+
//throw validation if repetitive columns present
|
|
221
|
+
if (repetitiveColsArray.length !== 0) {
|
|
222
|
+
setErrorText(
|
|
223
|
+
formatMessage(messages.repetitiveColsErrorDescription, {
|
|
224
|
+
repetitiveCols: repetitiveColsArray,
|
|
225
|
+
}),
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
//perform required data transformation
|
|
230
|
+
return dataTransformHandler(cols, results, headerAliasPresent);
|
|
231
|
+
} else {
|
|
232
|
+
setErrorText(formatMessage(messages.nonCsvErrorDescription));
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const dataTransformHandler = (cols, results, headerAliasPresent) => {
|
|
237
|
+
//getting the index of the cols for 6 mandatory cols and also taking care of alias of headers of mandatory columns
|
|
238
|
+
const indexArray = [];
|
|
239
|
+
for (let i = 0; i < cols.length; i++) {
|
|
240
|
+
if (
|
|
241
|
+
MANDATORY_COLUMNS.includes(cols[i]) ||
|
|
242
|
+
HEADER_ALIASES.includes(cols[i]) ||
|
|
243
|
+
TEMPLATE_ID_ALIASES.includes(cols[i]) ||
|
|
244
|
+
TEMPLATE_MESSAGE_ALIASES.includes(cols[i])
|
|
245
|
+
) {
|
|
246
|
+
indexArray.push(i);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//transforming data to keep only mandatory cols data using their index
|
|
250
|
+
const transformedResults = [];
|
|
251
|
+
for (const key in results.data) {
|
|
252
|
+
transformedResults.push(indexArray.map((i) => results.data[key][i]));
|
|
253
|
+
}
|
|
254
|
+
//add rejection header
|
|
255
|
+
transformedResults[0].push(CAPILLARY_REJECTION_REASON);
|
|
256
|
+
return checkCSVRowsValidation(transformedResults, headerAliasPresent[0]);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
//returns the headers(first row) of the uploaded file in lowercase
|
|
260
|
+
const getAllColumnNames = (data) => data.map((x) => x.toLowerCase().trim());
|
|
261
|
+
|
|
262
|
+
const onDrop = (droppedFile) => {
|
|
263
|
+
// Sets isFileDropped state to true to load the spinner while the file is being parsed.
|
|
264
|
+
setIsFileDropped(true);
|
|
265
|
+
|
|
266
|
+
Papa.parse(droppedFile[0], {
|
|
267
|
+
complete: (results, file) => {
|
|
268
|
+
setIsFileDropped(false); // Stop the spinner
|
|
269
|
+
const validatedResults = checkCSVValidation(results, file);
|
|
270
|
+
const selectedFiles = cloneDeep(files);
|
|
271
|
+
if (uploadValidated) {
|
|
272
|
+
//remove capillary rejection reason column if already present on reupload
|
|
273
|
+
//ex: user reuploads a csv which was earlier validated and rejection reasons were added for failed templates
|
|
274
|
+
const rejectionIndex = getAllColumnNames(results.data[0]).indexOf(
|
|
275
|
+
CAPILLARY_REJECTION_REASON.toLowerCase(),
|
|
276
|
+
);
|
|
277
|
+
if (rejectionIndex !== -1) {
|
|
278
|
+
results.data.forEach((row) => {
|
|
279
|
+
row.splice(rejectionIndex, 1);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
//adding validated rejection reason column to original results
|
|
283
|
+
for (let i = 0; i < validatedResults.length; i++) {
|
|
284
|
+
results.data[i].push(validatedResults[i][6]);
|
|
285
|
+
}
|
|
286
|
+
updateSaveTemplate(true);
|
|
287
|
+
}
|
|
288
|
+
selectedFiles.push({
|
|
289
|
+
file,
|
|
290
|
+
fileResult: results,
|
|
291
|
+
columnMapping: [],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
setFiles(selectedFiles);
|
|
295
|
+
setPreviewFileIndex(selectedFiles.length - 1);
|
|
296
|
+
setDropZone(false);
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const deleteCSV = () => {
|
|
302
|
+
setFiles([]);
|
|
303
|
+
setTotalFailCount(0);
|
|
304
|
+
setErrorText('');
|
|
305
|
+
setIsFileDropped(false);
|
|
306
|
+
setPreviewFileIndex(null);
|
|
307
|
+
setDropZone(true);
|
|
308
|
+
updateSaveTemplate(false);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
//common function to handle csv downloads
|
|
312
|
+
const downloadHandler = (event, data, downloadedFileName) => {
|
|
313
|
+
event.target.setAttribute(
|
|
314
|
+
'href',
|
|
315
|
+
`data:text/csv;charset=utf-8,${encodeURIComponent(data)}`,
|
|
316
|
+
);
|
|
317
|
+
event.target.setAttribute('download', downloadedFileName);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const downloadSampleCSV = (event) => {
|
|
321
|
+
const csvContent = SAMPLE_CSV_DATA.map((e) => e.join(',')).join('\n');
|
|
322
|
+
downloadHandler(event, csvContent, SAMPLE_CSV);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const downloadIssuesCSV = (event) => {
|
|
326
|
+
let csvContent = [];
|
|
327
|
+
const tempArray = [];
|
|
328
|
+
const fileData = files[0].fileResult.data;
|
|
329
|
+
const rejectionIndex = fileData[0].indexOf(CAPILLARY_REJECTION_REASON);
|
|
330
|
+
|
|
331
|
+
if (rejectionIndex !== -1) {
|
|
332
|
+
tempArray.push(fileData[0]);
|
|
333
|
+
for (let row = 1; row < fileData.length; row++) {
|
|
334
|
+
if (fileData[row][rejectionIndex]) {
|
|
335
|
+
tempArray.push(fileData[row]);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
csvContent = tempArray
|
|
339
|
+
.map((e) => e.join(','))
|
|
340
|
+
.map((e) => e.replace(/(\r\n|\n|\r)/gm, ' '))
|
|
341
|
+
.join('\n');
|
|
342
|
+
}
|
|
343
|
+
downloadHandler(event, csvContent, DOWNLOAD_ISSUES_CSV);
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const createCallback = ({ errorMessage }) => {
|
|
347
|
+
if (!errorMessage) {
|
|
348
|
+
CapNotification.success({
|
|
349
|
+
message: formatMessage(messages.smsCreateNotification),
|
|
350
|
+
});
|
|
351
|
+
actions.clearCreateResponse();
|
|
352
|
+
} else {
|
|
353
|
+
CapNotification.error({
|
|
354
|
+
message: errorMessage,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const onDoneCallback = () => {
|
|
360
|
+
//start spinner
|
|
361
|
+
setIsFileDropped(true);
|
|
362
|
+
const fileData = files[0].fileResult.data;
|
|
363
|
+
const rejectionIndex = fileData[0].indexOf(CAPILLARY_REJECTION_REASON);
|
|
364
|
+
const tempArray = [];
|
|
365
|
+
tempArray.push(fileData[0]);
|
|
366
|
+
|
|
367
|
+
// filter out non rejected rows
|
|
368
|
+
for (let row = 1; row < fileData.length; row++) {
|
|
369
|
+
if (!fileData[row][rejectionIndex]) {
|
|
370
|
+
tempArray.push(fileData[row]);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Filter out the columns index that needs to save
|
|
375
|
+
const cols = getAllColumnNames(tempArray[0]);
|
|
376
|
+
const indexArray = [];
|
|
377
|
+
for (let i = 0; i < cols.length; i++) {
|
|
378
|
+
if (
|
|
379
|
+
SAVED_COLUMNS.includes(cols[i]) ||
|
|
380
|
+
HEADER_ALIASES.includes(cols[i]) ||
|
|
381
|
+
TEMPLATE_ID_ALIASES.includes(cols[i]) ||
|
|
382
|
+
TEMPLATE_MESSAGE_ALIASES.includes(cols[i])
|
|
383
|
+
) {
|
|
384
|
+
indexArray.push(i);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
//transforming data to keep only saved cols data using their index
|
|
389
|
+
const transformedResults = [];
|
|
390
|
+
for (const key in tempArray) {
|
|
391
|
+
transformedResults.push(
|
|
392
|
+
indexArray.map((i) => {
|
|
393
|
+
if (key === '0') {
|
|
394
|
+
return MAPPED_SAVED_COLUMN[tempArray[key][i]];
|
|
395
|
+
}
|
|
396
|
+
return tempArray[key][i];
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// prepare data based on API
|
|
402
|
+
const savedData = [];
|
|
403
|
+
transformedResults.forEach((arr, index) => {
|
|
404
|
+
if (index !== 0) {
|
|
405
|
+
const obj = {};
|
|
406
|
+
arr.forEach((data, i) => {
|
|
407
|
+
obj[transformedResults[0][i]] = arr[i];
|
|
408
|
+
});
|
|
409
|
+
obj['unicode-validity'] = false;
|
|
410
|
+
obj['updated-sms-editor'] = '';
|
|
411
|
+
obj['var-mapped'] = {};
|
|
412
|
+
const history = obj;
|
|
413
|
+
history.tabKey = 1;
|
|
414
|
+
savedData.push({
|
|
415
|
+
versions: { base: obj },
|
|
416
|
+
history: [history],
|
|
417
|
+
type: SMS,
|
|
418
|
+
traiEnable: true,
|
|
419
|
+
name: obj.template_name,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
actions.createSMSTRAITemplates(
|
|
424
|
+
{ templates: savedData },
|
|
425
|
+
(resp, errorMessage) => {
|
|
426
|
+
createCallback({ errorMessage });
|
|
427
|
+
onCreateComplete();
|
|
428
|
+
},
|
|
429
|
+
);
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const isDisableDone = () => {
|
|
433
|
+
//only if there is a file which is validated and has passed templates
|
|
434
|
+
//the done is not disabled otherwise it is disabled
|
|
435
|
+
if (
|
|
436
|
+
previewFileIndex !== null &&
|
|
437
|
+
saveTemplate &&
|
|
438
|
+
files[0].fileResult.data.length - 1 - totalFailCount >= 1
|
|
439
|
+
) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
return true;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const onCancel = () => {
|
|
446
|
+
setFiles([]);
|
|
447
|
+
updateSaveTemplate(false);
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const InstructionComp = (inst) => (
|
|
451
|
+
<CapRow className="instruction">
|
|
452
|
+
<CapColumn span={3}>
|
|
453
|
+
<span className="instruction-num">{inst.number}</span>
|
|
454
|
+
</CapColumn>
|
|
455
|
+
<CapColumn span={21}>{inst.message}</CapColumn>
|
|
456
|
+
</CapRow>
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const fileData = files[previewFileIndex] || {};
|
|
460
|
+
const { fileResult, file } = fileData;
|
|
461
|
+
return (
|
|
462
|
+
<>
|
|
463
|
+
<CapRow span={24} className="upload-type-group-create-container">
|
|
464
|
+
<CapColumn span={16}>
|
|
465
|
+
<CapHeading type="h4" className="upload-file-title">
|
|
466
|
+
{formatMessage(messages.uploadFile)}
|
|
467
|
+
</CapHeading>
|
|
468
|
+
{previewFileIndex === null && dropZone && (
|
|
469
|
+
<>
|
|
470
|
+
<CapSpin spinning={isFileDropped}>
|
|
471
|
+
<Dropzone onDrop={onDrop} onFileDialogCancel={onCancel}>
|
|
472
|
+
{({ getRootProps, getInputProps }) => (
|
|
473
|
+
<div {...getRootProps()} className="file-drop-zone">
|
|
474
|
+
<input {...getInputProps()} />
|
|
475
|
+
<div>
|
|
476
|
+
<img
|
|
477
|
+
src={dragdropImage}
|
|
478
|
+
role="presentation"
|
|
479
|
+
alt="dropzone"
|
|
480
|
+
/>
|
|
481
|
+
<div className="drop-zone-text">
|
|
482
|
+
<CapHeading type="h2" className="drop-zone-text-1">
|
|
483
|
+
{formatMessage(messages.dropCsvFile)}
|
|
484
|
+
</CapHeading>
|
|
485
|
+
<CapHeading type="h3" className="drop-zone-text-2">
|
|
486
|
+
{formatMessage(messages.or)}
|
|
487
|
+
</CapHeading>
|
|
488
|
+
<CapButton
|
|
489
|
+
type="secondary"
|
|
490
|
+
className="drop-zone-text-3"
|
|
491
|
+
>
|
|
492
|
+
{formatMessage(messages.selectFileFromComputer)}
|
|
493
|
+
</CapButton>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
)}
|
|
498
|
+
</Dropzone>
|
|
499
|
+
</CapSpin>
|
|
500
|
+
</>
|
|
501
|
+
)}
|
|
502
|
+
{previewFileIndex !== null && (
|
|
503
|
+
<>
|
|
504
|
+
<CapRow span={24} className="upload-type-group-create-container">
|
|
505
|
+
<div className="upload-file-preview">
|
|
506
|
+
<CapDivider
|
|
507
|
+
type="horizontal"
|
|
508
|
+
className="upload-file-preview-divider-1"
|
|
509
|
+
/>
|
|
510
|
+
<CapRow>
|
|
511
|
+
<CapColumn span={13}>
|
|
512
|
+
<CapLabel.CapLabelInline type="label9">
|
|
513
|
+
{file.name}
|
|
514
|
+
</CapLabel.CapLabelInline>
|
|
515
|
+
<CapLabel.CapLabelInline
|
|
516
|
+
type="label1"
|
|
517
|
+
className="number-of-records"
|
|
518
|
+
>
|
|
519
|
+
(
|
|
520
|
+
{formatMessage(messages.numberOfRecords, {
|
|
521
|
+
value: fileResult.data.length - 1,
|
|
522
|
+
})}
|
|
523
|
+
)
|
|
524
|
+
</CapLabel.CapLabelInline>
|
|
525
|
+
</CapColumn>
|
|
526
|
+
{errorText && (
|
|
527
|
+
<>
|
|
528
|
+
<CapColumn
|
|
529
|
+
span={10}
|
|
530
|
+
className="upload-failed-container"
|
|
531
|
+
>
|
|
532
|
+
<CapLabel
|
|
533
|
+
type="label9"
|
|
534
|
+
className="upload-failed-message"
|
|
535
|
+
>
|
|
536
|
+
{formatMessage(messages.failedToUpload)}
|
|
537
|
+
</CapLabel>
|
|
538
|
+
<CapIcon
|
|
539
|
+
type="warning"
|
|
540
|
+
className="upload-failed-icon"
|
|
541
|
+
svgProps={{ color: CAP_RED01 }}
|
|
542
|
+
></CapIcon>
|
|
543
|
+
</CapColumn>
|
|
544
|
+
</>
|
|
545
|
+
)}
|
|
546
|
+
{!errorText && (
|
|
547
|
+
<>
|
|
548
|
+
<CapColumn
|
|
549
|
+
span={3}
|
|
550
|
+
className="validation-count-container"
|
|
551
|
+
>
|
|
552
|
+
<CapLabel
|
|
553
|
+
type="label9"
|
|
554
|
+
className="validation-count-label"
|
|
555
|
+
>
|
|
556
|
+
{formatMessage(messages.passedVal, {
|
|
557
|
+
number:
|
|
558
|
+
fileResult.data.length - 1 - totalFailCount,
|
|
559
|
+
})}
|
|
560
|
+
</CapLabel>
|
|
561
|
+
<CapIcon
|
|
562
|
+
type="check-circle"
|
|
563
|
+
className="validation-count-icon"
|
|
564
|
+
svgProps={{ color: CAP_PRIMARY.base }}
|
|
565
|
+
></CapIcon>
|
|
566
|
+
</CapColumn>
|
|
567
|
+
<CapColumn
|
|
568
|
+
span={3}
|
|
569
|
+
className="validation-count-container"
|
|
570
|
+
>
|
|
571
|
+
<CapLabel
|
|
572
|
+
type="label9"
|
|
573
|
+
className="validation-count-label"
|
|
574
|
+
>
|
|
575
|
+
{formatMessage(messages.failedVal, {
|
|
576
|
+
number: totalFailCount,
|
|
577
|
+
})}
|
|
578
|
+
</CapLabel>
|
|
579
|
+
<CapIcon
|
|
580
|
+
type="warning"
|
|
581
|
+
className="validation-count-icon"
|
|
582
|
+
svgProps={{ color: CAP_RED01 }}
|
|
583
|
+
></CapIcon>
|
|
584
|
+
</CapColumn>
|
|
585
|
+
<CapColumn span={4}>
|
|
586
|
+
{totalFailCount >= 1 && (
|
|
587
|
+
<a
|
|
588
|
+
onClick={downloadIssuesCSV}
|
|
589
|
+
className="download-issues-csv-link"
|
|
590
|
+
>
|
|
591
|
+
{formatMessage(messages.downloadIssues)}
|
|
592
|
+
</a>
|
|
593
|
+
)}
|
|
594
|
+
</CapColumn>
|
|
595
|
+
</>
|
|
596
|
+
)}
|
|
597
|
+
<CapColumn span={1} className="delete-csv">
|
|
598
|
+
<CapIcon
|
|
599
|
+
type="delete"
|
|
600
|
+
size="s"
|
|
601
|
+
onClick={deleteCSV}
|
|
602
|
+
></CapIcon>
|
|
603
|
+
</CapColumn>
|
|
604
|
+
</CapRow>
|
|
605
|
+
<CapDivider
|
|
606
|
+
type="horizontal"
|
|
607
|
+
className="upload-file-preview-divider-2"
|
|
608
|
+
/>
|
|
609
|
+
{errorText && <CapError>{errorText}</CapError>}
|
|
610
|
+
</div>
|
|
611
|
+
</CapRow>
|
|
612
|
+
</>
|
|
613
|
+
)}
|
|
614
|
+
</CapColumn>
|
|
615
|
+
<CapColumn span={8} className="upload-instructions-container">
|
|
616
|
+
<CapHeading type="h4">
|
|
617
|
+
{formatMessage(messages.uploadInstructions)}
|
|
618
|
+
</CapHeading>
|
|
619
|
+
<div className="upload-instruction-list">
|
|
620
|
+
<InstructionComp
|
|
621
|
+
number={1}
|
|
622
|
+
message={
|
|
623
|
+
<FormattedMessage
|
|
624
|
+
{...messages.downloadInstruction}
|
|
625
|
+
values={{
|
|
626
|
+
link: (
|
|
627
|
+
<a
|
|
628
|
+
onClick={downloadSampleCSV}
|
|
629
|
+
className="download-sample-link"
|
|
630
|
+
>
|
|
631
|
+
{formatMessage(messages.sampleCsv)}
|
|
632
|
+
</a>
|
|
633
|
+
),
|
|
634
|
+
}}
|
|
635
|
+
/>
|
|
636
|
+
}
|
|
637
|
+
/>
|
|
638
|
+
<InstructionComp
|
|
639
|
+
number={2}
|
|
640
|
+
message={formatMessage(messages.firstRowInstruction)}
|
|
641
|
+
/>
|
|
642
|
+
<InstructionComp
|
|
643
|
+
number={3}
|
|
644
|
+
message={formatMessage(messages.secondRowInstruction)}
|
|
645
|
+
/>
|
|
646
|
+
<InstructionComp
|
|
647
|
+
number={4}
|
|
648
|
+
message={formatMessage(messages.duplicateEntriesInstruction)}
|
|
649
|
+
/>
|
|
650
|
+
</div>
|
|
651
|
+
</CapColumn>
|
|
652
|
+
<SMSTraiFooter>
|
|
653
|
+
<CapButton
|
|
654
|
+
className="done-button"
|
|
655
|
+
onClick={onDoneCallback}
|
|
656
|
+
disabled={isDisableDone()}
|
|
657
|
+
>
|
|
658
|
+
<FormattedMessage {...messages.doneButtonLabel} />
|
|
659
|
+
</CapButton>
|
|
660
|
+
</SMSTraiFooter>
|
|
661
|
+
</CapRow>
|
|
662
|
+
</>
|
|
663
|
+
);
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
SmsTraiCreate.propTypes = {
|
|
667
|
+
dispatch: PropTypes.func.isRequired,
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
const mapStateToProps = createStructuredSelector({});
|
|
671
|
+
|
|
672
|
+
const mapDispatchToProps = (dispatch) => ({
|
|
673
|
+
actions: bindActionCreators(smsTraiActions, dispatch),
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
export default withCreatives({
|
|
677
|
+
WrappedComponent: injectIntl(SmsTraiCreate),
|
|
678
|
+
mapStateToProps,
|
|
679
|
+
mapDispatchToProps,
|
|
680
|
+
userAuth: true,
|
|
681
|
+
});
|