Skip to content

Commit c508819

Browse files
Merge pull request #4359 from OneCommunityGlobal/Ajay-Implemented-the-Active-In-Active-team-members-in-team-page
Ajay implemented the active in active team members in team page
2 parents ad032e6 + da34ffd commit c508819

8 files changed

Lines changed: 1694 additions & 1243 deletions

File tree

src/actions/allTeamsAction.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ export const getAllUserTeams = () => {
122122
.then(res => {
123123
dispatch(teamMembersFectchACtion(res.data));
124124
return res.data;
125-
// console.log("getAllUserTeams: res:", res.data)
126125
})
127126
.catch(() => {
128127
dispatch(teamMembersFectchACtion(undefined));

src/components/Teams/Team.jsx

Lines changed: 145 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,138 @@
11
/* eslint-disable react/destructuring-assignment */
22
import './Team.css';
3-
import { connect, useSelector } from 'react-redux';
3+
import React, { useEffect, useState } from 'react';
4+
import PropTypes from 'prop-types';
5+
import { connect, useSelector, useDispatch } from 'react-redux';
46
import { Button } from 'reactstrap';
57
import hasPermission from '~/utils/permissions';
68
import { boxStyle, boxStyleDark } from '~/styles';
79
import { DELETE } from '../../languages/en/ui';
10+
import { getTeamMembers } from '../../actions/allTeamsAction';
11+
import { fetchTeamMembersCached, getCachedTeamMembers } from './teamMembersCache';
12+
13+
function computeCounts(members, loading, localMembers) {
14+
const list = Array.isArray(members) ? members : [];
15+
16+
if (loading && localMembers == null) return { total: '…', active: '…', inactive: '…' };
17+
const tot = list.length;
18+
if (tot === 0) return { total: 0, active: 0, inactive: 0 };
19+
20+
let act = 0;
21+
let sawFlag = false;
22+
for (const m of list) {
23+
const flag = m?.isActive ?? m?.active;
24+
if (typeof flag === 'boolean') {
25+
sawFlag = true;
26+
if (flag) act += 1;
27+
}
28+
}
29+
if (!sawFlag) return { total: tot, active: '…', inactive: '…' };
30+
return { total: tot, active: act, inactive: tot - act };
31+
}
832
import headerStyles from './TeamTableHeader.module.css';
933

1034
export function Team(props) {
11-
const darkMode = useSelector(state => state.theme.darkMode);
35+
const dispatch = useDispatch();
36+
const darkMode = useSelector(s => s.theme.darkMode);
1237
const canDeleteTeam = props.hasPermission('deleteTeam');
1338
const canPutTeam = props.hasPermission('putTeam');
1439

40+
// Keep a raw id for callbacks (number stays number in tests)
41+
const teamIdRaw =
42+
typeof props.teamId === 'string' && /^\d+$/.test(props.teamId)
43+
? Number(props.teamId)
44+
: props.teamId;
45+
46+
// string key for cache/DOM ids
47+
const teamIdKey = String(props.teamId ?? '');
48+
49+
const [localMembers, setLocalMembers] = useState(() => getCachedTeamMembers(teamIdKey) || null);
50+
const [loading, setLoading] = useState(false);
51+
52+
useEffect(() => {
53+
const cached = getCachedTeamMembers(teamIdKey);
54+
if (cached && !localMembers) setLocalMembers(cached);
55+
// eslint-disable-next-line react-hooks/exhaustive-deps
56+
}, [teamIdKey]);
57+
58+
useEffect(() => {
59+
if (!getCachedTeamMembers(teamIdKey) && teamIdKey) {
60+
fetchTeamMembersCached(dispatch, getTeamMembers, teamIdKey)
61+
.then(setLocalMembers)
62+
.catch(() => {});
63+
}
64+
}, [dispatch, teamIdKey]);
65+
66+
const members = localMembers ?? props.team?.members ?? [];
67+
const { total, active, inactive } = computeCounts(members, loading, localMembers);
68+
69+
// Fire callback immediately (keeps tests & UX snappy), then refresh members
70+
const handleOpenMembers = () => {
71+
if (typeof props.onMembersClick === 'function') {
72+
props.onMembersClick(teamIdRaw, props.name, props.teamCode);
73+
}
74+
75+
setLoading(true);
76+
fetchTeamMembersCached(dispatch, getTeamMembers, teamIdKey)
77+
.then(setLocalMembers)
78+
.catch(() => {})
79+
.finally(() => setLoading(false));
80+
};
81+
1582
return (
16-
<tr className="teams__tr" id={`tr_${props.teamId}`}>
83+
<tr className="teams__tr" id={`tr_${teamIdKey}`}>
1784
<th className="teams__order--input" scope="row">
1885
<div>{(props.index ?? 0) + 1}</div>
1986
</th>
2087
{/* Wrap long names vertically */}
21-
<td className={headerStyles.teamNameCol}>{props.name}</td>
88+
<td className={headerStyles.teamNameCol}>
89+
{props.name} ({total} | {active} | {inactive})
90+
</td>
2291
<td className="teams__active--input">
2392
<button
2493
data-testid="active-marker"
2594
type="button"
2695
onClick={() => {
2796
if (canDeleteTeam || canPutTeam) {
28-
props.onStatusClick(props.name, props.teamId, props.active, props.teamCode);
97+
props.onStatusClick(props.name, teamIdRaw, props.active, props.teamCode);
2998
}
3099
}}
31-
style={{
32-
boxStyle,
33-
}}
100+
// style={boxStyle}
34101
aria-label={`Change status for team ${props.name}`}
35102
>
36103
<div className={props.active ? 'isActive' : 'isNotActive'}>
37-
<i className="fa fa-circle" aria-hidden="true"></i>
104+
<i className="fa fa-circle" aria-hidden="true" />
38105
</div>
39106
</button>
40107
</td>
108+
41109
<td className="centered-cell">
42110
<button
43111
style={darkMode ? {} : boxStyle}
44112
type="button"
45113
className="btn btn-outline-info"
46-
onClick={() => {
47-
props.onMembersClick(props.teamId, props.name, props.teamCode);
114+
onMouseEnter={() => {
115+
if (!getCachedTeamMembers(teamIdKey)) {
116+
fetchTeamMembersCached(dispatch, getTeamMembers, teamIdKey)
117+
.then(setLocalMembers)
118+
.catch(() => {});
119+
}
48120
}}
121+
onClick={handleOpenMembers}
49122
data-testid="members-btn"
50123
aria-label="Users"
124+
disabled={loading}
51125
>
52-
<i className="fa fa-users" aria-hidden="true" />
126+
{loading ? <i className="fa fa-spinner fa-spin" /> : <i className="fa fa-users" />}
53127
</button>
54128
</td>
129+
55130
{(canDeleteTeam || canPutTeam) && (
56131
<td>
57132
<span className="usermanagement-actions-cell">
58133
<Button
59134
color="success"
60-
// className="btn btn-outline-success"
61-
onClick={() => {
62-
props.onEditTeam(props.name, props.teamId, props.active, props.teamCode);
63-
}}
135+
onClick={() => props.onEditTeam(props.name, teamIdRaw, props.active, props.teamCode)}
64136
style={darkMode ? {} : boxStyle}
65137
disabled={!canPutTeam}
66138
>
@@ -70,10 +142,9 @@ export function Team(props) {
70142
<span className="usermanagement-actions-cell">
71143
<Button
72144
color="danger"
73-
// className="btn btn-outline-danger"
74-
onClick={() => {
75-
props.onDeleteClick(props.name, props.teamId, props.active, props.teamCode);
76-
}}
145+
onClick={() =>
146+
props.onDeleteClick(props.name, teamIdRaw, props.active, props.teamCode)
147+
}
77148
style={darkMode ? boxStyleDark : boxStyle}
78149
disabled={!canDeleteTeam}
79150
>
@@ -85,4 +156,58 @@ export function Team(props) {
85156
</tr>
86157
);
87158
}
159+
160+
Team.propTypes = {
161+
// injected by connect
162+
hasPermission: PropTypes.func.isRequired,
163+
164+
// basic identity/labels
165+
teamId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
166+
name: PropTypes.string.isRequired,
167+
teamCode: PropTypes.string,
168+
index: PropTypes.number,
169+
170+
// status flags
171+
active: PropTypes.bool,
172+
173+
// callbacks
174+
onMembersClick: PropTypes.func,
175+
onStatusClick: PropTypes.func,
176+
onEditTeam: PropTypes.func,
177+
onDeleteClick: PropTypes.func,
178+
179+
// optional team object (used for members & modifiedDatetime)
180+
team: PropTypes.shape({
181+
_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
182+
teamName: PropTypes.string,
183+
teamCode: PropTypes.string,
184+
isActive: PropTypes.bool,
185+
members: PropTypes.arrayOf(
186+
PropTypes.shape({
187+
_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
188+
firstName: PropTypes.string,
189+
lastName: PropTypes.string,
190+
isActive: PropTypes.bool,
191+
active: PropTypes.bool,
192+
}),
193+
),
194+
modifiedDatetime: PropTypes.oneOfType([
195+
PropTypes.string,
196+
PropTypes.number,
197+
PropTypes.instanceOf(Date),
198+
]),
199+
}),
200+
};
201+
202+
Team.defaultProps = {
203+
teamCode: '',
204+
index: 0,
205+
active: false,
206+
onMembersClick: undefined,
207+
onStatusClick: undefined,
208+
onEditTeam: undefined,
209+
onDeleteClick: undefined,
210+
team: undefined,
211+
};
212+
88213
export default connect(null, { hasPermission })(Team);

0 commit comments

Comments
 (0)