@plusscommunities/pluss-core-web 1.7.3-beta.0 → 1.7.3-beta.1

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.
Files changed (116) hide show
  1. package/.babelrc +3 -0
  2. package/dist/components.js +390 -33094
  3. package/dist/components.js.map +1 -1
  4. package/dist/index.cjs.js +10388 -0
  5. package/dist/index.esm.js +10349 -0
  6. package/dist/index.js +394 -33098
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.umd.js +10361 -0
  9. package/package.json +31 -22
  10. package/rollup.config.js +57 -0
  11. package/src/actions/AuthActions.js +83 -0
  12. package/src/actions/LocalActions.js +8 -0
  13. package/src/actions/NavActions.js +8 -0
  14. package/src/actions/ScheduledActionsActions.js +8 -0
  15. package/src/actions/TemplateActions.js +15 -0
  16. package/src/actions/UsersActions.js +95 -0
  17. package/src/actions/index.js +6 -0
  18. package/src/actions/types.js +23 -0
  19. package/src/analytics.js +183 -0
  20. package/src/apis/analyticsActions.js +53 -0
  21. package/src/apis/authActions.js +58 -0
  22. package/src/apis/fileActions.js +152 -0
  23. package/src/apis/index.js +10 -0
  24. package/src/apis/profileActions.js +133 -0
  25. package/src/apis/scheduledActionsActions.js +12 -0
  26. package/src/apis/stringActions.js +25 -0
  27. package/src/apis/templateActions.js +41 -0
  28. package/src/apis/typeActions.js +186 -0
  29. package/src/apis/userActions.js +169 -0
  30. package/src/apis/utilityActions.js +35 -0
  31. package/src/colours.js +134 -0
  32. package/src/components/AddButton.js +27 -0
  33. package/src/components/AnalyticsFilter.js +115 -0
  34. package/src/components/Attachment.js +27 -0
  35. package/src/components/AudienceIncluder.js +175 -0
  36. package/src/components/AudienceSelector.js +700 -0
  37. package/src/components/Button.js +66 -0
  38. package/src/components/CheckBox.js +77 -0
  39. package/src/components/ColourOptions.js +197 -0
  40. package/src/components/ColourPicker.js +267 -0
  41. package/src/components/Comment.js +46 -0
  42. package/src/components/CommentSection.js +116 -0
  43. package/src/components/DatePicker.js +290 -0
  44. package/src/components/DropdownInput.js +223 -0
  45. package/src/components/DurationInput.js +51 -0
  46. package/src/components/ExportCsvPopup.js +222 -0
  47. package/src/components/FileInput.js +389 -0
  48. package/src/components/GenericInput.js +169 -0
  49. package/src/components/Header.js +82 -0
  50. package/src/components/HubSidebar.js +134 -0
  51. package/src/components/ImageInput.js +987 -0
  52. package/src/components/InputGroup.js +18 -0
  53. package/src/components/MakerPopup.js +310 -0
  54. package/src/components/MoreMenu.js +22 -0
  55. package/src/components/OptionsSection.js +84 -0
  56. package/src/components/OverlayPage.js +74 -0
  57. package/src/components/OverlayPageBottomButtons.js +48 -0
  58. package/src/components/OverlayPageContents.js +48 -0
  59. package/src/components/OverlayPageSection.js +38 -0
  60. package/src/components/P60Icon.js +40 -0
  61. package/src/components/PageTitle.js +13 -0
  62. package/src/components/Popup.js +104 -0
  63. package/src/components/ProfilePic.js +50 -0
  64. package/src/components/RadioButton.js +144 -0
  65. package/src/components/Reactions.js +77 -0
  66. package/src/components/SVGIcon.js +33 -0
  67. package/src/components/SideNavItem.js +98 -0
  68. package/src/components/StatBox.js +49 -0
  69. package/src/components/StatusButton.js +22 -0
  70. package/src/components/SuccessPopup.js +57 -0
  71. package/src/components/Tabs.js +56 -0
  72. package/src/components/Tag.js +62 -0
  73. package/src/components/Text.js +20 -0
  74. package/src/components/TextFormatPopup.js +54 -0
  75. package/src/components/TimePicker.js +225 -0
  76. package/src/components/UserListing.js +111 -0
  77. package/src/components/index.js +46 -0
  78. package/src/components/svg-icons.json +821 -0
  79. package/src/config.js +25 -0
  80. package/src/helper/HelpDeskWidget.js +16 -0
  81. package/src/helper/api/getUrl.js +15 -0
  82. package/src/helper/api/getUrlParams.js +9 -0
  83. package/src/helper/api/safeReadParams.js +6 -0
  84. package/src/helper/auth/getUserFromState.js +8 -0
  85. package/src/helper/colours/getAppColourFromState.js +10 -0
  86. package/src/helper/files/canvasImageUploader.js +159 -0
  87. package/src/helper/files/generateImageName.js +10 -0
  88. package/src/helper/files/get1400.js +32 -0
  89. package/src/helper/files/getExtension.js +9 -0
  90. package/src/helper/files/getFileName.js +13 -0
  91. package/src/helper/files/getThumb300.js +36 -0
  92. package/src/helper/files/isVideo.js +8 -0
  93. package/src/helper/helper.js +97 -0
  94. package/src/helper/index.js +44 -0
  95. package/src/helper/site/getMerchantsFromState.js +9 -0
  96. package/src/helper/site/getSiteLevelFromState.js +34 -0
  97. package/src/helper/site/getSiteName.js +16 -0
  98. package/src/helper/site/getSiteNameFromRoles.js +12 -0
  99. package/src/helper/site/getSiteSettingFromState.js +10 -0
  100. package/src/helper/storage/readJSONFromStorage.js +9 -0
  101. package/src/helper/storage/readStorageWithCookie.js +22 -0
  102. package/src/helper/storage/setLocalStorage.js +5 -0
  103. package/src/helper/strings/getFirstName.js +10 -0
  104. package/src/helper/strings/htmlDecode.js +4 -0
  105. package/src/helper/strings/htmlEncode.js +4 -0
  106. package/src/helper/strings/isEmail.js +11 -0
  107. package/src/helper/strings/isUrl.js +13 -0
  108. package/src/helper/strings/onlyAlphanumeric.js +8 -0
  109. package/src/helper/strings/randomString.js +10 -0
  110. package/src/helper/strings/toParagraphed.js +17 -0
  111. package/src/index.js +12 -0
  112. package/src/reducers/ScheduledActionsReducer.js +14 -0
  113. package/src/reducers/TemplatesReducer.js +29 -0
  114. package/src/reducers/index.js +7 -0
  115. package/src/session.js +289 -0
  116. package/src/urls.js +21 -0
@@ -0,0 +1,987 @@
1
+ import React, { Component } from 'react';
2
+ import { connect } from 'react-redux';
3
+ import _ from 'lodash';
4
+ import $ from 'jquery';
5
+ import moment from 'moment';
6
+ import Dropzone from 'react-dropzone';
7
+ import FontAwesome from 'react-fontawesome';
8
+ import { get1400, getThumb300, isVideo } from '../helper';
9
+ import { fileActions } from '../apis';
10
+ import { validateAccess, getApiError } from '../session';
11
+ import { Popup } from './Popup';
12
+ import { Button } from './Button';
13
+ import { GenericInput } from './GenericInput';
14
+
15
+ const DEFAULT_INPUT = {
16
+ uploadingImage: false,
17
+ value: '',
18
+ displayValue: '',
19
+ };
20
+
21
+ class ImageInputComponent extends Component {
22
+ state = {
23
+ selectedTab: 'upload',
24
+ inputs: [
25
+ {
26
+ uploadingImage: false,
27
+ value: '',
28
+ displayValue: '',
29
+ },
30
+ ],
31
+ libraryLoadTriggered: false,
32
+ libraryLoaded: false,
33
+ imageLibrary: [],
34
+ loadingFolders: false,
35
+ addingFolder: false,
36
+ deletingFolder: {},
37
+ deletingImage: {},
38
+ folders: [],
39
+ addFolderOpen: false,
40
+ addFolderInput: '',
41
+ selectedFolder: null,
42
+ saveErrorMessage: '',
43
+ };
44
+
45
+ UNSAFE_componentWillMount() {
46
+ setTimeout(() => {
47
+ if (this.props.hasDefault) {
48
+ this.setState({
49
+ inputs: [
50
+ {
51
+ uploadingImage: false,
52
+ value: this.props.hasDefault,
53
+ displayValue: this.props.hasDefault,
54
+ },
55
+ ],
56
+ });
57
+ if (this.props.refreshCallback) {
58
+ this.props.refreshCallback(this.props.hasDefault);
59
+ }
60
+ }
61
+ }, 100);
62
+ }
63
+
64
+ componentDidMount() {
65
+ this.getFolders();
66
+ this.getLibrary();
67
+ }
68
+
69
+ getUploading(index) {
70
+ return this.state.inputs[index].uploadingImage;
71
+ }
72
+
73
+ getValue() {
74
+ if (this.props.multiple) {
75
+ return _.filter(
76
+ this.state.inputs.map((input) => {
77
+ return input.value;
78
+ }),
79
+ (url) => {
80
+ return !_.isEmpty(url);
81
+ },
82
+ );
83
+ }
84
+ return this.state.inputs[0].value;
85
+ }
86
+
87
+ setValue(val) {
88
+ if (typeof val === 'string') {
89
+ this.setState({
90
+ inputs: [
91
+ {
92
+ uploadingImage: false,
93
+ value: val,
94
+ displayValue: val,
95
+ },
96
+ ],
97
+ });
98
+ } else {
99
+ const inputs = [];
100
+ _.forEach(val, (str) => {
101
+ inputs.push({
102
+ uploadingImage: false,
103
+ value: str,
104
+ displayValue: str,
105
+ });
106
+ });
107
+ if (!this.props.limit || this.props.limit > inputs.length) inputs.push(DEFAULT_INPUT);
108
+ this.setState({ inputs });
109
+ }
110
+ }
111
+
112
+ getImageStyle(displayValue) {
113
+ if (_.isEmpty(displayValue)) {
114
+ return null;
115
+ }
116
+ const image = isVideo(displayValue) ? get1400(displayValue) : displayValue;
117
+ if (this.props.noCompress) {
118
+ return { backgroundImage: `url(${image})`, backgroundSize: 'contain' };
119
+ }
120
+ return { backgroundImage: `url(${getThumb300(image)})` };
121
+ }
122
+
123
+ getAccept = () => {
124
+ const accept = {
125
+ 'image/*': ['.jpeg', '.png'],
126
+ };
127
+ if (this.props.allowVideo) {
128
+ accept['video/*'] = ['.mp4'];
129
+ }
130
+ return accept;
131
+ };
132
+
133
+ getContainerClasses() {
134
+ let result = this.props.className || '';
135
+ if (this.props.noMenu) {
136
+ result += ' imageInputOuter-noMenu';
137
+ }
138
+ if (!this.props.multiple) {
139
+ result += ' imageInputOuter-single';
140
+ }
141
+ if (this.props.grid) {
142
+ result += ' imageInputOuter-grid';
143
+ }
144
+ return result;
145
+ }
146
+
147
+ getClassNames(input, inputsLength) {
148
+ let classes = 'imageInput';
149
+ if (input.uploadingImage) {
150
+ classes += ' imageInput-uploading';
151
+ } else if (!_.isEmpty(input.value) && !this.props.onlyAllowUpload) {
152
+ classes += ' imageInput-hasImage';
153
+ }
154
+ if (inputsLength === 1) {
155
+ classes += ' imageInput-only';
156
+ }
157
+ return classes;
158
+ }
159
+
160
+ getErrorState() {
161
+ if (!_.isUndefined(this.props.showError) && this.props.showError()) {
162
+ return ' imageInput_upload--error';
163
+ }
164
+ return '';
165
+ }
166
+
167
+ refreshFolders = async () => {
168
+ const res = await fileActions.getMediaFolders(this.props.auth.site);
169
+ return res.data;
170
+ };
171
+
172
+ getFolders() {
173
+ this.setState({ loadingFolders: true }, async () => {
174
+ try {
175
+ const folders = await this.refreshFolders();
176
+ this.setState({ loadingFolders: false, folders });
177
+ } catch (error) {
178
+ console.log('getFolders - error', error);
179
+ this.setState({ loadingFolders: false });
180
+ }
181
+ });
182
+ }
183
+
184
+ checkRefreshCallback = (inputs) => {
185
+ if (_.isUndefined(this.props.refreshCallback)) {
186
+ return;
187
+ }
188
+ if (!this.props.multiple) {
189
+ const image = _.find(inputs, (i) => {
190
+ return !i.uploadingImage && !_.isEmpty(i.value);
191
+ });
192
+ this.props.refreshCallback(image ? image.value : undefined);
193
+ } else {
194
+ const images = _.filter(inputs, (i) => {
195
+ return !i.uploadingImage && !_.isEmpty(i.value);
196
+ }).map((i) => {
197
+ return i.value;
198
+ });
199
+ this.props.refreshCallback(images);
200
+ }
201
+ };
202
+
203
+ parseImagesArray = (images) => {
204
+ return _.map(images, (f) => {
205
+ const uri = typeof f === 'string' ? f : f.uri;
206
+ const image1400 = get1400(uri);
207
+ return {
208
+ Value: uri,
209
+ Thumb: getThumb300(uri),
210
+ 1400: image1400,
211
+ Selected: _.some(this.state.inputs, (input) => input.value === uri || input.value === image1400),
212
+ };
213
+ });
214
+ };
215
+
216
+ getLibrary() {
217
+ if (this.state.libraryLoadTriggered) return;
218
+
219
+ this.setState({ libraryLoadTriggered: true }, async () => {
220
+ try {
221
+ const res = await fileActions.getMediaLibrary();
222
+ // console.log('getLibrary', res.data);
223
+ this.setState({
224
+ libraryLoaded: true,
225
+ imageLibrary: this.parseImagesArray(res.data),
226
+ });
227
+ } catch (error) {
228
+ console.log('getLibrary - error', error);
229
+ }
230
+ });
231
+ }
232
+
233
+ toggleLibraryItem(image) {
234
+ const newInputs = _.clone(this.state.inputs);
235
+ if (image.Selected) {
236
+ // Unselelct selected image
237
+ const index = _.findIndex(newInputs, (input) => {
238
+ return input.value === image.Value || input.value === image[1400];
239
+ });
240
+ if (index !== -1) {
241
+ newInputs.splice(index, 1);
242
+ }
243
+ image.Selected = false;
244
+ } else {
245
+ // Select unselected image
246
+ const newFile = {
247
+ uploadingImage: false,
248
+ value: image.Value,
249
+ displayValue: image[1400],
250
+ fromLibrary: true,
251
+ };
252
+ if (!this.props.multiple) {
253
+ newInputs[newInputs.length - 1] = newFile;
254
+ // Single image selector -> Unselect others
255
+ this.state.imageLibrary.map((i) => (i.Selected = false));
256
+ if (this.state.selectedFolder && this.state.selectedFolder.parsedImages)
257
+ this.state.selectedFolder.parsedImages.map((i) => (i.Selected = false));
258
+ image.Selected = true;
259
+ } else if (!(this.props.limit && newInputs.length === this.props.limit)) {
260
+ newInputs.splice(newInputs.length - 1, 0, newFile);
261
+ image.Selected = true;
262
+ }
263
+ }
264
+
265
+ if (_.isEmpty(newInputs)) newInputs.push(DEFAULT_INPUT);
266
+ this.setState({
267
+ inputs: newInputs,
268
+ imageLibrary: _.clone(this.state.imageLibrary),
269
+ selectedFolder: this.state.selectedFolder ? _.clone(this.state.selectedFolder) : undefined,
270
+ });
271
+ this.checkRefreshCallback(newInputs);
272
+ }
273
+
274
+ addFolder = () => {
275
+ this.setState({ addFolderOpen: true }, () => {
276
+ const input = document.getElementById('addFolderInput');
277
+ if (input) input.focus();
278
+ });
279
+ };
280
+
281
+ cancelAddStates = { addFolderOpen: false, addFolderInput: '', saveErrorMessage: '' };
282
+ cancelAddFolder = () => {
283
+ this.setState(this.cancelAddStates);
284
+ };
285
+
286
+ saveFolder = () => {
287
+ this.setState({ addingFolder: true }, async () => {
288
+ try {
289
+ await fileActions.addMediaFolder(this.props.auth.site, this.state.addFolderInput);
290
+ const folders = await this.refreshFolders();
291
+ this.setState({
292
+ addingFolder: false,
293
+ folders,
294
+ ...this.cancelAddStates,
295
+ });
296
+ } catch (error) {
297
+ console.log('addFolder - error', error);
298
+ this.setState({ addingFolder: false, saveErrorMessage: getApiError(error).message });
299
+ }
300
+ });
301
+ };
302
+
303
+ isStockFolder = (folder) => {
304
+ if (!folder) folder = this.state.selectedFolder;
305
+ return folder && _.isNil(folder.RowId);
306
+ };
307
+
308
+ selectFolder = (selectedFolder) => {
309
+ if (!this.isStockFolder(selectedFolder)) {
310
+ selectedFolder.parsedImages = this.parseImagesArray(selectedFolder.Images);
311
+ }
312
+ this.setState({ selectedFolder });
313
+ };
314
+
315
+ selectRoot = () => {
316
+ this.getFolders();
317
+ this.setState({ selectedFolder: null });
318
+ };
319
+
320
+ checkSetDisplayValue(input, value) {
321
+ const displayValue = get1400(value);
322
+ const storeValue = isVideo(value) || this.props.noCompress ? value : get1400(value);
323
+ $('<img/>')
324
+ .attr('src', `${displayValue}?t=${moment().valueOf()}`)
325
+ .on('load', () => {
326
+ $(this).remove();
327
+ input.value = storeValue;
328
+ input.displayValue = storeValue;
329
+ input.uploadingImage = false;
330
+ const newInputs = _.clone(this.state.inputs);
331
+ this.setState({ inputs: newInputs }, () => {
332
+ const { selectedTab, selectedFolder } = this.state;
333
+ if (selectedTab === 'library' && selectedFolder) {
334
+ // Uploading to a user folder
335
+ const { RowId, Images } = selectedFolder;
336
+ const images = [...Images, value];
337
+ fileActions.addImagesToFolder(RowId, this.props.auth.site, [value]).catch((error) => {
338
+ console.log('addImagesToFolder error', error);
339
+ });
340
+ selectedFolder.Images = images;
341
+ selectedFolder.parsedImages = this.parseImagesArray(images);
342
+ this.setState({ selectedFolder: _.clone(selectedFolder) });
343
+ }
344
+ });
345
+ this.checkRefreshCallback(newInputs);
346
+ })
347
+ .on('error', () => {
348
+ $(this).remove();
349
+ setTimeout(() => {
350
+ this.checkSetDisplayValue(input, value);
351
+ }, 200);
352
+ });
353
+ }
354
+
355
+ onDrop = (files, fromLibrary = false) => {
356
+ if (this.props.multiple) {
357
+ const newInputs = _.clone(this.state.inputs);
358
+ files.forEach((file) => {
359
+ if (this.props.limit && newInputs.length === this.props.limit) {
360
+ // hit limit - replace last input if it's empty
361
+ if (_.isEmpty(newInputs[newInputs.length - 1].value)) {
362
+ const newInput = newInputs[newInputs.length - 1];
363
+ this.handleFile(newInput, file);
364
+ }
365
+ } else {
366
+ const newInput = {
367
+ ...DEFAULT_INPUT,
368
+ fromLibrary,
369
+ };
370
+ newInputs.splice(newInputs.length - 1, 0, newInput);
371
+ this.handleFile(newInput, file);
372
+ }
373
+ });
374
+ this.setState({
375
+ inputs: newInputs,
376
+ });
377
+ this.checkRefreshCallback(newInputs);
378
+ } else {
379
+ this.handleFile(this.state.inputs[0], files[0]);
380
+ }
381
+ };
382
+
383
+ handleFile = async (input, file) => {
384
+ if (!file || input.uploadingImage) {
385
+ return;
386
+ }
387
+ input.uploadingImage = true;
388
+ const newInputs = _.clone(this.state.inputs);
389
+ this.setState({
390
+ inputs: newInputs,
391
+ });
392
+ this.checkRefreshCallback(newInputs);
393
+ try {
394
+ let uploadFile = file;
395
+ if (!isVideo(file.name)) {
396
+ uploadFile = await fileActions.compressImage(
397
+ file,
398
+ this.props.maxSize || 1400,
399
+ this.props.quality || 0.8,
400
+ this.props.noCompress || false,
401
+ );
402
+ }
403
+ const url = await fileActions.uploadMediaAsync(uploadFile, uploadFile.name);
404
+ this.checkSetDisplayValue(input, url);
405
+ } catch (error) {
406
+ console.log('handleFile error', error);
407
+ this.setState({ uploadingImage: false });
408
+ }
409
+ };
410
+
411
+ removeImage(input) {
412
+ const newState = {};
413
+ if (input.fromLibrary) {
414
+ input.fromLibrary = undefined;
415
+ const selectedFolderImages = this.state.selectedFolder ? this.state.selectedFolder.parsedImages : [];
416
+ const imageList = this.isStockFolder() ? this.state.imageLibrary : selectedFolderImages;
417
+ const libraryFile = _.find(imageList, (f) => {
418
+ return f.Value === input.value || f[1400] === input.value;
419
+ });
420
+ if (libraryFile) {
421
+ libraryFile.Selected = false;
422
+ if (this.isStockFolder()) {
423
+ newState.imageLibrary = _.clone(this.state.imageLibrary);
424
+ } else {
425
+ newState.selectedFolder = this.state.selectedFolder ? _.clone(this.state.selectedFolder) : undefined;
426
+ }
427
+ }
428
+ }
429
+
430
+ input.value = '';
431
+ input.displayValue = '';
432
+
433
+ const newInputs = _.clone(this.state.inputs);
434
+
435
+ if (newInputs.length > 1) {
436
+ newInputs.splice(newInputs.indexOf(input), 1);
437
+ if (newInputs.length === this.props.limit - 1 && !_.isEmpty(newInputs[newInputs.length - 1].value)) {
438
+ // was full
439
+ newInputs.push(DEFAULT_INPUT);
440
+ }
441
+ }
442
+
443
+ newState.inputs = newInputs;
444
+
445
+ this.setState(newState);
446
+
447
+ this.checkRefreshCallback(newInputs);
448
+ }
449
+
450
+ selectTab(tab) {
451
+ this.setState({ selectedTab: tab });
452
+ }
453
+
454
+ handleChange = (event) => {
455
+ var stateChange = {};
456
+ stateChange[event.target.getAttribute('id')] = event.target.value;
457
+ this.setState(stateChange);
458
+ };
459
+
460
+ deleteFolder = (event, folder) => {
461
+ event.stopPropagation();
462
+
463
+ if (window.confirm(`Are you sure you want to delete ${folder.Name}?`)) {
464
+ const deletingFolder = { ...this.state.deletingFolder };
465
+ deletingFolder[folder.RowId] = true;
466
+ this.setState({ deletingFolder: _.clone(deletingFolder), ...this.cancelAddStates }, async () => {
467
+ try {
468
+ await fileActions.deleteMediaFolder(folder.RowId, this.props.auth.site);
469
+ const folders = await this.refreshFolders();
470
+ deletingFolder[folder.RowId] = false;
471
+ this.setState({
472
+ deletingFolder: _.clone(deletingFolder),
473
+ folders,
474
+ });
475
+ } catch (error) {
476
+ console.log('addFolder - error', getApiError(error));
477
+ deletingFolder[folder.RowId] = false;
478
+ this.setState({ deletingFolder: _.clone(deletingFolder) });
479
+ }
480
+ });
481
+ }
482
+ };
483
+
484
+ deleteImage = (event, image) => {
485
+ if (window.confirm('Are you sure you want to delete the image?')) {
486
+ // Unselect the image first if selected
487
+ if (image.Selected) this.toggleLibraryItem(image);
488
+
489
+ const deletingImage = { ...this.state.deletingImage };
490
+ deletingImage[image.Value] = true;
491
+ this.setState({ deletingImage: _.clone(deletingImage) }, async () => {
492
+ try {
493
+ const { selectedFolder } = this.state;
494
+ const { RowId, Images } = selectedFolder;
495
+ await fileActions.deleteImagesFromFolder(RowId, this.props.auth.site, [image.Value]);
496
+ const images = Images.filter((i) => i.uri !== image.Value);
497
+ selectedFolder.Images = images;
498
+ selectedFolder.parsedImages = this.parseImagesArray(images);
499
+ deletingImage[image.Value] = false;
500
+ this.setState({ deletingImage: _.clone(deletingImage), selectedFolder: _.clone(selectedFolder) });
501
+ } catch (error) {
502
+ console.log('deleteImage - error', getApiError(error));
503
+ deletingImage[image.Value] = false;
504
+ this.setState({ deletingImage: _.clone(deletingImage) });
505
+ }
506
+ });
507
+ }
508
+ };
509
+
510
+ canManageLibrary = () => {
511
+ return validateAccess(this.props.auth.site, 'manageImageLibrary', this.props.auth);
512
+ };
513
+
514
+ canAddImageToLibrary = () => {
515
+ return validateAccess(this.props.auth.site, 'addToImageLibrary', this.props.auth);
516
+ };
517
+
518
+ renderInput(input, index, inputsLength) {
519
+ const renderContent = () => {
520
+ if (this.props.onlyAllowUpload) {
521
+ return null;
522
+ }
523
+ if (isVideo(input.displayValue)) {
524
+ return <video className="imageInput_image" style={this.props.style} controls src={input.displayValue} />;
525
+ }
526
+ if (this.props.simpleStyle && !_.isEmpty(input.displayValue) && !input.uploadingImage) {
527
+ return (
528
+ <div className="imageInput_simpleContainer">
529
+ <div
530
+ className="imageInput_image"
531
+ style={{
532
+ ...this.getImageStyle(input.displayValue),
533
+ ...this.props.style,
534
+ // height: this.props.style && this.props.style.height ? this.props.style.height : 80,
535
+ }}
536
+ >
537
+ <Dropzone disabled={this.props.disabled} accept={this.getAccept()} onDrop={(files) => this.onDrop(files, true)}>
538
+ {(state) => {
539
+ const { getRootProps, getInputProps } = state;
540
+
541
+ return (
542
+ <div className="imageInput_buttonContainer-simple" {...getRootProps()}>
543
+ <input {...getInputProps()} />
544
+ <Button buttonType="secondary" className="imageInput_button imageInput_button-simple" disabled>
545
+ Change image
546
+ </Button>
547
+ </div>
548
+ );
549
+ }}
550
+ </Dropzone>
551
+ </div>
552
+ </div>
553
+ );
554
+ }
555
+ return <div className="imageInput_image" style={{ ...this.getImageStyle(input.displayValue), ...this.props.style }}></div>;
556
+ };
557
+
558
+ const renderRemove = () => {
559
+ return (
560
+ !this.props.disableRemove &&
561
+ !this.props.disabled &&
562
+ (this.props.simpleStyle ? (
563
+ <FontAwesome name="remove" className="imageInput_removeIcon" onClick={this.removeImage.bind(this, input)} />
564
+ ) : (
565
+ <p className="imageInput_remove" onClick={this.removeImage.bind(this, input)}>
566
+ remove
567
+ </p>
568
+ ))
569
+ );
570
+ };
571
+
572
+ const renderDownload = () => {
573
+ if (isVideo(input.displayValue)) return null;
574
+ if (this.props.noDownload) return null;
575
+ return (
576
+ <a href={input.value} target="_blank" className="imageInput_download">
577
+ <img
578
+ alt=""
579
+ className="dlicon"
580
+ src="https://pluss60-dev-uploads.s3.ap-southeast-2.amazonaws.com/uploads/users/ap-southeast-2:efbab8db-136a-446e-b14a-d00af0067841/public/025971e94153af280049fdf3e3/downloadicon.png"
581
+ ></img>
582
+ </a>
583
+ );
584
+ };
585
+
586
+ const mediaText = this.props.allowVideo ? 'media' : `image${this.props.multiple ? 's' : ''}`;
587
+ return (
588
+ <div key={index} className={this.getClassNames(input, inputsLength)} style={{ ...this.props.style }}>
589
+ <Dropzone disabled={this.props.disabled} accept={this.getAccept()} onDrop={this.onDrop}>
590
+ {(state) => {
591
+ const { getRootProps, getInputProps, isDragActive } = state;
592
+ return (
593
+ <div style={{ ...this.props.style }} className={`imageInput_upload ${this.getErrorState()}`} {...getRootProps()}>
594
+ <input {...getInputProps()} />
595
+ <div
596
+ className={`${isDragActive ? 'imageInput_dropZoneActive' : ''} ${this.props.horizontalText ? ' imageInput_horizontalText' : ''
597
+ }`}
598
+ >
599
+ {!this.props.simpleStyle && (
600
+ <img
601
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
602
+ className="imageInput_icon"
603
+ alt="file"
604
+ />
605
+ )}
606
+ <p className="imageInput_helpText">{isDragActive ? `Drop ${mediaText} here` : `Drag and drop ${mediaText} or`}</p>
607
+ {!isDragActive && (
608
+ <Button buttonType="secondary" className="imageInput_button" disabled>
609
+ {this.props.multiple ? `Upload ${mediaText}` : `Upload an ${mediaText}`}
610
+ </Button>
611
+ )}
612
+ </div>
613
+ </div>
614
+ );
615
+ }}
616
+ </Dropzone>
617
+ <div className="imageInput_uploading" style={{ ...this.props.style }}>
618
+ <FontAwesome className="spinner imageInput_spinner" name="spinner fa-pulse fa-fw" />
619
+ </div>
620
+ {renderContent()}
621
+ {renderRemove()}
622
+ {renderDownload()}
623
+ </div>
624
+ );
625
+ }
626
+
627
+ renderLibraryImage(image, index, allowDelete) {
628
+ // console.log(image);
629
+ let classes = 'imageInput imageInput-hasImage imageInput-libraryImage';
630
+ if (image.Selected) {
631
+ classes += ' imageInput-librarySelected';
632
+ }
633
+ if (this.props.multiple && this.props.limit && this.state.inputs.length === this.props.limit) {
634
+ classes += ' imageInput-libraryDisabled';
635
+ }
636
+ return (
637
+ <div key={index} className={classes}>
638
+ <div
639
+ className="imageInput_image"
640
+ style={{ ...this.getImageStyle(image[1400]) }}
641
+ onClick={this.toggleLibraryItem.bind(this, image)}
642
+ ></div>
643
+ <div className="imageInput_selected">
644
+ <FontAwesome className="imageInput_selectedIcon" name={'check'} />
645
+ </div>
646
+ {allowDelete && (
647
+ <div className="imageInput_delete">
648
+ {this.state.deletingImage[image.Value] ? (
649
+ <FontAwesome style={styles.saveImageSpinner} name="spinner fa-pulse fa-fw" />
650
+ ) : (
651
+ <FontAwesome className="imageInput_deleteIcon" name="minus-circle" onClick={(event) => this.deleteImage(event, image)} />
652
+ )}
653
+ </div>
654
+ )}
655
+ </div>
656
+ );
657
+ }
658
+
659
+ renderError() {
660
+ if (!_.isUndefined(this.props.showError) && this.props.showError()) {
661
+ return <div className={'fieldLabel fieldLabel-warning'}>{this.props.errorMessage ? this.props.errorMessage : 'Required'}</div>;
662
+ }
663
+ return null;
664
+ }
665
+
666
+ renderMenu() {
667
+ if (this.props.noMenu || this.props.disabled) {
668
+ return null;
669
+ }
670
+ return (
671
+ <div className="imageInputMenu">
672
+ <p
673
+ className={`imageInputMenu__option ${this.state.selectedTab === 'upload' ? 'imageInputMenu__option--selected' : ''}`}
674
+ onClick={() => {
675
+ this.selectTab('upload');
676
+ }}
677
+ >
678
+ UPLOAD
679
+ </p>
680
+ <p
681
+ className={`imageInputMenu__option ${this.state.selectedTab === 'library' ? 'imageInputMenu__option--selected' : ''}`}
682
+ onClick={() => {
683
+ this.selectTab('library');
684
+ }}
685
+ >
686
+ LIBRARY
687
+ </p>
688
+ </div>
689
+ );
690
+ }
691
+
692
+ renderAddFolder() {
693
+ const isSaving = this.state.addingFolder;
694
+
695
+ if (this.state.addFolderOpen) {
696
+ return (
697
+ <div className={'imageFolderContainer'}>
698
+ <FontAwesome name={'folder'} className={'folderIcon'} />
699
+ <GenericInput
700
+ id="addFolderInput"
701
+ type="text"
702
+ placeholder="Enter folder name here"
703
+ value={this.state.addFolderInput}
704
+ onChange={this.handleChange}
705
+ style={styles.addFolderInputContainer}
706
+ inputStyle={styles.addFolderInputText}
707
+ readOnly={isSaving}
708
+ showError={() => !_.isEmpty(this.state.saveErrorMessage)}
709
+ errorMessage={this.state.saveErrorMessage}
710
+ />
711
+ {isSaving ? (
712
+ <div className={'addControlContainer'}>
713
+ <FontAwesome style={styles.saveFolderSpinner} name="spinner fa-pulse fa-fw" />
714
+ </div>
715
+ ) : (
716
+ <div className={'addControlContainer'}>
717
+ <Button inline buttonType="secondary" onClick={this.cancelAddFolder} style={styles.addFolderCancel}>
718
+ Cancel
719
+ </Button>
720
+ <Button
721
+ inline
722
+ buttonType="primary"
723
+ isActive={!_.isEmpty(this.state.addFolderInput)}
724
+ onClick={this.saveFolder}
725
+ style={styles.addFolderSave}
726
+ >
727
+ Save
728
+ </Button>
729
+ </div>
730
+ )}
731
+ </div>
732
+ );
733
+ }
734
+ return (
735
+ <div className={'imageFolderContainer'} onClick={this.addFolder}>
736
+ <FontAwesome name={'plus'} className={'addIcon'} />
737
+ <div className={'addText'}>Add New Folder</div>
738
+ </div>
739
+ );
740
+ }
741
+
742
+ renderFolder(folder, index) {
743
+ const timeStamp = moment(folder.Timestamp).format('DD MMM, YYYY, h:mma');
744
+ const countText = `${folder.Images ? folder.Images.length : 0} images`;
745
+
746
+ return (
747
+ <div key={index} className={'imageFolderContainer'} onClick={this.selectFolder.bind(this, folder)}>
748
+ <FontAwesome name={'folder'} className={'folderIcon'} />
749
+ <div className={'folderTitle'}>{folder.Name}</div>
750
+ {!this.isStockFolder(folder) && (
751
+ <div className={'folderAttributeGroup'}>
752
+ <div className={'folderAttribute'}>{folder.User && folder.User.displayName}</div>
753
+ <div className={'folderAttribute'} style={{ width: '130px' }}>
754
+ {timeStamp}
755
+ </div>
756
+ <div className={'folderAttribute'} style={{ textAlign: 'right', paddingRight: '5px' }}>
757
+ {countText}
758
+ </div>
759
+ {this.state.deletingFolder[folder.RowId] ? (
760
+ <FontAwesome style={styles.saveFolderSpinner} name="spinner fa-pulse fa-fw" />
761
+ ) : (
762
+ this.canManageLibrary() && (
763
+ <a onClick={(event) => this.deleteFolder(event, folder)}>
764
+ <FontAwesome style={styles.removeFolderButton} name="minus-circle" />
765
+ </a>
766
+ )
767
+ )}
768
+ </div>
769
+ )}
770
+ </div>
771
+ );
772
+ }
773
+
774
+ renderFolders() {
775
+ return (
776
+ <div className={`imageInputContainer imageInputContainer-library`}>
777
+ {this.canAddImageToLibrary() && this.renderAddFolder()}
778
+ {this.state.folders.map((folder, index) => {
779
+ return this.renderFolder(folder, index);
780
+ })}
781
+ </div>
782
+ );
783
+ }
784
+
785
+ renderBreadCrumbs() {
786
+ return (
787
+ <div className={'imageBreadCrumb'}>
788
+ <div className={'root'} onClick={this.selectRoot}>
789
+ Library
790
+ </div>
791
+ <FontAwesome name={'angle-left'} className={'separator'} />
792
+ <div className={'folder'}>{this.state.selectedFolder.Name}</div>
793
+ </div>
794
+ );
795
+ }
796
+
797
+ renderFolderImages() {
798
+ const stockFolder = this.isStockFolder();
799
+ const canDelete = !stockFolder && this.canManageLibrary();
800
+ const uploadingImages = this.state.inputs.filter((i) => i.uploadingImage);
801
+ const mediaText = this.props.allowVideo ? 'media' : `image${this.props.multiple ? 's' : ''}`;
802
+
803
+ return (
804
+ <div className={`imageInputContainer imageInputContainer-library`} style={{ ...this.props.style }}>
805
+ {this.renderBreadCrumbs()}
806
+ {stockFolder ? (
807
+ this.state.imageLibrary.map((image, index) => {
808
+ return this.renderLibraryImage(image, index);
809
+ })
810
+ ) : (
811
+ <div>
812
+ {this.canAddImageToLibrary() && (
813
+ <Dropzone disabled={this.props.disabled} accept={this.getAccept()} onDrop={(files) => this.onDrop(files, true)}>
814
+ {(state) => {
815
+ const { getRootProps, getInputProps, isDragActive } = state;
816
+ return (
817
+ <div style={{ padding: '15px' }} className={`imageInput_upload folder ${this.getErrorState()}`} {...getRootProps()}>
818
+ <input {...getInputProps()} />
819
+ <div className={isDragActive ? 'imageInput_dropZoneActive' : ''}>
820
+ <img
821
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
822
+ className="imageInput_icon"
823
+ alt="file"
824
+ />
825
+ <p className="imageInput_helpText">{isDragActive ? `Drop ${mediaText} here` : `Drag and drop ${mediaText} or`}</p>
826
+ {!isDragActive && (
827
+ <Button buttonType="secondary" className="imageInput_button" disabled>
828
+ {this.props.multiple ? `Upload ${mediaText}` : `Upload an ${mediaText}`}
829
+ </Button>
830
+ )}
831
+ </div>
832
+ </div>
833
+ );
834
+ }}
835
+ </Dropzone>
836
+ )}
837
+ {this.state.selectedFolder.parsedImages.map((image, index) => {
838
+ return this.renderLibraryImage(image, index, canDelete);
839
+ })}
840
+ {!_.isNil(uploadingImages) &&
841
+ uploadingImages.map((input, index) => {
842
+ return (
843
+ <div key={index} className={this.getClassNames(input)}>
844
+ <div className="imageInput_uploading">
845
+ <FontAwesome className="spinner imageInput_spinner" name="spinner fa-pulse fa-fw" />
846
+ </div>
847
+ </div>
848
+ );
849
+ })}
850
+ </div>
851
+ )}
852
+ </div>
853
+ );
854
+ }
855
+
856
+ renderUploadImages(inputs) {
857
+ return (
858
+ <div className={`imageInputContainer ${inputs.length === 1 ? 'imageInputContainer-noPadding' : ''}`} style={{ ...this.props.style }}>
859
+ {inputs.map((input, index) => {
860
+ return this.renderInput(input, index, inputs.length);
861
+ })}
862
+ </div>
863
+ );
864
+ }
865
+
866
+ renderContent() {
867
+ return this.renderUploadImages(this.state.inputs);
868
+ }
869
+
870
+ renderLibraryPopup() {
871
+ if (this.state.selectedTab === 'upload') {
872
+ return null;
873
+ }
874
+ let content = null;
875
+ if (this.state.selectedFolder) {
876
+ content = this.renderFolderImages();
877
+ } else {
878
+ content = this.renderFolders();
879
+ }
880
+ return (
881
+ <Popup
882
+ onClose={() => {
883
+ this.selectTab('upload');
884
+ }}
885
+ hasPadding
886
+ buttons={[
887
+ {
888
+ type: 'primary',
889
+ onClick: () => {
890
+ this.selectTab('upload');
891
+ },
892
+ isActive: true,
893
+ text: 'Done',
894
+ },
895
+ ]}
896
+ title="Select images"
897
+ >
898
+ {content}
899
+ </Popup>
900
+ );
901
+ }
902
+
903
+ renderButtons() {
904
+ if (this.props.noMenu || this.props.disabled) {
905
+ return null;
906
+ }
907
+ return (
908
+ <div className="imageInputRight">
909
+ <Dropzone disabled={this.props.disabled} accept={this.getAccept()} onDrop={(files) => this.onDrop(files, true)}>
910
+ {(state) => {
911
+ const { getRootProps, getInputProps } = state;
912
+ return (
913
+ <div {...getRootProps()} className="imageInputRight_button">
914
+ <input {...getInputProps()} />
915
+ <FontAwesome className="imageInputRight_button_icon" name="plus-circle" />
916
+ <p className="imageInputRight_button_text">Upload File</p>
917
+ </div>
918
+ );
919
+ }}
920
+ </Dropzone>
921
+ <div
922
+ className="imageInputRight_button"
923
+ onClick={() => {
924
+ this.selectTab('library');
925
+ }}
926
+ >
927
+ <FontAwesome className="imageInputRight_button_icon" name="picture-o" />
928
+ <p className="imageInputRight_button_text">Image Library</p>
929
+ </div>
930
+ </div>
931
+ );
932
+ }
933
+
934
+ render() {
935
+ return (
936
+ <div style={this.props.containerStyle} className={`imageInputOuter ${this.getContainerClasses()}`}>
937
+ {this.renderButtons()}
938
+ {this.renderContent()}
939
+ {this.renderLibraryPopup()}
940
+ </div>
941
+ );
942
+ }
943
+ }
944
+
945
+ const styles = {
946
+ addFolderInputContainer: {
947
+ marginBottom: '0px',
948
+ paddingRight: '100px',
949
+ width: '70%',
950
+ },
951
+ addFolderInputText: {
952
+ paddingBottom: '0px',
953
+ },
954
+ addFolderCancel: {
955
+ paddingLeft: '8px',
956
+ paddingRight: '8px',
957
+ width: '80px',
958
+ marginRight: '16px',
959
+ },
960
+ addFolderSave: {
961
+ paddingLeft: '8px',
962
+ paddingRight: '8px',
963
+ width: '80px',
964
+ },
965
+ saveFolderSpinner: {
966
+ fontSize: '20px',
967
+ marginLeft: '16px',
968
+ },
969
+ saveImageSpinner: {
970
+ fontSize: '18px',
971
+ },
972
+ removeFolderButton: {
973
+ fontSize: '20px',
974
+ padding: '5px',
975
+ marginLeft: '16px',
976
+ cursor: 'pointer',
977
+ },
978
+ };
979
+
980
+ const mapStateToProps = (state) => {
981
+ return {
982
+ auth: state.auth,
983
+ };
984
+ };
985
+
986
+ const ImageInput = connect(mapStateToProps, {}, null, { forwardRef: true })(ImageInputComponent);
987
+ export { ImageInput };