@plusscommunities/pluss-newsletter-web-sharing 1.2.8

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 (52) hide show
  1. package/.babelrc +3 -0
  2. package/dist/index.cjs.js +7451 -0
  3. package/dist/index.esm.js +7399 -0
  4. package/dist/index.umd.js +7434 -0
  5. package/package.default.json +43 -0
  6. package/package.json +45 -0
  7. package/package.test.json +43 -0
  8. package/rollup.config.js +59 -0
  9. package/src/actions/NewsActions.js +102 -0
  10. package/src/actions/index.js +9 -0
  11. package/src/actions/types.js +10 -0
  12. package/src/components/ActivityText.js +73 -0
  13. package/src/components/MakerPopup/index.js +3 -0
  14. package/src/components/PreviewFull.js +32 -0
  15. package/src/components/PreviewGrid.js +19 -0
  16. package/src/components/PreviewWidget.js +28 -0
  17. package/src/components/Reactions/index.js +3 -0
  18. package/src/components/ViewFull.js +19 -0
  19. package/src/components/ViewWidget.js +17 -0
  20. package/src/components/index.js +25 -0
  21. package/src/components/text/index.js +4 -0
  22. package/src/config/index.js +9 -0
  23. package/src/feature.config.default.js +235 -0
  24. package/src/feature.config.js +235 -0
  25. package/src/feature.config.sharing.js +235 -0
  26. package/src/feature.config.test.js +235 -0
  27. package/src/helper/index.js +17 -0
  28. package/src/images/full.png +0 -0
  29. package/src/images/fullNoTitle.png +0 -0
  30. package/src/images/fullNoTitleCondensed.png +0 -0
  31. package/src/images/previewWidget.png +0 -0
  32. package/src/images/widget.png +0 -0
  33. package/src/index.js +33 -0
  34. package/src/js/Events.js +26 -0
  35. package/src/js/index.js +20 -0
  36. package/src/reducers/NewsReducer.js +82 -0
  37. package/src/screens/Newsletter/AddNewsletterEntry.js +1186 -0
  38. package/src/screens/Newsletter/AvailableNews.js +124 -0
  39. package/src/screens/Newsletter/GenerateNewsletter.js +891 -0
  40. package/src/screens/Newsletter/ListNewsletterEntries.js +234 -0
  41. package/src/screens/Newsletter/NewsHub.js +328 -0
  42. package/src/screens/Newsletter/NewsHubAnalytics.js +320 -0
  43. package/src/screens/Newsletter/NewsletterAnalytics.js +140 -0
  44. package/src/screens/Newsletter/NewsletterSubmission.js +476 -0
  45. package/src/screens/Newsletter/NewsletterSubmissions.js +278 -0
  46. package/src/screens/Newsletter/NewsletterTemplate.js +1051 -0
  47. package/src/screens/Newsletter/PublishAvailableNews.js +450 -0
  48. package/src/session/index.js +7 -0
  49. package/src/webapi/eventActions.js +18 -0
  50. package/src/webapi/helper.js +4 -0
  51. package/src/webapi/index.js +11 -0
  52. package/src/webapi/newsletterActions.js +153 -0
@@ -0,0 +1,891 @@
1
+ import React, { Component } from 'react';
2
+ import { Table } from 'react-bootstrap';
3
+ import { connect } from 'react-redux';
4
+ import _ from 'lodash';
5
+ import moment from 'moment';
6
+ import { v4 } from 'uuid';
7
+ import FontAwesome from 'react-fontawesome';
8
+ import { Button, Header, CheckBox, GenericInput, DatePicker, Popup, ImageInput } from '../../components';
9
+ import { newsLoaded, setAuth } from '../../actions';
10
+ import { eventActions, newsletterActions, typeActions } from '../../webapi';
11
+ import { Text } from '../../components/text';
12
+ import { MakerPopup } from '../../components/MakerPopup';
13
+ import { COLOUR_BRANDING_APP, getEventTimes } from '../../js';
14
+ import { get1400, getPluralS, getSiteNameFromRoles, onlyAlphanumeric, setLocalStorage } from '../../helper';
15
+ import { logo } from '../../config';
16
+ import { values } from '../../feature.config';
17
+
18
+ class GenerateNewsletter extends Component {
19
+ constructor(props) {
20
+ super(props);
21
+
22
+ let nextMonth = moment();
23
+ nextMonth.add(1, 'month');
24
+ nextMonth = nextMonth.startOf('month');
25
+ const nextMonthEnd = moment(nextMonth).endOf('month');
26
+
27
+ this.state = {
28
+ loadingAll: false,
29
+ selectedNews: [],
30
+ selectedEvents: [],
31
+ startDate: nextMonth.format('YYYY-MM-DD'),
32
+ startDateText: nextMonth.format('DD/MM/YYYY'),
33
+ endDate: nextMonthEnd.format('YYYY-MM-DD'),
34
+ endDateText: nextMonthEnd.format('DD/MM/YYYY'),
35
+ issueTitle: moment().format('MMMM YYYY'),
36
+ newsletterTitle: `${getSiteNameFromRoles(this.props.auth.site, this.props.auth.user.Roles)} ${values.textNewsletterTitlePostfix}`,
37
+ };
38
+ }
39
+
40
+ componentDidMount() {
41
+ this.getBranding();
42
+ this.getData();
43
+ this.loadEvents();
44
+ }
45
+
46
+ getBranding() {
47
+ typeActions.getSiteBranding(this.props.auth.site).then((res) => {
48
+ const brandData = {
49
+ siteName: res.data.Name,
50
+ colour: res.data.MainBrandingColour || COLOUR_BRANDING_APP,
51
+ logo: res.data.Logo || logo,
52
+ headerType: res.data.HeaderType || 'white',
53
+ };
54
+
55
+ const lastUpdate = moment.utc().valueOf();
56
+
57
+ setLocalStorage(values.localStorageNewsletterBrandData, brandData, true);
58
+ setLocalStorage(values.localStorageNewsletterMakerLastUpdate, lastUpdate);
59
+
60
+ this.setState({
61
+ loadingBranding: false,
62
+ brandData,
63
+ lastUpdate,
64
+ });
65
+ });
66
+ }
67
+
68
+ getData() {
69
+ this.setState({
70
+ loadingAll: true,
71
+ });
72
+ newsletterActions
73
+ .recursiveGetNews(this.props.auth.site)
74
+ .then((res) => {
75
+ this.setState({
76
+ loadingAll: false,
77
+ });
78
+ if (!_.isEmpty(res) && res[0].Site === this.props.auth.site) {
79
+ this.props.newsLoaded(res);
80
+ }
81
+ })
82
+ .catch((res) => {
83
+ this.setState({ loadingAll: false });
84
+ });
85
+ }
86
+
87
+ getNewsAuthor = (post) => {
88
+ if (post.hideAuthor) {
89
+ return null;
90
+ }
91
+ let author = post.Author;
92
+ if (this.props.auth.siteSettings && this.props.auth.siteSettings[values.optionForceNewsAuthorName]) {
93
+ author = author || post.SubmittedBy;
94
+ }
95
+ return author;
96
+ };
97
+
98
+ getSelectedEvents = () => {
99
+ if (!this.state.includeEvents) {
100
+ return [];
101
+ }
102
+ return _.sortBy(this.state.selectedEvents, (e) => {
103
+ return moment(e.Date).valueOf();
104
+ }).map((e) => {
105
+ return {
106
+ Title: _.trim(e.Title),
107
+ Timestamp: e.TimeSort,
108
+ Date: moment.utc(e.TimeSort).local().format('dddd D MMMM'),
109
+ Time: moment.utc(e.TimeSort).local().format('h:mma'),
110
+ };
111
+ });
112
+ };
113
+
114
+ loadEvents() {
115
+ this.setState({
116
+ loadingEvents: true,
117
+ });
118
+
119
+ eventActions
120
+ .getEvents(this.props.auth.site, 0, true)
121
+ .then((res) => {
122
+ this.setState({
123
+ loadingEvents: false,
124
+ });
125
+ if (res.data != null && !_.isEmpty(res.data) && res.data[0].Site === this.props.auth.site) {
126
+ this.setEvents(res.data);
127
+ }
128
+ })
129
+ .catch((res) => {
130
+ this.setState({ loadingEvents: false });
131
+ alert('Something went wrong with the request. Please try again.');
132
+ });
133
+ }
134
+
135
+ setEvents(data) {
136
+ const eventReps = getEventTimes(data);
137
+ const events = _.filter(eventReps, (e) => {
138
+ return (
139
+ e.TimeSort >= moment(this.state.startDate, 'YYYY-MM-DD').valueOf() &&
140
+ e.TimeSort <= moment(this.state.endDate, 'YYYY-MM-DD').valueOf()
141
+ );
142
+ });
143
+ this.setState({
144
+ selectedEvents: events,
145
+ });
146
+ this.checkCompileMakerData();
147
+ }
148
+
149
+ toggleIncludeEvents = () => {
150
+ this.setState({
151
+ includeEvents: !this.state.includeEvents,
152
+ });
153
+ this.checkCompileMakerData();
154
+ };
155
+
156
+ checkCompileMakerData() {
157
+ const lastChange = moment.utc().valueOf();
158
+ this.setState({
159
+ lastChange,
160
+ });
161
+ // if no changes have been made for 1 second, refresh preview
162
+ setTimeout(() => {
163
+ if (lastChange === this.state.lastChange) {
164
+ this.compileMakerData();
165
+ }
166
+ }, 1000);
167
+ }
168
+
169
+ compileMakerData() {
170
+ const makerData = {
171
+ title: this.state.newsletterTitle,
172
+ month: this.state.issueTitle,
173
+ news: this.state.selectedNews.map((n) => {
174
+ return {
175
+ Images: n.Images,
176
+ Title: n.Title,
177
+ Text: n.Text,
178
+ Author: this.getNewsAuthor(n),
179
+ };
180
+ }),
181
+ events: this.getSelectedEvents(),
182
+ };
183
+ const lastUpdate = moment.utc().valueOf();
184
+
185
+ setLocalStorage(values.localStorageNewsletterMakerData, makerData, true);
186
+ setLocalStorage(values.localStorageNewsletterMakerLastUpdate, lastUpdate);
187
+
188
+ this.setState({ makerData, lastUpdate });
189
+ }
190
+
191
+ validateForm() {
192
+ return !_.isEmpty(this.state.selectedNews);
193
+ }
194
+
195
+ handleNewsChange = (key, value) => {
196
+ this.setState({
197
+ editingNews: { ...this.state.editingNews, [key]: value },
198
+ });
199
+ };
200
+
201
+ toggleNewsAuthor = () => {
202
+ this.setState({
203
+ editingNews: { ...this.state.editingNews, hideAuthor: !this.state.editingNews.hideAuthor },
204
+ });
205
+ };
206
+
207
+ handleAuthorNameChange = (value) => {
208
+ this.setState({
209
+ editingNews: { ...this.state.editingNews, Author: { ...this.getNewsAuthor(this.state.editingNews), displayName: value } },
210
+ });
211
+ };
212
+
213
+ handleStartDateTextChange(value) {
214
+ const newState = { startDateText: value };
215
+ const m = moment(value, 'DD/MM/YYYY');
216
+
217
+ if (m.isValid() && m.year() > 1900) {
218
+ newState.startDate = m.format('YYYY-MM-DD');
219
+ }
220
+ this.setState(newState);
221
+ }
222
+
223
+ handleStartDateChange(date) {
224
+ var stateChange = {
225
+ startDate: date,
226
+ startDateText: moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY'),
227
+ showStartDate: false,
228
+ };
229
+ this.setState(stateChange);
230
+ }
231
+
232
+ handleEndDateTextChange(value) {
233
+ const newState = { endDateText: value };
234
+ const m = moment(value, 'DD/MM/YYYY');
235
+
236
+ if (m.isValid() && m.year() > 1900) {
237
+ newState.endDate = m.format('YYYY-MM-DD');
238
+ }
239
+ this.setState(newState);
240
+ }
241
+
242
+ handleEndDateChange(date) {
243
+ var stateChange = {
244
+ endDate: date,
245
+ endDateText: moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY'),
246
+ showEndDate: false,
247
+ };
248
+ this.setState(stateChange);
249
+ }
250
+
251
+ handleTextChange = (event) => {
252
+ var stateChange = {};
253
+ stateChange[event.target.getAttribute('id')] = event.target.value;
254
+ this.setState(stateChange);
255
+ this.checkCompileMakerData();
256
+ };
257
+
258
+ handleSubmit = () => {
259
+ if (!this.validateForm()) {
260
+ return;
261
+ }
262
+ this.setState({
263
+ makerOpen: true,
264
+ });
265
+ };
266
+
267
+ closeMaker = () => {
268
+ this.setState({
269
+ makerOpen: false,
270
+ });
271
+ };
272
+
273
+ onPressNews = (post) => {
274
+ if (this.isSelected(post.RowId)) {
275
+ this.setState({
276
+ selectedNews: _.filter(this.state.selectedNews, (n) => {
277
+ return n.RowId !== post.RowId;
278
+ }),
279
+ });
280
+ } else {
281
+ const newSelection = [...this.state.selectedNews, post];
282
+ this.setState({
283
+ selectedNews: newSelection,
284
+ });
285
+ }
286
+ this.checkCompileMakerData();
287
+ };
288
+
289
+ onMoveUpNews = (post, index) => {
290
+ if (index === 0) return;
291
+ let newNews = [...this.state.selectedNews];
292
+ newNews.splice(index, 1);
293
+ newNews.splice(index - 1, 0, post);
294
+ this.setState({
295
+ selectedNews: newNews,
296
+ });
297
+ this.checkCompileMakerData();
298
+ };
299
+
300
+ onMoveDownNews = (post, index) => {
301
+ if (index === this.state.selectedNews.length - 1) return;
302
+ let newNews = [...this.state.selectedNews];
303
+ newNews.splice(index + 2, 0, post);
304
+ newNews.splice(index, 1);
305
+ this.setState({
306
+ selectedNews: newNews,
307
+ });
308
+ this.checkCompileMakerData();
309
+ };
310
+
311
+ checkSetImage(post) {
312
+ if (this.refs.newsImageInput) {
313
+ if (post.Images) {
314
+ this.refs.newsImageInput.getWrappedInstance().setValue(post.Images);
315
+ } else {
316
+ this.refs.newsImageInput.getWrappedInstance().setValue(post.Image);
317
+ }
318
+ } else {
319
+ setTimeout(() => {
320
+ this.checkSetImage(post);
321
+ }, 100);
322
+ }
323
+ }
324
+
325
+ checkSetAuthorImage(post) {
326
+ if (this.refs.authorImage) {
327
+ const author = this.getNewsAuthor(post);
328
+ if (author && author.profilePic) {
329
+ this.refs.authorImage.getWrappedInstance().setValue(author.profilePic);
330
+ }
331
+ } else {
332
+ setTimeout(() => {
333
+ this.checkSetAuthorImage(post);
334
+ }, 100);
335
+ }
336
+ }
337
+
338
+ onPressAddNews = () => {
339
+ const post = { Title: '', Text: '', Images: [], Author: {}, hideAuthor: true, isNew: true, RowId: v4() };
340
+ this.setState({
341
+ editingNews: post,
342
+ });
343
+ this.checkSetImage(post);
344
+ this.checkSetAuthorImage(post);
345
+ };
346
+
347
+ onPressEditNews = (post) => {
348
+ const author = this.getNewsAuthor(post) || {};
349
+ this.setState({
350
+ editingNews: { ...post, hideAuthor: !author || _.isEmpty(Object.keys(author)), Author: author },
351
+ });
352
+ this.checkSetImage(post);
353
+ this.checkSetAuthorImage(post);
354
+ };
355
+
356
+ cancelEditNews = () => {
357
+ this.setState({
358
+ editingNews: null,
359
+ });
360
+ };
361
+
362
+ saveEditNews = () => {
363
+ const newNews = [...this.state.selectedNews];
364
+ const author = this.getNewsAuthor(this.state.editingNews);
365
+ if (author) {
366
+ author.profilePic = this.refs.authorImage.getWrappedInstance().getValue();
367
+ }
368
+ const newEntry = {
369
+ ...this.state.editingNews,
370
+ Images: this.refs.newsImageInput
371
+ .getWrappedInstance()
372
+ .getValue()
373
+ .map((url) => {
374
+ return get1400(url);
375
+ }),
376
+ isNew: null,
377
+ };
378
+ if (this.state.editingNews.isNew) {
379
+ newNews.push(newEntry);
380
+ } else {
381
+ const index = _.findIndex(newNews, (n) => {
382
+ return n.RowId === this.state.editingNews.RowId;
383
+ });
384
+ newNews.splice(index, 1, newEntry);
385
+ }
386
+ this.setState({
387
+ selectedNews: newNews,
388
+ editingNews: null,
389
+ });
390
+ this.checkCompileMakerData();
391
+ };
392
+
393
+ onPressEditEvents = () => {
394
+ this.setState({
395
+ editingEvents: [...this.state.selectedEvents],
396
+ });
397
+ };
398
+
399
+ cancelEditEvents = () => {
400
+ this.setState({
401
+ editingEvents: null,
402
+ });
403
+ };
404
+
405
+ saveEditEvents = () => {
406
+ this.setState({
407
+ selectedEvents: [...this.state.editingEvents],
408
+ editingEvents: null,
409
+ });
410
+ this.checkCompileMakerData();
411
+ };
412
+
413
+ onRemoveEvent = (entryId) => {
414
+ this.setState({
415
+ editingEvents: _.filter(this.state.editingEvents, (e) => {
416
+ return e.EntryId !== entryId;
417
+ }),
418
+ });
419
+ };
420
+
421
+ isSelected(id) {
422
+ return _.some(this.state.selectedNews, (n) => {
423
+ return n.RowId === id;
424
+ });
425
+ }
426
+
427
+ renderEntry(post, index, isSelected) {
428
+ if (isSelected) {
429
+ return (
430
+ <tr key={post.RowId}>
431
+ <td>{moment.utc(post.Timestamp).local().format('DD-MM-YYYY')}</td>
432
+ <td className="table-TitleColumn">
433
+ <span>{post.Title}</span>
434
+ </td>
435
+ <td className="table-options">
436
+ <div style={{ display: 'flex', alignItems: 'center' }}>
437
+ <FontAwesome
438
+ className="pointer"
439
+ style={{ fontSize: 20, padding: 5, marginLeft: 12, visibility: index > 0 ? 'visible' : 'hidden' }}
440
+ name="arrow-up"
441
+ onClick={() => {
442
+ this.onMoveUpNews(post, index);
443
+ }}
444
+ />
445
+ <FontAwesome
446
+ className="pointer"
447
+ style={{
448
+ fontSize: 20,
449
+ padding: 5,
450
+ marginLeft: 12,
451
+ visibility: index < this.state.selectedNews.length - 1 ? 'visible' : 'hidden',
452
+ }}
453
+ name="arrow-down"
454
+ onClick={() => {
455
+ this.onMoveDownNews(post, index);
456
+ }}
457
+ />
458
+ <FontAwesome
459
+ className="pointer"
460
+ style={{ fontSize: 20, padding: 5, marginLeft: 12 }}
461
+ name="pencil"
462
+ onClick={() => {
463
+ this.onPressEditNews(post);
464
+ }}
465
+ />
466
+ <FontAwesome
467
+ className="pointer"
468
+ style={{ fontSize: 20, padding: 5, marginLeft: 12 }}
469
+ name="remove"
470
+ onClick={() => {
471
+ this.onPressNews(post);
472
+ }}
473
+ />
474
+ </div>
475
+ </td>
476
+ </tr>
477
+ );
478
+ }
479
+ return (
480
+ <tr
481
+ key={post.RowId}
482
+ className="pointer"
483
+ onClick={() => {
484
+ this.onPressNews(post);
485
+ }}
486
+ >
487
+ <td>{moment.utc(post.Timestamp).local().format('DD-MM-YYYY')}</td>
488
+ <td className="table-TitleColumn">
489
+ <span>{post.Title}</span>
490
+ </td>
491
+ <td className="table-options">
492
+ <div style={{ display: 'flex', alignItems: 'center' }}>
493
+ <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 12 }} name="plus" />
494
+ </div>
495
+ </td>
496
+ </tr>
497
+ );
498
+ }
499
+
500
+ renderSelection() {
501
+ if (_.isEmpty(this.state.selectedNews)) {
502
+ return <Text type="body">{values.textNoSelectedNews}</Text>;
503
+ }
504
+ return (
505
+ <Table className="plussTable" striped bordered condensed hover>
506
+ <thead>
507
+ <tr>
508
+ <th style={{ width: 100 }}>Date published</th>
509
+ <th>Title</th>
510
+ <th style={{ width: 30 }} />
511
+ </tr>
512
+ </thead>
513
+ <tbody>
514
+ {this.state.selectedNews.map((post, index) => {
515
+ return this.renderEntry(post, index, true);
516
+ })}
517
+ </tbody>
518
+ </Table>
519
+ );
520
+ }
521
+
522
+ renderContent() {
523
+ let source = _.sortBy(this.props.news, 'DateUnix');
524
+ if (this.state.sortDesc) {
525
+ source.reverse();
526
+ }
527
+ source = _.filter(source, (ev) => {
528
+ if (!ev) {
529
+ return false;
530
+ }
531
+ return true;
532
+ });
533
+
534
+ return (
535
+ <Table className="plussTable" striped bordered condensed hover>
536
+ <thead>
537
+ <tr>
538
+ <th style={{ width: 100 }}>Date published</th>
539
+ <th>Title</th>
540
+ <th style={{ width: 30 }} />
541
+ </tr>
542
+ </thead>
543
+ <tbody>
544
+ {source.map((post, index) => {
545
+ if (this.isSelected(post.RowId)) {
546
+ return null;
547
+ }
548
+ return this.renderEntry(post, index, false);
549
+ })}
550
+ </tbody>
551
+ </Table>
552
+ );
553
+ }
554
+
555
+ renderMakerPopup() {
556
+ if (!this.state.makerOpen) {
557
+ return null;
558
+ }
559
+ return (
560
+ <MakerPopup
561
+ title={values.textCreateNewsletter}
562
+ onClose={this.closeMaker}
563
+ initialData={this.state.makerData}
564
+ templateId="4a423aa0-eba1-46d1-baf3-1b3d972b505e"
565
+ minWidth={600}
566
+ inputs={[
567
+ {
568
+ key: 'title',
569
+ type: 'string',
570
+ title: values.textNewsletterTitle,
571
+ },
572
+ {
573
+ key: 'month',
574
+ type: 'string',
575
+ title: 'Issue Title',
576
+ },
577
+ {
578
+ key: 'news',
579
+ type: 'array',
580
+ title: values.textSelectedNews,
581
+ readOnly: true,
582
+ },
583
+ {
584
+ key: 'events',
585
+ type: 'array',
586
+ title: 'Calendar Events',
587
+ readOnly: true,
588
+ },
589
+ ]}
590
+ requestData={{
591
+ colorSpace: 'cmyk',
592
+ postProcessing: {
593
+ optimize: false,
594
+ targetDPI: 300,
595
+ },
596
+ fileName: onlyAlphanumeric(`${this.state.makerData.title}_${this.state.makerData.month}`),
597
+ }}
598
+ />
599
+ );
600
+ }
601
+
602
+ renderEditNewsPopup() {
603
+ if (!this.state.editingNews) {
604
+ return null;
605
+ }
606
+ return (
607
+ <Popup
608
+ hasPadding
609
+ title={`${this.state.editingNews.isNew ? 'Add' : 'Edit'} ${values.textAddEditNewsPostfix}`}
610
+ minWidth={600}
611
+ onClose={this.cancelEditNews}
612
+ buttons={[
613
+ {
614
+ type: 'primary',
615
+ onClick: this.saveEditNews,
616
+ isActive: true,
617
+ text: 'Save',
618
+ },
619
+ {
620
+ type: 'tertiary',
621
+ onClick: this.cancelEditNews,
622
+ isActive: true,
623
+ text: 'Cancel',
624
+ },
625
+ ]}
626
+ >
627
+ <Text type="highlightedHelp" className="marginTop-10">
628
+ {values.textNoteThisWillNotChangeInTheApp(this.state.editingNews.isNew ? 'add' : 'change')}
629
+ </Text>
630
+ <GenericInput
631
+ className="marginTop-10"
632
+ id="editingNewsTitle"
633
+ label="Title"
634
+ alwaysShowLabel
635
+ value={this.state.editingNews.Title}
636
+ onChange={(e) => this.handleNewsChange('Title', e.target.value)}
637
+ />
638
+ <GenericInput
639
+ className="marginTop-10"
640
+ id="editingNewsTitle"
641
+ label="Text"
642
+ alwaysShowLabel
643
+ value={this.state.editingNews.Text}
644
+ onChange={(e) => this.handleNewsChange('Text', e.target.value)}
645
+ type="textarea"
646
+ />
647
+ <Text type="formLabel" className="marginTop-10">
648
+ Images
649
+ </Text>
650
+ <ImageInput className="marginTop-5" ref="newsImageInput" multiple limit={50} />
651
+
652
+ <Text type="formLabel" className="marginTop-10">
653
+ Author
654
+ </Text>
655
+ <CheckBox
656
+ className="marginTop-5"
657
+ id={`hideAuthor`}
658
+ label="Hide Author"
659
+ isActive={this.state.editingNews.hideAuthor}
660
+ onChange={this.toggleNewsAuthor}
661
+ />
662
+ <div className="marginTop-5" style={{ display: this.state.editingNews.hideAuthor ? 'none' : 'flex' }}>
663
+ <div className="marginRight-10">
664
+ <Text type="formLabel" className="marginTop-10">
665
+ Author Image
666
+ </Text>
667
+ <ImageInput
668
+ ref="authorImage"
669
+ containerStyle={{ width: 150, height: 150 }}
670
+ style={{ width: 150, height: 150, borderRadius: 4 }}
671
+ simpleStyle
672
+ noMenu
673
+ noDownload
674
+ />
675
+ </div>
676
+ <div className="flex-1">
677
+ <GenericInput
678
+ className="marginTop-10"
679
+ id="authorName"
680
+ label="Author Name"
681
+ alwaysShowLabel
682
+ value={this.state.editingNews.Author.displayName}
683
+ onChange={(e) => this.handleAuthorNameChange(e.target.value)}
684
+ />
685
+ </div>
686
+ </div>
687
+ </Popup>
688
+ );
689
+ }
690
+
691
+ renderEvent(event, index) {
692
+ return (
693
+ <tr key={event.EntryId}>
694
+ <td>{moment.utc(event.TimeSort).local().format('DD-MM-YYYY h:mma')}</td>
695
+ <td className="table-TitleColumn">
696
+ <span>{event.Title}</span>
697
+ </td>
698
+ <td className="table-options">
699
+ <div style={{ display: 'flex', alignItems: 'center' }}>
700
+ <FontAwesome
701
+ className="pointer"
702
+ style={{ fontSize: 20, padding: 5, marginLeft: 12 }}
703
+ name="remove"
704
+ onClick={() => {
705
+ this.onRemoveEvent(event.EntryId);
706
+ }}
707
+ />
708
+ </div>
709
+ </td>
710
+ </tr>
711
+ );
712
+ }
713
+
714
+ renderEditEventsPopup() {
715
+ if (!this.state.editingEvents) {
716
+ return null;
717
+ }
718
+ return (
719
+ <Popup
720
+ hasPadding
721
+ title="Edit events"
722
+ minWidth={600}
723
+ onClose={this.cancelEditEvents}
724
+ buttons={[
725
+ {
726
+ type: 'primary',
727
+ onClick: this.saveEditEvents,
728
+ isActive: true,
729
+ text: 'Save',
730
+ },
731
+ {
732
+ type: 'tertiary',
733
+ onClick: this.cancelEditEvents,
734
+ isActive: true,
735
+ text: 'Cancel',
736
+ },
737
+ ]}
738
+ >
739
+ <Text type="highlightedHelp" className="marginTop-10">
740
+ Note: Your changes will be reset if you change the calendar time frame
741
+ </Text>
742
+
743
+ <Table className="plussTable" striped bordered condensed hover>
744
+ <thead>
745
+ <tr>
746
+ <th style={{ width: 100 }}>Time</th>
747
+ <th>Title</th>
748
+ <th style={{ width: 30 }} />
749
+ </tr>
750
+ </thead>
751
+ <tbody>
752
+ {this.state.editingEvents.map((event, index) => {
753
+ return this.renderEvent(event, index);
754
+ })}
755
+ </tbody>
756
+ </Table>
757
+ </Popup>
758
+ );
759
+ }
760
+
761
+ renderPreview() {
762
+ if (this.state.loadingBranding) {
763
+ return null;
764
+ }
765
+ return (
766
+ <iframe
767
+ key={this.state.lastUpdate}
768
+ title="NewsletterPreview"
769
+ className="newsletterPreview"
770
+ src={`${window.location.origin}${values.routeNewsletterTemplate}`}
771
+ />
772
+ );
773
+ }
774
+
775
+ render() {
776
+ return (
777
+ <div>
778
+ {this.renderEditNewsPopup()}
779
+ {this.renderEditEventsPopup()}
780
+ {this.renderMakerPopup()}
781
+ <Header />
782
+ <div className="pageContainer paddingVertical-20 paddingHorizontal-40">
783
+ <div className="marginBottom-40 flex flex-between flex-center">
784
+ <Text type="h1" className="">
785
+ {values.textCreateNewsletter}
786
+ </Text>
787
+ <Button inline buttonType="primary" onClick={this.handleSubmit} isActive={this.validateForm()}>
788
+ Create
789
+ </Button>
790
+ </div>
791
+ <div className="flex flex-reverse marginTop-60">
792
+ <div className="newsletterPreviewOuter">{this.renderPreview()}</div>
793
+ <div className="flex-1 paddingRight-20">
794
+ <div>
795
+ <Text type="h3">Basic Info</Text>
796
+ <GenericInput
797
+ className="marginTop-10"
798
+ id="newsletterTitle"
799
+ label={values.textNewsletterTitle}
800
+ alwaysShowLabel
801
+ value={this.state.newsletterTitle}
802
+ onChange={this.handleTextChange}
803
+ />
804
+ <GenericInput
805
+ className="marginTop-10"
806
+ id="issueTitle"
807
+ label="Issue Title"
808
+ alwaysShowLabel
809
+ value={this.state.issueTitle}
810
+ onChange={this.handleTextChange}
811
+ />
812
+ <Text type="h3" className="marginTop-20">
813
+ Event Calendar
814
+ </Text>
815
+ <CheckBox
816
+ className="marginTop-10"
817
+ id={`includeEvents`}
818
+ label="Include event calendar"
819
+ isActive={this.state.includeEvents}
820
+ onChange={this.toggleIncludeEvents}
821
+ />
822
+ {this.state.includeEvents && (
823
+ <Text type="body" className="marginTop-10">
824
+ {this.state.selectedEvents.length} event{getPluralS(this.state.selectedEvents.length)}
825
+ <FontAwesome className="pointer" style={{ marginLeft: 12 }} name="pencil" onClick={this.onPressEditEvents} />
826
+ </Text>
827
+ )}
828
+ {this.state.includeEvents && (
829
+ <div className="flex">
830
+ <div className="flex-1 paddingRight-20">
831
+ <GenericInput
832
+ className="marginTop-10"
833
+ id="eventStartDate"
834
+ label="Start Date"
835
+ alwaysShowLabel
836
+ value={this.state.startDateText}
837
+ onChange={(e) => this.handleStartDateTestChange(e.target.value)}
838
+ onClick={(e) => this.setState({ showStartDate: !this.state.showStartDate })}
839
+ />
840
+ {this.state.showStartDate && (
841
+ <DatePicker selectedDate={this.state.startDate} selectDate={this.handleStartDateChange.bind(this)} />
842
+ )}
843
+ </div>
844
+ <div className="flex-1 paddingLeft-20">
845
+ <GenericInput
846
+ className="marginTop-10"
847
+ id="eventEndDate"
848
+ label="End Date"
849
+ alwaysShowLabel
850
+ value={this.state.endDateText}
851
+ onChange={(e) => this.handleEndDateTextChange(e.target.value)}
852
+ onClick={(e) => this.setState({ showEndDate: !this.state.showEndDate })}
853
+ />
854
+ {this.state.showEndDate && (
855
+ <DatePicker selectedDate={this.state.endDate} selectDate={this.handleEndDateChange.bind(this)} />
856
+ )}
857
+ </div>
858
+ </div>
859
+ )}
860
+ </div>
861
+ <div className="marginTop-20 marginBottom-10 flex flex-center">
862
+ <Text type="h3" className="marginRight-20">
863
+ Selected
864
+ </Text>
865
+
866
+ <Button inline buttonType="primary" onClick={this.onPressAddNews} isActive>
867
+ Add new post
868
+ </Button>
869
+ </div>
870
+ {this.renderSelection()}
871
+ <Text type="h3" className="marginTop-20">
872
+ {values.textSelectNews}
873
+ </Text>
874
+ {this.renderContent()}
875
+ </div>
876
+ </div>
877
+ </div>
878
+ </div>
879
+ );
880
+ }
881
+ }
882
+
883
+ const mapStateToProps = (state) => {
884
+ const { auth } = state;
885
+ return {
886
+ news: state[values.reducerKey].news,
887
+ auth,
888
+ };
889
+ };
890
+
891
+ export default connect(mapStateToProps, { newsLoaded, setAuth })(GenerateNewsletter);