Skip to content

Commit 219e69e

Browse files
Merge pull request #4667 from OneCommunityGlobal/development
Frontend Release to Main [4.70]
2 parents 0f3b206 + d903969 commit 219e69e

58 files changed

Lines changed: 2759 additions & 1677 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 53 additions & 201 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/index.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,6 @@ body.bm-dashboard-dark .nav-link {
242242
color: #ffffff !important;
243243
}
244244

245-
body.dark-mode .navbar,
246-
body.bm-dashboard-dark .navbar {
247-
background-color: #1b2a41 !important;
248-
}
249245

250246
body.dark-mode .navbar-brand,
251247
body.bm-dashboard-dark .navbar-brand {

src/actions/__tests__/userManagement.test.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const mockStore = configureMockStore(middlewares);
1414

1515
describe('User Management Actions', () => {
1616
let store;
17-
17+
1818
beforeEach(() => {
1919
store = mockStore({});
2020
vi.clearAllMocks();
@@ -46,7 +46,7 @@ describe('User Management Actions', () => {
4646

4747
it('should update user to active status', async () => {
4848
const reactivationDate = null;
49-
49+
5050
axios.patch.mockResolvedValueOnce({ data: {} });
5151

5252
await store.dispatch(actions.updateUserStatus(mockUser, UserStatus.Active, reactivationDate));
@@ -143,7 +143,7 @@ describe('User Management Actions', () => {
143143
);
144144
});
145145

146-
146+
147147
});
148148

149149
describe('updateUserFinalDayStatus', () => {
@@ -205,6 +205,7 @@ describe('User Management Actions', () => {
205205
{ id: 1, name: 'John Doe', email: 'john@example.com' },
206206
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
207207
];
208+
const mockSource = 'Report';
208209

209210
axios.get.mockResolvedValueOnce({ data: mockBasicInfo });
210211

@@ -213,20 +214,20 @@ describe('User Management Actions', () => {
213214
{ type: 'RECEIVE_USER_PROFILE_BASIC_INFO', payload: mockBasicInfo }
214215
];
215216

216-
await store.dispatch(actions.getUserProfileBasicInfo());
217+
await store.dispatch(actions.getUserProfileBasicInfo({source: mockSource}));
217218
expect(store.getActions()).toEqual(expectedActions);
218-
expect(axios.get).toHaveBeenCalledWith(ENDPOINTS.USER_PROFILE_BASIC_INFO);
219+
expect(axios.get).toHaveBeenCalledWith(ENDPOINTS.USER_PROFILE_BASIC_INFO(mockSource));
219220
});
220221

221222
it('should handle errors when fetching basic info', async () => {
222223
axios.get.mockRejectedValueOnce(new Error('Network error'));
223-
224+
const mockSource = '';
224225
const expectedActions = [
225226
{ type: 'FETCH_USER_PROFILE_BASIC_INFO' },
226227
{ type: 'FETCH_USER_PROFILE_BASIC_INFO_ERROR' }
227228
];
228229

229-
await store.dispatch(actions.getUserProfileBasicInfo());
230+
await store.dispatch(actions.getUserProfileBasicInfo({ source: mockSource }));
230231
expect(store.getActions()).toEqual(expectedActions);
231232
});
232233
});
@@ -276,4 +277,4 @@ describe('User Management Actions', () => {
276277
expect(store.getActions()).toContainEqual(expectedAction);
277278
});
278279
});
279-
});
280+
});

src/actions/userManagement.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,15 +309,18 @@ export const updateUserFinalDay = (user, finalDayDate, isSet) => {
309309

310310
/**
311311
* fetching all user profiles basic info
312+
* Added `source` parameter to identify the calling component.
312313
*/
313-
export const getUserProfileBasicInfo = (userId) => {
314+
export const getUserProfileBasicInfo = ({ userId, source }) => {
314315
// API request to fetch basic user profile information
315-
let userProfileBasicInfoPromise;
316-
if (userId)
316+
let userProfileBasicInfoPromise;
317+
if (userId)
317318
userProfileBasicInfoPromise = axios.get(`${ENDPOINTS.USER_PROFILE_BASIC_INFO}?userId=${userId}`);
318-
else
319+
else if (source)
320+
userProfileBasicInfoPromise = axios.get(ENDPOINTS.USER_PROFILE_BASIC_INFO(source));
321+
else
319322
userProfileBasicInfoPromise = axios.get(ENDPOINTS.USER_PROFILE_BASIC_INFO);
320-
323+
321324
return async dispatch => {
322325
// Dispatch action indicating the start of the fetch process
323326
await dispatch(userProfilesBasicInfoFetchStartAction());

src/components/BMDashboard/ItemList/SelectItem.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ export default function SelectItem({
7676
<option value="all" key="all-option">
7777
All
7878
</option>
79-
{itemSet.map(itemName => (
80-
<option key={`item-${itemName}`} value={itemName}>
81-
{itemName}
79+
{itemSet.map(item => (
80+
<option key={`item-${item}`} value={item}>
81+
{item}
8282
</option>
8383
))}
8484
</>

src/components/CommunityPortal/Activities/ActivityAttendance.jsx

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Doughnut } from 'react-chartjs-2';
33
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
44
import { v4 as uuidv4 } from 'uuid';
55
import { FaRegClock, FaIdCard } from 'react-icons/fa';
6-
import './ActivityAttendance.css';
6+
import styles from './ActivityAttendance.module.css';
77
import { useState } from 'react';
88
import profileImg from '../../../assets/images/profile.png';
99

@@ -35,9 +35,9 @@ function StatsChart({ stats }) {
3535
};
3636

3737
return (
38-
<div className="chart-container">
38+
<div className={styles.chartContainer}>
3939
<Doughnut data={data} options={options} />
40-
<div className="chart-label">{percentage}%</div>
40+
<div className={styles.chartLabel}>{percentage}%</div>
4141
</div>
4242
);
4343
}
@@ -59,9 +59,9 @@ const exportToCSV = students => {
5959

6060
function StatsCard({ title, value, color }) {
6161
return (
62-
<div className="stats-card">
62+
<div className={styles.statsCard}>
6363
<h3>{title}</h3>
64-
<p className="stats-value" style={{ color }}>
64+
<p className={styles.statsValue} style={{ color }}>
6565
{value}
6666
</p>
6767
</div>
@@ -70,39 +70,71 @@ function StatsCard({ title, value, color }) {
7070

7171
function StudentRow({ img, name, time, id }) {
7272
return (
73-
<div className="student-row">
74-
<div className="student-left">
75-
<img src={img} alt={name} className="student-img" />
76-
<div className="student-name">{name}</div>
73+
<div className={styles.studentRow}>
74+
<div className={styles.studentLeft}>
75+
<img src={img} alt={name} className={styles.studentImg} />
76+
<div className={styles.studentName}>{name}</div>
7777
</div>
78-
<div className="student-center">
79-
<div className="student-time">
80-
<FaRegClock className="student-icon" /> {time}
78+
<div className={styles.studentCenter}>
79+
<div className={styles.studentTime}>
80+
<FaRegClock className={styles.studentIcon} /> {time}
8181
</div>
8282
</div>
83-
<div className="student-right">
84-
<div className="student-id">
85-
<FaIdCard className="student-icon" /> {id}
83+
<div className={styles.studentRight}>
84+
<div className={styles.studentId}>
85+
<FaIdCard className={styles.studentIcon} /> {id}
8686
</div>
8787
</div>
8888
</div>
8989
);
9090
}
9191

92-
function LiveUpdates({ students, searchTerm }) {
93-
const filteredStudents = students.filter(student =>
94-
student.name.toLowerCase().includes(searchTerm.toLowerCase()),
95-
);
92+
function LiveUpdates({ students, searchTerm, selectedStatus, onStatusChange }) {
93+
const normalizedSearch = searchTerm.trim().toLowerCase();
94+
95+
const filteredStudents = students.filter(student => {
96+
const matchesStatus = selectedStatus === 'All' || student.status === selectedStatus;
97+
98+
const matchesSearch =
99+
!normalizedSearch ||
100+
student.name.toLowerCase().includes(normalizedSearch) ||
101+
student.id.toLowerCase().includes(normalizedSearch);
102+
103+
return matchesStatus && matchesSearch;
104+
});
105+
106+
const statuses = ['All', 'In', 'Out', 'Leave'];
96107

97108
return (
98-
<div className="live-updates">
99-
<div className="updates-header">
100-
<h3>Live Student Update</h3>
101-
<button className="export-btn" type="button" onClick={() => exportToCSV(filteredStudents)}>
109+
<div className={styles.liveUpdates}>
110+
<div className={styles.updatesHeader}>
111+
<div className={styles.updatesHeaderLeft}>
112+
<h3>Live Student Update</h3>
113+
<div className={styles.statusFilters}>
114+
{statuses.map(status => (
115+
<button
116+
key={status}
117+
type="button"
118+
className={`${styles.statusFilterBtn} ${
119+
selectedStatus === status ? styles.statusFilterBtnActive : ''
120+
}`}
121+
onClick={() => onStatusChange(status)}
122+
>
123+
{status}
124+
</button>
125+
))}
126+
</div>
127+
</div>
128+
<button
129+
className={styles.exportBtn}
130+
type="button"
131+
onClick={() => exportToCSV(filteredStudents)}
132+
>
102133
Export Data
103134
</button>
104135
</div>
105-
<div className="updates-list">
136+
137+
<div className={styles.updatesList}>
106138
{filteredStudents.length > 0 ? (
107139
filteredStudents.map(student => (
108140
<StudentRow
@@ -114,7 +146,7 @@ function LiveUpdates({ students, searchTerm }) {
114146
/>
115147
))
116148
) : (
117-
<p className="no-results">No students found.</p>
149+
<p className={styles.noResults}>No students found.</p>
118150
)}
119151
</div>
120152
</div>
@@ -124,6 +156,7 @@ function LiveUpdates({ students, searchTerm }) {
124156
function ActivityAttendance() {
125157
const darkMode = useSelector(state => state.theme.darkMode);
126158
const [searchTerm, setSearchTerm] = useState('');
159+
const [statusFilter, setStatusFilter] = useState('All');
127160

128161
const statsData = [
129162
{ id: uuidv4(), title: 'Total Community Members', value: 400, color: '#4CAF50' },
@@ -133,44 +166,53 @@ function ActivityAttendance() {
133166
];
134167

135168
const students = [
136-
{ img: profileImg, name: 'Ramakant Sharma', time: '12:30', id: '3CO-JVY' },
137-
{ img: profileImg, name: 'John Doe', time: '12:45', id: '3CO-JXK' },
138-
{ img: profileImg, name: 'Jane Smith', time: '01:00', id: '3CO-JYW' },
139-
{ img: profileImg, name: 'Alice Johnson', time: '01:15', id: '3CO-JZP' },
169+
{ img: profileImg, name: 'Ramakant Sharma', time: '12:30', id: '3CO-JVY', status: 'In' },
170+
{ img: profileImg, name: 'John Doe', time: '12:45', id: '3CO-JXK', status: 'Out' },
171+
{ img: profileImg, name: 'Jane Smith', time: '01:00', id: '3CO-JYW', status: 'Leave' },
172+
{ img: profileImg, name: 'Alice Johnson', time: '01:15', id: '3CO-JZP', status: 'In' },
140173
];
141174

142175
return (
143-
<div className={`activity-attendance-page ${darkMode ? 'activity-attendance-dark-mode' : ''}`}>
144-
<div className="dashboard-container">
176+
<div
177+
className={`${styles.activityAttendancePage} ${
178+
darkMode ? styles.activityAttendanceDarkMode : ''
179+
}`}
180+
>
181+
<div className={styles.dashboardContainer}>
145182
{/* Title and Search Bar */}
146-
<div className="dashboard-title">
147-
<div className="title-text">
183+
<div className={styles.dashboardTitle}>
184+
<div className={styles.titleText}>
148185
<h2>Welcome Admin</h2>
149186
<p>Senior Admin - One Community</p>
150187
</div>
151-
<div className="search-container">
188+
<div className={styles.searchContainer}>
152189
<input
153190
type="text"
154191
placeholder="Search Students..."
155-
className="search-bar"
192+
className={styles.attendanceSearchBar}
156193
value={searchTerm}
157194
onChange={e => setSearchTerm(e.target.value)}
158195
/>
159196
</div>
160197
</div>
161198

162-
<div className="dashboard-main">
163-
<div className="stats-chart-container">
199+
<div className={styles.dashboardMain}>
200+
<div className={styles.statsChartContainer}>
164201
<StatsChart stats={statsData} />
165-
<div className="stats-grid">
202+
<div className={styles.statsGrid}>
166203
{statsData.map(stat => (
167204
<StatsCard key={stat.id} title={stat.title} value={stat.value} color={stat.color} />
168205
))}
169206
</div>
170207
</div>
171208

172209
{/* Live Student Updates */}
173-
<LiveUpdates students={students} searchTerm={searchTerm} />
210+
<LiveUpdates
211+
students={students}
212+
searchTerm={searchTerm}
213+
selectedStatus={statusFilter}
214+
onStatusChange={setStatusFilter}
215+
/>
174216
</div>
175217
</div>
176218
</div>

0 commit comments

Comments
 (0)