@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,234 @@
1
+ import React, { Component } from 'react';
2
+ import { Table } from 'react-bootstrap';
3
+ import { connect } from 'react-redux';
4
+ import moment from 'moment';
5
+ import _ from 'lodash';
6
+ import FontAwesome from 'react-fontawesome';
7
+ import { Link } from 'react-router-dom';
8
+ import { newsLoaded, removeNews } from '../../actions';
9
+ import { newsletterActions } from '../../webapi';
10
+ import { checkLoggedIn, validateAccess } from '../../session';
11
+ import { Text } from '../../components/text';
12
+ import { values } from '../../feature.config';
13
+
14
+ class ListNewsletter extends Component {
15
+ state = {
16
+ newsletterEntries: [],
17
+ sortColumn: 'DateUnix', // column to sort by
18
+ sortDesc: false, // if true, sort descending rather than ascending
19
+ now: moment.utc(),
20
+ onlyFuture: true,
21
+ };
22
+
23
+ UNSAFE_componentWillMount() {
24
+ checkLoggedIn(this, this.props.auth);
25
+ this.updateProps(this.props);
26
+ }
27
+
28
+ componentDidMount() {
29
+ this.getData();
30
+ }
31
+
32
+ UNSAFE_componentWillReceiveProps(nextProps) {
33
+ this.updateProps(nextProps);
34
+ }
35
+
36
+ updateProps(props) {
37
+ this.setState({
38
+ newsletterEntries: props.news,
39
+ });
40
+ }
41
+
42
+ getData() {
43
+ newsletterActions.recursiveGetNews(this.props.auth.site).then((res) => {
44
+ if (!_.isEmpty(res) && res[0].Site === this.props.auth.site) {
45
+ this.props.newsLoaded(res);
46
+ }
47
+ });
48
+ }
49
+
50
+ sortByCol(col) {
51
+ if (this.state.sortColumn === col) {
52
+ this.setState({
53
+ sortDesc: !this.state.sortDesc,
54
+ });
55
+ } else {
56
+ this.setState({
57
+ sortColumn: col,
58
+ sortDesc: false,
59
+ });
60
+ }
61
+ }
62
+
63
+ removeNewsletterEntry(newsletter) {
64
+ if (window.confirm(values.textAreYouSureYouWantToDelete)) {
65
+ this.props.removeNews(newsletter.RowId);
66
+ newsletterActions
67
+ .deleteNewsletterEntry(this.props.auth.site, newsletter.RowId)
68
+ .then((res) => {
69
+ this.getData();
70
+ })
71
+ .catch((res) => {
72
+ alert('Something went wrong with the request. Please try again.');
73
+ });
74
+ }
75
+ }
76
+
77
+ renderNewsletter(source) {
78
+ // let source = _.sortBy(this.state.newsletterEntries, this.state.sortColumn);
79
+ // if (this.state.sortDesc) {
80
+ // source.reverse();
81
+ // }
82
+ // source = _.filter(source, (ev) => {
83
+ // if (!ev) {
84
+ // return false
85
+ // }
86
+ // return true;
87
+ // });
88
+
89
+ // if (this.props.onlyFuture) {
90
+ // const nownow = moment().startOf('month');
91
+ // source = _.filter(source, (ev) => {
92
+ // return nownow.isSameOrBefore(moment.utc(ev.UnixTimestamp).local());
93
+ // });
94
+ // }
95
+
96
+ return source.map((ev) => {
97
+ if (ev != null) {
98
+ return (
99
+ <tr key={ev.RowId}>
100
+ <td>{moment.utc(ev.Timestamp).local().format('DD-MM-YYYY')}</td>
101
+ <td className="table-TitleColumn">
102
+ {ev.SourceId ? (
103
+ <span>{ev.Title}</span>
104
+ ) : (
105
+ <Link to={`${values.routeAddNewsletterEntry}/${ev.RowId}`}>
106
+ <span>{ev.Title}</span>
107
+ </Link>
108
+ )}
109
+ </td>
110
+ <td className="table-options">
111
+ <div style={{ display: 'flex', alignItems: 'center' }}>
112
+ {validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth) && (
113
+ <Link to={`${values.routeNewsletterAnalytics}${ev.RowId}`}>
114
+ <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 12, cursor: 'pointer' }} name="bar-chart" />
115
+ </Link>
116
+ )}
117
+ {validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth) && !ev.SourceId && (
118
+ <Link to={`${values.routeAddNewsletterEntry}/${ev.RowId}`}>
119
+ <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 12, cursor: 'pointer' }} name="pencil" />
120
+ </Link>
121
+ )}
122
+ {validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth) && (
123
+ <a
124
+ onClick={() => {
125
+ this.removeNewsletterEntry(ev);
126
+ }}
127
+ >
128
+ <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 8, cursor: 'pointer' }} name="minus-circle" />
129
+ </a>
130
+ )}
131
+ </div>
132
+ </td>
133
+ </tr>
134
+ );
135
+ }
136
+ return null;
137
+ });
138
+ }
139
+
140
+ renderSort(col) {
141
+ if (col !== this.state.sortColumn) {
142
+ return null;
143
+ }
144
+ return <FontAwesome style={{ marginLeft: 5 }} name={this.state.sortDesc ? 'chevron-up' : 'chevron-down'} />;
145
+ }
146
+
147
+ sortIsActive(col) {
148
+ if (col !== this.state.sortColumn) {
149
+ return '';
150
+ }
151
+ return ' table--columnActive';
152
+ }
153
+
154
+ renderView(source) {
155
+ return (
156
+ <Table className="plussTable" striped bordered condensed hover>
157
+ <thead>
158
+ <tr>
159
+ <th
160
+ className={`${this.sortIsActive('DateUnix')}`}
161
+ style={{ cursor: 'pointer', width: 140 }}
162
+ onClick={() => {
163
+ this.sortByCol('DateUnix');
164
+ }}
165
+ >
166
+ Date published{this.renderSort('DateUnix')}
167
+ </th>
168
+ <th
169
+ className={`${this.sortIsActive('Title')}`}
170
+ style={{ cursor: 'pointer' }}
171
+ onClick={() => {
172
+ this.sortByCol('Title');
173
+ }}
174
+ >
175
+ Title{this.renderSort('Title')}
176
+ </th>
177
+ <th style={{ width: 70 }} />
178
+ </tr>
179
+ </thead>
180
+ <tbody>{this.renderNewsletter(source)}</tbody>
181
+ </Table>
182
+ );
183
+ }
184
+
185
+ renderEmpty() {
186
+ return (
187
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: 32 }}>
188
+ <div className="emptyState" />
189
+ <div className="marginTop-32" style={{ maxWidth: 500, textAlign: 'center' }}>
190
+ <Text type="h3">{values.textThereAreNoNews}</Text>
191
+ </div>
192
+ </div>
193
+ );
194
+ }
195
+
196
+ renderContent() {
197
+ let source = _.sortBy(this.state.newsletterEntries, this.state.sortColumn);
198
+ if (this.state.sortDesc) {
199
+ source.reverse();
200
+ }
201
+ source = _.filter(source, (ev) => {
202
+ if (!ev) {
203
+ return false;
204
+ }
205
+ return true;
206
+ });
207
+
208
+ if (this.props.onlyFuture) {
209
+ const nownow = moment().startOf('month');
210
+ source = _.filter(source, (ev) => {
211
+ return nownow.isSameOrBefore(moment.utc(ev.UnixTimestamp).local());
212
+ });
213
+ }
214
+ // source = []
215
+ if (_.isEmpty(source)) {
216
+ return this.renderEmpty();
217
+ }
218
+ return this.renderView(source);
219
+ }
220
+
221
+ render() {
222
+ return <div style={{ minWidth: '100%' }}>{this.renderContent()}</div>;
223
+ }
224
+ }
225
+
226
+ const mapStateToProps = (state) => {
227
+ const { auth } = state;
228
+ return {
229
+ news: state[values.reducerKey].news,
230
+ auth,
231
+ };
232
+ };
233
+
234
+ export default connect(mapStateToProps, { newsLoaded, removeNews })(ListNewsletter);
@@ -0,0 +1,328 @@
1
+ import React, { Component } from 'react';
2
+ import { connect } from 'react-redux';
3
+ import _ from 'lodash';
4
+ import moment from 'moment';
5
+ import FontAwesome from 'react-fontawesome';
6
+ import { Header, AddButton, Button } from '../../components';
7
+ import { isTheBest, validateAccess } from '../../session';
8
+ import { newsLoaded, newsSubmissionsLoaded, setAuth } from '../../actions';
9
+ import { newsletterActions } from '../../webapi';
10
+ import { COLOUR_BRANDING_OFF, COLOUR_BRANDING_ACTION, TEXT_LIGHT } from '../../js';
11
+ import ListNewsletterEntries from './ListNewsletterEntries';
12
+ import NewsletterSubmissions from './NewsletterSubmissions';
13
+ import NewsHubAnalytics from './NewsHubAnalytics';
14
+ import AvailableNews from './AvailableNews';
15
+ import { getUrlParams } from '../../helper';
16
+ import { hasAvailableNews } from '../../config';
17
+ import { Text } from '../../components/text';
18
+ import { values } from '../../feature.config';
19
+
20
+ class NewsHub extends Component {
21
+ constructor(props) {
22
+ super(props);
23
+
24
+ const params = getUrlParams();
25
+ let selectedSection = 'thisMonth';
26
+ if (params.tab) {
27
+ selectedSection = params.tab;
28
+ }
29
+
30
+ this.state = {
31
+ selectedSection,
32
+ location: '',
33
+
34
+ loadingAll: false,
35
+ loadingSubmissions: false,
36
+
37
+ submissionEntries: [],
38
+
39
+ allList: [],
40
+ thisMonth: [],
41
+ now: moment.utc(),
42
+ onlyFuture: true,
43
+ search: '',
44
+ };
45
+ }
46
+
47
+ UNSAFE_componentWillMount() {
48
+ this.updateProps(this.props);
49
+ }
50
+
51
+ componentDidMount() {
52
+ this.getData();
53
+ this.getSubmissions();
54
+ }
55
+
56
+ UNSAFE_componentWillReceiveProps(nextProps) {
57
+ this.updateProps(nextProps);
58
+ }
59
+
60
+ updateProps(props) {
61
+ const thisMonth = [];
62
+ const nownow = moment().startOf('month');
63
+
64
+ props.news.forEach((rep) => {
65
+ const isThisMonth = nownow.isSameOrBefore(moment.utc(rep.UnixTimestamp).local());
66
+ if (isThisMonth) {
67
+ thisMonth.push(rep);
68
+ }
69
+ });
70
+
71
+ this.setState({
72
+ allList: props.news,
73
+ thisMonth,
74
+ submissionEntries: props.submissions,
75
+ });
76
+ }
77
+
78
+ getData() {
79
+ this.setState({
80
+ loadingAll: true,
81
+ });
82
+ newsletterActions
83
+ .recursiveGetNews(this.props.auth.site)
84
+ .then((res) => {
85
+ this.setState({
86
+ loadingAll: false,
87
+ });
88
+ if (!_.isEmpty(res) && res[0].Site === this.props.auth.site) {
89
+ this.props.newsLoaded(res);
90
+ }
91
+ })
92
+ .catch((res) => {
93
+ this.setState({ loadingAll: false });
94
+ });
95
+ }
96
+
97
+ getSubmissions() {
98
+ this.setState({
99
+ loadingSubmissions: true,
100
+ });
101
+ newsletterActions
102
+ .getNewsletterSubmissions(this.props.auth.site)
103
+ .then((res) => {
104
+ this.setState({
105
+ loadingSubmissions: false,
106
+ });
107
+ if (res.data != null && !_.isEmpty(res.data) && res.data[0].Site === this.props.auth.site) {
108
+ this.props.newsSubmissionsLoaded(res.data);
109
+ }
110
+ })
111
+ .catch((res) => {
112
+ this.setState({ loadingSubmissions: false });
113
+ });
114
+ }
115
+
116
+ goToGenerate = () => {
117
+ this.props.history.push(values.routeGenerateNewsletter);
118
+ };
119
+
120
+ addNew() {
121
+ if (
122
+ validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth) ||
123
+ validateAccess(this.props.auth.site, values.permissionNewsletterSubmit, this.props.auth)
124
+ ) {
125
+ this.props.history.push(values.routeAddNewsletterEntry);
126
+ }
127
+ }
128
+
129
+ canAddNew(isClass) {
130
+ if (validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth)) {
131
+ return isClass ? '' : true;
132
+ }
133
+ return isClass ? ' hub-sideContent-topButton--hide' : false;
134
+ }
135
+
136
+ getSideBarSectionColour(id) {
137
+ return this.state.selectedSection === id ? { backgroundColor: '#fff' } : {};
138
+ }
139
+
140
+ renderStats(gweg, loading) {
141
+ if (loading) {
142
+ return <FontAwesome style={styles.spinner} name="spinner fa-pulse fa-fw" />;
143
+ }
144
+ return gweg;
145
+ }
146
+
147
+ renderLeftBar() {
148
+ if (
149
+ validateAccess(this.props.auth.site, values.permissionNewsletterSubmit, this.props.auth) &&
150
+ !validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth)
151
+ ) {
152
+ return null;
153
+ }
154
+ return (
155
+ <div className="hub-sideContent">
156
+ {/* Top Add Button */}
157
+ <div className="hub-sideContent-topButton" />
158
+ <div style={{ paddingLeft: 15, width: '100%' }}>
159
+ {/* Title */}
160
+ <div className="fontMedium fontSize-36 text-dark" style={styles.sideBarTitleSection}>
161
+ {values.textTitleNews}
162
+ </div>
163
+ {/* Content */}
164
+ {/* Analytics */}
165
+ <div
166
+ onClick={() => {
167
+ this.setState({ selectedSection: 'Analytics' });
168
+ }}
169
+ className="sideBarSection"
170
+ style={this.getSideBarSectionColour('Analytics')}
171
+ >
172
+ <div className="fontMedium fontSize-36 text-dark" style={{ lineHeight: '50px' }}>
173
+ <FontAwesome name="line-chart" className="sideBarSection__icon" />
174
+ </div>
175
+ <div className="fontRegular fontSize-16 text-light lineHeight-22">Analytics</div>
176
+ </div>
177
+ {/* All Articles */}
178
+ <div
179
+ onClick={() => {
180
+ this.setState({ selectedSection: 'all' });
181
+ }}
182
+ className="sideBarSection"
183
+ style={this.getSideBarSectionColour('all')}
184
+ >
185
+ <div className="fontMedium fontSize-36 text-dark" style={{ lineHeight: '50px' }}>
186
+ {this.renderStats(this.state.allList.length, this.state.loadingAll)}
187
+ </div>
188
+ <div className="fontRegular fontSize-16 text-light lineHeight-22">All posts</div>
189
+ </div>
190
+ <div
191
+ onClick={() => {
192
+ this.setState({ selectedSection: 'thisMonth' });
193
+ }}
194
+ className="sideBarSection"
195
+ style={this.getSideBarSectionColour('thisMonth')}
196
+ >
197
+ <div className="fontMedium fontSize-36 text-dark" style={{ lineHeight: '50px' }}>
198
+ {this.renderStats(this.state.thisMonth.length, this.state.loadingAll)}
199
+ </div>
200
+ <div className="fontRegular fontSize-16 text-light lineHeight-22">This month</div>
201
+ </div>
202
+ {/* Article Submissions */}
203
+ {this.props.auth.site !== 'hq' && validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth) && (
204
+ <div
205
+ onClick={() => {
206
+ this.setState({ selectedSection: 'submissions' });
207
+ }}
208
+ className="sideBarSection"
209
+ style={this.getSideBarSectionColour('submissions')}
210
+ >
211
+ <div
212
+ className="fontMedium fontSize-36"
213
+ style={{
214
+ lineHeight: '50px',
215
+ color: !this.state.loadingSubmissions && this.state.submissionEntries.length > 0 ? COLOUR_BRANDING_ACTION : TEXT_LIGHT,
216
+ }}
217
+ >
218
+ {this.renderStats(this.state.submissionEntries.length, this.state.loadingSubmissions)}
219
+ </div>
220
+ <div className="fontRegular fontSize-16 text-light lineHeight-22">Submissions</div>
221
+ </div>
222
+ )}
223
+ {hasAvailableNews && (
224
+ <div
225
+ onClick={() => {
226
+ this.setState({ selectedSection: 'available' });
227
+ }}
228
+ className="sideBarSection"
229
+ style={this.getSideBarSectionColour('available')}
230
+ >
231
+ <div className="fontMedium fontSize-36 text-dark" style={{ lineHeight: '50px' }}>
232
+ <FontAwesome name="newspaper-o" className="sideBarSection__icon" />
233
+ </div>
234
+ <div className="fontRegular fontSize-16 text-light lineHeight-22">Available posts</div>
235
+ </div>
236
+ )}
237
+ </div>
238
+ </div>
239
+ );
240
+ }
241
+
242
+ renderEventSubmitButton() {
243
+ return (
244
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '100%', height: 'fit-content' }}>
245
+ <AddButton onClick={this.addNew.bind(this)} text="SUBMIT NEW POST" />
246
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: 32 }}>
247
+ <div className="emptyState" />
248
+ <div className="marginTop-32" style={{ maxWidth: 500, textAlign: 'center' }}>
249
+ <Text type="h3">{values.textSubmitYourNewNewsPostHere}</Text>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ );
254
+ }
255
+
256
+ renderRight() {
257
+ if (
258
+ validateAccess(this.props.auth.site, values.permissionNewsletterSubmit, this.props.auth) &&
259
+ !validateAccess(this.props.auth.site, values.permissionNewsletter, this.props.auth)
260
+ ) {
261
+ return this.renderEventSubmitButton();
262
+ }
263
+ if (this.state.selectedSection === 'Analytics') {
264
+ return <NewsHubAnalytics news={this.props.news} />;
265
+ }
266
+ if (this.state.selectedSection === 'submissions') {
267
+ return <NewsletterSubmissions />;
268
+ }
269
+ if (this.state.selectedSection === 'available') {
270
+ return <AvailableNews />;
271
+ }
272
+ return <ListNewsletterEntries onlyFuture={this.state.selectedSection === 'thisMonth'} />;
273
+ }
274
+
275
+ render() {
276
+ return (
277
+ <div className="hub-wrapperContainer">
278
+ {this.renderLeftBar()}
279
+ <div className="hub-headerContentWrapper">
280
+ <Header>
281
+ {this.canAddNew() && <AddButton onClick={this.addNew.bind(this)} text="NEW POST" />}
282
+
283
+ {isTheBest(this.props.auth, true) && (
284
+ <Button leftIcon="print" buttonType="outlined" onClick={this.goToGenerate} narrow isActive className="marginLeft-16">
285
+ {values.textPrintNewsletter}
286
+ </Button>
287
+ )}
288
+ </Header>
289
+ <div className="hub-contentWrapper">{this.renderRight()}</div>
290
+ </div>
291
+ </div>
292
+ );
293
+ }
294
+ }
295
+
296
+ const styles = {
297
+ sideBarTitleSection: {
298
+ lineHeight: '50px',
299
+ marginTop: 30,
300
+ marginBottom: 30,
301
+ paddingLeft: 24,
302
+ },
303
+ sideBarSection: {
304
+ weight: '100%',
305
+ minWidth: 200,
306
+ padding: 32,
307
+ paddingLeft: 24,
308
+ cursor: 'pointer',
309
+ display: 'flex',
310
+ flexDirection: 'column',
311
+ justifyContent: 'center',
312
+ },
313
+ spinner: {
314
+ fontSize: 32,
315
+ color: COLOUR_BRANDING_OFF,
316
+ },
317
+ };
318
+
319
+ const mapStateToProps = (state) => {
320
+ const { auth } = state;
321
+ return {
322
+ news: state[values.reducerKey].news,
323
+ submissions: state[values.reducerKey].submissions,
324
+ auth,
325
+ };
326
+ };
327
+
328
+ export default connect(mapStateToProps, { newsLoaded, newsSubmissionsLoaded, setAuth })(NewsHub);