@plusscommunities/pluss-core-web 1.0.5 → 1.0.6

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 (62) hide show
  1. package/dist/index.cjs.js +7349 -1360
  2. package/dist/index.esm.js +7341 -1358
  3. package/dist/index.umd.js +7346 -1364
  4. package/package.json +5 -3
  5. package/src/actions/AuthActions.js +83 -0
  6. package/src/actions/UsersActions.js +65 -0
  7. package/src/actions/index.js +2 -2
  8. package/src/actions/types.js +9 -9
  9. package/src/analytics.js +73 -0
  10. package/src/apis/analyticsActions.js +49 -0
  11. package/src/apis/authActions.js +58 -0
  12. package/src/apis/fileActions.js +92 -94
  13. package/src/apis/index.js +7 -0
  14. package/src/apis/profileActions.js +133 -0
  15. package/src/apis/stringActions.js +25 -0
  16. package/src/apis/typeActions.js +186 -0
  17. package/src/apis/userActions.js +128 -0
  18. package/src/apis/utilityActions.js +35 -0
  19. package/src/colours.js +16 -16
  20. package/src/components/AnalyticsFilter.js +110 -0
  21. package/src/components/AudienceIncluder.js +174 -0
  22. package/src/components/AudienceSelector.js +549 -0
  23. package/src/components/CheckBox.js +77 -0
  24. package/src/components/DatePicker.js +268 -0
  25. package/src/components/DropdownInput.js +223 -0
  26. package/src/components/FileInput.js +314 -0
  27. package/src/components/ImageInput.js +971 -0
  28. package/src/components/MakerPopup.js +300 -0
  29. package/src/components/OptionsSection.js +64 -0
  30. package/src/components/P60Icon.js +40 -0
  31. package/src/components/ProfilePic.js +35 -0
  32. package/src/components/Reactions.js +77 -0
  33. package/src/components/Tag.js +62 -0
  34. package/src/components/TextFormatPopup.js +54 -0
  35. package/src/components/TimePicker.js +205 -0
  36. package/src/components/UserListing.js +64 -0
  37. package/src/components/index.js +23 -7
  38. package/src/components/svg-icons.json +6 -0
  39. package/src/config.js +10 -0
  40. package/src/helper/HelpDeskWidget.js +52 -0
  41. package/src/helper/api/getUrl.js +15 -0
  42. package/src/helper/api/getUrlParams.js +9 -0
  43. package/src/helper/api/safeReadParams.js +6 -0
  44. package/src/helper/colours.js/getAppColourFromState.js +10 -0
  45. package/src/helper/files/canvasImageUploader.js +159 -0
  46. package/src/helper/files/get1400.js +28 -0
  47. package/src/helper/files/getExtension.js +9 -0
  48. package/src/helper/files/getFileName.js +13 -0
  49. package/src/helper/files/getThumb300.js +32 -0
  50. package/src/helper/files/isVideo.js +8 -0
  51. package/src/{helper.js → helper/helper.js} +19 -130
  52. package/src/helper/index.js +29 -0
  53. package/src/helper/site/getSiteName.js +16 -0
  54. package/src/helper/site/getSiteNameFromRoles.js +12 -0
  55. package/src/helper/storage/readJSONFromStorage.js +9 -0
  56. package/src/helper/storage/setLocalStorage.js +5 -0
  57. package/src/helper/strings/isEmail.js +11 -0
  58. package/src/helper/strings/onlyAlphanumeric.js +8 -0
  59. package/src/helper/strings/randomString.js +10 -0
  60. package/src/helper/strings/toParagraphed.js +17 -0
  61. package/src/index.js +2 -1
  62. package/src/session.js +107 -107
@@ -0,0 +1,35 @@
1
+ import Config from '../config';
2
+ import { authedFunction, unauthedFunction } from '../session';
3
+ import { getUrl } from '../helper';
4
+
5
+ export const utilityActions = {
6
+ getUpvotyToken: () => {
7
+ return authedFunction({
8
+ method: 'GET',
9
+ url: getUrl('utility', 'upvoty/usertoken'),
10
+ });
11
+ },
12
+ getBoardHash: () => {
13
+ return authedFunction({
14
+ method: 'GET',
15
+ url: getUrl('utility', 'upvoty/boardhash'),
16
+ });
17
+ },
18
+ getRoadmapHash: () => {
19
+ return authedFunction({
20
+ method: 'GET',
21
+ url: getUrl('utility', 'upvoty/roadmap'),
22
+ });
23
+ },
24
+ generateMake: (templateId, data) => {
25
+ return unauthedFunction({
26
+ method: 'POST',
27
+ url: `https://api.make.cm/make/t/${templateId}/sync`,
28
+ data,
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ 'X-MAKE-API-KEY': Config.env.makeApiKey,
32
+ },
33
+ });
34
+ },
35
+ };
package/src/colours.js CHANGED
@@ -2,11 +2,11 @@
2
2
  // const COLOUR_2 = '#3179cb';
3
3
  // const COLOUR_3 = '#1bb7ee';
4
4
 
5
- // const COLOUR_BRANDING_MAIN = '#4a57b7';
5
+ const COLOUR_BRANDING_MAIN = '#4a57b7';
6
6
  // const COLOUR_BRANDING_DARK = '#364196';
7
7
  // const COLOUR_BRANDING_LIGHT = '#597db4';
8
- // const COLOUR_BRANDING_OFF = '#597db4';
9
- // const COLOUR_BRANDING_ACTION = '#5c90df';
8
+ const COLOUR_BRANDING_OFF = '#597db4';
9
+ const COLOUR_BRANDING_ACTION = '#5c90df';
10
10
  const COLOUR_BRANDING_INACTIVE = '#dbddf1';
11
11
 
12
12
  // const TEXT_DARKEST = '#262a2d';
@@ -16,9 +16,9 @@ const COLOUR_BRANDING_INACTIVE = '#dbddf1';
16
16
  // const TEXT_DARK_ALPHA10 = 'rgba(60, 60, 80, .1)';
17
17
  // const TEXT_MID = '#5a5a6e';
18
18
  // const TEXT_MID_ALPHA50 = 'rgba(90, 90, 110, .5)';
19
- // const TEXT_LIGHT = '#717b81';
19
+ const TEXT_LIGHT = '#717b81';
20
20
  // const TEXT_SUPER_LIGHT = '#d2d9e5';
21
- // const COLOUR_DUSK = '#536280';
21
+ const COLOUR_DUSK = '#536280';
22
22
  const COLOUR_DUSK_LIGHT = '#67758f';
23
23
  // const COLOUR_DUSK_LIGHTER = '#a1afcc';
24
24
 
@@ -42,11 +42,11 @@ const LINEGREY = COLOUR_BRANDING_INACTIVE;
42
42
  // const COLOUR_NAV_GREY = '#f6f6f6';
43
43
  // const COLOUR_TRANSPARENT = 'rgba(0,0,0,0)';
44
44
  // const COLOUR_SIDEBAR = '#303030';
45
- // const COLOUR_LIGHTGREY = '#8695b2';
45
+ const COLOUR_LIGHTGREY = '#8695b2';
46
46
 
47
- // const INACTIVE_TEXT = COLOUR_LIGHTGREY;
47
+ const INACTIVE_TEXT = COLOUR_LIGHTGREY;
48
48
 
49
- // const COLOUR_BRANDING_APP = '#4a57b7';
49
+ const COLOUR_BRANDING_APP = '#4a57b7';
50
50
 
51
51
  // const hexToRGB = (hex) => {
52
52
  // // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
@@ -96,9 +96,9 @@ export {
96
96
  // TEXT_DARK_ALPHA10,
97
97
  // TEXT_MID,
98
98
  // TEXT_MID_ALPHA50,
99
- // TEXT_LIGHT,
99
+ TEXT_LIGHT,
100
100
  // TEXT_SUPER_LIGHT,
101
- // COLOUR_DUSK,
101
+ COLOUR_DUSK,
102
102
  COLOUR_DUSK_LIGHT,
103
103
  // COLOUR_DUSK_LIGHTER,
104
104
  LINEGREY,
@@ -121,14 +121,14 @@ export {
121
121
  // COLOUR_NAV_GREY,
122
122
  // COLOUR_TRANSPARENT,
123
123
  // COLOUR_SIDEBAR,
124
- // COLOUR_BRANDING_MAIN,
125
- // COLOUR_BRANDING_OFF,
124
+ COLOUR_BRANDING_MAIN,
125
+ COLOUR_BRANDING_OFF,
126
126
  // COLOUR_BRANDING_LIGHT,
127
127
  // COLOUR_BRANDING_DARK,
128
- // COLOUR_BRANDING_ACTION,
128
+ COLOUR_BRANDING_ACTION,
129
129
  // COLOUR_GREEN,
130
- // INACTIVE_TEXT,
131
- // COLOUR_LIGHTGREY,
132
- // COLOUR_BRANDING_APP,
130
+ INACTIVE_TEXT,
131
+ COLOUR_LIGHTGREY,
132
+ COLOUR_BRANDING_APP,
133
133
  COLOUR_BRANDING_INACTIVE,
134
134
  };
@@ -0,0 +1,110 @@
1
+ import React, { PureComponent } from 'react';
2
+ import { DropdownButton, MenuItem } from 'react-bootstrap';
3
+ import _ from 'lodash';
4
+ import moment from 'moment';
5
+ import { getAnalyticsFilterOptions } from '../analytics';
6
+ import { GenericInput, DatePicker } from './';
7
+
8
+ class AnalyticsFilter extends PureComponent {
9
+ constructor(props) {
10
+ super(props);
11
+ const filterOptions = getAnalyticsFilterOptions();
12
+ const selectedFilter = this.props.defaultFilter || filterOptions[1];
13
+ this.state = {
14
+ filterOptions,
15
+ selectedFilter,
16
+ dateRangeText: 'Select date range',
17
+ startDate: moment(),
18
+ endDate: moment(),
19
+ calendarVisible: true,
20
+ };
21
+ }
22
+
23
+ renderOptions = () => {
24
+ return this.state.filterOptions.map((ev) => {
25
+ return (
26
+ <MenuItem key={ev.dayCount} eventKey={ev.dayCount} active={this.state.selectedFilter === ev}>
27
+ {ev.text}
28
+ </MenuItem>
29
+ );
30
+ });
31
+ };
32
+
33
+ selectFilter = (key) => {
34
+ const selectedFilter = _.find(this.state.filterOptions, (o) => {
35
+ return o.dayCount === key;
36
+ });
37
+ this.setState(
38
+ {
39
+ selectedFilter,
40
+ dateRangeText: 'Select date range',
41
+ startDate: moment(),
42
+ endDate: moment(),
43
+ calendarVisible: true,
44
+ },
45
+ () => {
46
+ this.props.filterChanged(selectedFilter);
47
+ },
48
+ );
49
+ };
50
+
51
+ getDateRangeText = (start, end) => {
52
+ return `${moment(start).format('DD/MM/YYYY')} to ${moment(end).format('DD/MM/YYYY')}`;
53
+ };
54
+
55
+ toggleDateSelectors = () => {
56
+ this.setState({ calendarVisible: !this.state.calendarVisible });
57
+ };
58
+
59
+ startDateChanged = (date) => {
60
+ if (moment(date).isSameOrBefore(this.state.endDate)) {
61
+ this.setState({ startDate: date, dateRangeText: this.getDateRangeText(date, this.state.endDate) }, () => {
62
+ this.props.filterDateRangeChanged(this.state.startDate, this.state.endDate);
63
+ });
64
+ }
65
+ };
66
+
67
+ endDateChanged = (date) => {
68
+ if (moment(date).isSameOrAfter(this.state.startDate)) {
69
+ this.setState({ endDate: date, dateRangeText: this.getDateRangeText(this.state.startDate, date) }, () => {
70
+ this.props.filterDateRangeChanged(this.state.startDate, this.state.endDate);
71
+ });
72
+ }
73
+ };
74
+
75
+ render() {
76
+ return (
77
+ <div>
78
+ <div className="analyticsFilter__dropDown">
79
+ <DropdownButton
80
+ id="secondLevelSort"
81
+ bsSize="xsmall"
82
+ className="siteSelector__button analyticsFilter__dropDown__button"
83
+ title={this.state.selectedFilter.text}
84
+ onSelect={this.selectFilter}
85
+ >
86
+ {this.renderOptions()}
87
+ </DropdownButton>
88
+ {this.state.selectedFilter.dayCount === 0 && (
89
+ <GenericInput
90
+ id="customDateRange"
91
+ placeholder={'Select date range'}
92
+ className="analyticsFilter__dropDown__input"
93
+ value={this.state.dateRangeText}
94
+ onClick={this.toggleDateSelectors}
95
+ readOnly
96
+ />
97
+ )}
98
+ </div>
99
+ {this.state.selectedFilter.dayCount === 0 && this.state.calendarVisible && (
100
+ <div className="analyticsFilter__dateRange">
101
+ <DatePicker hideTop selectedDate={this.state.startDate} selectDate={this.startDateChanged} />
102
+ <DatePicker hideTop selectedDate={this.state.endDate} selectDate={this.endDateChanged} />
103
+ </div>
104
+ )}
105
+ </div>
106
+ );
107
+ }
108
+ }
109
+
110
+ export { AnalyticsFilter };
@@ -0,0 +1,174 @@
1
+ import React, { Component } from 'react';
2
+ import _ from 'lodash';
3
+ import { withRouter } from 'react-router';
4
+ import { connect } from 'react-redux';
5
+ import { GenericInput, Text } from './';
6
+ import Config from '../config';
7
+ import { SVGIcon } from './SVGIcon';
8
+ import { COLOUR_DUSK } from '../colours';
9
+
10
+ class AudienceIncluder extends Component {
11
+ constructor(props) {
12
+ super(props);
13
+ this.state = {
14
+ showUserAdd: false,
15
+ userSearch: '',
16
+ returnList: [],
17
+ userList: [],
18
+
19
+ selectionList: [],
20
+ };
21
+ }
22
+
23
+ UNSAFE_componentWillMount() {
24
+ this.updateProps(this.props);
25
+ }
26
+
27
+ UNSAFE_componentWillReceiveProps(nextProps) {
28
+ this.updateProps(nextProps);
29
+ }
30
+
31
+ updateProps(props) {
32
+ var stateChange = { selectionList: props.selectionList, userList: props.userList };
33
+
34
+ if (!_.isEmpty(this.state.userSearch) && this.state.userSearch.length > 1) {
35
+ stateChange.returnList = _.filter(props.userList, (ev) => {
36
+ return ev.displayName.toLowerCase().indexOf(this.state.userSearch.toLowerCase()) > -1;
37
+ });
38
+
39
+ stateChange.returnList = _.reject(stateChange.returnList, (item) =>
40
+ _.find(props.selectionList, (t) => {
41
+ return t.Id === item.Id;
42
+ }),
43
+ );
44
+ }
45
+ this.setState(stateChange);
46
+ }
47
+
48
+ showUserAdd() {
49
+ this.setState({ showUserAdd: true });
50
+ }
51
+
52
+ handleChange(event) {
53
+ if (this.state.searchUserLoading) {
54
+ return;
55
+ }
56
+ var stateChange = { returnList: [] };
57
+ stateChange[event.target.getAttribute('id')] = event.target.value;
58
+
59
+ if (!_.isEmpty(event.target.value) && event.target.value.length > 1) {
60
+ stateChange.returnList = _.filter(this.props.userList, (ev) => {
61
+ return ev.displayName.toLowerCase().indexOf(event.target.value.toLowerCase()) > -1;
62
+ });
63
+
64
+ stateChange.returnList = _.reject(stateChange.returnList, (item) =>
65
+ _.find(this.state.selectionList, (t) => {
66
+ return t.Id === item.Id;
67
+ }),
68
+ );
69
+ }
70
+
71
+ this.setState(stateChange);
72
+ }
73
+
74
+ renderSearch() {
75
+ return (
76
+ <div style={{}}>
77
+ {this.state.returnList.map((user, index) => {
78
+ return (
79
+ <div
80
+ onClick={() => {
81
+ this.props.add(user);
82
+ }}
83
+ key={user.Id}
84
+ className="pointer"
85
+ style={{ marginBottom: 8, display: 'flex', flexDirection: 'row', alignItems: 'center' }}
86
+ >
87
+ <img
88
+ style={{ height: 30, width: 30, borderRadius: 15, marginRight: 16 }}
89
+ src={user.profilePic != null ? user.profilePic : Config.env.defaultProfileImage}
90
+ alt="user profilePic"
91
+ />
92
+ <div className={`fontMedium fontSize-16 text-dark'`}>{user.displayName}</div>
93
+ </div>
94
+ );
95
+ })}
96
+ </div>
97
+ );
98
+ }
99
+
100
+ renderUserAdd() {
101
+ return (
102
+ <div className="" style={{ backgroundColor: '#fff' }}>
103
+ {' '}
104
+ {/* Search for peeps */}
105
+ <GenericInput
106
+ id="userSearch"
107
+ type="text"
108
+ label="Search for User"
109
+ placeholder="Search for user"
110
+ value={this.state.userSearch}
111
+ onChange={(e) => this.handleChange(e)}
112
+ alwaysShowLabel
113
+ />
114
+ {this.renderSearch()}
115
+ </div>
116
+ );
117
+ }
118
+
119
+ renderAttendees() {
120
+ if (this.state.selectionList && !_.isEmpty(this.state.selectionList)) {
121
+ return (
122
+ <div style={{ marginTop: 16 }}>
123
+ <Text type="formLabel" className="marginBottom-8">
124
+ Selected Users
125
+ </Text>
126
+ {this.state.selectionList.map((user, index) => {
127
+ return (
128
+ <div
129
+ key={user.Id}
130
+ style={{ marginBottom: 8, display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}
131
+ >
132
+ <div className="flex" style={{ alignItems: 'center' }}>
133
+ <img
134
+ style={{ height: 30, width: 30, borderRadius: 15, marginRight: 16 }}
135
+ src={user.profilePic != null ? user.profilePic : Config.env.defaultProfileImage}
136
+ alt="user profilePic"
137
+ />
138
+ <div className={`fontMedium fontSize-16 text-dark'`}>{user.displayName}</div>
139
+ </div>
140
+ <SVGIcon
141
+ className="removeIcon"
142
+ icon="close"
143
+ onClick={() => {
144
+ this.props.remove(user);
145
+ }}
146
+ colour={COLOUR_DUSK}
147
+ />
148
+ {/* <ConfirmActions declineOnly onDecline={() => {}} /> */}
149
+ </div>
150
+ );
151
+ })}
152
+ </div>
153
+ );
154
+ }
155
+ return null;
156
+ }
157
+
158
+ render() {
159
+ return (
160
+ <div>
161
+ <div>{this.renderUserAdd()}</div>
162
+ <div>{this.renderAttendees()}</div>
163
+ </div>
164
+ );
165
+ }
166
+ }
167
+
168
+ const mapStateToProps = (state) => {
169
+ return { auth: state.auth };
170
+ };
171
+
172
+ let exportObj = connect(mapStateToProps, {})(withRouter(AudienceIncluder));
173
+
174
+ export { exportObj as AudienceIncluder };
@@ -0,0 +1,549 @@
1
+ import React, { Component } from 'react';
2
+ import FontAwesome from 'react-fontawesome';
3
+ import _ from 'lodash';
4
+ import { connect } from 'react-redux';
5
+ import { setAuth } from '../actions';
6
+ import { RadioButton, DropdownInput } from './';
7
+ import { profileActions, typeActions, userActions } from '../apis';
8
+ import { COLOUR_DUSK } from '../colours';
9
+ import { AudienceIncluder } from './AudienceIncluder';
10
+ import { Text } from './';
11
+
12
+ class AudienceSelector extends Component {
13
+ state = {
14
+ categories: [
15
+ {
16
+ Title: 'All Primary Users',
17
+ Key: 'resident',
18
+ ShortName: 'Primary',
19
+ },
20
+ {
21
+ Title: 'All Staff Users',
22
+ Key: 'staff',
23
+ ShortName: 'Staff',
24
+ },
25
+ {
26
+ Title: 'All Linked Users',
27
+ Key: 'family',
28
+ ShortName: 'Linked',
29
+ },
30
+ ],
31
+ types: [],
32
+ AudienceType: 'All',
33
+
34
+ showInclude: false,
35
+ includeList: [],
36
+ showExclude: false,
37
+ excludeList: [],
38
+ Type: '',
39
+ Category: '',
40
+
41
+ userList: [],
42
+ tagList: [],
43
+ };
44
+
45
+ componentDidMount() {
46
+ if (!this.props.disallowUserType) {
47
+ this.getUserTypes();
48
+ }
49
+ if (!this.props.disallowSingleUsers) {
50
+ this.getUsers();
51
+ }
52
+ if (this.props.allowTags) {
53
+ this.getUserTags();
54
+ }
55
+ }
56
+
57
+ onSubmit() {
58
+ if (this.state.AudienceType === 'User' && _.isEmpty(this.state.includeList)) {
59
+ this.setState({ showInclude: true });
60
+ }
61
+ }
62
+
63
+ getAudienceType() {
64
+ return this.state.AudienceType;
65
+ }
66
+
67
+ getAudienceTypeSelection() {
68
+ if (this.state.AudienceType === 'Category') {
69
+ return this.state.Category;
70
+ }
71
+ if (this.state.AudienceType === 'UserTags') {
72
+ return this.state.Tag;
73
+ }
74
+ return this.state.Type;
75
+ }
76
+
77
+ getIncludeList() {
78
+ return this.state.includeList;
79
+ }
80
+
81
+ getExcludeList() {
82
+ return this.state.excludeList;
83
+ }
84
+
85
+ getUsers() {
86
+ userActions
87
+ .fetchUsers(this.props.auth.site)
88
+ .then((res) => {
89
+ this.setState({
90
+ userList: res.data.results.Items,
91
+ });
92
+ })
93
+ .catch((error) => {});
94
+ }
95
+
96
+ getUserTags() {
97
+ profileActions
98
+ .getUserTagsBySite(this.props.auth.site)
99
+ .then((res) => {
100
+ this.setState({
101
+ tagList: res.data.map((t) => {
102
+ return { ...t, Key: t.Id };
103
+ }),
104
+ });
105
+ })
106
+ .catch((error) => {});
107
+ }
108
+
109
+ setType(type, selection) {
110
+ const newState = {
111
+ AudienceType: type,
112
+ };
113
+ if (type === 'UserType') {
114
+ newState.Type = selection;
115
+ }
116
+ if (type === 'Category') {
117
+ newState.Category = selection;
118
+ }
119
+ this.setState(newState);
120
+ }
121
+
122
+ onChangeOption(type) {
123
+ this.setState({
124
+ AudienceType: type,
125
+ Type: '',
126
+ Category: '',
127
+ includeList: [],
128
+ excludeList: [],
129
+ });
130
+ setTimeout(() => {
131
+ this.props.updateValidation(this.validateAudienceSelection());
132
+ }, 50);
133
+ }
134
+
135
+ getOptions() {
136
+ const options = [
137
+ {
138
+ Label: 'All users',
139
+ Value: 'All',
140
+ onChange: () => this.onChangeOption('All'),
141
+ },
142
+ {
143
+ Label: 'By user category',
144
+ Value: 'Category',
145
+ onChange: () => this.onChangeOption('Category'),
146
+ },
147
+ ];
148
+ if (!this.props.disallowUserType) {
149
+ options.push({
150
+ Label: 'By user type',
151
+ Value: 'UserType',
152
+ onChange: () => this.onChangeOption('UserType'),
153
+ });
154
+ }
155
+ if (this.props.allowTags) {
156
+ options.push({
157
+ Label: 'By user tag',
158
+ Value: 'UserTags',
159
+ onChange: () => this.onChangeOption('UserTags'),
160
+ });
161
+ }
162
+ if (!this.props.disallowSingleUsers) {
163
+ options.push({
164
+ Label: 'To single users',
165
+ Value: 'User',
166
+ onChange: () => this.onChangeOption('User'),
167
+ });
168
+ }
169
+ return options;
170
+ }
171
+
172
+ validateAudienceSelection() {
173
+ if (this.state.AudienceType === 'Category' && !this.validateCategory()) {
174
+ return false;
175
+ }
176
+ if (this.state.AudienceType === 'UserType' && !this.validateType()) {
177
+ return false;
178
+ }
179
+ if (this.state.AudienceType === 'User' && _.isEmpty(this.state.includeList)) {
180
+ return false;
181
+ }
182
+ if (this.state.AudienceType === 'UserTag' && !this.validateTag()) {
183
+ return false;
184
+ }
185
+ return true;
186
+ }
187
+
188
+ selectType(key) {
189
+ this.setState({
190
+ Type: key,
191
+ includeList: [],
192
+ excludeList: [],
193
+ });
194
+ setTimeout(() => {
195
+ this.props.updateValidation(this.validateAudienceSelection());
196
+ }, 50);
197
+ }
198
+
199
+ getTypeTitle() {
200
+ const typeObject = _.find(this.state.types, (t) => {
201
+ return t.typeName === this.state.Type;
202
+ });
203
+ if (typeObject) {
204
+ return typeObject.displayName;
205
+ }
206
+ return '';
207
+ }
208
+
209
+ selectTag = (key) => {
210
+ this.setState({
211
+ Tag: key,
212
+ includeList: [],
213
+ excludeList: [],
214
+ });
215
+ setTimeout(() => {
216
+ this.props.updateValidation(this.validateAudienceSelection());
217
+ }, 50);
218
+ };
219
+
220
+ getTagTitle() {
221
+ const tag = _.find(this.state.tagList, (t) => {
222
+ return t.Id === this.state.Tag;
223
+ });
224
+ if (tag) {
225
+ return tag.Title;
226
+ }
227
+ return '';
228
+ }
229
+
230
+ getUserTypes() {
231
+ typeActions
232
+ .getUserTypes(this.props.auth.site)
233
+ .then((res) => {
234
+ this.props.setAuth({ userTypes: _.cloneDeep(res.data) });
235
+ res.data.forEach((e) => {
236
+ e.Title = e.displayName;
237
+ e.Key = e.typeName;
238
+
239
+ if (e.category) {
240
+ const category = _.find(this.state.categories, (c) => {
241
+ return c.Key === e.category;
242
+ });
243
+ const shortName = category ? `(${category.ShortName}) ` : '';
244
+ e.Title = `${shortName} ${e.Title}`;
245
+ }
246
+ });
247
+ this.setState({
248
+ types: res.data,
249
+ });
250
+ })
251
+ .catch((error) => {});
252
+ }
253
+
254
+ getUserList() {
255
+ if (this.state.AudienceType === 'UserType' && !_.isEmpty(this.state.Type)) {
256
+ let users = [];
257
+ this.state.userList.forEach((element) => {
258
+ if (element.type !== this.state.Type) users.push(element);
259
+ });
260
+ return _.sortBy(users, 'displayName');
261
+ }
262
+ if (this.state.AudienceType === 'Category' && !_.isEmpty(this.state.Category)) {
263
+ let users = [];
264
+ let types = _.filter(this.state.types, (type) => {
265
+ return type.category === this.state.Category;
266
+ }).map((type) => {
267
+ return type.typeName;
268
+ });
269
+ this.state.userList.forEach((element) => {
270
+ if (types.indexOf(element.type) === -1) {
271
+ users.push(element);
272
+ }
273
+ });
274
+ return _.sortBy(users, 'displayName');
275
+ }
276
+ return _.sortBy(this.state.userList, 'displayName');
277
+ }
278
+
279
+ selectCat(key, e) {
280
+ this.setState({
281
+ Category: key,
282
+ includeList: [],
283
+ excludeList: [],
284
+ });
285
+ setTimeout(() => {
286
+ this.props.updateValidation(this.validateAudienceSelection());
287
+ }, 50);
288
+ }
289
+
290
+ getCatTitle() {
291
+ const typeObject = _.find(this.state.categories, (t) => {
292
+ return t.Key === this.state.Category;
293
+ });
294
+ if (typeObject) {
295
+ return typeObject.Title;
296
+ }
297
+ return '';
298
+ }
299
+
300
+ validateCategory() {
301
+ return !_.isUndefined(this.state.Category) && !_.isEmpty(this.state.Category);
302
+ }
303
+
304
+ validateType() {
305
+ return !_.isUndefined(this.state.Type) && !_.isEmpty(this.state.Type);
306
+ }
307
+
308
+ validateTag() {
309
+ return !_.isEmpty(this.state.Tag);
310
+ }
311
+
312
+ showUserPickers(isInclude) {
313
+ if (isInclude && this.state.AudienceType === 'User') {
314
+ return true;
315
+ }
316
+ if (isInclude && !this.props.allowIncludes) {
317
+ return false;
318
+ }
319
+ if (!isInclude && !this.props.allowExcludes) {
320
+ return false;
321
+ }
322
+ if (isInclude && this.state.AudienceType === 'All') {
323
+ return false;
324
+ }
325
+ if (!isInclude && this.state.AudienceType === 'User') {
326
+ return false;
327
+ }
328
+ if (
329
+ (this.state.AudienceType === 'Category' && _.isEmpty(this.state.Category)) ||
330
+ (this.state.AudienceType === 'UserType' && _.isEmpty(this.state.Type))
331
+ ) {
332
+ return false;
333
+ }
334
+ return true;
335
+ }
336
+
337
+ getUserExcludeList() {
338
+ if (this.state.AudienceType === 'UserType' && !_.isEmpty(this.state.Type)) {
339
+ let users = [];
340
+ this.state.userList.forEach((element) => {
341
+ if (element.type === this.state.Type) users.push(element);
342
+ });
343
+ return _.sortBy(users, 'displayName');
344
+ }
345
+ if (this.state.AudienceType === 'Category' && !_.isEmpty(this.state.Category)) {
346
+ let users = [];
347
+ let types = _.filter(this.state.types, (type) => {
348
+ return type.category === this.state.Category;
349
+ }).map((type) => {
350
+ return type.typeName;
351
+ });
352
+ this.state.userList.forEach((element) => {
353
+ if (types.indexOf(element.type) > -1) {
354
+ users.push(element);
355
+ }
356
+ });
357
+ return _.sortBy(users, 'displayName');
358
+ }
359
+ return _.sortBy(this.state.userList, 'displayName');
360
+ }
361
+
362
+ addToInc(user) {
363
+ let list = [...this.state.includeList];
364
+ list.push({
365
+ Id: user.Id,
366
+ displayName: user.displayName,
367
+ profilePic: user.profilePic,
368
+ });
369
+ this.setState({ includeList: list });
370
+ setTimeout(() => {
371
+ this.props.updateValidation(this.validateAudienceSelection());
372
+ }, 50);
373
+ }
374
+
375
+ removeUserInc(user) {
376
+ this.setState({
377
+ includeList: _.filter(this.state.includeList, (ev) => {
378
+ return ev.Id !== user.Id;
379
+ }),
380
+ });
381
+ setTimeout(() => {
382
+ this.props.updateValidation(this.validateAudienceSelection());
383
+ }, 50);
384
+ }
385
+
386
+ addToEx(user) {
387
+ let list = [...this.state.excludeList];
388
+ list.push({
389
+ Id: user.Id,
390
+ displayName: user.displayName,
391
+ profilePic: user.profilePic,
392
+ });
393
+ this.setState({ excludeList: list });
394
+ }
395
+
396
+ removeUserEx(user) {
397
+ this.setState({
398
+ excludeList: _.filter(this.state.excludeList, (ev) => {
399
+ return ev.Id !== user.Id;
400
+ }),
401
+ });
402
+ }
403
+
404
+ renderExcludes() {
405
+ if (!this.showUserPickers(false)) {
406
+ return null;
407
+ }
408
+ return (
409
+ <div key="excludes" className="marginTop-16">
410
+ <div
411
+ className="flex flex-between pointer"
412
+ onClick={() => {
413
+ this.setState({ showExclude: !this.state.showExclude });
414
+ }}
415
+ >
416
+ <div className="flex">
417
+ <Text type="formTitleSmall" className="marginBottom-16">
418
+ Exclude Users From Selection {`(${this.state.excludeList.length})`}
419
+ </Text>
420
+ </div>
421
+ <FontAwesome style={{ color: COLOUR_DUSK, fontSize: 12 }} name={this.state.showExclude ? 'chevron-up' : 'chevron-down'} />
422
+ </div>
423
+ {this.state.showExclude && (
424
+ <AudienceIncluder
425
+ add={this.addToEx.bind(this)}
426
+ remove={this.removeUserEx.bind(this)}
427
+ selectionList={this.state.excludeList}
428
+ userList={this.getUserExcludeList()}
429
+ />
430
+ )}
431
+ </div>
432
+ );
433
+ }
434
+
435
+ renderIncludes() {
436
+ if (!this.showUserPickers(true)) {
437
+ return null;
438
+ }
439
+ return (
440
+ <div key="includes" className="marginTop-16">
441
+ <div
442
+ className="flex flex-between pointer"
443
+ onClick={() => {
444
+ this.setState({ showInclude: !this.state.showInclude });
445
+ }}
446
+ >
447
+ <div className="flex">
448
+ <Text type="formTitleSmall" className="marginBottom-16">
449
+ {this.state.AudienceType !== 'User' ? 'Add Other' : 'Select'} Users {`(${this.state.includeList.length})`}
450
+ </Text>
451
+ </div>
452
+ <FontAwesome style={{ color: COLOUR_DUSK, fontSize: 12 }} name={this.state.showInclude ? 'chevron-up' : 'chevron-down'} />
453
+ </div>
454
+ {this.state.showInclude && (
455
+ <AudienceIncluder
456
+ add={this.addToInc.bind(this)}
457
+ remove={this.removeUserInc.bind(this)}
458
+ selectionList={this.state.includeList}
459
+ userList={this.getUserList()}
460
+ />
461
+ )}
462
+ </div>
463
+ );
464
+ }
465
+
466
+ render() {
467
+ return (
468
+ <div>
469
+ {/* <div className={this.props.className || 'padding-32 paddingVertical-40 bottomDivideBorder'}> */}
470
+ <Text type="formTitleSmall" className="marginBottom-16">
471
+ {this.props.title || 'Target Audience'}
472
+ </Text>
473
+ <RadioButton
474
+ isActive={this.state.AudienceType}
475
+ rowStyle={{ flexDirection: 'column' }}
476
+ buttonStyle={{ marginBottom: 8 }}
477
+ options={this.getOptions()}
478
+ disabled={this.props.disabled}
479
+ />
480
+ {this.state.AudienceType === 'Category' && (
481
+ <DropdownInput
482
+ id="typeSelect"
483
+ // label='User Category'
484
+ alwaysShowLabel
485
+ placeholder="User Category"
486
+ value={this.getCatTitle()}
487
+ isValid={() => {
488
+ return this.validateCategory();
489
+ }}
490
+ showError={() => {
491
+ return this.state.showWarnings && !this.validateCategory();
492
+ }}
493
+ options={this.state.categories}
494
+ onSelect={this.selectCat.bind(this)}
495
+ style={{ width: 325, marginTop: 16 }}
496
+ isRequired
497
+ disabled={this.props.disabled}
498
+ />
499
+ )}
500
+ {this.state.AudienceType === 'UserType' && (
501
+ <DropdownInput
502
+ id="typeSelect"
503
+ // label='User Type'
504
+ alwaysShowLabel
505
+ placeholder="User Type"
506
+ value={this.getTypeTitle()}
507
+ isValid={() => {
508
+ return this.validateType();
509
+ }}
510
+ showError={() => {
511
+ return this.state.showWarnings && !this.validateType();
512
+ }}
513
+ options={this.state.types}
514
+ onSelect={this.selectType.bind(this)}
515
+ style={{ width: 325, marginTop: 16 }}
516
+ isRequired
517
+ disabled={this.props.disabled}
518
+ />
519
+ )}
520
+ {this.state.AudienceType === 'UserTags' && (
521
+ <DropdownInput
522
+ id="tagSelect"
523
+ alwaysShowLabel
524
+ placeholder="User Tag"
525
+ value={this.getTagTitle()}
526
+ isValid={() => {
527
+ return this.validateTag();
528
+ }}
529
+ showError={() => {
530
+ return this.state.showWarnings && !this.validateTag();
531
+ }}
532
+ options={this.state.tagList}
533
+ onSelect={this.selectTag}
534
+ style={{ width: 325, marginTop: 16 }}
535
+ isRequired
536
+ disabled={this.props.disabled}
537
+ />
538
+ )}
539
+ {/* </div> */}
540
+ {this.renderIncludes()}
541
+ {this.renderExcludes()}
542
+ </div>
543
+ );
544
+ }
545
+ }
546
+
547
+ const toExport = connect(null, { setAuth }, null, { withRef: true })(AudienceSelector);
548
+
549
+ export { toExport as AudienceSelector };