Skip to content

Commit ed1fb10

Browse files
Updated team members components
1 parent 1738c8d commit ed1fb10

4 files changed

Lines changed: 118 additions & 129 deletions

File tree

src/components/HGNHelpSkillsDashboard/CommunityMembersPage.jsx

Lines changed: 15 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { useEffect, useMemo, useState } from 'react';
2-
import axios from 'axios';
3-
import RankedUserList from './RankedUserList';
4-
import styles from './style/CommunityMembersPage.module.css';
1+
import React, { useState, useMemo } from 'react';
2+
import RankedUserList from './RankedUserList'; // wherever your RankedUserList is
53

64
const availableSkills = ['React', 'Redux', 'HTML', 'CSS', 'MongoDB', 'Database', 'Agile'];
75
const RANKED_USERS_ENDPOINT = 'http://localhost:4500/api/hgnform/ranked';
@@ -41,114 +39,32 @@ function CommunityMembersPage() {
4139
);
4240
};
4341

44-
const toggleSortOrder = () => {
45-
setSortOrder(prev => (prev === 'asc' ? 'desc' : 'asc'));
46-
};
47-
48-
const clearFilters = () => {
49-
setSelectedSkills([]);
50-
};
51-
52-
const filteredUsers = useMemo(() => {
53-
const normalizedSearch = searchTerm.trim().toLowerCase();
54-
const normalizedSelectedSkills = selectedSkills.map(skill => skill.toLowerCase());
55-
let result = rankedUsers;
56-
57-
if (normalizedSearch) {
58-
result = rankedUsers.filter(user => {
59-
const nameMatches = user.name?.toLowerCase().includes(normalizedSearch);
60-
const skillMatches = Array.isArray(user.topSkills)
61-
? user.topSkills.some(skill => skill.toLowerCase().includes(normalizedSearch))
62-
: false;
63-
return nameMatches || skillMatches;
64-
});
65-
}
66-
67-
if (normalizedSelectedSkills.length) {
68-
result = result.filter(user => {
69-
if (!Array.isArray(user.topSkills) || user.topSkills.length === 0) return false;
70-
return user.topSkills.some(skill =>
71-
normalizedSelectedSkills.includes((skill || '').toLowerCase()),
72-
);
73-
});
74-
}
75-
76-
return [...result].sort((a, b) => {
77-
const first = a.name || '';
78-
const second = b.name || '';
79-
return sortOrder === 'asc' ? first.localeCompare(second) : second.localeCompare(first);
80-
});
81-
}, [rankedUsers, searchTerm, sortOrder, selectedSkills]);
82-
83-
const emptyMessage =
84-
searchTerm || selectedSkills.length
85-
? 'No community members match your current filters.'
86-
: 'No community members available yet.';
42+
// EFFECTIVE SKILLS = what we pass to RankedUserList
43+
const effectiveSkills = useMemo(() => {
44+
return selectedSkills.length > 0 ? selectedSkills : availableSkills;
45+
}, [selectedSkills]);
8746

8847
return (
89-
<div className={styles.container}>
90-
<h1 className={styles.heading}>One Community Members</h1>
91-
<div className={styles.controlsRow}>
92-
<div className={styles.searchWrapper}>
93-
<input
94-
type="search"
95-
value={searchTerm}
96-
onChange={event => setSearchTerm(event.target.value)}
97-
placeholder="Search by team member name or skills"
98-
className={styles.searchInput}
99-
aria-label="Search community members"
100-
/>
101-
</div>
102-
<button
103-
type="button"
104-
className={styles.filterButton}
105-
onClick={() => setShowFilters(prev => !prev)}
106-
>
107-
{showFilters ? 'Hide Filters' : 'Filter'}
108-
</button>
109-
<button type="button" className={styles.sortButton} onClick={toggleSortOrder}>
110-
{sortOrder === 'asc' ? 'A→Z Sort' : 'Z→A Sort'}
111-
</button>
112-
{(selectedSkills.length > 0 || searchTerm) && (
113-
<button
114-
type="button"
115-
className={styles.clearButton}
116-
onClick={() => {
117-
clearFilters();
118-
setSearchTerm('');
119-
}}
120-
>
121-
Clear All
122-
</button>
123-
)}
124-
</div>
48+
<div>
49+
<h1>Community Members</h1>
12550

126-
{showFilters && (
127-
<div className={styles.filtersPanel}>
51+
<div style={{ marginBottom: 16 }}>
52+
<strong>Filter by skills:</strong>
53+
<div style={{ display: 'flex', gap: 10, marginTop: 8, flexWrap: 'wrap' }}>
12854
{availableSkills.map(skill => (
129-
<label key={skill} className={styles.filterOption}>
55+
<label key={skill} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
13056
<input
13157
type="checkbox"
13258
checked={selectedSkills.includes(skill)}
13359
onChange={() => handleCheckboxChange(skill)}
13460
/>
135-
<span>{skill}</span>
61+
{skill}
13662
</label>
13763
))}
13864
</div>
139-
)}
140-
141-
<p className={styles.helperText}>
142-
When multiple filters are selected, the score represents the average value, and the options
143-
are ranked based on their scoring. Click each profile to learn more details.
144-
</p>
65+
</div>
14566

146-
<RankedUserList
147-
users={filteredUsers}
148-
loading={loading}
149-
error={error}
150-
emptyMessage={emptyMessage}
151-
/>
67+
<RankedUserList selectedSkills={effectiveSkills} />
15268
</div>
15369
);
15470
}

src/components/HGNHelpSkillsDashboard/RankedUserList.jsx

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,56 @@ import PropTypes from 'prop-types';
22
import UserCard from './UserCard';
33
import styles from './style/UserCard.module.css';
44

5-
function RankedUserList({ users, loading, error, emptyMessage }) {
6-
if (loading) {
7-
return <p>Loading community members...</p>;
8-
}
5+
function RankedUserList({ selectedSkills = [] }) {
6+
const [rankedUsers, setRankedUsers] = useState([]);
7+
const [loading, setLoading] = useState(true);
8+
const [error, setError] = useState(null);
99

10-
if (error) {
11-
return <p className="text-danger">{error}</p>;
12-
}
10+
useEffect(() => {
11+
let canceled = false;
1312

14-
if (!users.length) {
15-
return <p>{emptyMessage}</p>;
16-
}
13+
const fetchRankedUsers = async () => {
14+
setLoading(true);
15+
setError(null);
16+
17+
try {
18+
const params = {};
19+
if (Array.isArray(selectedSkills) && selectedSkills.length > 0) {
20+
params.skills = selectedSkills.join(',');
21+
}
22+
23+
const response = await axios.get('http://localhost:4500/api/hgnform/ranked', { params });
24+
25+
if (!canceled) {
26+
const data = response?.data || [];
27+
setRankedUsers(Array.isArray(data) ? data : []);
28+
}
29+
} catch (err) {
30+
console.error('Failed to fetch ranked users', err);
31+
if (!canceled) {
32+
setError(err);
33+
setRankedUsers([]);
34+
}
35+
} finally {
36+
if (!canceled) setLoading(false);
37+
}
38+
};
39+
40+
fetchRankedUsers();
41+
42+
return () => {
43+
canceled = true;
44+
};
45+
}, [selectedSkills]);
46+
47+
if (loading) return <p>Loading ranked users...</p>;
48+
if (error) return <p>Failed to load users.</p>;
49+
if (!rankedUsers.length) return <p>No members found.</p>;
1750

1851
return (
19-
<div className={styles.containerGrid}>
20-
{users.map(user => (
21-
<UserCard key={user._id || user.email || user.name} user={user} />
52+
<div className="user-card-container">
53+
{rankedUsers.map(user => (
54+
<UserCard key={user._id || user.id || user.uuid} user={user} />
2255
))}
2356
</div>
2457
);

src/components/HGNHelpSkillsDashboard/UserCard.jsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1+
import React from 'react';
12
import styles from './style/UserCard.module.css';
23
import avatar from './style/avatar.png';
34
import emailIcon from './style/email_icon.png';
45
import slackIcon from './style/slack_icon.png';
56

7+
import { useSelector } from 'react-redux';
8+
69
function UserCard({ user }) {
7-
const { name, email, slack, score, topSkills = [] } = user;
10+
const darkMode = useSelector(state => state.theme?.darkMode);
11+
const { name, email, slack, score, topSkills } = user;
812

913
const getScoreColor = userScore => {
1014
if (userScore >= 5) return '#00754A';
1115
return '#D93D3D';
1216
};
1317

1418
return (
15-
<div className={`${styles.userCard}`}>
19+
<div className={`${styles.userCard} ${darkMode ? styles.darkMode : ''}`}>
1620
<img src={avatar} alt="Avatar" className={`${styles.avatar}`} />
1721
<div className={`${styles.info}`}>
18-
<div className={`${styles.userName}`}>{name}</div>
22+
<div className={`${styles.userName}`} title={name}>
23+
{name}
24+
</div>
1925
{email && (
2026
<div className={`${styles.contactLine}`}>
2127
<img src={emailIcon} alt="Email" className={`${styles.contactIcon}`} />
@@ -41,7 +47,9 @@ function UserCard({ user }) {
4147

4248
<div className={`${styles.skillsSection}`}>
4349
<div className={`${styles.skillsLabel}`}>Top Skills:</div>
44-
<div className={`${styles.skillsText}`}>{topSkills.join(', ')}</div>
50+
<div className={`${styles.skillsText}`}>
51+
{Array.isArray(topSkills) ? topSkills.join(', ') : topSkills || ''}
52+
</div>
4553
</div>
4654
</div>
4755
</div>

src/components/HGNHelpSkillsDashboard/style/UserCard.module.css

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
.userCard {
2-
width: 100%;
32
max-width: 360px;
4-
height: auto;
5-
padding: 28px 24px;
3+
width: 100%;
4+
box-sizing: border-box;
5+
padding: 30px 24px;
66
background: #ffffff;
77
border: 2px solid #eeeeee;
88
box-shadow: 1px 4px 4px rgba(0, 0, 0, 0.25);
@@ -18,30 +18,36 @@
1818
.avatar {
1919
width: 140px;
2020
height: 140px;
21+
max-width: 40%;
22+
max-height: 140px;
2123
border-radius: 50%;
2224
margin-bottom: 8px;
25+
object-fit: cover;
2326
}
2427

2528
.info {
2629
display: flex;
2730
flex-direction: column;
28-
align-items: stretch;
29-
text-align: left;
30-
gap: 8px;
31-
margin-top: 12px;
31+
align-items: center;
32+
text-align: center;
33+
margin-top: 16px;
34+
padding: 0;
3235
width: 100%;
33-
padding: 0 16px;
36+
box-sizing: border-box;
37+
padding-left: 8px;
38+
padding-right: 8px;
3439
}
3540

3641
.userName {
3742
width: 100%;
3843
font-size: 24px;
3944
font-weight: 500;
4045
color: black;
41-
margin-bottom: 6px;
42-
word-break: break-word;
43-
line-height: 1.25;
44-
text-align: center;
46+
margin-bottom: 8px;
47+
white-space: nowrap;
48+
overflow: hidden;
49+
text-overflow: ellipsis;
50+
max-width: 100%;
4551
}
4652

4753
.contactLine {
@@ -52,7 +58,7 @@
5258
font-size: 13px;
5359
color: #616161;
5460
margin-bottom: 6px;
55-
flex-wrap: wrap;
61+
justify-content: center;
5662
}
5763

5864
.contactIcon {
@@ -112,6 +118,32 @@
112118
color: #616161;
113119
line-height: 21px;
114120
word-wrap: break-word;
121+
overflow-wrap: break-word;
122+
}
123+
124+
.userCard.darkMode {
125+
background: #2f4157;
126+
border-color: #444;
127+
box-shadow: none;
128+
}
129+
130+
.userCard.darkMode .contactLine {
131+
color: #ccc;
132+
}
133+
134+
.darkMode .skillsText {
135+
color: #ffffff !important;
136+
}
137+
138+
.darkMode .userName {
139+
color: #ffffff !important;
140+
}
141+
142+
.darkMode .scoreLine,
143+
.darkMode .scoreLabel,
144+
.darkMode .scoreValue,
145+
.darkMode .scoreMax {
146+
color: #ffffff !important;
115147
}
116148

117149
.containerGrid {

0 commit comments

Comments
 (0)