@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,1186 @@
1
+ import React, { Component } from 'react';
2
+ import { FormGroup } from 'react-bootstrap';
3
+ import _ from 'lodash';
4
+ import $ from 'jquery';
5
+ import moment from 'moment';
6
+ import { connect } from 'react-redux';
7
+ import FontAwesome from 'react-fontawesome';
8
+ import { newsUpdate, usersLoaded, addRecentlyCreated } from '../../actions';
9
+ import {
10
+ Button,
11
+ GenericInput,
12
+ ImageInput,
13
+ RadioButton,
14
+ OverlayPage,
15
+ OverlayPageContents,
16
+ OverlayPageSection,
17
+ OverlayPageBottomButtons,
18
+ UserListing,
19
+ P60Icon,
20
+ DatePicker,
21
+ TimePicker,
22
+ SuccessPopup,
23
+ AudienceSelector,
24
+ CheckBox,
25
+ TextFormatPopup,
26
+ OptionsSection,
27
+ FileInput,
28
+ } from '../../components';
29
+ import { newsletterActions, fileActions, userActions } from '../../webapi';
30
+ import { safeReadParams, getThumb300, get1400, getUTCFromTimeDatePickers, getPluralOptions, isVideo, getFileName } from '../../helper';
31
+ import { newsHaveTags, DEFAULT_ALLOW_COMMENTS } from '../../config';
32
+ import { checkLoggedIn, validateAccess } from '../../session';
33
+ import { Text } from '../../components/text';
34
+ import { values } from '../../feature.config';
35
+
36
+ class AddNewsletterEntry extends Component {
37
+ initialState = {
38
+ updateId: safeReadParams(this.props, 'updateId'),
39
+ titleInput: '',
40
+ textInput: '',
41
+ videoInput: '',
42
+ tagsInput: '',
43
+ publisherTitleInput: '',
44
+ tagged: [],
45
+ contacts: [],
46
+ showTagged: false,
47
+ showWarnings: false,
48
+ success: false,
49
+ submitting: false,
50
+ userSearch: '',
51
+ HasSponsor: false,
52
+ Sponsor: null,
53
+ isFeatured: false,
54
+ featuredExpiryDate: moment().format('YYYY-MM-DD'),
55
+ featuredExpiryDateText: moment().format('DD/MM/YYYY'),
56
+ differentPublish: false,
57
+ isTvMode: false,
58
+ allowComments: DEFAULT_ALLOW_COMMENTS,
59
+ shouldNotify: false,
60
+ publishDate: moment().format('YYYY-MM-DD'),
61
+ publishDateText: moment().format('DD/MM/YYYY'),
62
+ publishTime: '12:00pm',
63
+ attachments: [],
64
+ authorDisplay: false,
65
+ customAuthorInput: '',
66
+
67
+ isAudienceValid: true,
68
+ textFormatOpen: false,
69
+ selectedOption: 'audience',
70
+ };
71
+
72
+ state = { ...this.initialState };
73
+
74
+ UNSAFE_componentWillMount() {
75
+ checkLoggedIn(this, this.props.auth);
76
+ if (this.state.updateId) {
77
+ this.getData();
78
+ }
79
+ }
80
+
81
+ componentDidMount() {
82
+ setTimeout(() => {
83
+ if (
84
+ validateAccess(this.props.auth.site, values.permissionNewsletterSubmit, this.props.auth) &&
85
+ !validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth) &&
86
+ this.state.updateId != null
87
+ ) {
88
+ window.history.back();
89
+ } else {
90
+ this.getUsers();
91
+ }
92
+ }, 500);
93
+ this.props.addRecentlyCreated(values.featureKey);
94
+ }
95
+
96
+ getUsers() {
97
+ userActions.fetchUsers(this.props.auth.site).then((res) => {
98
+ if (res.data != null && !_.isEmpty(res.data.results.Items) && res.data.results.Items[0].site === this.props.auth.site) {
99
+ this.props.usersLoaded(res.data.results.Items);
100
+ }
101
+ });
102
+ }
103
+
104
+ toggleTextFormat(isOpen) {
105
+ this.setState({
106
+ textFormatOpen: isOpen,
107
+ });
108
+ }
109
+
110
+ updateAudienceValidation(valid) {
111
+ this.setState({
112
+ isAudienceValid: valid,
113
+ });
114
+ }
115
+
116
+ getData() {
117
+ newsletterActions.getNewsletterEntry(this.state.updateId).then((res) => {
118
+ this.parseUpdate(res.data);
119
+ });
120
+ }
121
+
122
+ parseUpdate(update) {
123
+ const newState = {
124
+ titleInput: update.Title,
125
+ textInput: update.Text,
126
+ videoInput: update.Video,
127
+ attachments: _.isEmpty(update.Attachments) ? [] : update.Attachments,
128
+ isTvMode: Boolean(update.TVMode),
129
+ isFeatured: Boolean(update.IsFeatured),
130
+ allowComments: Boolean(update.AllowComments),
131
+ featuredExpiryDate: moment(update.FeaturedExpiry || undefined).format('YYYY-MM-DD'),
132
+ featuredExpiryDateText: moment(update.FeaturedExpiry || undefined).format('DD/MM/YYYY'),
133
+ authorDisplay: update.AuthorDisplay,
134
+ };
135
+ if (update.AuthorDisplay === 'custom') {
136
+ newState.customAuthorInput = update.Author ? update.Author.displayName : '';
137
+ }
138
+
139
+ if (!_.isEmpty(update.Tags)) {
140
+ newState.tagsInput = update.Tags.join(', ');
141
+ }
142
+
143
+ if (!_.isEmpty(update.Tagged)) {
144
+ newState.tagged = update.Tagged;
145
+ }
146
+
147
+ if (!_.isUndefined(update.HasSponsor)) {
148
+ newState.HasSponsor = update.HasSponsor;
149
+ }
150
+ if (!_.isUndefined(update.Sponsor)) {
151
+ newState.Sponsor = update.Sponsor;
152
+ }
153
+ if (update.Publisher && !_.isEmpty(update.Publisher.Title)) {
154
+ newState.publisherTitleInput = update.Publisher.Title;
155
+ }
156
+
157
+ this.checkSetImage(update);
158
+ // this.checkSetPublisherImage(update);
159
+
160
+ this.setState(newState);
161
+ }
162
+
163
+ handlePDFFileChange(event, attachment) {
164
+ const file = event.target.files[0];
165
+ if (!file || attachment.Uploading) {
166
+ return;
167
+ }
168
+ attachment.Uploading = true;
169
+ this.setState({
170
+ attachments: this.state.attachments,
171
+ });
172
+ /* fileActions.readBase64(file)
173
+ .then((res) => { */
174
+ fileActions
175
+ .uploadMediaAsync(file, file.name)
176
+ .then((fileRes) => {
177
+ const newAttachments = [...this.state.attachments];
178
+ const attachmentIndex = newAttachments.indexOf(attachment);
179
+ if (attachmentIndex !== -1) {
180
+ newAttachments[attachmentIndex].Uploading = false;
181
+ newAttachments[attachmentIndex].Source = fileRes;
182
+ const fileSplit = fileRes.split('/');
183
+ newAttachments[attachmentIndex].Title = _.last(fileSplit).split('.')[0];
184
+ this.setState({
185
+ attachments: newAttachments,
186
+ });
187
+ }
188
+ })
189
+ .catch((uploadErrorRes) => {
190
+ attachment.Uploading = false;
191
+ this.setState({
192
+ attachments: this.state.attachments,
193
+ });
194
+ });
195
+ }
196
+
197
+ selectOption = (o) => {
198
+ this.setState({
199
+ selectedOption: o,
200
+ });
201
+ };
202
+
203
+ handleAttachmentChange(index, column, event) {
204
+ var stateChange = {
205
+ attachments: this.state.attachments,
206
+ };
207
+ stateChange.attachments[index][column] = event.target.value;
208
+ this.setState(stateChange);
209
+ }
210
+
211
+ setAttachments(input) {
212
+ input.Attachments = [];
213
+ this.state.attachments.forEach((attachment) => {
214
+ if (!attachment.Removed && !_.isEmpty(attachment.Source)) {
215
+ input.Attachments.push({
216
+ Id: attachment.Id,
217
+ Title: attachment.Title,
218
+ Source: attachment.Source,
219
+ });
220
+ }
221
+ });
222
+ }
223
+
224
+ addAttachment = (url) => {
225
+ if (_.isEmpty(url)) {
226
+ return;
227
+ }
228
+ const newReps = [...this.state.attachments];
229
+ const id = _.isEmpty(this.state.attachments) ? 0 : _.maxBy(this.state.attachments, 'Id').Id + 1;
230
+ newReps.push({
231
+ Id: id,
232
+ Title: getFileName(url, true),
233
+ Source: url,
234
+ Uploading: false,
235
+ Type: 'pdf',
236
+ });
237
+ this.setState({
238
+ attachments: newReps,
239
+ });
240
+
241
+ this.pdFileInput && this.pdFileInput.getWrappedInstance().setValue('');
242
+ };
243
+
244
+ removeAttachment(index) {
245
+ const newReps = [...this.state.attachments];
246
+ newReps[index].Removed = true;
247
+ this.setState({
248
+ attachments: newReps,
249
+ });
250
+ }
251
+
252
+ inputsDisabled() {
253
+ if (this.state.submitting) {
254
+ return true;
255
+ }
256
+ return false;
257
+ }
258
+
259
+ checkSetPublisherImage(update) {
260
+ if (this.refs.publisherImage) {
261
+ if (update.Publisher && !_.isEmpty(update.Publisher.Image)) {
262
+ console.log(update.Publisher.Image);
263
+ this.refs.publisherImage.getWrappedInstance().setValue(update.Publisher.Image);
264
+ } else {
265
+ setTimeout(() => {
266
+ this.checkSetPublisherImage(update);
267
+ }, 100);
268
+ }
269
+ }
270
+ }
271
+
272
+ checkSetImage(update) {
273
+ if (this.refs.imageInput) {
274
+ if (update.Images) {
275
+ this.refs.imageInput.getWrappedInstance().setValue(update.Images);
276
+ } else {
277
+ this.refs.imageInput.getWrappedInstance().setValue(update.Image);
278
+ }
279
+ } else {
280
+ setTimeout(() => {
281
+ this.checkSetImage(update);
282
+ }, 100);
283
+ }
284
+ }
285
+
286
+ isEmpty(text) {
287
+ return text === '';
288
+ }
289
+
290
+ handleChange(event) {
291
+ var stateChange = {};
292
+ stateChange[event.target.getAttribute('id')] = event.target.value;
293
+ this.setState(stateChange);
294
+ }
295
+
296
+ handleExpiryDateTextChange(value) {
297
+ const newState = { featuredExpiryDateText: value };
298
+ const m = moment(value, 'DD/MM/YYYY');
299
+
300
+ if (m.isValid() && m.year() > 1900) {
301
+ newState.featuredExpiryDate = m.format('YYYY-MM-DD');
302
+ }
303
+ this.setState(newState);
304
+ }
305
+
306
+ handleExpiryDateChange(date) {
307
+ var stateChange = {
308
+ featuredExpiryDate: date,
309
+ featuredExpiryDateText: moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY'),
310
+ showExpiryDate: false,
311
+ };
312
+ this.setState(stateChange);
313
+ }
314
+
315
+ handlePublishDateTextChange(value) {
316
+ const newState = { publishDateText: value };
317
+ const m = moment(value, 'DD/MM/YYYY');
318
+
319
+ if (m.isValid() && m.year() > 1900) {
320
+ newState.publishDate = m.format('YYYY-MM-DD');
321
+ }
322
+ this.setState(newState);
323
+ }
324
+
325
+ handlePublishDateChange(date) {
326
+ var stateChange = {
327
+ publishDate: date,
328
+ publishDateText: moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY'),
329
+ showPublishDate: false,
330
+ };
331
+ this.setState(stateChange);
332
+ }
333
+
334
+ validateLoading() {
335
+ if (this.state.submitting) {
336
+ return false;
337
+ }
338
+ return true;
339
+ }
340
+
341
+ validateImage() {
342
+ if (_.isEmpty(this.state.imageInput)) {
343
+ return true;
344
+ }
345
+ return this.state.imageInput.match(/\.(jpeg|jpg|gif|png|ashx)/) != null;
346
+ }
347
+
348
+ validateCompulsoryText() {
349
+ if (this.isEmpty(this.state.titleInput)) {
350
+ return false;
351
+ }
352
+ if (this.isEmpty(this.state.textInput)) {
353
+ return false;
354
+ }
355
+ return true;
356
+ }
357
+
358
+ validateForm() {
359
+ if (!this.validateCompulsoryText()) {
360
+ return false;
361
+ }
362
+ if (!this.validateImage()) {
363
+ return false;
364
+ }
365
+ if (!this.validateLoading()) {
366
+ return false;
367
+ }
368
+ if (!this.validatePublishTime()) {
369
+ return false;
370
+ }
371
+ if (!this.state.isAudienceValid) {
372
+ return false;
373
+ }
374
+ if (this.state.authorDisplay === 'custom' && _.isEmpty(this.state.customAuthorInput)) {
375
+ return false;
376
+ }
377
+ return true;
378
+ }
379
+
380
+ validatePublishTime() {
381
+ if (!this.state.differentPublish) {
382
+ return true;
383
+ }
384
+ if (!_.isUndefined(this.state.publishTime)) {
385
+ return this.state.publishTime.trim() !== '';
386
+ }
387
+ return false;
388
+ }
389
+
390
+ setTags(input) {
391
+ const tags = [];
392
+
393
+ if (!this.isEmpty(this.state.tagsInput)) {
394
+ this.state.tagsInput.split(',').forEach((tag) => {
395
+ tags.push(tag.trim());
396
+ });
397
+ }
398
+
399
+ input.Tags = tags;
400
+ }
401
+
402
+ toggleTagged() {
403
+ if (!this.state.showTagged) {
404
+ setTimeout(() => {
405
+ $('#NewsContainer').animate({ scrollLeft: '1000px' });
406
+ }, 50);
407
+ }
408
+ this.setState({
409
+ showTagged: !this.state.showTagged,
410
+ userSearch: '',
411
+ });
412
+ }
413
+
414
+ compileJson() {
415
+ const result = {
416
+ Title: this.state.titleInput,
417
+ Image: '',
418
+ Images: this.refs.imageInput
419
+ .getWrappedInstance()
420
+ .getValue()
421
+ .map((url) => {
422
+ return isVideo(url) ? url : get1400(url);
423
+ }),
424
+ Text: this.state.textInput,
425
+ Thumbnail: '',
426
+ Video: this.state.videoInput,
427
+ AudienceType: this.audienceSelector.getWrappedInstance().getAudienceType(),
428
+ AudienceTypeSelection: this.audienceSelector.getWrappedInstance().getAudienceTypeSelection(),
429
+ Site: this.props.auth.site,
430
+ Tagged: this.state.tagged,
431
+ HasSponsor: this.state.HasSponsor,
432
+ Sponsor: null,
433
+ Tags: [],
434
+ TVMode: this.state.isTvMode,
435
+ AllowComments: _.includes(this.props.auth.hidden, values.optionNewsComments) ? false : this.state.allowComments,
436
+ Notification: _.includes(this.props.auth.hidden, values.optionNewsNotifications) ? false : this.state.shouldNotify,
437
+ IsFeatured: this.state.isFeatured,
438
+ FeaturedExpiry: this.state.isFeatured ? getUTCFromTimeDatePickers(this.state.featuredExpiryDate, '23:59').valueOf() : undefined,
439
+ Publisher: null,
440
+ AuthorDisplay: this.state.authorDisplay,
441
+ };
442
+ if (this.state.authorDisplay === 'custom') {
443
+ result.Author = { displayName: this.state.customAuthorInput };
444
+ }
445
+ if (!_.isEmpty(result.Images)) {
446
+ result.Thumbnail = getThumb300(result.Images[0]);
447
+ result.Image = get1400(result.Images[0]);
448
+ }
449
+
450
+ // if (baseClient === 'pluss60') {
451
+ // const publisherImage = this.refs.publisherImage.getWrappedInstance().getValue();
452
+ // if (!_.isEmpty(publisherImage) || !_.isEmpty(this.state.publisherTitleInput)) {
453
+ // result.Publisher = {
454
+ // Image: publisherImage,
455
+ // Title: this.state.publisherTitleInput,
456
+ // };
457
+ // }
458
+ // }
459
+
460
+ if (this.state.updateId != null) {
461
+ result.RowId = this.state.updateId;
462
+ }
463
+
464
+ if (this.state.differentPublish) {
465
+ const timeToUse = getUTCFromTimeDatePickers(this.state.publishDate, this.state.publishTime);
466
+ result.Timestamp = timeToUse.toISOString();
467
+ result.UnixTimestamp = timeToUse.valueOf();
468
+ }
469
+
470
+ if (newsHaveTags) {
471
+ this.setTags(result);
472
+ }
473
+ this.setAttachments(result);
474
+
475
+ return result;
476
+ }
477
+
478
+ handleSearchChange(event) {
479
+ if (this.state.searchUserLoading) {
480
+ return;
481
+ }
482
+ var stateChange = {};
483
+ stateChange[event.target.getAttribute('id')] = event.target.value;
484
+ this.setState(stateChange);
485
+ }
486
+
487
+ handleSubmit = () => {
488
+ console.log(this.audienceSelector);
489
+ this.audienceSelector.getWrappedInstance().onSubmit();
490
+ if (!this.validateForm()) {
491
+ this.setState({ showWarnings: true });
492
+ return;
493
+ }
494
+ this.setState({ submitting: true });
495
+ const data = this.compileJson();
496
+
497
+ newsletterActions
498
+ .addOrEditNewsletterEntry(data, this.props.auth.site)
499
+ .then((res) => {
500
+ this.setState({
501
+ success: true,
502
+ submitting: false,
503
+ });
504
+ this.props.newsUpdate(this.props.auth.site);
505
+ })
506
+ .catch((res) => {
507
+ this.setState({ submitting: false });
508
+ alert('Something went wrong with the request. Please try again.');
509
+ });
510
+ };
511
+
512
+ tagUser(user) {
513
+ if (
514
+ _.some(this.state.tagged, (u) => {
515
+ return u.userId === user.Id;
516
+ })
517
+ ) {
518
+ return this.removeTaggedUser({ userId: user.Id });
519
+ }
520
+ const newTagged = _.clone(this.state.tagged);
521
+ newTagged.push({
522
+ profilePic: user.profilePic,
523
+ displayName: user.displayName,
524
+ userId: user.Id,
525
+ });
526
+ this.setState({
527
+ tagged: newTagged,
528
+ });
529
+ }
530
+
531
+ removeTaggedUser(user) {
532
+ this.setState({
533
+ tagged: _.filter(this.state.tagged, (u) => {
534
+ return u.userId !== user.userId;
535
+ }),
536
+ });
537
+ }
538
+
539
+ clearForm() {
540
+ this.setState(this.initialState);
541
+ }
542
+
543
+ clearSuccess() {
544
+ this.setState({
545
+ success: false,
546
+ submitting: false,
547
+ showWarnings: false,
548
+ });
549
+ }
550
+
551
+ renderSuccess() {
552
+ if (!this.state.success) {
553
+ return null;
554
+ }
555
+ return (
556
+ <SuccessPopup
557
+ text={`Article has been ${this.state.updateId != null ? 'edited' : 'added'}`}
558
+ buttons={[
559
+ {
560
+ type: 'outlined',
561
+ onClick: () => {
562
+ window.history.back();
563
+ },
564
+ text: 'Go to home',
565
+ },
566
+ ]}
567
+ />
568
+ );
569
+ }
570
+
571
+ renderSubmit() {
572
+ if (this.state.submitting) {
573
+ return <Button buttonType="secondary">Saving...</Button>;
574
+ }
575
+ return (
576
+ <div>
577
+ <Button
578
+ inline
579
+ buttonType="tertiary"
580
+ onClick={() => {
581
+ window.history.back();
582
+ }}
583
+ isActive
584
+ style={{ marginRight: 16 }}
585
+ >
586
+ {this.state.updateId == null ? 'Cancel' : 'Back'}
587
+ </Button>
588
+ <Button inline buttonType="primary" onClick={this.handleSubmit} isActive={this.validateForm()}>
589
+ Save
590
+ </Button>
591
+ </div>
592
+ );
593
+ }
594
+
595
+ renderPublishOptions() {
596
+ return (
597
+ <div className="optionsContent_bottom optionsContent_bottom-noTopPadding">
598
+ {this.renderNotify()}
599
+ {this.renderTvMode()}
600
+ {this.renderComments()}
601
+ </div>
602
+ );
603
+ }
604
+
605
+ renderPublishTime() {
606
+ return (
607
+ <div className="optionsContent_bottom">
608
+ <RadioButton
609
+ label={
610
+ this.state.updateId ? 'Do you want to change the publish date?' : 'Should this post be published on a different day than today?'
611
+ }
612
+ isActive={this.state.differentPublish}
613
+ options={[
614
+ { Label: 'Yes', Value: true, onChange: () => this.setState({ differentPublish: true }) },
615
+ { Label: 'No', Value: false, onChange: () => this.setState({ differentPublish: false }) },
616
+ ]}
617
+ />
618
+ {this.state.differentPublish && (
619
+ <div>
620
+ <GenericInput
621
+ id={`publishTime`}
622
+ type="time"
623
+ label="Time"
624
+ className="marginTop-16"
625
+ value={this.state.publishTime}
626
+ isValid={() => {
627
+ return this.validatePublishTime();
628
+ }}
629
+ showError={() => {
630
+ return this.state.showWarnings && !this.validatePublishTime();
631
+ }}
632
+ style={{ marginBottom: 0 }}
633
+ inputComponent={
634
+ <TimePicker
635
+ selectedTime={this.state.publishTime}
636
+ selectTime={(val) => {
637
+ this.setState({ publishTime: val });
638
+ }}
639
+ />
640
+ }
641
+ />
642
+ {/* Test Input Date Selector */}
643
+ <GenericInput
644
+ className="marginTop-16"
645
+ id="publishDateText"
646
+ label="Publish Date"
647
+ alwaysShowLabel
648
+ value={this.state.publishDateText}
649
+ onChange={(e) => this.handlePublishDateTextChange(e.target.value)}
650
+ onClick={(e) => this.setState({ showPublishDate: !this.state.showPublishDate })}
651
+ />
652
+ {this.state.showPublishDate && (
653
+ <DatePicker selectedDate={this.state.publishDate} selectDate={this.handlePublishDateChange.bind(this)} />
654
+ )}
655
+ </div>
656
+ )}
657
+ </div>
658
+ );
659
+ }
660
+
661
+ renderAudience() {
662
+ return (
663
+ <div className="optionsContent_bottom">
664
+ <AudienceSelector
665
+ updateValidation={this.updateAudienceValidation.bind(this)}
666
+ ref={(a) => {
667
+ this.audienceSelector = a;
668
+ }}
669
+ auth={this.props.auth}
670
+ disallowSingleUsers
671
+ disallowUserType={this.props.auth.site === 'hq'}
672
+ customSite={this.props.auth.site === 'hq' ? 'All sites' : false}
673
+ />
674
+ </div>
675
+ );
676
+ }
677
+
678
+ renderSelectedOption() {
679
+ return (
680
+ <div>
681
+ <div style={{ display: this.state.selectedOption === 'audience' ? 'block' : 'none' }}>{this.renderAudience()}</div>
682
+ <div style={{ display: this.state.selectedOption === 'publishOptions' ? 'block' : 'none' }}>{this.renderPublishOptions()}</div>
683
+ <div style={{ display: this.state.selectedOption === 'attachments' ? 'block' : 'none' }}>{this.renderAttachments()}</div>
684
+ <div style={{ display: this.state.selectedOption === 'featured' ? 'block' : 'none' }}>{this.renderFeatured()}</div>
685
+ <div style={{ display: this.state.selectedOption === 'publishTime' ? 'block' : 'none' }}>{this.renderPublishTime()}</div>
686
+ <div style={{ display: this.state.selectedOption === 'author' ? 'block' : 'none' }}>{this.renderAuthor()}</div>
687
+ <div style={{ display: this.state.selectedOption === 'video' ? 'block' : 'none' }}>{this.renderVideo()}</div>
688
+ <div style={{ display: this.state.selectedOption === 'tags' ? 'block' : 'none' }}>{this.renderTags()}</div>
689
+ </div>
690
+ );
691
+ }
692
+
693
+ renderOptionsSection() {
694
+ let options = [
695
+ {
696
+ key: 'audience',
697
+ icon: 'audience',
698
+ text: 'Audience',
699
+ },
700
+ {
701
+ key: 'publishOptions',
702
+ icon: 'bell',
703
+ text: 'Publish Options',
704
+ },
705
+ {
706
+ key: 'publishTime',
707
+ icon: 'clock',
708
+ text: 'Schedule Post',
709
+ },
710
+ {
711
+ key: 'featured',
712
+ icon: 'star',
713
+ text: values.textFeaturedNews,
714
+ },
715
+ {
716
+ key: 'attachments',
717
+ icon: 'attachments',
718
+ text: 'Attachments',
719
+ },
720
+ {
721
+ key: 'author',
722
+ icon: 'user-card',
723
+ text: 'Author Name',
724
+ },
725
+ {
726
+ key: 'video',
727
+ icon: 'videocamera',
728
+ text: 'Video',
729
+ },
730
+ {
731
+ key: 'tags',
732
+ icon: 'tag',
733
+ text: 'Tags',
734
+ },
735
+ ];
736
+ if (this.props.forceNewsAuthorName) {
737
+ options = _.filter(options, (o) => {
738
+ return o.key !== 'author';
739
+ });
740
+ }
741
+ if (!newsHaveTags) {
742
+ options = _.filter(options, (o) => {
743
+ return o.key !== 'tags';
744
+ });
745
+ }
746
+ return (
747
+ <OptionsSection options={options} selected={this.state.selectedOption} selectOption={this.selectOption}>
748
+ {this.renderSelectedOption()}
749
+ </OptionsSection>
750
+ );
751
+ }
752
+
753
+ renderForm() {
754
+ if (this.state.success) {
755
+ return null;
756
+ }
757
+ return (
758
+ <div>
759
+ <div className="padding-60 paddingVertical-40 bottomDivideBorder">
760
+ <Text type="formTitleLarge" className="marginBottom-24">
761
+ {this.state.updateId == null ? 'New' : 'Edit'} Post
762
+ </Text>
763
+ {/* title */}
764
+ <GenericInput
765
+ id="titleInput"
766
+ type="text"
767
+ label="Title"
768
+ placeholder="Insert title here"
769
+ showError={() => {
770
+ return this.state.showWarnings && this.isEmpty(this.state.titleInput);
771
+ }}
772
+ isValid={() => {
773
+ return !this.isEmpty(this.state.titleInput);
774
+ }}
775
+ value={this.state.titleInput}
776
+ onChange={(e) => this.handleChange(e)}
777
+ isRequired
778
+ alwaysShowLabel
779
+ />
780
+ {/* description */}
781
+ <GenericInput
782
+ id="textInput"
783
+ type="textarea"
784
+ label="Text"
785
+ placeholder="Enter story here"
786
+ value={this.state.textInput}
787
+ onChange={(e) => this.handleChange(e)}
788
+ isRequired
789
+ showError={() => {
790
+ return this.state.showWarnings && this.isEmpty(this.state.textInput);
791
+ }}
792
+ isValid={() => {
793
+ return !this.isEmpty(this.state.textInput);
794
+ }}
795
+ help={
796
+ <a
797
+ onClick={() => {
798
+ this.toggleTextFormat(true);
799
+ }}
800
+ className="pointer"
801
+ >
802
+ Text formatting
803
+ </a>
804
+ }
805
+ alwaysShowLabel
806
+ />
807
+ </div>
808
+ <div className="padding-60 paddingVertical-40 bottomDivideBorder">
809
+ {/* image */}
810
+ <Text type="formTitleSmall" className="marginBottom-16">
811
+ Images & Videos
812
+ </Text>
813
+ {/* image */}
814
+ <ImageInput ref="imageInput" multiple limit={50} allowVideo containerStyle={{ width: 960 }} />
815
+ </div>
816
+ {this.renderOptionsSection()}
817
+ </div>
818
+ );
819
+ }
820
+
821
+ renderAddAttachment() {
822
+ if (this.inputsDisabled()) {
823
+ return null;
824
+ }
825
+ return (
826
+ <FileInput
827
+ ref={(ref) => {
828
+ this.pdFileInput = ref;
829
+ }}
830
+ style={{ height: 120, width: 240 }}
831
+ refreshCallback={this.addAttachment}
832
+ accept="application/pdf"
833
+ simpleStyle
834
+ />
835
+ );
836
+ }
837
+
838
+ renderAttachments() {
839
+ return (
840
+ <div className="optionsContent_bottom">
841
+ <FormGroup controlId="eventAttachment">
842
+ {this.state.attachments.map((attachment, index) => {
843
+ if (attachment.Removed) {
844
+ return null;
845
+ }
846
+ return (
847
+ <div key={index} className="pdfAttachmentInput">
848
+ <img
849
+ src="https://s3-ap-southeast-2.amazonaws.com/pluss60-dev-media/pluss/document.svg"
850
+ className="pdfAttachmentInput_icon"
851
+ alt="file"
852
+ />
853
+ <Text type="formTitleSmall" className="pdfAttachmentInput_title">
854
+ {getFileName(attachment.Source)}
855
+ </Text>
856
+ <GenericInput
857
+ id={`eventAttachmentTitle${index}`}
858
+ label="Title"
859
+ type="text"
860
+ placeholder="Title"
861
+ value={attachment.Title}
862
+ onChange={(e) => this.handleAttachmentChange(index, 'Title', e)}
863
+ disabled={this.inputsDisabled()}
864
+ alwaysShowLabel
865
+ style={{ margin: 0, flex: 1 }}
866
+ />
867
+ <P60Icon
868
+ className="removeoption pdfAttachmentInput_remove"
869
+ icon="remove-circle"
870
+ onClick={() => {
871
+ this.removeAttachment(index);
872
+ }}
873
+ />
874
+ </div>
875
+ );
876
+ })}
877
+ {this.renderAddAttachment()}
878
+ </FormGroup>
879
+ </div>
880
+ );
881
+ }
882
+
883
+ renderFeatured() {
884
+ return (
885
+ <div className="optionsContent_bottom">
886
+ <RadioButton
887
+ label={values.textIsThisAFeaturedNewsArticle}
888
+ isActive={this.state.isFeatured}
889
+ options={[
890
+ { Label: 'Yes', Value: true, onChange: () => this.setState({ isFeatured: true }) },
891
+ { Label: 'No', Value: false, onChange: () => this.setState({ isFeatured: false }) },
892
+ ]}
893
+ disabled={this.inputsDisabled()}
894
+ />
895
+ <div className="genericInput-help" style={{ marginTop: 4 }}>
896
+ {values.textFeaturedNewsArticlesWillBeHighlighted}
897
+ </div>
898
+ {this.state.isFeatured && (
899
+ <div>
900
+ <GenericInput
901
+ className="marginTop-16"
902
+ id="featuredExpiryDateText"
903
+ label="Expiry Date"
904
+ alwaysShowLabel
905
+ value={this.state.featuredExpiryDateText}
906
+ onChange={(e) => this.handleExpiryDateTextChange(e.target.value)}
907
+ onClick={(e) => this.setState({ showExpiryDate: !this.state.showExpiryDate })}
908
+ />
909
+ {this.state.showExpiryDate && (
910
+ <DatePicker selectedDate={this.state.featuredExpiryDate} selectDate={this.handleExpiryDateChange.bind(this)} />
911
+ )}
912
+ </div>
913
+ )}
914
+ </div>
915
+ );
916
+ }
917
+
918
+ renderComments() {
919
+ if (_.includes(this.props.auth.hidden, values.optionNewsComments)) return null;
920
+
921
+ return (
922
+ <CheckBox
923
+ className="marginTop-16"
924
+ label="Allow comments"
925
+ isActive={this.state.allowComments}
926
+ onChange={() => {
927
+ this.setState({ allowComments: !this.state.allowComments });
928
+ }}
929
+ />
930
+ );
931
+ }
932
+
933
+ renderTags() {
934
+ if (!newsHaveTags) return null;
935
+ return (
936
+ <div className="optionsContent_bottom">
937
+ <GenericInput
938
+ id="tagsInput"
939
+ type="text"
940
+ label="Tags"
941
+ help="Separate tags by commas"
942
+ placeholder="Humour, Social, etc."
943
+ value={this.state.tagsInput}
944
+ onChange={(e) => this.handleChange(e)}
945
+ alwaysShowLabel
946
+ />
947
+ </div>
948
+ );
949
+ }
950
+
951
+ renderNotify() {
952
+ if (this.state.eventId || _.includes(this.props.auth.hidden, 'eventNotifications')) {
953
+ return null;
954
+ }
955
+ return (
956
+ <div className="marginTop-16">
957
+ <RadioButton
958
+ label="Do you want to send a notification with this post?"
959
+ isActive={this.state.shouldNotify}
960
+ options={[
961
+ { Label: 'Yes', Value: true, onChange: () => this.setState({ shouldNotify: true }) },
962
+ { Label: 'No', Value: false, onChange: () => this.setState({ shouldNotify: false }) },
963
+ ]}
964
+ />
965
+ <div className="genericInput-help" style={{ marginTop: 4 }}>
966
+ This will send an alert to all users who have enabled push notifications.
967
+ </div>
968
+ </div>
969
+ );
970
+ }
971
+
972
+ renderTvMode() {
973
+ return (
974
+ <div className="marginTop-16">
975
+ <RadioButton
976
+ label="Do you want to publish this article to TV Mode?"
977
+ isActive={this.state.isTvMode}
978
+ options={[
979
+ { Label: 'Yes', Value: true, onChange: () => this.setState({ isTvMode: true }) },
980
+ { Label: 'No', Value: false, onChange: () => this.setState({ isTvMode: false }) },
981
+ ]}
982
+ />
983
+ <div className="highlightedHelp marginTop-8">{values.textTipKeepYourTVNewsFeedActiveAndCurrent}</div>
984
+ </div>
985
+ );
986
+ }
987
+
988
+ renderAuthor() {
989
+ if (this.props.forceNewsAuthorName) return null;
990
+
991
+ return (
992
+ <div className="optionsContent_bottom">
993
+ <RadioButton
994
+ label="Do you want to show the author of this article?"
995
+ isActive={!this.state.authorDisplay}
996
+ options={[{ Label: 'No', Value: true, onChange: () => this.setState({ authorDisplay: false }) }]}
997
+ />
998
+ <RadioButton
999
+ style={{ marginTop: 8 }}
1000
+ isActive={this.state.authorDisplay === 'name'}
1001
+ options={[{ Label: 'Yes, use my name', Value: true, onChange: () => this.setState({ authorDisplay: 'name' }) }]}
1002
+ />
1003
+ <RadioButton
1004
+ style={{ marginTop: 8 }}
1005
+ isActive={this.state.authorDisplay === 'custom'}
1006
+ options={[{ Label: 'Yes, use the following:', Value: true, onChange: () => this.setState({ authorDisplay: 'custom' }) }]}
1007
+ />
1008
+ {this.state.authorDisplay === 'custom' && (
1009
+ <GenericInput
1010
+ style={{ marginTop: 8 }}
1011
+ id="customAuthorInput"
1012
+ type="text"
1013
+ placeholder="Enter custom name"
1014
+ value={this.state.customAuthorInput}
1015
+ onChange={(e) => this.handleChange(e)}
1016
+ />
1017
+ )}
1018
+ </div>
1019
+ );
1020
+ }
1021
+
1022
+ renderVideo() {
1023
+ return (
1024
+ <div className="optionsContent_bottom">
1025
+ <GenericInput
1026
+ id="videoInput"
1027
+ type="text"
1028
+ // label="Video"
1029
+ help="Enter a direct URL to a video file (i.e. .mp4) or a YouTube video"
1030
+ placeholder="https://..."
1031
+ value={this.state.videoInput}
1032
+ onChange={(e) => this.handleChange(e)}
1033
+ alwaysShowLabel
1034
+ />
1035
+ </div>
1036
+ );
1037
+ }
1038
+
1039
+ renderSideForm() {
1040
+ if (this.state.success) {
1041
+ return null;
1042
+ }
1043
+ return (
1044
+ <OverlayPageSection className="pageSectionWrapper--newPopupSide">
1045
+ <div>
1046
+ <div
1047
+ className={`sideSection padding-60 paddingVertical-40 bottomDivideBorder pointer ${
1048
+ this.state.showTagged ? 'sideSection--highlighted' : ''
1049
+ }`}
1050
+ onClick={this.toggleTagged.bind(this)}
1051
+ >
1052
+ <Text type="formTitleSmall" className="marginBottom-16">
1053
+ Tagged People
1054
+ </Text>
1055
+ <p className="fieldLabel">
1056
+ {this.state.tagged.length} {getPluralOptions(this.state.tagged.length, 'person', 'people')}
1057
+ </p>
1058
+ </div>
1059
+ </div>
1060
+ </OverlayPageSection>
1061
+ );
1062
+ }
1063
+
1064
+ renderRemoveTag(user) {
1065
+ return (
1066
+ <div onClick={this.removeTaggedUser.bind(this, user)}>
1067
+ <P60Icon className="actionIcon" icon="remove-circle" />
1068
+ </div>
1069
+ );
1070
+ }
1071
+
1072
+ renderTick(user) {
1073
+ if (
1074
+ !_.some(this.state.tagged, (u) => {
1075
+ return u.userId === user.Id;
1076
+ })
1077
+ ) {
1078
+ return null;
1079
+ }
1080
+ return (
1081
+ <div className={`aCheck aCheck--active`}>
1082
+ <div className="aCheck-Tick">
1083
+ <FontAwesome className="aCheck-font" name={'check'} />
1084
+ </div>
1085
+ </div>
1086
+ );
1087
+ }
1088
+
1089
+ renderSubText(user) {
1090
+ if (_.isEmpty(user.unit)) {
1091
+ return null;
1092
+ }
1093
+ return (
1094
+ <p className="text-subtitle" style={{ margin: 0 }}>
1095
+ {user.unit}
1096
+ </p>
1097
+ );
1098
+ }
1099
+
1100
+ renderResults() {
1101
+ if (this.state.userSearch == null) {
1102
+ return null;
1103
+ }
1104
+ let source = _.sortBy(
1105
+ _.filter(this.state.contacts, (ev) => {
1106
+ return ev.displayName.toLowerCase().indexOf(this.state.userSearch.toLowerCase()) > -1 && ev.category === 'resident';
1107
+ }),
1108
+ 'displayName',
1109
+ );
1110
+
1111
+ return source.map((user, index) => {
1112
+ return (
1113
+ <UserListing
1114
+ user={user}
1115
+ onClick={this.tagUser.bind(this, user)}
1116
+ key={user.id}
1117
+ rightContent={this.renderTick(user)}
1118
+ subContent={this.renderSubText(user)}
1119
+ size={50}
1120
+ />
1121
+ );
1122
+ });
1123
+ }
1124
+
1125
+ renderTagged() {
1126
+ if (!this.state.showTagged || this.state.success) {
1127
+ return null;
1128
+ }
1129
+ return (
1130
+ <OverlayPageSection className="pageSectionWrapper--newPopupSide pageSectionWrapper--newPopupSide--purple">
1131
+ <div className="paddingVertical-40">
1132
+ <div className="paddingHorizontal-32">
1133
+ <Text type="formTitleSmall" className="text-white">
1134
+ Tagged People
1135
+ </Text>
1136
+ </div>
1137
+ <div className="marginTop-24 paddingHorizontal-32">
1138
+ {this.state.tagged.map((user, index) => {
1139
+ return <UserListing user={user} key={user.userId} whiteText rightContent={this.renderRemoveTag(user)} />;
1140
+ })}
1141
+ </div>
1142
+ <div className="marginTop-24">
1143
+ <div className="paddingHorizontal-32 paddingVertical-16" style={{ backgroundColor: '#fff' }}>
1144
+ {' '}
1145
+ {/* Search for peeps */}
1146
+ <GenericInput
1147
+ id="userSearch"
1148
+ type="text"
1149
+ label="Search for User"
1150
+ placeholder="Search for user"
1151
+ value={this.state.userSearch}
1152
+ onChange={(e) => this.handleSearchChange(e)}
1153
+ />
1154
+ {this.renderResults()}
1155
+ </div>
1156
+ </div>
1157
+ </div>
1158
+ </OverlayPageSection>
1159
+ );
1160
+ }
1161
+
1162
+ render() {
1163
+ return (
1164
+ <OverlayPage>
1165
+ <TextFormatPopup onClose={this.toggleTextFormat.bind(this, false)} isOpen={this.state.textFormatOpen} />
1166
+ <OverlayPageContents id="NewsContainer" noBottomButtons={this.state.success}>
1167
+ <OverlayPageSection className="pageSectionWrapper--newPopup">
1168
+ {this.renderForm()}
1169
+ {this.renderSuccess()}
1170
+ </OverlayPageSection>
1171
+ {this.renderSideForm()}
1172
+ {this.renderTagged()}
1173
+ </OverlayPageContents>
1174
+ <OverlayPageBottomButtons>{this.renderSubmit()}</OverlayPageBottomButtons>
1175
+ </OverlayPage>
1176
+ );
1177
+ }
1178
+ }
1179
+
1180
+ const mapStateToProps = (state) => {
1181
+ const { auth } = state;
1182
+ const { allUsers } = state.users;
1183
+ return { auth, users: allUsers, forceNewsAuthorName: auth && auth.siteSettings && auth.siteSettings[values.optionForceNewsAuthorName] };
1184
+ };
1185
+
1186
+ export default connect(mapStateToProps, { newsUpdate, usersLoaded, addRecentlyCreated })(AddNewsletterEntry);