@plusscommunities/pluss-circles-web-groups 1.5.7-beta.0 → 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.
@@ -9,6 +9,7 @@ import { circleActions } from "../apis";
9
9
  import { Table } from "react-bootstrap";
10
10
  import FontAwesome from "react-fontawesome";
11
11
  import { values } from "../values.config";
12
+ import badgeStyles from "./Circles.module.css";
12
13
 
13
14
  const { Components, Session, Helper } = PlussCore;
14
15
 
@@ -18,11 +19,17 @@ class Circles extends Component {
18
19
  this.state = {
19
20
  userSearch: "",
20
21
  sortBy: "newest",
22
+ showUnreadOnly: false,
23
+ pollingInterval: null,
21
24
  };
22
25
  }
23
26
 
24
27
  componentDidMount() {
25
28
  this.getData();
29
+ // Poll for updates every 10 seconds
30
+ this.pollingInterval = setInterval(() => {
31
+ this.getData();
32
+ }, 10000);
26
33
  }
27
34
 
28
35
  getData = () => {
@@ -40,6 +47,31 @@ class Circles extends Component {
40
47
  });
41
48
  };
42
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
+ };
43
75
  getCircles() {
44
76
  let result = this.props.circles;
45
77
  if (this.state.selectedTypeFilter) {
@@ -62,6 +94,11 @@ class Circles extends Component {
62
94
  });
63
95
  if (this.state.sortBy === "newest") {
64
96
  result.reverse();
97
+ if (this.state.showUnreadOnly) {
98
+ result = _.filter(result, (circle) => {
99
+ return this.isCircleUnread(circle);
100
+ });
101
+ }
65
102
  }
66
103
  return result;
67
104
  }
@@ -223,12 +260,23 @@ class Circles extends Component {
223
260
  };
224
261
 
225
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
+
226
271
  return (
227
272
  <tr key={circle.Id}>
228
273
  <td className="table-TitleColumn">
229
- <Link to={`/${values.featureKey}/${values.entityKey}/${circle.Id}`}>
230
- {this.getTitle(circle)}
231
- </Link>
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>
232
280
  </td>
233
281
  <td>{moment(circle.Changed).local().format("D MMM YYYY")}</td>
234
282
  <td>
@@ -281,6 +329,35 @@ class Circles extends Component {
281
329
  );
282
330
  }
283
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
+
284
361
  renderFilters() {
285
362
  let userFilter = (
286
363
  <Components.Tag
@@ -289,6 +366,16 @@ class Circles extends Component {
289
366
  text="User"
290
367
  />
291
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
+
292
379
  let typeFilter = (
293
380
  <Components.Tag
294
381
  className="marginRight-10"
@@ -331,6 +418,7 @@ class Circles extends Component {
331
418
  </Components.Text>
332
419
  {userFilter}
333
420
  {typeFilter}
421
+ {unreadFilter}
334
422
  <Components.Text type="h5" className="marginRight-20 marginLeft-20">
335
423
  Sort by:
336
424
  </Components.Text>
@@ -433,6 +521,7 @@ class Circles extends Component {
433
521
  {values.textFeatureTitle}
434
522
  </Components.Text>
435
523
  {this.renderFilters()}
524
+ {this.renderUnreadFilterInfo()}
436
525
 
437
526
  <Table
438
527
  className="plussTable"
@@ -0,0 +1,96 @@
1
+ /* Badge styling for unread count */
2
+ :root {
3
+ --badge-bg-color: #597db4; /* COLOUR_BRANDING_OFF */
4
+ }
5
+
6
+ .badgeTag {
7
+ font-size: 11px;
8
+ font-weight: 400;
9
+ padding: 3px 10px;
10
+ background-color: var(--badge-bg-color) !important;
11
+ color: #fff !important;
12
+ border-radius: 999px !important;
13
+ border: none !important;
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ line-height: 1;
18
+ letter-spacing: 0.02em;
19
+ }
20
+
21
+ /* Unread filter info banner */
22
+ .unreadFilterBanner {
23
+ background-color: #f0f4f8;
24
+ border-left: 3px solid var(--badge-bg-color);
25
+ border-radius: 4px;
26
+ padding: 12px 16px;
27
+ margin-top: 16px;
28
+ margin-bottom: 16px;
29
+ display: flex;
30
+ align-items: center;
31
+ }
32
+
33
+ .unreadFilterContent {
34
+ display: flex;
35
+ align-items: center;
36
+ gap: 10px;
37
+ width: 100%;
38
+ }
39
+
40
+ .unreadFilterIcon {
41
+ font-size: 16px;
42
+ color: var(--badge-bg-color);
43
+ flex-shrink: 0;
44
+ }
45
+
46
+ .unreadFilterText {
47
+ font-size: 14px;
48
+ line-height: 1.5;
49
+ color: #333333;
50
+ }
51
+
52
+ .unreadFilterButton {
53
+ background: none;
54
+ border: none;
55
+ color: var(--badge-bg-color);
56
+ font-size: 14px;
57
+ font-weight: 500;
58
+ text-decoration: underline;
59
+ cursor: pointer;
60
+ padding: 4px 8px;
61
+ margin-left: 4px;
62
+ min-height: 32px;
63
+ min-width: 32px;
64
+ transition: all 0.2s ease;
65
+ }
66
+
67
+ .unreadFilterButton:hover {
68
+ background-color: rgba(89, 125, 180, 0.1);
69
+ text-decoration: none;
70
+ }
71
+
72
+ .unreadFilterButton:focus {
73
+ outline: 2px solid var(--badge-bg-color);
74
+ outline-offset: 2px;
75
+ border-radius: 2px;
76
+ }
77
+
78
+ /* Mobile responsiveness */
79
+ @media (max-width: 768px) {
80
+ .unreadFilterBanner {
81
+ flex-direction: column;
82
+ align-items: flex-start;
83
+ padding: 12px;
84
+ }
85
+
86
+ .unreadFilterContent {
87
+ flex-direction: column;
88
+ align-items: flex-start;
89
+ gap: 8px;
90
+ }
91
+
92
+ .unreadFilterText {
93
+ font-size: 13px;
94
+ }
95
+ }
96
+