@plusscommunities/pluss-feeds-web 1.0.7 → 1.0.9-beta.0

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.
@@ -1,169 +0,0 @@
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 { feedsSubmissionsLoaded } from '../actions';
9
- import { feedActions } from '../apis';
10
- import { PlussCore } from '../feature.config';
11
- import { values } from '../values.config';
12
-
13
- const { Session } = PlussCore;
14
-
15
- class FeedSubmissions extends Component {
16
- state = {
17
- feedSubmissions: [],
18
- sortColumn: 'created', // column to sort by
19
- sortDesc: false, // if true, sort descending rather than ascending
20
- };
21
-
22
- UNSAFE_componentWillMount() {
23
- Session.checkLoggedIn(this, this.props.auth);
24
- this.updateProps(this.props);
25
- }
26
-
27
- componentDidMount() {
28
- this.getData();
29
- }
30
-
31
- UNSAFE_componentWillReceiveProps(nextProps) {
32
- this.updateProps(nextProps);
33
- }
34
-
35
- updateProps(props) {
36
- this.setState({ feedSubmissions: props.submissions });
37
- }
38
-
39
- getData = async () => {
40
- const { auth } = this.props;
41
- try {
42
- const res = await feedActions.getFeedSubmissions(auth.site);
43
- const submissions = res.data.Items;
44
- // console.log('getSubmissions', submissions && submissions[0]);
45
- if (!_.isEmpty(submissions) && submissions[0].site === auth.site) {
46
- this.props.feedsSubmissionsLoaded(submissions);
47
- }
48
- } catch (error) {
49
- console.log('getData error', error);
50
- }
51
- };
52
-
53
- sortByCol(col) {
54
- if (this.state.sortColumn === col) {
55
- this.setState({
56
- sortDesc: !this.state.sortDesc,
57
- });
58
- } else {
59
- this.setState({
60
- sortColumn: col,
61
- sortDesc: false,
62
- });
63
- }
64
- }
65
-
66
- renderSubmissions() {
67
- let source = _.sortBy(this.props.source, (feed) => {
68
- if (this.state.sortColumn === 'userName') return feed.user.displayName;
69
- if (this.state.sortColumn === 'authorName') return feed.author.displayName;
70
- if (this.state.sortColumn !== 'created') return feed[this.state.sortColumn];
71
- return feed.created;
72
- });
73
- if (this.state.sortDesc) source.reverse();
74
- source = _.filter(source, (feed) => feed);
75
-
76
- return source.map((feed) => {
77
- if (feed != null) {
78
- return (
79
- <tr key={feed.id}>
80
- <td className="table-TitleColumn">
81
- <Link to={`${values.routeFeedDetails}/${feed.id}`}>
82
- <span>{feed.title}</span>
83
- </Link>
84
- </td>
85
- <td>{moment.utc(feed.createdTime).local().format('ddd D MMM YYYY h:mma')}</td>
86
- <td>{feed.user && feed.user.displayName}</td>
87
- <td>{feed.author && feed.author.displayName}</td>
88
- </tr>
89
- );
90
- }
91
- return null;
92
- });
93
- }
94
-
95
- renderSort(col) {
96
- if (col !== this.state.sortColumn) {
97
- return null;
98
- }
99
- return <FontAwesome style={{ marginLeft: 5 }} name={this.state.sortDesc ? 'chevron-up' : 'chevron-down'} />;
100
- }
101
-
102
- sortIsActive(col) {
103
- if (col !== this.state.sortColumn) {
104
- return '';
105
- }
106
- return ' table--columnActive';
107
- }
108
-
109
- render() {
110
- return (
111
- <div style={{ minWidth: '100%' }}>
112
- <Table className="plussTable" striped bordered condensed hover>
113
- <thead>
114
- <tr>
115
- <th
116
- className={`${this.sortIsActive('title')}`}
117
- style={{ cursor: 'pointer', minWidth: 200 }}
118
- onClick={() => {
119
- this.sortByCol('title');
120
- }}
121
- >
122
- Title{this.renderSort('title')}
123
- </th>
124
- <th
125
- className={`${this.sortIsActive('created')}`}
126
- style={{ cursor: 'pointer' }}
127
- onClick={() => {
128
- this.sortByCol('created');
129
- }}
130
- >
131
- Time{this.renderSort('created')}
132
- </th>
133
- <th
134
- className={`${this.sortIsActive('userName')}`}
135
- style={{ cursor: 'pointer' }}
136
- onClick={() => {
137
- this.sortByCol('userName');
138
- }}
139
- >
140
- Posted To{this.renderSort('userName')}
141
- </th>
142
- <th
143
- className={`${this.sortIsActive('authorName')}`}
144
- style={{ cursor: 'pointer' }}
145
- onClick={() => {
146
- this.sortByCol('authorName');
147
- }}
148
- >
149
- Author{this.renderSort('authorName')}
150
- </th>
151
- <th style={{ width: 50 }} />
152
- </tr>
153
- </thead>
154
- <tbody>{this.renderSubmissions()}</tbody>
155
- </Table>
156
- </div>
157
- );
158
- }
159
- }
160
-
161
- const mapStateToProps = (state) => {
162
- const { auth } = state;
163
- return {
164
- submissions: state[values.reducerKey].submissions,
165
- auth,
166
- };
167
- };
168
-
169
- export default connect(mapStateToProps, { feedsSubmissionsLoaded })(FeedSubmissions);
@@ -1,324 +0,0 @@
1
- import React, { Component } from 'react';
2
- import { Table } from 'react-bootstrap';
3
- import { connect } from 'react-redux';
4
- import _ from 'lodash';
5
- import FontAwesome from 'react-fontawesome';
6
- import { withRouter } from 'react-router';
7
- import { feedTypesLoaded, feedTypesUpdate } from '../actions';
8
- import { feedActions } from '../apis';
9
- import Config, { PlussCore } from '../feature.config';
10
- import { values } from '../values.config';
11
-
12
- const { Session, Components } = PlussCore;
13
-
14
- class FeedTypes extends Component {
15
- constructor(props) {
16
- super(props);
17
- this.state = {
18
- feedTypeList: [],
19
- sortColumn: 'name',
20
- sortDesc: false,
21
- loading: true,
22
- showFeedType: false,
23
- feedTypeId: null,
24
- feedTypeLabel: '',
25
- feedTypeColour: '',
26
- showWarnings: false,
27
- submitting: false,
28
- };
29
- }
30
-
31
- componentWillMount() {
32
- this.updateProps(this.props);
33
- Session.checkLoggedIn(this, this.props.auth);
34
- }
35
-
36
- componentDidMount() {
37
- this.getFeedTypes();
38
- }
39
-
40
- componentWillReceiveProps(nextProps) {
41
- this.updateProps(nextProps);
42
- }
43
-
44
- updateProps(props) {
45
- this.setState({ feedTypeList: props.feedTypes });
46
- }
47
-
48
- getFeedTypes = async () => {
49
- try {
50
- const res = await feedActions.getFeedTypes(this.props.auth.site);
51
- if (res.data != null) this.props.feedTypesLoaded(res.data);
52
- } catch (error) {
53
- console.error('getFeedTypes', error);
54
- } finally {
55
- this.setState({ loading: false });
56
- }
57
- };
58
-
59
- sortByCol = (col) => {
60
- if (this.state.sortColumn === col) {
61
- this.setState({ sortDesc: !this.state.sortDesc });
62
- } else {
63
- this.setState({ sortColumn: col, sortDesc: false });
64
- }
65
- };
66
-
67
- isFeedTypeValid = () => {
68
- const { feedTypeLabel, feedTypeColour } = this.state;
69
-
70
- if (_.isEmpty(feedTypeLabel)) return false;
71
- if (_.isEmpty(feedTypeColour)) return false;
72
-
73
- return true;
74
- };
75
-
76
- onRemoveFeedType = async (ev) => {
77
- if (!window.confirm(`Are you sure you want to delete ${ev.label}?`)) return;
78
-
79
- try {
80
- await feedActions.deleteFeedType(this.props.auth.site, ev.id);
81
- const index = _.findIndex(this.state.feedTypeList, (feedType) => {
82
- return feedType != null && feedType.id === ev.id;
83
- });
84
- if (index > -1) {
85
- const newFeedTypes = [...this.state.feedTypeList];
86
- newFeedTypes[index].Deleted = true;
87
- this.props.feedTypesLoaded(newFeedTypes);
88
- }
89
- } catch (error) {
90
- console.error('onRemoveFeedType', error);
91
- alert('Something went wrong with the request. Please try again.');
92
- }
93
- };
94
-
95
- onShowFeedType = (feedType = null) => {
96
- this.setState({
97
- showFeedType: true,
98
- feedTypeId: feedType ? feedType.id : null,
99
- feedTypeLabel: feedType ? feedType.label : '',
100
- feedTypeColour: feedType ? feedType.colour : Config.env.colourBrandingMain,
101
- });
102
- };
103
-
104
- onHideFeedType = () => {
105
- if (this.state.submitting) return;
106
-
107
- this.setState({
108
- showWarnings: false,
109
- showFeedType: false,
110
- feedTypeId: null,
111
- feedTypeLabel: '',
112
- feedTypeColour: '',
113
- });
114
- };
115
-
116
- onHandleChange = (event) => {
117
- var stateChange = {};
118
- stateChange[event.target.getAttribute('id')] = event.target.value;
119
- this.setState(stateChange);
120
- };
121
-
122
- onSave = () => {
123
- const { site } = this.props.auth;
124
- const { submitting, feedTypeId, feedTypeLabel, feedTypeColour } = this.state;
125
- if (submitting) return;
126
- if (!this.isFeedTypeValid()) {
127
- this.setState({ showWarnings: true });
128
- return;
129
- }
130
- this.setState({ submitting: true }, async () => {
131
- try {
132
- if (feedTypeId) {
133
- await feedActions.editFeedType(site, feedTypeId, feedTypeLabel, feedTypeColour);
134
- } else {
135
- await feedActions.addFeedType(site, feedTypeLabel, feedTypeColour);
136
- }
137
- this.props.feedTypesUpdate(site);
138
- this.setState({ submitting: false }, this.onHideFeedType);
139
- } catch (error) {
140
- console.error('onSave', error);
141
- this.setState({ submitting: false });
142
- alert('Something went wrong with the request. Please try again.');
143
- }
144
- });
145
- };
146
-
147
- renderList() {
148
- const canEdit = Session.validateAccess(this.props.auth.site, values.permissionFeedManagement, this.props.auth);
149
- let source = _.sortBy(this.state.feedTypeList, this.state.sortColumn);
150
- if (this.state.sortDesc) source.reverse();
151
- source = _.filter(source, (ev) => {
152
- if (!ev) return false;
153
- if (ev.Deleted) return false;
154
- return true;
155
- });
156
-
157
- return source.map((ev, index) => {
158
- if (ev != null) {
159
- return (
160
- <tr key={index}>
161
- <td className="table-TitleColumn">
162
- <a onClick={() => this.onShowFeedType(ev)}>{ev.label}</a>
163
- </td>
164
- <td>
165
- <div className="flex flex-row flex-center">
166
- <div className="colourPicker_button" style={{ width: 80, height: 30, marginBottom: 'unset', backgroundColor: ev.colour }} />
167
- <div className="marginLeft-10">{ev.colour && ev.colour.toUpperCase()}</div>
168
- </div>
169
- </td>
170
- <td className="table-options">
171
- {canEdit ? (
172
- <div style={{ display: 'flex', alignItems: 'center' }}>
173
- <a onClick={() => this.onShowFeedType(ev)}>
174
- <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 12, cursor: 'pointer' }} name="pencil" />
175
- </a>
176
- <a onClick={() => this.onRemoveFeedType(ev)}>
177
- <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 8, cursor: 'pointer' }} name="minus-circle" />
178
- </a>
179
- </div>
180
- ) : null}
181
- </td>
182
- </tr>
183
- );
184
- }
185
- return null;
186
- });
187
- }
188
-
189
- renderView() {
190
- return (
191
- <Table className="plussTable" striped bordered condensed hover style={{ minWidth: '100%' }}>
192
- <thead>
193
- <tr>
194
- <th style={{ cursor: 'pointer' }} onClick={() => this.sortByCol('label')}>
195
- Feed type
196
- </th>
197
- <th style={{ cursor: 'pointer' }} onClick={() => this.sortByCol('colour')}>
198
- Type colour
199
- </th>
200
- <th style={{ width: 70 }} />
201
- </tr>
202
- </thead>
203
- <tbody>{this.renderList()}</tbody>
204
- </Table>
205
- );
206
- }
207
-
208
- renderEmpty() {
209
- return (
210
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: 32 }}>
211
- <div className="emptyState" />
212
- <div className="marginTop-32" style={{ maxWidth: 600, textAlign: 'center' }}>
213
- <span className="fontRegular fontSize-13">
214
- Please add the feed types that exist in your village which work alongside the Care Feeds area.
215
- </span>
216
- </div>
217
- <div className="marginTop-8 fontRegular fontSize-13" style={{ maxWidth: 600, textAlign: 'center' }}>
218
- Examples might be; Lifestyle.
219
- </div>
220
- </div>
221
- );
222
- }
223
-
224
- renderContent() {
225
- if (_.isEmpty(this.state.feedTypeList)) return this.renderEmpty();
226
- return this.renderView();
227
- }
228
-
229
- renderFeedTypes() {
230
- const { feedTypeList, loading } = this.state;
231
- const canAdd = Session.validateAccess(this.props.auth.site, values.permissionFeedManagement, this.props.auth);
232
-
233
- if (feedTypeList.length === 0 && loading) {
234
- return (
235
- <div style={{ minWidth: '100%' }}>
236
- <div className="padding-60 paddingVertical-40" style={{ textAlign: 'center' }}>
237
- <FontAwesome style={{ fontSize: 30, color: Config.env.colourBrandingOff }} name="spinner fa-pulse fa-fw" />
238
- </div>
239
- </div>
240
- );
241
- }
242
- return (
243
- <div style={{ minWidth: '100%' }}>
244
- {canAdd ? (
245
- <div className="marginBottom-32">
246
- <Components.AddButton onClick={() => this.onShowFeedType()} text="NEW FEED TYPE" />
247
- </div>
248
- ) : null}
249
- {this.renderContent()}
250
- </div>
251
- );
252
- }
253
-
254
- renderNewFeedTypePopup() {
255
- const { submitting, showFeedType, feedTypeId, showWarnings, feedTypeLabel, feedTypeColour } = this.state;
256
- if (!showFeedType) return null;
257
-
258
- return (
259
- <Components.Popup
260
- maxWidth={800}
261
- minWidth={450}
262
- hasPadding
263
- buttons={[
264
- {
265
- type: 'primaryAction',
266
- onClick: this.onSave,
267
- isActive: !submitting && this.isFeedTypeValid(),
268
- text: submitting ? 'Saving...' : 'Save',
269
- className: 'popupButton',
270
- },
271
- {
272
- type: 'outlinedAction',
273
- onClick: this.onHideFeedType,
274
- isActive: !submitting,
275
- text: 'Cancel',
276
- className: 'popupButton',
277
- },
278
- ]}
279
- onClose={this.onHideFeedType}
280
- title={feedTypeId ? 'Edit Type' : 'New Type'}
281
- >
282
- <Components.GenericInput
283
- id="feedTypeLabel"
284
- type="text"
285
- label="Label"
286
- placeholder="Feed type label"
287
- value={feedTypeLabel}
288
- onChange={this.onHandleChange}
289
- isRequired
290
- isValid={() => !_.isEmpty(feedTypeLabel)}
291
- showError={() => showWarnings && _.isEmpty(feedTypeLabel)}
292
- alwaysShowLabel
293
- />
294
- <Components.ColourOptions
295
- options={['vibrant', 'picker']}
296
- defaultTab="vibrant"
297
- value={feedTypeColour}
298
- onColourSelected={(feedTypeColour) => this.setState({ feedTypeColour })}
299
- />
300
- <div className="marginTop-24">
301
- <div className="fieldLabel marginBottom-4">Preview</div>
302
- <Components.Tag text={this.state.feedTypeLabel} style={{ backgroundColor: feedTypeColour, borderColor: feedTypeColour }} />
303
- </div>
304
- </Components.Popup>
305
- );
306
- }
307
-
308
- render() {
309
- return (
310
- <div style={{ minWidth: '100%' }}>
311
- <div>{this.renderFeedTypes()}</div>
312
- {this.renderNewFeedTypePopup()}
313
- <div className="hub_tidioPadding"></div>
314
- </div>
315
- );
316
- }
317
- }
318
-
319
- const mapStateToProps = (state) => {
320
- const { auth } = state;
321
- return { feedTypes: state[values.reducerKey].feedTypes, auth };
322
- };
323
-
324
- export default connect(mapStateToProps, { feedTypesLoaded, feedTypesUpdate })(withRouter(FeedTypes));
@@ -1,25 +0,0 @@
1
- import React, { Component } from 'react';
2
- import fullNoTitle from '../images/fullNoTitle.png';
3
-
4
- class PreviewFull extends Component {
5
- render() {
6
- const { backgroundColor, widgetTitle, titleClassName } = this.props;
7
- return (
8
- <div style={styles.container}>
9
- <p className={titleClassName}>{widgetTitle}</p>
10
- <img style={{ ...styles.image, backgroundColor }} src={fullNoTitle} alt="fullNoTitle" />
11
- </div>
12
- );
13
- }
14
- }
15
-
16
- const styles = {
17
- container: {
18
- position: 'relative',
19
- },
20
- image: {
21
- width: '100%',
22
- },
23
- };
24
-
25
- export default PreviewFull;
@@ -1,29 +0,0 @@
1
- import React, { Component } from 'react';
2
- import { values } from '../values.config';
3
-
4
- const SVG_PATH = values.svgPathGridIcon;
5
- const SVG_VIEWBOX = values.svgPathGridIconViewBox || '0 0 30 30';
6
-
7
- class PreviewGrid extends Component {
8
- render() {
9
- const { colour } = this.props;
10
-
11
- if (Array.isArray(SVG_PATH)) {
12
- return (
13
- <svg xmlns="http://www.w3.org/2000/svg" viewBox={SVG_VIEWBOX}>
14
- {SVG_PATH.map((p) => {
15
- return <path key={p.substring(0, 20)} d={p} fill={colour} />;
16
- })}
17
- </svg>
18
- );
19
- } else {
20
- return (
21
- <svg xmlns="http://www.w3.org/2000/svg" viewBox={SVG_VIEWBOX}>
22
- <path key={SVG_PATH.substring(0, 20)} d={SVG_PATH} fill={colour} />
23
- </svg>
24
- );
25
- }
26
- }
27
- }
28
-
29
- export default PreviewGrid;
@@ -1,27 +0,0 @@
1
- import React, { Component } from 'react';
2
- import previewWidget from '../images/previewWidget.png';
3
-
4
- class PreviewWidget extends Component {
5
- render() {
6
- const { backgroundColor, widgetTitle, titleClassName } = this.props;
7
- return (
8
- <div style={styles.container}>
9
- <img style={{ ...styles.image, backgroundColor }} src={previewWidget} alt="previewWidget" />
10
- <p className={titleClassName} style={styles.title}>
11
- {widgetTitle}
12
- </p>
13
- </div>
14
- );
15
- }
16
- }
17
-
18
- const styles = {
19
- container: {
20
- position: 'relative',
21
- },
22
- image: {
23
- width: '100%',
24
- },
25
- };
26
-
27
- export default PreviewWidget;
@@ -1,18 +0,0 @@
1
- import React, { Component } from 'react';
2
- import full from '../images/full.png';
3
-
4
- class ViewFull extends Component {
5
- render() {
6
- return <img style={styles.image} src={full} alt="full" />;
7
- }
8
- }
9
-
10
- const styles = {
11
- image: {
12
- width: '100%',
13
- height: '100%',
14
- objectFit: 'contain',
15
- },
16
- };
17
-
18
- export default ViewFull;
@@ -1,16 +0,0 @@
1
- import React, { Component } from 'react';
2
- import widget from '../images/widget.png';
3
-
4
- class ViewWidget extends Component {
5
- render() {
6
- return <img style={styles.image} src={widget} alt="widget" />;
7
- }
8
- }
9
-
10
- const styles = {
11
- image: {
12
- width: 80,
13
- },
14
- };
15
-
16
- export default ViewWidget;
@@ -1,86 +0,0 @@
1
- // import * as PlussCore from '../../pluss-core/src';
2
- import * as PlussCore from '@plusscommunities/pluss-core-web';
3
- import { values } from './values.config';
4
-
5
- export { PlussCore };
6
-
7
- const FeatureConfig = {
8
- key: values.featureKey,
9
- aliases: values.aliases,
10
- singularName: values.textSingularName,
11
- description: 'Submit and track resident feed.',
12
- emptyText: values.textEmptyState,
13
- widgetOptions: [],
14
- menu: {
15
- order: values.menuOrder,
16
- text: values.textMenuTitle,
17
- icon: 'list-burger',
18
- url: values.routeHub,
19
- countProps: null,
20
- visibleExps: {
21
- type: 'and',
22
- exps: [
23
- { type: 'feature', value: values.featureKey },
24
- {
25
- type: 'or',
26
- exps: [
27
- { type: 'permission', value: values.permissionFeedManagement },
28
- { type: 'permission', value: values.permissionFeedPost },
29
- { type: 'permission', value: values.permissionFeedDraft },
30
- ],
31
- },
32
- ],
33
- },
34
- },
35
- addUrl: values.routeAddFeed,
36
- addPermission: values.permissionFeedPost,
37
- activities: [],
38
- permissions: [
39
- {
40
- displayName: values.textPermissionFeedManagement,
41
- key: values.permissionFeedManagement,
42
- hq: false,
43
- },
44
- {
45
- displayName: values.textPermissionFeedPost,
46
- key: values.permissionFeedPost,
47
- hq: false,
48
- },
49
- {
50
- displayName: values.textPermissionFeedDraft,
51
- key: values.permissionFeedDraft,
52
- hq: false,
53
- },
54
- ],
55
- hiddenSections: [],
56
- moreSections: [],
57
- routes: [
58
- { path: values.routeHub, component: values.screenFeedHub, exact: false },
59
- { path: values.routeAddFeed, component: values.screenAddFeed, exact: true },
60
- { path: `${values.routeAddFeed}/:feedId`, component: values.screenAddFeed, exact: true },
61
- { path: `${values.routeFeedDetails}/:feedId`, component: values.screenFeed, exact: true },
62
- { path: values.routeFeedTypes, component: values.screenFeedTypes, exact: false },
63
- ],
64
- env: {
65
- baseStage: '',
66
- baseAPIUrl: '',
67
- baseUploadsUrl: '',
68
- uploadBucket: '',
69
- colourBrandingMain: '',
70
- colourBrandingOff: '',
71
- colourBrandingApp: '',
72
- defaultProfileImage: '',
73
- utcOffset: '',
74
- hasAvailableNews: false,
75
- newsHaveTags: true,
76
- defaultAllowComments: true,
77
- makeApiKey: '',
78
- logo: '',
79
- clientName: '',
80
- },
81
- init: (environment) => {
82
- FeatureConfig.env = environment;
83
- PlussCore.Config.init(environment);
84
- },
85
- };
86
- export default FeatureConfig;
Binary file
Binary file