@plusscommunities/pluss-circles-web 1.5.6 → 1.5.7-beta.2

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,387 +1,569 @@
1
- import React, { Component } from 'react';
2
- import { connect } from 'react-redux';
3
- import _ from 'lodash';
4
- import moment from 'moment';
5
- import { Link } from 'react-router-dom';
6
- import { PlussCore } from '../feature.config';
7
- import { circlesLoaded, circleRemoved } from '../actions';
8
- import { circleActions } from '../apis';
9
- import { Table } from 'react-bootstrap';
10
- import FontAwesome from 'react-fontawesome';
11
- import { values } from '../values.config';
1
+ import React, { Component } from "react";
2
+ import { connect } from "react-redux";
3
+ import _ from "lodash";
4
+ import moment from "moment";
5
+ import { Link } from "react-router-dom";
6
+ import { PlussCore } from "../feature.config";
7
+ import { circlesLoaded, circleRemoved } from "../actions";
8
+ import { circleActions } from "../apis";
9
+ import { Table } from "react-bootstrap";
10
+ import FontAwesome from "react-fontawesome";
11
+ import { values } from "../values.config";
12
+ import badgeStyles from "./Circles.module.css";
12
13
 
13
14
  const { Components, Session, Helper } = PlussCore;
14
15
 
15
16
  class Circles extends Component {
16
- constructor(props) {
17
- super(props);
18
- this.state = {
19
- userSearch: '',
20
- };
21
- }
22
-
23
- componentDidMount() {
24
- this.getData();
25
- }
26
-
27
- getData = () => {
28
- const { auth } = this.props;
29
- this.setState({ loadingAll: true }, async () => {
30
- try {
31
- const res = await circleActions.getAll(auth.site);
32
- console.log('getData', res.data);
33
- this.props.circlesLoaded(res.data);
34
- this.setState({ loadingAll: false });
35
- } catch (error) {
36
- console.error('getData', error);
37
- this.setState({ loadingAll: false });
38
- }
39
- });
40
- };
41
-
42
- getCircles() {
43
- let result = this.props.circles;
44
- if (this.state.selectedTypeFilter) {
45
- result = _.filter(result, (circle) => {
46
- return this.state.selectedTypeFilter === 'circle' ? !circle.IsPrivate : circle.IsPrivate;
47
- });
48
- }
49
- if (this.state.selectedUserFilter) {
50
- result = _.filter(result, (circle) => {
51
- return _.some(circle.Audience, (user) => {
52
- return user.userId === this.state.selectedUserFilter.userId;
53
- });
54
- });
55
- }
56
- return result;
57
- }
58
-
59
- getUsers() {
60
- let users = [];
61
- this.props.circles.forEach((circle) => {
62
- circle.Audience.forEach((user) => {
63
- users.push(user);
64
- });
65
- });
66
- users = _.sortBy(
67
- _.uniqBy(users, (user) => {
68
- return user.userId;
69
- }),
70
- 'displayName',
71
- );
72
- return _.filter(users, (u) => {
73
- if (!_.isEmpty(this.state.userSearch)) {
74
- return (u.displayName || '').toLowerCase().indexOf(this.state.userSearch.toLowerCase()) > -1;
75
- }
76
- return true;
77
- });
78
- }
79
-
80
- validateCircleAdmin(circle, onlyCreator) {
81
- if (Session.validateAccess(this.props.auth.site, values.permission, this.props.auth)) {
82
- return true;
83
- }
84
- if (onlyCreator) {
85
- return (circle.Creator && circle.Creator.userId === this.props.user.Id) || circle.CreatorId === this.props.user.Id;
86
- }
87
- return _.some(circle.Audience, (user) => {
88
- return user.userId === this.props.user.Id && user.isAdmin;
89
- });
90
- }
91
-
92
- canAddNew = () => {
93
- const { auth } = this.props;
94
- return Session.validateAccess(auth.site, values.permission, auth);
95
- };
96
-
97
- onAddNew = () => {
98
- const { auth } = this.props;
99
- if (Session.validateAccess(auth.site, values.permission, auth)) {
100
- this.props.history.push(`/${values.featureKey}/add`);
101
- }
102
- };
103
-
104
- openUserFilter = () => {
105
- this.setState({
106
- userFilterOpen: true,
107
- });
108
- };
109
-
110
- closeUserFilter = () => {
111
- this.setState({
112
- userFilterOpen: false,
113
- });
114
- };
115
-
116
- selectUserFilter = (user) => {
117
- this.setState({
118
- selectedUserFilter: user,
119
- userFilterOpen: false,
120
- });
121
- };
122
-
123
- removeUserFilter = (event) => {
124
- event.stopPropagation();
125
- this.setState({
126
- selectedUserFilter: null,
127
- });
128
- };
129
-
130
- openTypeFilter = () => {
131
- this.setState({
132
- typeFilterOpen: true,
133
- });
134
- };
135
-
136
- closeTypeFilter = () => {
137
- this.setState({
138
- typeFilterOpen: false,
139
- });
140
- };
141
-
142
- selectTypeFilter = (type) => {
143
- this.setState({
144
- selectedTypeFilter: type,
145
- typeFilterOpen: false,
146
- });
147
- };
148
-
149
- removeTypeFilter = (event) => {
150
- event.stopPropagation();
151
- this.setState({
152
- selectedTypeFilter: null,
153
- });
154
- };
155
-
156
- onHandleChange = (event) => {
157
- var stateChange = {};
158
- stateChange[event.target.getAttribute('id')] = event.target.value;
159
- this.setState(stateChange);
160
- };
161
-
162
- removeCircle = (circle) => {
163
- if (
164
- window.confirm(
165
- `Are you sure you want to delete that ${values.entityName}? You will no longer be able to access the messages in the ${values.entityName}.`,
166
- )
167
- ) {
168
- this.props.circleRemoved(circle.Id);
169
- circleActions
170
- .delete(circle.Id)
171
- .then((res) => {
172
- this.getData();
173
- })
174
- .catch((res) => {
175
- alert('Something went wrong with the request. Please try again.');
176
- });
177
- }
178
- };
179
-
180
- getTypeFilterText = (type) => {
181
- if (type === 'private') {
182
- return 'Private Message';
183
- }
184
- return _.capitalize(values.entityName);
185
- };
186
-
187
- getTitle = (circle) => {
188
- if (circle.IsPrivate) {
189
- return `PM: ${circle.Audience.map((user) => {
190
- return user.displayName;
191
- }).join(', ')}`;
192
- }
193
- return circle.Title;
194
- };
195
-
196
- renderRow(circle) {
197
- return (
198
- <tr key={circle.Id}>
199
- <td className="table-TitleColumn">
200
- <Link to={`/${values.featureKey}/${values.entityKey}/${circle.Id}`}>{this.getTitle(circle)}</Link>
201
- </td>
202
- <td>{moment(circle.Changed).local().format('D MMM YYYY')}</td>
203
- <td>
204
- {circle.Audience.map((user) => {
205
- return (
206
- <Components.ProfilePic
207
- size={30}
208
- image={user.profilePic}
209
- hoverText={user.displayName}
210
- containerClass="circleTableProfilePic"
211
- />
212
- );
213
- })}
214
- </td>
215
- <td className="table-options">
216
- <div style={{ display: 'flex', alignItems: 'center' }}>
217
- {this.validateCircleAdmin(circle) && !circle.IsPrivate && (
218
- <Link to={`/${values.featureKey}/edit/${circle.Id}`}>
219
- <FontAwesome
220
- style={{
221
- fontSize: 20,
222
- padding: 5,
223
- marginLeft: 12,
224
- cursor: 'pointer',
225
- }}
226
- name="pencil"
227
- />
228
- </Link>
229
- )}
230
- {this.validateCircleAdmin(circle, true) && !circle.IsPrivate && (
231
- <a
232
- onClick={() => {
233
- this.removeCircle(circle);
234
- }}
235
- >
236
- <FontAwesome
237
- style={{
238
- fontSize: 20,
239
- padding: 5,
240
- marginLeft: 12,
241
- cursor: 'pointer',
242
- }}
243
- name="minus-circle"
244
- />
245
- </a>
246
- )}
247
- </div>
248
- </td>
249
- </tr>
250
- );
251
- }
252
-
253
- renderFilters() {
254
- let userFilter = <Components.Tag className="marginRight-10" onClick={this.openUserFilter} text="User" />;
255
- let typeFilter = <Components.Tag className="marginRight-10" onClick={this.openTypeFilter} text="Type" />;
256
-
257
- if (this.state.selectedUserFilter) {
258
- userFilter = (
259
- <Components.Tag className="marginRight-10" onClick={this.openUserFilter} rightIcon="close" rightClick={this.removeUserFilter}>
260
- <Components.UserListing size={15} user={this.state.selectedUserFilter} textClass="tag_text" />
261
- </Components.Tag>
262
- );
263
- }
264
- if (this.state.selectedTypeFilter) {
265
- typeFilter = (
266
- <Components.Tag
267
- className="marginRight-10"
268
- onClick={this.openTypeFilter}
269
- rightIcon="close"
270
- rightClick={this.removeTypeFilter}
271
- text={this.getTypeFilterText(this.state.selectedTypeFilter)}
272
- />
273
- );
274
- }
275
- return (
276
- <div className="flex flex-center marginTop-20">
277
- <Components.Text type="h5" className="marginRight-20">
278
- Filter by
279
- </Components.Text>
280
- {userFilter}
281
- {typeFilter}
282
- </div>
283
- );
284
- }
285
-
286
- renderUserFilterPopup() {
287
- if (!this.state.userFilterOpen) {
288
- return null;
289
- }
290
- return (
291
- <Components.Popup title="Select User" maxWidth={600} minWidth={400} minHeight={400} hasPadding onClose={this.closeUserFilter}>
292
- <Components.GenericInput
293
- id="userSearch"
294
- type="text"
295
- label="Search"
296
- placeholder="Enter name"
297
- value={this.state.userSearch}
298
- onChange={(e) => this.onHandleChange(e)}
299
- alwaysShowLabel
300
- />
301
- {this.getUsers().map((user) => {
302
- return (
303
- <Components.UserListing
304
- key={user.userId}
305
- user={user}
306
- onClick={() => {
307
- this.selectUserFilter(user);
308
- }}
309
- />
310
- );
311
- })}
312
- </Components.Popup>
313
- );
314
- }
315
-
316
- renderTypeFilterPopup() {
317
- if (!this.state.typeFilterOpen) {
318
- return null;
319
- }
320
- return (
321
- <Components.Popup title="Select Type" maxWidth={600} minWidth={400} hasPadding onClose={this.closeTypeFilter}>
322
- <Components.Tag
323
- onClick={() => {
324
- this.selectTypeFilter('circle');
325
- }}
326
- text={_.capitalize(values.entityName)}
327
- className="marginRight-10"
328
- />
329
- <Components.Tag
330
- onClick={() => {
331
- this.selectTypeFilter('private');
332
- }}
333
- text="Private Message"
334
- />
335
- </Components.Popup>
336
- );
337
- }
338
-
339
- render() {
340
- return (
341
- <div>
342
- {this.renderTypeFilterPopup()}
343
- {this.renderUserFilterPopup()}
344
- <Components.Header>
345
- {this.canAddNew() && <Components.AddButton onClick={this.onAddNew} text={`NEW ${_.upperCase(values.entityName)}`} />}
346
- </Components.Header>
347
- <div className="pageContainer paddingVertical-20 paddingHorizontal-40">
348
- <Components.Text type="h1" className="">
349
- {values.textFeatureTitle}
350
- </Components.Text>
351
- {this.renderFilters()}
352
-
353
- <Table className="plussTable" striped bordered condensed hover style={{ minWidth: '100%' }}>
354
- <thead>
355
- <tr>
356
- <th>Title</th>
357
- <th>Last updated</th>
358
- <th>Members</th>
359
- <th style={{ width: 50 }} />
360
- </tr>
361
- </thead>
362
- <tbody>
363
- {this.getCircles().map((circle) => {
364
- return this.renderRow(circle);
365
- })}
366
- </tbody>
367
- </Table>
368
- </div>
369
- </div>
370
- );
371
- }
17
+ constructor(props) {
18
+ super(props);
19
+ this.state = {
20
+ userSearch: "",
21
+ sortBy: "newest",
22
+ showUnreadOnly: false,
23
+ pollingInterval: null,
24
+ };
25
+ }
26
+
27
+ componentDidMount() {
28
+ this.getData();
29
+ // Poll for updates every 10 seconds
30
+ this.pollingInterval = setInterval(() => {
31
+ this.getData();
32
+ }, 10000);
33
+ }
34
+
35
+ getData = () => {
36
+ const { auth } = this.props;
37
+ this.setState({ loadingAll: true }, async () => {
38
+ try {
39
+ const res = await circleActions.getAll(auth.site);
40
+ console.log("getData", res.data);
41
+ this.props.circlesLoaded(res.data);
42
+ this.setState({ loadingAll: false });
43
+ } catch (error) {
44
+ console.error("getData", error);
45
+ this.setState({ loadingAll: false });
46
+ }
47
+ });
48
+ };
49
+
50
+ componentWillUnmount() {
51
+ if (this.pollingInterval) {
52
+ clearInterval(this.pollingInterval);
53
+ }
54
+ }
55
+
56
+ isCircleUnread(circle) {
57
+ const userId = this.props.user.Id;
58
+ if (!circle.Unread || !circle.Unread[userId]) {
59
+ return false;
60
+ }
61
+ return circle.Unread[userId] > 0;
62
+ }
63
+
64
+ getUnreadCount(circle) {
65
+ const userId = this.props.user.Id;
66
+ if (!circle.Unread || !circle.Unread[userId]) {
67
+ return 0;
68
+ }
69
+ return circle.Unread[userId];
70
+ }
71
+
72
+ selectUnreadFilter = (showUnreadOnly) => {
73
+ this.setState({ showUnreadOnly });
74
+ };
75
+ getCircles() {
76
+ let result = this.props.circles;
77
+ if (this.state.selectedTypeFilter) {
78
+ result = _.filter(result, (circle) => {
79
+ return this.state.selectedTypeFilter === "circle"
80
+ ? !circle.IsPrivate
81
+ : circle.IsPrivate;
82
+ });
83
+ }
84
+ if (this.state.selectedUserFilter) {
85
+ result = _.filter(result, (circle) => {
86
+ return _.some(circle.Audience, (user) => {
87
+ return user.userId === this.state.selectedUserFilter.userId;
88
+ });
89
+ });
90
+ }
91
+ // Apply sorting
92
+ result = _.sortBy(result, (circle) => {
93
+ return moment(circle.Changed).valueOf();
94
+ });
95
+ if (this.state.sortBy === "newest") {
96
+ result.reverse();
97
+ if (this.state.showUnreadOnly) {
98
+ result = _.filter(result, (circle) => {
99
+ return this.isCircleUnread(circle);
100
+ });
101
+ }
102
+ }
103
+ return result;
104
+ }
105
+
106
+ getUsers() {
107
+ let users = [];
108
+ this.props.circles.forEach((circle) => {
109
+ circle.Audience.forEach((user) => {
110
+ users.push(user);
111
+ });
112
+ });
113
+ users = _.sortBy(
114
+ _.uniqBy(users, (user) => {
115
+ return user.userId;
116
+ }),
117
+ "displayName",
118
+ );
119
+ return _.filter(users, (u) => {
120
+ if (!_.isEmpty(this.state.userSearch)) {
121
+ return (
122
+ (u.displayName || "")
123
+ .toLowerCase()
124
+ .indexOf(this.state.userSearch.toLowerCase()) > -1
125
+ );
126
+ }
127
+ return true;
128
+ });
129
+ }
130
+
131
+ validateCircleAdmin(circle, onlyCreator) {
132
+ if (
133
+ Session.validateAccess(
134
+ this.props.auth.site,
135
+ values.permission,
136
+ this.props.auth,
137
+ )
138
+ ) {
139
+ return true;
140
+ }
141
+ if (onlyCreator) {
142
+ return (
143
+ (circle.Creator && circle.Creator.userId === this.props.user.Id) ||
144
+ circle.CreatorId === this.props.user.Id
145
+ );
146
+ }
147
+ return _.some(circle.Audience, (user) => {
148
+ return user.userId === this.props.user.Id && user.isAdmin;
149
+ });
150
+ }
151
+
152
+ canAddNew = () => {
153
+ const { auth } = this.props;
154
+ return Session.validateAccess(auth.site, values.permission, auth);
155
+ };
156
+
157
+ onAddNew = () => {
158
+ const { auth } = this.props;
159
+ if (Session.validateAccess(auth.site, values.permission, auth)) {
160
+ this.props.history.push(`/${values.featureKey}/add`);
161
+ }
162
+ };
163
+
164
+ openUserFilter = () => {
165
+ this.setState({
166
+ userFilterOpen: true,
167
+ });
168
+ };
169
+
170
+ closeUserFilter = () => {
171
+ this.setState({
172
+ userFilterOpen: false,
173
+ });
174
+ };
175
+
176
+ selectUserFilter = (user) => {
177
+ this.setState({
178
+ selectedUserFilter: user,
179
+ userFilterOpen: false,
180
+ });
181
+ };
182
+
183
+ removeUserFilter = (event) => {
184
+ event.stopPropagation();
185
+ this.setState({
186
+ selectedUserFilter: null,
187
+ });
188
+ };
189
+
190
+ openTypeFilter = () => {
191
+ this.setState({
192
+ typeFilterOpen: true,
193
+ });
194
+ };
195
+
196
+ closeTypeFilter = () => {
197
+ this.setState({
198
+ typeFilterOpen: false,
199
+ });
200
+ };
201
+
202
+ selectTypeFilter = (type) => {
203
+ this.setState({
204
+ selectedTypeFilter: type,
205
+ typeFilterOpen: false,
206
+ });
207
+ };
208
+
209
+ removeTypeFilter = (event) => {
210
+ event.stopPropagation();
211
+ this.setState({
212
+ selectedTypeFilter: null,
213
+ });
214
+ };
215
+
216
+ selectSort = (sortType) => {
217
+ this.setState({
218
+ sortBy: sortType,
219
+ });
220
+ };
221
+
222
+ onHandleChange = (event) => {
223
+ var stateChange = {};
224
+ stateChange[event.target.getAttribute("id")] = event.target.value;
225
+ this.setState(stateChange);
226
+ };
227
+
228
+ removeCircle = (circle) => {
229
+ if (
230
+ window.confirm(
231
+ `Are you sure you want to delete that ${values.entityName}? You will no longer be able to access the messages in the ${values.entityName}.`,
232
+ )
233
+ ) {
234
+ this.props.circleRemoved(circle.Id);
235
+ circleActions
236
+ .delete(circle.Id)
237
+ .then((res) => {
238
+ this.getData();
239
+ })
240
+ .catch((res) => {
241
+ alert("Something went wrong with the request. Please try again.");
242
+ });
243
+ }
244
+ };
245
+
246
+ getTypeFilterText = (type) => {
247
+ if (type === "private") {
248
+ return "Private Message";
249
+ }
250
+ return _.capitalize(values.entityName);
251
+ };
252
+
253
+ getTitle = (circle) => {
254
+ if (circle.IsPrivate) {
255
+ return `PM: ${circle.Audience.map((user) => {
256
+ return user.displayName;
257
+ }).join(", ")}`;
258
+ }
259
+ return circle.Title;
260
+ };
261
+
262
+ renderRow(circle) {
263
+ const unreadCount = this.getUnreadCount(circle);
264
+ const badge = unreadCount > 0 ? (
265
+ <Components.Tag
266
+ className={badgeStyles.badgeTag}
267
+ text={`${unreadCount} new`}
268
+ />
269
+ ) : null;
270
+
271
+ return (
272
+ <tr key={circle.Id}>
273
+ <td className="table-TitleColumn">
274
+ <div className="flex flex-center" style={{ gap: "8px" }}>
275
+ <Link to={`/${values.featureKey}/${values.entityKey}/${circle.Id}`}>
276
+ {this.getTitle(circle)}
277
+ </Link>
278
+ {badge}
279
+ </div>
280
+ </td>
281
+ <td>{moment(circle.Changed).local().format("D MMM YYYY")}</td>
282
+ <td>
283
+ {circle.Audience.map((user) => {
284
+ return (
285
+ <Components.ProfilePic
286
+ size={30}
287
+ image={user.profilePic}
288
+ hoverText={user.displayName}
289
+ containerClass="circleTableProfilePic"
290
+ />
291
+ );
292
+ })}
293
+ </td>
294
+ <td className="table-options">
295
+ <div style={{ display: "flex", alignItems: "center" }}>
296
+ {this.validateCircleAdmin(circle) && !circle.IsPrivate && (
297
+ <Link to={`/${values.featureKey}/edit/${circle.Id}`}>
298
+ <FontAwesome
299
+ style={{
300
+ fontSize: 20,
301
+ padding: 5,
302
+ marginLeft: 12,
303
+ cursor: "pointer",
304
+ }}
305
+ name="pencil"
306
+ />
307
+ </Link>
308
+ )}
309
+ {this.validateCircleAdmin(circle, true) && !circle.IsPrivate && (
310
+ <a
311
+ onClick={() => {
312
+ this.removeCircle(circle);
313
+ }}
314
+ >
315
+ <FontAwesome
316
+ style={{
317
+ fontSize: 20,
318
+ padding: 5,
319
+ marginLeft: 12,
320
+ cursor: "pointer",
321
+ }}
322
+ name="minus-circle"
323
+ />
324
+ </a>
325
+ )}
326
+ </div>
327
+ </td>
328
+ </tr>
329
+ );
330
+ }
331
+
332
+ renderUnreadFilterInfo() {
333
+ const { showUnreadOnly } = this.state;
334
+
335
+ if (!showUnreadOnly) {
336
+ return null;
337
+ }
338
+
339
+ return (
340
+ <div className={badgeStyles.unreadFilterBanner}>
341
+ <div className={badgeStyles.unreadFilterContent}>
342
+ <FontAwesome
343
+ name="info-circle"
344
+ className={badgeStyles.unreadFilterIcon}
345
+ />
346
+ <span className={badgeStyles.unreadFilterText}>
347
+ Showing only items with new messages.{" "}
348
+ <button
349
+ className={badgeStyles.unreadFilterButton}
350
+ onClick={() => this.selectUnreadFilter(false)}
351
+ aria-label="Turn off unread filter"
352
+ >
353
+ Turn off filter
354
+ </button>
355
+ </span>
356
+ </div>
357
+ </div>
358
+ );
359
+ }
360
+
361
+ renderFilters() {
362
+ let userFilter = (
363
+ <Components.Tag
364
+ className="marginRight-10"
365
+ onClick={this.openUserFilter}
366
+ text="User"
367
+ />
368
+ );
369
+
370
+ let unreadFilter = (
371
+ <Components.Tag
372
+ className="marginRight-10"
373
+ onClick={() => this.selectUnreadFilter(!this.state.showUnreadOnly)}
374
+ leftIcon={this.state.showUnreadOnly ? "check" : null}
375
+ text="Unread Only"
376
+ />
377
+ );
378
+
379
+ let typeFilter = (
380
+ <Components.Tag
381
+ className="marginRight-10"
382
+ onClick={this.openTypeFilter}
383
+ text="Type"
384
+ />
385
+ );
386
+
387
+ if (this.state.selectedUserFilter) {
388
+ userFilter = (
389
+ <Components.Tag
390
+ className="marginRight-10"
391
+ onClick={this.openUserFilter}
392
+ rightIcon="close"
393
+ rightClick={this.removeUserFilter}
394
+ >
395
+ <Components.UserListing
396
+ size={15}
397
+ user={this.state.selectedUserFilter}
398
+ textClass="tag_text"
399
+ />
400
+ </Components.Tag>
401
+ );
402
+ }
403
+ if (this.state.selectedTypeFilter) {
404
+ typeFilter = (
405
+ <Components.Tag
406
+ className="marginRight-10"
407
+ onClick={this.openTypeFilter}
408
+ rightIcon="close"
409
+ rightClick={this.removeTypeFilter}
410
+ text={this.getTypeFilterText(this.state.selectedTypeFilter)}
411
+ />
412
+ );
413
+ }
414
+ return (
415
+ <div className="flex flex-center marginTop-20">
416
+ <Components.Text type="h5" className="marginRight-20">
417
+ Filter by
418
+ </Components.Text>
419
+ {userFilter}
420
+ {typeFilter}
421
+ {unreadFilter}
422
+ <Components.Text type="h5" className="marginRight-20 marginLeft-20">
423
+ Sort by:
424
+ </Components.Text>
425
+ <Components.Tag
426
+ className="marginRight-10"
427
+ onClick={() => this.selectSort("newest")}
428
+ leftIcon={this.state.sortBy === "newest" ? "check" : null}
429
+ text="Newest first"
430
+ />
431
+ <Components.Tag
432
+ onClick={() => this.selectSort("oldest")}
433
+ leftIcon={this.state.sortBy === "oldest" ? "check" : null}
434
+ text="Oldest first"
435
+ />
436
+ </div>
437
+ );
438
+ }
439
+
440
+ renderUserFilterPopup() {
441
+ if (!this.state.userFilterOpen) {
442
+ return null;
443
+ }
444
+ return (
445
+ <Components.Popup
446
+ title="Select User"
447
+ maxWidth={600}
448
+ minWidth={400}
449
+ minHeight={400}
450
+ hasPadding
451
+ onClose={this.closeUserFilter}
452
+ >
453
+ <Components.GenericInput
454
+ id="userSearch"
455
+ type="text"
456
+ label="Search"
457
+ placeholder="Enter name"
458
+ value={this.state.userSearch}
459
+ onChange={(e) => this.onHandleChange(e)}
460
+ alwaysShowLabel
461
+ />
462
+ {this.getUsers().map((user) => {
463
+ return (
464
+ <Components.UserListing
465
+ key={user.userId}
466
+ user={user}
467
+ onClick={() => {
468
+ this.selectUserFilter(user);
469
+ }}
470
+ />
471
+ );
472
+ })}
473
+ </Components.Popup>
474
+ );
475
+ }
476
+
477
+ renderTypeFilterPopup() {
478
+ if (!this.state.typeFilterOpen) {
479
+ return null;
480
+ }
481
+ return (
482
+ <Components.Popup
483
+ title="Select Type"
484
+ maxWidth={600}
485
+ minWidth={400}
486
+ hasPadding
487
+ onClose={this.closeTypeFilter}
488
+ >
489
+ <Components.Tag
490
+ onClick={() => {
491
+ this.selectTypeFilter("circle");
492
+ }}
493
+ text={_.capitalize(values.entityName)}
494
+ className="marginRight-10"
495
+ />
496
+ <Components.Tag
497
+ onClick={() => {
498
+ this.selectTypeFilter("private");
499
+ }}
500
+ text="Private Message"
501
+ />
502
+ </Components.Popup>
503
+ );
504
+ }
505
+
506
+ render() {
507
+ return (
508
+ <div>
509
+ {this.renderTypeFilterPopup()}
510
+ {this.renderUserFilterPopup()}
511
+ <Components.Header>
512
+ {this.canAddNew() && (
513
+ <Components.AddButton
514
+ onClick={this.onAddNew}
515
+ text={`NEW ${_.upperCase(values.entityName)}`}
516
+ />
517
+ )}
518
+ </Components.Header>
519
+ <div className="pageContainer paddingVertical-20 paddingHorizontal-40">
520
+ <Components.Text type="h1" className="">
521
+ {values.textFeatureTitle}
522
+ </Components.Text>
523
+ {this.renderFilters()}
524
+ {this.renderUnreadFilterInfo()}
525
+
526
+ <Table
527
+ className="plussTable"
528
+ striped
529
+ bordered
530
+ condensed
531
+ hover
532
+ style={{ minWidth: "100%" }}
533
+ >
534
+ <thead>
535
+ <tr>
536
+ <th>Title</th>
537
+ <th>Last updated</th>
538
+ <th>Members</th>
539
+ <th style={{ width: 50 }} />
540
+ </tr>
541
+ </thead>
542
+ <tbody>
543
+ {this.getCircles().map((circle) => {
544
+ return this.renderRow(circle);
545
+ })}
546
+ </tbody>
547
+ </Table>
548
+ </div>
549
+ </div>
550
+ );
551
+ }
372
552
  }
373
553
 
374
554
  const styles = {};
375
555
 
376
556
  const mapStateToProps = (state) => {
377
- const { circles } = state[values.reducerKey];
378
- const { auth } = state;
379
-
380
- return {
381
- circles,
382
- auth,
383
- user: Helper.getUserFromState(state),
384
- };
557
+ const { circles } = state[values.reducerKey];
558
+ const { auth } = state;
559
+
560
+ return {
561
+ circles,
562
+ auth,
563
+ user: Helper.getUserFromState(state),
564
+ };
385
565
  };
386
566
 
387
- export default connect(mapStateToProps, { circlesLoaded, circleRemoved })(Circles);
567
+ export default connect(mapStateToProps, { circlesLoaded, circleRemoved })(
568
+ Circles,
569
+ );