@plusscommunities/pluss-core-web 1.6.13-beta.0 → 1.6.15-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.
Files changed (112) hide show
  1. package/package.json +1 -2
  2. package/rollup.config.js +45 -45
  3. package/src/actions/AuthActions.js +40 -40
  4. package/src/actions/LocalActions.js +5 -5
  5. package/src/actions/NavActions.js +5 -5
  6. package/src/actions/ScheduledActionsActions.js +5 -5
  7. package/src/actions/TemplateActions.js +9 -9
  8. package/src/actions/UsersActions.js +74 -70
  9. package/src/actions/index.js +6 -6
  10. package/src/actions/types.js +17 -17
  11. package/src/analytics.js +178 -158
  12. package/src/apis/analyticsActions.js +50 -50
  13. package/src/apis/authActions.js +55 -55
  14. package/src/apis/fileActions.js +147 -142
  15. package/src/apis/index.js +10 -10
  16. package/src/apis/profileActions.js +130 -130
  17. package/src/apis/scheduledActionsActions.js +9 -9
  18. package/src/apis/stringActions.js +26 -22
  19. package/src/apis/templateActions.js +38 -38
  20. package/src/apis/typeActions.js +183 -183
  21. package/src/apis/userActions.js +166 -166
  22. package/src/apis/utilityActions.js +32 -32
  23. package/src/colours.js +79 -77
  24. package/src/components/AddButton.js +29 -20
  25. package/src/components/AnalyticsFilter.js +136 -102
  26. package/src/components/Attachment.js +36 -22
  27. package/src/components/AudienceIncluder.js +209 -165
  28. package/src/components/AudienceSelector.js +680 -680
  29. package/src/components/Button.js +79 -56
  30. package/src/components/CheckBox.js +77 -65
  31. package/src/components/ColourOptions.js +205 -181
  32. package/src/components/ColourPicker.js +286 -244
  33. package/src/components/Comment.js +48 -39
  34. package/src/components/CommentSection.js +113 -103
  35. package/src/components/DatePicker.js +298 -255
  36. package/src/components/DropdownInput.js +236 -215
  37. package/src/components/DurationInput.js +46 -43
  38. package/src/components/ExportCsvPopup.js +236 -217
  39. package/src/components/FileInput.js +359 -346
  40. package/src/components/GenericInput.js +184 -153
  41. package/src/components/Header.js +88 -65
  42. package/src/components/HubSidebar.js +136 -124
  43. package/src/components/ImageInput.js +1182 -973
  44. package/src/components/InputGroup.js +20 -13
  45. package/src/components/MakerPopup.js +285 -278
  46. package/src/components/MoreMenu.js +21 -17
  47. package/src/components/OptionsSection.js +86 -74
  48. package/src/components/OverlayPage.js +61 -61
  49. package/src/components/OverlayPageBottomButtons.js +38 -38
  50. package/src/components/OverlayPageContents.js +38 -38
  51. package/src/components/OverlayPageSection.js +24 -24
  52. package/src/components/P60Icon.js +38 -33
  53. package/src/components/PageTitle.js +8 -8
  54. package/src/components/Popup.js +114 -95
  55. package/src/components/ProfilePic.js +40 -40
  56. package/src/components/RadioButton.js +142 -126
  57. package/src/components/Reactions.js +66 -66
  58. package/src/components/SVGIcon.js +31 -25
  59. package/src/components/SideNavItem.js +112 -83
  60. package/src/components/StatBox.js +54 -44
  61. package/src/components/StatusButton.js +31 -17
  62. package/src/components/SuccessPopup.js +61 -48
  63. package/src/components/Tabs.js +50 -47
  64. package/src/components/Tag.js +54 -54
  65. package/src/components/Text.js +18 -14
  66. package/src/components/TextFormatPopup.js +77 -49
  67. package/src/components/TimePicker.js +214 -200
  68. package/src/components/ToggleSwitch.js +60 -0
  69. package/src/components/UserListing.js +110 -97
  70. package/src/components/index.js +46 -46
  71. package/src/components/svg-icons.json +819 -819
  72. package/src/config.js +22 -22
  73. package/src/helper/HelpDeskWidget.js +4 -4
  74. package/src/helper/api/getUrl.js +16 -11
  75. package/src/helper/api/getUrlParams.js +8 -5
  76. package/src/helper/api/safeReadParams.js +3 -2
  77. package/src/helper/auth/getUserFromState.js +4 -4
  78. package/src/helper/colours/getAppColourFromState.js +10 -5
  79. package/src/helper/files/canvasImageUploader.js +152 -140
  80. package/src/helper/files/generateImageName.js +6 -6
  81. package/src/helper/files/get1400.js +33 -25
  82. package/src/helper/files/getExtension.js +5 -5
  83. package/src/helper/files/getFileName.js +9 -9
  84. package/src/helper/files/getThumb300.js +37 -29
  85. package/src/helper/files/isVideo.js +3 -3
  86. package/src/helper/helper.js +59 -56
  87. package/src/helper/index.js +29 -29
  88. package/src/helper/site/getMerchantsFromState.js +8 -4
  89. package/src/helper/site/getSiteLevelFromState.js +29 -29
  90. package/src/helper/site/getSiteName.js +11 -11
  91. package/src/helper/site/getSiteNameFromRoles.js +7 -7
  92. package/src/helper/site/getSiteSettingFromState.js +10 -5
  93. package/src/helper/storage/readJSONFromStorage.js +5 -5
  94. package/src/helper/storage/readStorageWithCookie.js +17 -17
  95. package/src/helper/storage/setLocalStorage.js +1 -1
  96. package/src/helper/strings/getFirstName.js +5 -5
  97. package/src/helper/strings/htmlDecode.js +2 -2
  98. package/src/helper/strings/htmlEncode.js +2 -2
  99. package/src/helper/strings/isEmail.js +7 -6
  100. package/src/helper/strings/isUrl.js +7 -7
  101. package/src/helper/strings/onlyAlphanumeric.js +4 -4
  102. package/src/helper/strings/randomString.js +6 -6
  103. package/src/helper/strings/toParagraphed.js +12 -12
  104. package/src/index.js +22 -11
  105. package/src/reducers/ScheduledActionsReducer.js +8 -8
  106. package/src/reducers/TemplatesReducer.js +22 -22
  107. package/src/reducers/index.js +4 -4
  108. package/src/session.js +70 -70
  109. package/src/urls.js +17 -16
  110. package/dist/index.cjs.js +0 -11623
  111. package/dist/index.esm.js +0 -11583
  112. package/dist/index.umd.js +0 -11595
@@ -12,689 +12,689 @@ import { DropdownInput } from "./DropdownInput";
12
12
  import { Tag } from "./Tag";
13
13
 
14
14
  class AudienceSelector extends Component {
15
- constructor(props) {
16
- super(props);
17
-
18
- this.state = {
19
- categories: [
20
- {
21
- Title: "All Primary Users",
22
- Key: "resident",
23
- ShortName: "Primary",
24
- },
25
- {
26
- Title: "All Staff Users",
27
- Key: "staff",
28
- ShortName: "Staff",
29
- },
30
- {
31
- Title: "All Linked Users",
32
- Key: "family",
33
- ShortName: "Linked",
34
- },
35
- ],
36
- types: [],
37
- AudienceType: props.audienceType || "All",
38
- showInclude: false,
39
- includeList: [],
40
- showExclude: false,
41
- excludeList: [],
42
- Type: "",
43
- Category: "",
44
-
45
- userList: [],
46
- tagList: [],
47
- AudienceTagList: props.audienceTypeSelection || [],
48
- };
49
- }
50
-
51
- componentDidMount() {
52
- const { custom, disallowSingleUsers, allowTags } = this.props;
53
- this.getUserTypes();
54
- if (!disallowSingleUsers) this.getUsers();
55
- if (allowTags || custom) this.getUserTags();
56
- }
57
-
58
- componentDidUpdate(prevProps) {
59
- const newState = {};
60
- if (prevProps.audienceType !== this.props.audienceType)
61
- newState.AudienceType = this.props.audienceType;
62
- if (
63
- !_.isEqual(
64
- prevProps.audienceTypeSelection,
65
- this.props.audienceTypeSelection,
66
- )
67
- )
68
- newState.AudienceTagList = this.props.audienceTypeSelection;
69
- if (!_.isEmpty(newState)) this.setState(newState);
70
- }
71
-
72
- onSubmit() {
73
- if (
74
- this.state.AudienceType === "User" &&
75
- _.isEmpty(this.state.includeList)
76
- ) {
77
- this.setState({ showInclude: true });
78
- }
79
- }
80
-
81
- getAudienceType() {
82
- return this.state.AudienceType;
83
- }
84
-
85
- getAudienceTypeSelection() {
86
- const { AudienceType, AudienceTagList, Category, Tag, Type } = this.state;
87
- if (AudienceType === "Custom") return AudienceTagList || [];
88
- if (AudienceType === "Category") return Category;
89
- if (AudienceType === "UserTags") return Tag;
90
- return Type;
91
- }
92
-
93
- getIncludeList() {
94
- return this.state.includeList;
95
- }
96
-
97
- getExcludeList() {
98
- return this.state.excludeList;
99
- }
100
-
101
- getUsers() {
102
- userActions
103
- .fetchUsers(this.props.auth.site)
104
- .then((res) => {
105
- this.setState({
106
- userList: res.data.results.Items,
107
- });
108
- })
109
- .catch((error) => { });
110
- }
111
-
112
- getUserTags() {
113
- profileActions
114
- .getUserTagsBySite(this.props.auth.site)
115
- .then((res) => {
116
- this.setState({
117
- tagList: res.data.map((t) => {
118
- return { ...t, Key: t.Id };
119
- }),
120
- });
121
- })
122
- .catch((error) => { });
123
- }
124
-
125
- setType(type, selection) {
126
- const newState = { AudienceType: type };
127
- if (type === "Custom") newState.AudienceTagList = selection;
128
- if (type === "UserType") newState.Type = selection;
129
- if (type === "Category") newState.Category = selection;
130
- this.setState(newState);
131
- }
132
-
133
- onChangeSelection = () => {
134
- const { updateValidation, onChange } = this.props;
135
- const isValid = this.validateAudienceSelection();
136
- if (updateValidation) updateValidation(isValid);
137
- if (onChange)
138
- onChange(
139
- this.getAudienceType(),
140
- this.getAudienceTypeSelection(),
141
- isValid,
142
- );
143
- };
144
-
145
- onChangeOption(type) {
146
- this.setState({
147
- AudienceType: type,
148
- Type: "",
149
- Category: "",
150
- includeList: [],
151
- excludeList: [],
152
- AudienceTagList: [],
153
- });
154
- setTimeout(this.onChangeSelection, 50);
155
- }
156
-
157
- getOptions() {
158
- const options = [
159
- {
160
- Label: "All users",
161
- Value: "All",
162
- onChange: () => this.onChangeOption("All"),
163
- },
164
- ];
165
- if (this.props.custom) {
166
- options.push({
167
- Label: "Custom",
168
- Value: "Custom",
169
- onChange: () => this.onChangeOption("Custom"),
170
- });
171
- } else {
172
- options.push({
173
- Label: "By user category",
174
- Value: "Category",
175
- onChange: () => this.onChangeOption("Category"),
176
- });
177
- }
178
- if (!this.props.disallowUserType) {
179
- options.push({
180
- Label: "By user type",
181
- Value: "UserType",
182
- onChange: () => this.onChangeOption("UserType"),
183
- });
184
- }
185
- if (this.props.allowTags) {
186
- options.push({
187
- Label: "By user tag",
188
- Value: "UserTags",
189
- onChange: () => this.onChangeOption("UserTags"),
190
- });
191
- }
192
- if (!this.props.disallowSingleUsers) {
193
- options.push({
194
- Label: "To single users",
195
- Value: "User",
196
- onChange: () => this.onChangeOption("User"),
197
- });
198
- }
199
- return options;
200
- }
201
-
202
- validateAudienceSelection() {
203
- const { AudienceType, includeList } = this.state;
204
- if (AudienceType === "Custom" && !this.validateCustom()) return false;
205
- if (AudienceType === "Category" && !this.validateCategory()) return false;
206
- if (AudienceType === "UserType" && !this.validateType()) return false;
207
- if (AudienceType === "User" && _.isEmpty(includeList)) return false;
208
- if (AudienceType === "UserTags" && !this.validateTag()) return false;
209
- return true;
210
- }
211
-
212
- selectType(key) {
213
- this.setState({
214
- Type: key,
215
- includeList: [],
216
- excludeList: [],
217
- });
218
- setTimeout(this.onChangeSelection, 50);
219
- }
220
-
221
- getTypeTitle() {
222
- const typeObject = _.find(this.state.types, (t) => {
223
- return t.typeName === this.state.Type;
224
- });
225
- if (typeObject) {
226
- return typeObject.displayName;
227
- }
228
- return "";
229
- }
230
-
231
- selectTag = (key) => {
232
- this.setState({
233
- Tag: key,
234
- includeList: [],
235
- excludeList: [],
236
- });
237
- setTimeout(this.onChangeSelection, 50);
238
- };
239
-
240
- getTagTitle() {
241
- const tag = _.find(this.state.tagList, (t) => {
242
- return t.Id === this.state.Tag;
243
- });
244
- if (tag) {
245
- return tag.Title;
246
- }
247
- return "";
248
- }
249
-
250
- getUserTypes() {
251
- typeActions
252
- .getUserTypes(this.props.auth.site)
253
- .then((res) => {
254
- this.props.setAuth({ userTypes: _.cloneDeep(res.data) });
255
- res.data.forEach((e) => {
256
- e.Title = e.displayName;
257
- e.Key = e.typeName;
258
-
259
- if (e.category) {
260
- const category = _.find(this.state.categories, (c) => {
261
- return c.Key === e.category;
262
- });
263
- const shortName = category ? `(${category.ShortName}) ` : "";
264
- e.Title = `${shortName} ${e.Title}`;
265
- }
266
- });
267
- const newState = {
268
- types: res.data,
269
- };
270
- if (!_.some(res.data, (t) => t.category === "family")) {
271
- newState.categories = _.filter(
272
- this.state.categories,
273
- (c) => c.Key !== "family",
274
- );
275
- }
276
- this.setState(newState);
277
- })
278
- .catch((error) => { });
279
- }
280
-
281
- getUserList() {
282
- if (this.state.AudienceType === "UserType" && !_.isEmpty(this.state.Type)) {
283
- let users = [];
284
- this.state.userList.forEach((element) => {
285
- if (element.type !== this.state.Type) users.push(element);
286
- });
287
- return _.sortBy(users, "displayName");
288
- }
289
- if (
290
- this.state.AudienceType === "Category" &&
291
- !_.isEmpty(this.state.Category)
292
- ) {
293
- let users = [];
294
- let types = _.filter(this.state.types, (type) => {
295
- return type.category === this.state.Category;
296
- }).map((type) => {
297
- return type.typeName;
298
- });
299
- this.state.userList.forEach((element) => {
300
- if (types.indexOf(element.type) === -1) {
301
- users.push(element);
302
- }
303
- });
304
- return _.sortBy(users, "displayName");
305
- }
306
- return _.sortBy(this.state.userList, "displayName");
307
- }
308
-
309
- selectCat(key, e) {
310
- this.setState({
311
- Category: key,
312
- includeList: [],
313
- excludeList: [],
314
- });
315
- setTimeout(this.onChangeSelection, 50);
316
- }
317
-
318
- getCatTitle() {
319
- const typeObject = _.find(this.state.categories, (t) => {
320
- return t.Key === this.state.Category;
321
- });
322
- if (typeObject) {
323
- return typeObject.Title;
324
- }
325
- return "";
326
- }
327
-
328
- validateCustom = () => {
329
- return !_.isEmpty(this.state.AudienceTagList);
330
- };
331
-
332
- validateCategory() {
333
- return (
334
- !_.isUndefined(this.state.Category) && !_.isEmpty(this.state.Category)
335
- );
336
- }
337
-
338
- validateType() {
339
- return !_.isUndefined(this.state.Type) && !_.isEmpty(this.state.Type);
340
- }
341
-
342
- validateTag() {
343
- return !_.isEmpty(this.state.Tag);
344
- }
345
-
346
- showUserPickers(isInclude) {
347
- if (isInclude && this.state.AudienceType === "User") {
348
- return true;
349
- }
350
- if (isInclude && !this.props.allowIncludes) {
351
- return false;
352
- }
353
- if (!isInclude && !this.props.allowExcludes) {
354
- return false;
355
- }
356
- if (isInclude && this.state.AudienceType === "All") {
357
- return false;
358
- }
359
- if (!isInclude && this.state.AudienceType === "User") {
360
- return false;
361
- }
362
- if (
363
- (this.state.AudienceType === "Category" &&
364
- _.isEmpty(this.state.Category)) ||
365
- (this.state.AudienceType === "UserType" && _.isEmpty(this.state.Type))
366
- ) {
367
- return false;
368
- }
369
- return true;
370
- }
371
-
372
- getUserExcludeList() {
373
- if (this.state.AudienceType === "UserType" && !_.isEmpty(this.state.Type)) {
374
- let users = [];
375
- this.state.userList.forEach((element) => {
376
- if (element.type === this.state.Type) users.push(element);
377
- });
378
- return _.sortBy(users, "displayName");
379
- }
380
- if (
381
- this.state.AudienceType === "Category" &&
382
- !_.isEmpty(this.state.Category)
383
- ) {
384
- let users = [];
385
- let types = _.filter(this.state.types, (type) => {
386
- return type.category === this.state.Category;
387
- }).map((type) => {
388
- return type.typeName;
389
- });
390
- this.state.userList.forEach((element) => {
391
- if (types.indexOf(element.type) > -1) {
392
- users.push(element);
393
- }
394
- });
395
- return _.sortBy(users, "displayName");
396
- }
397
- return _.sortBy(this.state.userList, "displayName");
398
- }
399
-
400
- addToInc(user) {
401
- let list = [...this.state.includeList];
402
- list.push({
403
- Id: user.Id,
404
- displayName: user.displayName,
405
- profilePic: user.profilePic,
406
- });
407
- this.setState({ includeList: list });
408
- setTimeout(this.onChangeSelection, 50);
409
- }
410
-
411
- removeUserInc(user) {
412
- this.setState({
413
- includeList: _.filter(this.state.includeList, (ev) => {
414
- return ev.Id !== user.Id;
415
- }),
416
- });
417
- setTimeout(this.onChangeSelection, 50);
418
- }
419
-
420
- addToEx(user) {
421
- let list = [...this.state.excludeList];
422
- list.push({
423
- Id: user.Id,
424
- displayName: user.displayName,
425
- profilePic: user.profilePic,
426
- });
427
- this.setState({ excludeList: list });
428
- }
429
-
430
- removeUserEx(user) {
431
- this.setState({
432
- excludeList: _.filter(this.state.excludeList, (ev) => {
433
- return ev.Id !== user.Id;
434
- }),
435
- });
436
- }
437
-
438
- getAvailableAudienceTags = (includeSelected = false) => {
439
- const { categories, types, tagList, AudienceTagList } = this.state;
440
- const categoryTags = categories.map((c) => {
441
- return {
442
- AudienceType: "Category",
443
- AudienceTypeSelection: c.Key,
444
- Id: `category_${c.Key}`,
445
- Title: c.Title,
446
- };
447
- });
448
- const userTypeTags = types.map((t) => {
449
- return {
450
- AudienceType: "UserType",
451
- AudienceTypeSelection: t.typeName,
452
- Id: `userType_${t.typeName}`,
453
- Title: `User Type: ${t.displayName}`,
454
- };
455
- });
456
- const userTagTags = tagList.map((t) => {
457
- return {
458
- AudienceType: "UserTags",
459
- AudienceTypeSelection: t.Id,
460
- Id: `userTag_${t.Id}`,
461
- Title: `User Tag: ${t.Title}`,
462
- };
463
- });
464
- const result = [...categoryTags, ...userTypeTags, ...userTagTags];
465
- return includeSelected
466
- ? result
467
- : result.filter((t) => !_.some(AudienceTagList, (at) => at.Id === t.Id));
468
- };
469
-
470
- onAttachAudienceTag = (tag) => {
471
- const AudienceTagList = [...this.state.AudienceTagList, tag];
472
- this.setState({ AudienceTagList }, this.onChangeSelection);
473
- };
474
-
475
- onDetachAudienceTag = (tag) => {
476
- const AudienceTagList = _.filter(
477
- this.state.AudienceTagList,
478
- (at) => at.Id !== tag.Id,
479
- );
480
- this.setState({ AudienceTagList }, this.onChangeSelection);
481
- };
482
-
483
- renderExcludes() {
484
- if (!this.showUserPickers(false)) {
485
- return null;
486
- }
487
- return (
488
- <div key="excludes" className="marginTop-16">
489
- <div
490
- className="flex flex-between pointer"
491
- onClick={() => {
492
- this.setState({ showExclude: !this.state.showExclude });
493
- }}
494
- >
495
- <div className="flex">
496
- <Text type="formTitleSmall" className="marginBottom-16">
497
- Exclude Users From Selection{" "}
498
- {`(${this.state.excludeList.length})`}
499
- </Text>
500
- </div>
501
- <FontAwesome
502
- style={{ color: COLOUR_DUSK, fontSize: 12 }}
503
- name={this.state.showExclude ? "chevron-up" : "chevron-down"}
504
- />
505
- </div>
506
- {this.state.showExclude && (
507
- <AudienceIncluder
508
- add={this.addToEx.bind(this)}
509
- remove={this.removeUserEx.bind(this)}
510
- selectionList={this.state.excludeList}
511
- userList={this.getUserExcludeList()}
512
- />
513
- )}
514
- </div>
515
- );
516
- }
517
-
518
- renderIncludes() {
519
- if (!this.showUserPickers(true)) {
520
- return null;
521
- }
522
- return (
523
- <div key="includes" className="marginTop-16">
524
- <div
525
- className="flex flex-between pointer"
526
- onClick={() => {
527
- this.setState({ showInclude: !this.state.showInclude });
528
- }}
529
- >
530
- <div className="flex">
531
- <Text type="formTitleSmall" className="marginBottom-16">
532
- {this.state.AudienceType !== "User" ? "Add Other" : "Select"}{" "}
533
- Users {`(${this.state.includeList.length})`}
534
- </Text>
535
- </div>
536
- <FontAwesome
537
- style={{ color: COLOUR_DUSK, fontSize: 12 }}
538
- name={this.state.showInclude ? "chevron-up" : "chevron-down"}
539
- />
540
- </div>
541
- {this.state.showInclude && (
542
- <AudienceIncluder
543
- add={this.addToInc.bind(this)}
544
- remove={this.removeUserInc.bind(this)}
545
- selectionList={this.state.includeList}
546
- userList={this.getUserList()}
547
- />
548
- )}
549
- </div>
550
- );
551
- }
552
-
553
- renderAssignedAudiences() {
554
- const { AudienceTagList } = this.state;
555
- const { maxWidth } = this.props;
556
-
557
- return (
558
- <div className="selectedAudienceTags" style={{ maxWidth }}>
559
- {_.isEmpty(AudienceTagList) && (
560
- <Text type="body" className="text-italic">
561
- No audiences selected
562
- </Text>
563
- )}
564
- {AudienceTagList.map((t) => {
565
- return (
566
- <Tag
567
- key={t.Id}
568
- className="marginLeft-16 marginBottom-8"
569
- text={t.Title}
570
- rightIcon="remove"
571
- rightClick={() => this.onDetachAudienceTag(t)}
572
- />
573
- );
574
- })}
575
- </div>
576
- );
577
- }
578
-
579
- renderAvailableAudiences() {
580
- const { maxWidth } = this.props;
581
- const tags = this.getAvailableAudienceTags();
582
-
583
- return (
584
- <div className="availableAudienceTags" style={{ maxWidth }}>
585
- {tags.map((t) => {
586
- return (
587
- <Tag
588
- key={t.Id}
589
- className="marginLeft-16 marginBottom-8"
590
- text={t.Title}
591
- onClick={() => this.onAttachAudienceTag(t)}
592
- />
593
- );
594
- })}
595
- </div>
596
- );
597
- }
598
-
599
- renderCustom() {
600
- if (this.state.AudienceType !== "Custom") return null;
601
-
602
- return (
603
- <div className="optionsContent_bottom">
604
- <Text type="formLabel">Selected Audience</Text>
605
- {this.renderAssignedAudiences()}
606
- <Text type="formLabel" className="marginTop-32">
607
- Select Audience
608
- </Text>
609
- {this.renderAvailableAudiences()}
610
- </div>
611
- );
612
- }
613
-
614
- render() {
615
- return (
616
- <div>
617
- {/* <div className={this.props.className || 'padding-32 paddingVertical-40 bottomDivideBorder'}> */}
618
- <Text type="formTitleSmall" className="marginBottom-16">
619
- {this.props.title || "Target Audience"}
620
- </Text>
621
- <RadioButton
622
- isActive={this.state.AudienceType}
623
- rowStyle={{ flexDirection: "column" }}
624
- buttonStyle={{ marginBottom: 8 }}
625
- options={this.getOptions()}
626
- disabled={this.props.disabled}
627
- />
628
- {this.state.AudienceType === "Category" && (
629
- <DropdownInput
630
- id="typeSelect"
631
- // label='User Category'
632
- alwaysShowLabel
633
- placeholder="User Category"
634
- value={this.getCatTitle()}
635
- isValid={() => {
636
- return this.validateCategory();
637
- }}
638
- showError={() => {
639
- return this.state.showWarnings && !this.validateCategory();
640
- }}
641
- options={this.state.categories}
642
- onSelect={this.selectCat.bind(this)}
643
- style={{ width: 325, marginTop: 16 }}
644
- isRequired
645
- disabled={this.props.disabled}
646
- />
647
- )}
648
- {this.state.AudienceType === "UserType" && (
649
- <DropdownInput
650
- id="typeSelect"
651
- // label='User Type'
652
- alwaysShowLabel
653
- placeholder="User Type"
654
- value={this.getTypeTitle()}
655
- isValid={() => {
656
- return this.validateType();
657
- }}
658
- showError={() => {
659
- return this.state.showWarnings && !this.validateType();
660
- }}
661
- options={this.state.types}
662
- onSelect={this.selectType.bind(this)}
663
- style={{ width: 325, marginTop: 16 }}
664
- isRequired
665
- disabled={this.props.disabled}
666
- />
667
- )}
668
- {this.state.AudienceType === "UserTags" && (
669
- <DropdownInput
670
- id="tagSelect"
671
- alwaysShowLabel
672
- placeholder="User Tag"
673
- value={this.getTagTitle()}
674
- isValid={() => {
675
- return this.validateTag();
676
- }}
677
- showError={() => {
678
- return this.state.showWarnings && !this.validateTag();
679
- }}
680
- options={this.state.tagList}
681
- onSelect={this.selectTag}
682
- style={{ width: 325, marginTop: 16 }}
683
- isRequired
684
- disabled={this.props.disabled}
685
- />
686
- )}
687
- {/* </div> */}
688
- {this.renderIncludes()}
689
- {this.renderExcludes()}
690
- {this.renderCustom()}
691
- </div>
692
- );
693
- }
15
+ constructor(props) {
16
+ super(props);
17
+
18
+ this.state = {
19
+ categories: [
20
+ {
21
+ Title: "All Primary Users",
22
+ Key: "resident",
23
+ ShortName: "Primary",
24
+ },
25
+ {
26
+ Title: "All Staff Users",
27
+ Key: "staff",
28
+ ShortName: "Staff",
29
+ },
30
+ {
31
+ Title: "All Linked Users",
32
+ Key: "family",
33
+ ShortName: "Linked",
34
+ },
35
+ ],
36
+ types: [],
37
+ AudienceType: props.audienceType || "All",
38
+ showInclude: false,
39
+ includeList: [],
40
+ showExclude: false,
41
+ excludeList: [],
42
+ Type: "",
43
+ Category: "",
44
+
45
+ userList: [],
46
+ tagList: [],
47
+ AudienceTagList: props.audienceTypeSelection || [],
48
+ };
49
+ }
50
+
51
+ componentDidMount() {
52
+ const { custom, disallowSingleUsers, allowTags } = this.props;
53
+ this.getUserTypes();
54
+ if (!disallowSingleUsers) this.getUsers();
55
+ if (allowTags || custom) this.getUserTags();
56
+ }
57
+
58
+ componentDidUpdate(prevProps) {
59
+ const newState = {};
60
+ if (prevProps.audienceType !== this.props.audienceType)
61
+ newState.AudienceType = this.props.audienceType;
62
+ if (
63
+ !_.isEqual(
64
+ prevProps.audienceTypeSelection,
65
+ this.props.audienceTypeSelection,
66
+ )
67
+ )
68
+ newState.AudienceTagList = this.props.audienceTypeSelection;
69
+ if (!_.isEmpty(newState)) this.setState(newState);
70
+ }
71
+
72
+ onSubmit() {
73
+ if (
74
+ this.state.AudienceType === "User" &&
75
+ _.isEmpty(this.state.includeList)
76
+ ) {
77
+ this.setState({ showInclude: true });
78
+ }
79
+ }
80
+
81
+ getAudienceType() {
82
+ return this.state.AudienceType;
83
+ }
84
+
85
+ getAudienceTypeSelection() {
86
+ const { AudienceType, AudienceTagList, Category, Tag, Type } = this.state;
87
+ if (AudienceType === "Custom") return AudienceTagList || [];
88
+ if (AudienceType === "Category") return Category;
89
+ if (AudienceType === "UserTags") return Tag;
90
+ return Type;
91
+ }
92
+
93
+ getIncludeList() {
94
+ return this.state.includeList;
95
+ }
96
+
97
+ getExcludeList() {
98
+ return this.state.excludeList;
99
+ }
100
+
101
+ getUsers() {
102
+ userActions
103
+ .fetchUsers(this.props.auth.site)
104
+ .then((res) => {
105
+ this.setState({
106
+ userList: res.data.results.Items,
107
+ });
108
+ })
109
+ .catch((error) => {});
110
+ }
111
+
112
+ getUserTags() {
113
+ profileActions
114
+ .getUserTagsBySite(this.props.auth.site)
115
+ .then((res) => {
116
+ this.setState({
117
+ tagList: res.data.map((t) => {
118
+ return { ...t, Key: t.Id };
119
+ }),
120
+ });
121
+ })
122
+ .catch((error) => {});
123
+ }
124
+
125
+ setType(type, selection) {
126
+ const newState = { AudienceType: type };
127
+ if (type === "Custom") newState.AudienceTagList = selection;
128
+ if (type === "UserType") newState.Type = selection;
129
+ if (type === "Category") newState.Category = selection;
130
+ this.setState(newState);
131
+ }
132
+
133
+ onChangeSelection = () => {
134
+ const { updateValidation, onChange } = this.props;
135
+ const isValid = this.validateAudienceSelection();
136
+ if (updateValidation) updateValidation(isValid);
137
+ if (onChange)
138
+ onChange(
139
+ this.getAudienceType(),
140
+ this.getAudienceTypeSelection(),
141
+ isValid,
142
+ );
143
+ };
144
+
145
+ onChangeOption(type) {
146
+ this.setState({
147
+ AudienceType: type,
148
+ Type: "",
149
+ Category: "",
150
+ includeList: [],
151
+ excludeList: [],
152
+ AudienceTagList: [],
153
+ });
154
+ setTimeout(this.onChangeSelection, 50);
155
+ }
156
+
157
+ getOptions() {
158
+ const options = [
159
+ {
160
+ Label: "All users",
161
+ Value: "All",
162
+ onChange: () => this.onChangeOption("All"),
163
+ },
164
+ ];
165
+ if (this.props.custom) {
166
+ options.push({
167
+ Label: "Custom",
168
+ Value: "Custom",
169
+ onChange: () => this.onChangeOption("Custom"),
170
+ });
171
+ } else {
172
+ options.push({
173
+ Label: "By user category",
174
+ Value: "Category",
175
+ onChange: () => this.onChangeOption("Category"),
176
+ });
177
+ }
178
+ if (!this.props.disallowUserType) {
179
+ options.push({
180
+ Label: "By user type",
181
+ Value: "UserType",
182
+ onChange: () => this.onChangeOption("UserType"),
183
+ });
184
+ }
185
+ if (this.props.allowTags) {
186
+ options.push({
187
+ Label: "By user tag",
188
+ Value: "UserTags",
189
+ onChange: () => this.onChangeOption("UserTags"),
190
+ });
191
+ }
192
+ if (!this.props.disallowSingleUsers) {
193
+ options.push({
194
+ Label: "To single users",
195
+ Value: "User",
196
+ onChange: () => this.onChangeOption("User"),
197
+ });
198
+ }
199
+ return options;
200
+ }
201
+
202
+ validateAudienceSelection() {
203
+ const { AudienceType, includeList } = this.state;
204
+ if (AudienceType === "Custom" && !this.validateCustom()) return false;
205
+ if (AudienceType === "Category" && !this.validateCategory()) return false;
206
+ if (AudienceType === "UserType" && !this.validateType()) return false;
207
+ if (AudienceType === "User" && _.isEmpty(includeList)) return false;
208
+ if (AudienceType === "UserTags" && !this.validateTag()) return false;
209
+ return true;
210
+ }
211
+
212
+ selectType(key) {
213
+ this.setState({
214
+ Type: key,
215
+ includeList: [],
216
+ excludeList: [],
217
+ });
218
+ setTimeout(this.onChangeSelection, 50);
219
+ }
220
+
221
+ getTypeTitle() {
222
+ const typeObject = _.find(this.state.types, (t) => {
223
+ return t.typeName === this.state.Type;
224
+ });
225
+ if (typeObject) {
226
+ return typeObject.displayName;
227
+ }
228
+ return "";
229
+ }
230
+
231
+ selectTag = (key) => {
232
+ this.setState({
233
+ Tag: key,
234
+ includeList: [],
235
+ excludeList: [],
236
+ });
237
+ setTimeout(this.onChangeSelection, 50);
238
+ };
239
+
240
+ getTagTitle() {
241
+ const tag = _.find(this.state.tagList, (t) => {
242
+ return t.Id === this.state.Tag;
243
+ });
244
+ if (tag) {
245
+ return tag.Title;
246
+ }
247
+ return "";
248
+ }
249
+
250
+ getUserTypes() {
251
+ typeActions
252
+ .getUserTypes(this.props.auth.site)
253
+ .then((res) => {
254
+ this.props.setAuth({ userTypes: _.cloneDeep(res.data) });
255
+ res.data.forEach((e) => {
256
+ e.Title = e.displayName;
257
+ e.Key = e.typeName;
258
+
259
+ if (e.category) {
260
+ const category = _.find(this.state.categories, (c) => {
261
+ return c.Key === e.category;
262
+ });
263
+ const shortName = category ? `(${category.ShortName}) ` : "";
264
+ e.Title = `${shortName} ${e.Title}`;
265
+ }
266
+ });
267
+ const newState = {
268
+ types: res.data,
269
+ };
270
+ if (!_.some(res.data, (t) => t.category === "family")) {
271
+ newState.categories = _.filter(
272
+ this.state.categories,
273
+ (c) => c.Key !== "family",
274
+ );
275
+ }
276
+ this.setState(newState);
277
+ })
278
+ .catch((error) => {});
279
+ }
280
+
281
+ getUserList() {
282
+ if (this.state.AudienceType === "UserType" && !_.isEmpty(this.state.Type)) {
283
+ let users = [];
284
+ this.state.userList.forEach((element) => {
285
+ if (element.type !== this.state.Type) users.push(element);
286
+ });
287
+ return _.sortBy(users, "displayName");
288
+ }
289
+ if (
290
+ this.state.AudienceType === "Category" &&
291
+ !_.isEmpty(this.state.Category)
292
+ ) {
293
+ let users = [];
294
+ let types = _.filter(this.state.types, (type) => {
295
+ return type.category === this.state.Category;
296
+ }).map((type) => {
297
+ return type.typeName;
298
+ });
299
+ this.state.userList.forEach((element) => {
300
+ if (types.indexOf(element.type) === -1) {
301
+ users.push(element);
302
+ }
303
+ });
304
+ return _.sortBy(users, "displayName");
305
+ }
306
+ return _.sortBy(this.state.userList, "displayName");
307
+ }
308
+
309
+ selectCat(key, e) {
310
+ this.setState({
311
+ Category: key,
312
+ includeList: [],
313
+ excludeList: [],
314
+ });
315
+ setTimeout(this.onChangeSelection, 50);
316
+ }
317
+
318
+ getCatTitle() {
319
+ const typeObject = _.find(this.state.categories, (t) => {
320
+ return t.Key === this.state.Category;
321
+ });
322
+ if (typeObject) {
323
+ return typeObject.Title;
324
+ }
325
+ return "";
326
+ }
327
+
328
+ validateCustom = () => {
329
+ return !_.isEmpty(this.state.AudienceTagList);
330
+ };
331
+
332
+ validateCategory() {
333
+ return (
334
+ !_.isUndefined(this.state.Category) && !_.isEmpty(this.state.Category)
335
+ );
336
+ }
337
+
338
+ validateType() {
339
+ return !_.isUndefined(this.state.Type) && !_.isEmpty(this.state.Type);
340
+ }
341
+
342
+ validateTag() {
343
+ return !_.isEmpty(this.state.Tag);
344
+ }
345
+
346
+ showUserPickers(isInclude) {
347
+ if (isInclude && this.state.AudienceType === "User") {
348
+ return true;
349
+ }
350
+ if (isInclude && !this.props.allowIncludes) {
351
+ return false;
352
+ }
353
+ if (!isInclude && !this.props.allowExcludes) {
354
+ return false;
355
+ }
356
+ if (isInclude && this.state.AudienceType === "All") {
357
+ return false;
358
+ }
359
+ if (!isInclude && this.state.AudienceType === "User") {
360
+ return false;
361
+ }
362
+ if (
363
+ (this.state.AudienceType === "Category" &&
364
+ _.isEmpty(this.state.Category)) ||
365
+ (this.state.AudienceType === "UserType" && _.isEmpty(this.state.Type))
366
+ ) {
367
+ return false;
368
+ }
369
+ return true;
370
+ }
371
+
372
+ getUserExcludeList() {
373
+ if (this.state.AudienceType === "UserType" && !_.isEmpty(this.state.Type)) {
374
+ let users = [];
375
+ this.state.userList.forEach((element) => {
376
+ if (element.type === this.state.Type) users.push(element);
377
+ });
378
+ return _.sortBy(users, "displayName");
379
+ }
380
+ if (
381
+ this.state.AudienceType === "Category" &&
382
+ !_.isEmpty(this.state.Category)
383
+ ) {
384
+ let users = [];
385
+ let types = _.filter(this.state.types, (type) => {
386
+ return type.category === this.state.Category;
387
+ }).map((type) => {
388
+ return type.typeName;
389
+ });
390
+ this.state.userList.forEach((element) => {
391
+ if (types.indexOf(element.type) > -1) {
392
+ users.push(element);
393
+ }
394
+ });
395
+ return _.sortBy(users, "displayName");
396
+ }
397
+ return _.sortBy(this.state.userList, "displayName");
398
+ }
399
+
400
+ addToInc(user) {
401
+ let list = [...this.state.includeList];
402
+ list.push({
403
+ Id: user.Id,
404
+ displayName: user.displayName,
405
+ profilePic: user.profilePic,
406
+ });
407
+ this.setState({ includeList: list });
408
+ setTimeout(this.onChangeSelection, 50);
409
+ }
410
+
411
+ removeUserInc(user) {
412
+ this.setState({
413
+ includeList: _.filter(this.state.includeList, (ev) => {
414
+ return ev.Id !== user.Id;
415
+ }),
416
+ });
417
+ setTimeout(this.onChangeSelection, 50);
418
+ }
419
+
420
+ addToEx(user) {
421
+ let list = [...this.state.excludeList];
422
+ list.push({
423
+ Id: user.Id,
424
+ displayName: user.displayName,
425
+ profilePic: user.profilePic,
426
+ });
427
+ this.setState({ excludeList: list });
428
+ }
429
+
430
+ removeUserEx(user) {
431
+ this.setState({
432
+ excludeList: _.filter(this.state.excludeList, (ev) => {
433
+ return ev.Id !== user.Id;
434
+ }),
435
+ });
436
+ }
437
+
438
+ getAvailableAudienceTags = (includeSelected = false) => {
439
+ const { categories, types, tagList, AudienceTagList } = this.state;
440
+ const categoryTags = categories.map((c) => {
441
+ return {
442
+ AudienceType: "Category",
443
+ AudienceTypeSelection: c.Key,
444
+ Id: `category_${c.Key}`,
445
+ Title: c.Title,
446
+ };
447
+ });
448
+ const userTypeTags = types.map((t) => {
449
+ return {
450
+ AudienceType: "UserType",
451
+ AudienceTypeSelection: t.typeName,
452
+ Id: `userType_${t.typeName}`,
453
+ Title: `User Type: ${t.displayName}`,
454
+ };
455
+ });
456
+ const userTagTags = tagList.map((t) => {
457
+ return {
458
+ AudienceType: "UserTags",
459
+ AudienceTypeSelection: t.Id,
460
+ Id: `userTag_${t.Id}`,
461
+ Title: `User Tag: ${t.Title}`,
462
+ };
463
+ });
464
+ const result = [...categoryTags, ...userTypeTags, ...userTagTags];
465
+ return includeSelected
466
+ ? result
467
+ : result.filter((t) => !_.some(AudienceTagList, (at) => at.Id === t.Id));
468
+ };
469
+
470
+ onAttachAudienceTag = (tag) => {
471
+ const AudienceTagList = [...this.state.AudienceTagList, tag];
472
+ this.setState({ AudienceTagList }, this.onChangeSelection);
473
+ };
474
+
475
+ onDetachAudienceTag = (tag) => {
476
+ const AudienceTagList = _.filter(
477
+ this.state.AudienceTagList,
478
+ (at) => at.Id !== tag.Id,
479
+ );
480
+ this.setState({ AudienceTagList }, this.onChangeSelection);
481
+ };
482
+
483
+ renderExcludes() {
484
+ if (!this.showUserPickers(false)) {
485
+ return null;
486
+ }
487
+ return (
488
+ <div key="excludes" className="marginTop-16">
489
+ <div
490
+ className="flex flex-between pointer"
491
+ onClick={() => {
492
+ this.setState({ showExclude: !this.state.showExclude });
493
+ }}
494
+ >
495
+ <div className="flex">
496
+ <Text type="formTitleSmall" className="marginBottom-16">
497
+ Exclude Users From Selection{" "}
498
+ {`(${this.state.excludeList.length})`}
499
+ </Text>
500
+ </div>
501
+ <FontAwesome
502
+ style={{ color: COLOUR_DUSK, fontSize: 12 }}
503
+ name={this.state.showExclude ? "chevron-up" : "chevron-down"}
504
+ />
505
+ </div>
506
+ {this.state.showExclude && (
507
+ <AudienceIncluder
508
+ add={this.addToEx.bind(this)}
509
+ remove={this.removeUserEx.bind(this)}
510
+ selectionList={this.state.excludeList}
511
+ userList={this.getUserExcludeList()}
512
+ />
513
+ )}
514
+ </div>
515
+ );
516
+ }
517
+
518
+ renderIncludes() {
519
+ if (!this.showUserPickers(true)) {
520
+ return null;
521
+ }
522
+ return (
523
+ <div key="includes" className="marginTop-16">
524
+ <div
525
+ className="flex flex-between pointer"
526
+ onClick={() => {
527
+ this.setState({ showInclude: !this.state.showInclude });
528
+ }}
529
+ >
530
+ <div className="flex">
531
+ <Text type="formTitleSmall" className="marginBottom-16">
532
+ {this.state.AudienceType !== "User" ? "Add Other" : "Select"}{" "}
533
+ Users {`(${this.state.includeList.length})`}
534
+ </Text>
535
+ </div>
536
+ <FontAwesome
537
+ style={{ color: COLOUR_DUSK, fontSize: 12 }}
538
+ name={this.state.showInclude ? "chevron-up" : "chevron-down"}
539
+ />
540
+ </div>
541
+ {this.state.showInclude && (
542
+ <AudienceIncluder
543
+ add={this.addToInc.bind(this)}
544
+ remove={this.removeUserInc.bind(this)}
545
+ selectionList={this.state.includeList}
546
+ userList={this.getUserList()}
547
+ />
548
+ )}
549
+ </div>
550
+ );
551
+ }
552
+
553
+ renderAssignedAudiences() {
554
+ const { AudienceTagList } = this.state;
555
+ const { maxWidth } = this.props;
556
+
557
+ return (
558
+ <div className="selectedAudienceTags" style={{ maxWidth }}>
559
+ {_.isEmpty(AudienceTagList) && (
560
+ <Text type="body" className="text-italic">
561
+ No audiences selected
562
+ </Text>
563
+ )}
564
+ {AudienceTagList.map((t) => {
565
+ return (
566
+ <Tag
567
+ key={t.Id}
568
+ className="marginLeft-16 marginBottom-8"
569
+ text={t.Title}
570
+ rightIcon="remove"
571
+ rightClick={() => this.onDetachAudienceTag(t)}
572
+ />
573
+ );
574
+ })}
575
+ </div>
576
+ );
577
+ }
578
+
579
+ renderAvailableAudiences() {
580
+ const { maxWidth } = this.props;
581
+ const tags = this.getAvailableAudienceTags();
582
+
583
+ return (
584
+ <div className="availableAudienceTags" style={{ maxWidth }}>
585
+ {tags.map((t) => {
586
+ return (
587
+ <Tag
588
+ key={t.Id}
589
+ className="marginLeft-16 marginBottom-8"
590
+ text={t.Title}
591
+ onClick={() => this.onAttachAudienceTag(t)}
592
+ />
593
+ );
594
+ })}
595
+ </div>
596
+ );
597
+ }
598
+
599
+ renderCustom() {
600
+ if (this.state.AudienceType !== "Custom") return null;
601
+
602
+ return (
603
+ <div className="optionsContent_bottom">
604
+ <Text type="formLabel">Selected Audience</Text>
605
+ {this.renderAssignedAudiences()}
606
+ <Text type="formLabel" className="marginTop-32">
607
+ Select Audience
608
+ </Text>
609
+ {this.renderAvailableAudiences()}
610
+ </div>
611
+ );
612
+ }
613
+
614
+ render() {
615
+ return (
616
+ <div>
617
+ {/* <div className={this.props.className || 'padding-32 paddingVertical-40 bottomDivideBorder'}> */}
618
+ <Text type="formTitleSmall" className="marginBottom-16">
619
+ {this.props.title || "Target Audience"}
620
+ </Text>
621
+ <RadioButton
622
+ isActive={this.state.AudienceType}
623
+ rowStyle={{ flexDirection: "column" }}
624
+ buttonStyle={{ marginBottom: 8 }}
625
+ options={this.getOptions()}
626
+ disabled={this.props.disabled}
627
+ />
628
+ {this.state.AudienceType === "Category" && (
629
+ <DropdownInput
630
+ id="typeSelect"
631
+ // label='User Category'
632
+ alwaysShowLabel
633
+ placeholder="User Category"
634
+ value={this.getCatTitle()}
635
+ isValid={() => {
636
+ return this.validateCategory();
637
+ }}
638
+ showError={() => {
639
+ return this.state.showWarnings && !this.validateCategory();
640
+ }}
641
+ options={this.state.categories}
642
+ onSelect={this.selectCat.bind(this)}
643
+ style={{ width: 325, marginTop: 16 }}
644
+ isRequired
645
+ disabled={this.props.disabled}
646
+ />
647
+ )}
648
+ {this.state.AudienceType === "UserType" && (
649
+ <DropdownInput
650
+ id="typeSelect"
651
+ // label='User Type'
652
+ alwaysShowLabel
653
+ placeholder="User Type"
654
+ value={this.getTypeTitle()}
655
+ isValid={() => {
656
+ return this.validateType();
657
+ }}
658
+ showError={() => {
659
+ return this.state.showWarnings && !this.validateType();
660
+ }}
661
+ options={this.state.types}
662
+ onSelect={this.selectType.bind(this)}
663
+ style={{ width: 325, marginTop: 16 }}
664
+ isRequired
665
+ disabled={this.props.disabled}
666
+ />
667
+ )}
668
+ {this.state.AudienceType === "UserTags" && (
669
+ <DropdownInput
670
+ id="tagSelect"
671
+ alwaysShowLabel
672
+ placeholder="User Tag"
673
+ value={this.getTagTitle()}
674
+ isValid={() => {
675
+ return this.validateTag();
676
+ }}
677
+ showError={() => {
678
+ return this.state.showWarnings && !this.validateTag();
679
+ }}
680
+ options={this.state.tagList}
681
+ onSelect={this.selectTag}
682
+ style={{ width: 325, marginTop: 16 }}
683
+ isRequired
684
+ disabled={this.props.disabled}
685
+ />
686
+ )}
687
+ {/* </div> */}
688
+ {this.renderIncludes()}
689
+ {this.renderExcludes()}
690
+ {this.renderCustom()}
691
+ </div>
692
+ );
693
+ }
694
694
  }
695
695
 
696
696
  const toExport = connect(null, { setAuth }, null, { forwardRef: true })(
697
- AudienceSelector,
697
+ AudienceSelector,
698
698
  );
699
699
 
700
700
  export { toExport as AudienceSelector };