Skip to content

Commit 2ac898e

Browse files
Merge branch 'ft-main-admin-dashboard' of https://github.com/atlp-rwanda/atlp-pulse-fn into ft-main-admin-dashboard
2 parents 559a8bb + fdc13ed commit 2ac898e

File tree

5 files changed

+283
-88
lines changed

5 files changed

+283
-88
lines changed

src/Chart/TeamChart.tsx

+97-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/* eslint-disable no-console */
2+
/* eslint-disable no-restricted-syntax */
3+
/* eslint-disable no-plusplus */
14
import React from 'react';
25
import { Line } from 'react-chartjs-2';
36
import {
@@ -23,16 +26,102 @@ ChartJS.register(
2326

2427
interface TeamChartProps {
2528
timeframe?: 'daily' | 'weekly' | 'monthly';
29+
CurrentTeam: any[];
30+
loginsbyDate: any[];
2631
}
2732

28-
function TeamChart({ timeframe = 'daily' }: TeamChartProps) {
33+
function TeamChart({
34+
timeframe = 'daily',
35+
CurrentTeam,
36+
loginsbyDate,
37+
}: TeamChartProps) {
38+
function organizeLoginData(loginData: any) {
39+
const currentDate = new Date();
40+
const currentYear = currentDate.getFullYear();
41+
function getWeekNumber(date: any) {
42+
const tempDate: any = new Date(date);
43+
tempDate.setUTCDate(
44+
tempDate.getUTCDate() + 4 - (tempDate.getUTCDay() || 7),
45+
);
46+
const yearStart: any = new Date(
47+
Date.UTC(tempDate.getUTCFullYear(), 0, 1),
48+
);
49+
return Math.ceil(((tempDate - yearStart) / 86400000 + 1) / 7);
50+
}
51+
// Initialize result arrays
52+
const weeklyData = Array(54)
53+
.fill(0)
54+
.map((_, i) => ({ week: i + 1, success: 0, failed: 0 }));
55+
const monthlyData = Array(12)
56+
.fill(0)
57+
.map((_, i) => ({ month: i + 1, success: 0, failed: 0 }));
58+
const dailyData = Array(7)
59+
.fill(0)
60+
.map((_, i) => ({ day: i, success: 0, failed: 0 }));
61+
for (const [dateString, { success, failed }] of Object.entries(
62+
loginData,
63+
) as any) {
64+
const date = new Date(dateString);
65+
const isoWeekNumber = getWeekNumber(date);
66+
const month = date.getUTCMonth();
67+
const dayOfWeek = (date.getUTCDay() + 6) % 7;
68+
const weekStart = new Date(currentDate);
69+
weekStart.setUTCDate(
70+
currentDate.getUTCDate() - currentDate.getUTCDay() + 1,
71+
);
72+
const weekEnd = new Date(weekStart);
73+
weekEnd.setUTCDate(weekStart.getUTCDate() + 6);
74+
if (date >= weekStart && date <= weekEnd) {
75+
dailyData[dayOfWeek].success += success;
76+
dailyData[dayOfWeek].failed += failed;
77+
}
78+
// Weekly data
79+
if (isoWeekNumber <= 54) {
80+
weeklyData[isoWeekNumber - 1].success += success;
81+
weeklyData[isoWeekNumber - 1].failed += failed;
82+
}
83+
// Monthly data
84+
monthlyData[month].success += success;
85+
monthlyData[month].failed += failed;
86+
}
87+
const weekDays = [
88+
'Monday',
89+
'Tuesday',
90+
'Wednesday',
91+
'Thursday',
92+
'Friday',
93+
'Saturday',
94+
'Sunday',
95+
];
96+
const currentWeekData = dailyData.map((data, index) => ({
97+
day: weekDays[index],
98+
success: data.success,
99+
failed: data.failed,
100+
}));
101+
return {
102+
currentWeek: currentWeekData,
103+
weekly: weeklyData,
104+
monthly: monthlyData.map((data, index) => ({
105+
month: new Date(0, index).toLocaleString('en', { month: 'long' }),
106+
success: data.success,
107+
failed: data.failed,
108+
})),
109+
};
110+
}
111+
112+
const organizedData = organizeLoginData(loginsbyDate);
113+
114+
const weeklyDataset = organizedData.weekly
115+
.filter((_, index) => index % 3 === 0)
116+
.map((item) => item.success);
117+
29118
const chartData = {
30119
daily: {
31120
labels: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
32121
datasets: [
33122
{
34-
label: 'Andela',
35-
data: [1, 3, 0, 2, 1, 3, 2],
123+
label: CurrentTeam[0].name,
124+
data: organizedData.currentWeek.map((item: any) => item.success),
36125
fill: false,
37126
borderColor: '#4F46E5',
38127
tension: 0.4,
@@ -62,22 +151,22 @@ function TeamChart({ timeframe = 'daily' }: TeamChartProps) {
62151
],
63152
datasets: [
64153
{
65-
label: 'Andela',
66-
data: [1, 3, 0, 2, 1, 3, 2, 0, 2, 1, 3, 0, 2, 1, 4, 1, 2, 4],
154+
label: CurrentTeam[0].name,
155+
data: weeklyDataset,
67156
fill: false,
68157
borderColor: '#4F46E5',
69158
tension: 0.4,
70159
},
71160
],
72161
},
73162
monthly: {
74-
labels: Array.from({ length: 31 }, (_, i) =>
163+
labels: Array.from({ length: 12 }, (_, i) =>
75164
String(i + 1).padStart(2, '0'),
76165
),
77166
datasets: [
78167
{
79-
label: 'Andela',
80-
data: Array.from({ length: 31 }, () => Math.floor(Math.random() * 8)),
168+
label: CurrentTeam[0].name,
169+
data: organizedData.monthly.map((item: any) => item.success),
81170
fill: false,
82171
borderColor: '#4F46E5',
83172
tension: 0.4,

src/components/AdminDashboardTable.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ function DashboardTableDesign() {
6868
<TeamDetailsModal
6969
isOpen={isModalOpen}
7070
onClose={() => setIsModalOpen(false)}
71-
teamData={selectedTeam}
71+
selectedteam={selectedTeam}
72+
Teams={TeamsData?.getAllTeams}
7273
/>
7374
</div>
7475
);

src/components/AdminTeamDetails.tsx

+106-20
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import React, { useState } from 'react';
22
import { FaAngleDown } from 'react-icons/fa6';
33
import TeamChart from '../Chart/TeamChart';
44
import ProgressBar from '../Chart/ProgressBar';
5+
import UsersChart from '../Chart/usersChart';
56

67
interface TeamData {
78
ttlName?: string;
8-
team?: string;
9+
teams?: string;
910
organization?: string;
1011
program?: string;
1112
phase?: string;
@@ -18,7 +19,8 @@ interface TeamData {
1819
interface TeamDetailsModalProps {
1920
isOpen: boolean;
2021
onClose: () => void;
21-
teamData: TeamData | null;
22+
selectedteam: TeamData | null;
23+
Teams?: any;
2224
}
2325

2426
// Add this near the top of your TeamDetailsModal component
@@ -43,7 +45,8 @@ const loginStats = {
4345
function TeamDetailsModal({
4446
isOpen,
4547
onClose,
46-
teamData,
48+
selectedteam,
49+
Teams,
4750
}: TeamDetailsModalProps) {
4851
const [activeTab, setActiveTab] = useState<'overview' | 'logins'>('overview');
4952
const [timeframe, setTimeframe] = useState<'daily' | 'weekly' | 'monthly'>(
@@ -56,6 +59,75 @@ function TeamDetailsModal({
5659

5760
if (!isOpen) return null;
5861

62+
const CurrentTeam = Teams?.filter(
63+
(items: any) => items?.name === selectedteam?.teams,
64+
);
65+
66+
const average =
67+
(parseInt(CurrentTeam[0]?.avgRatings?.quality, 2) +
68+
parseInt(CurrentTeam[0]?.avgRatings?.quantity, 2) +
69+
parseInt(CurrentTeam[0]?.avgRatings?.professional_Skills, 2)) /
70+
3;
71+
72+
const activeMembers = CurrentTeam[0]?.members.filter(
73+
(item: any) => item.status.status !== 'suspended',
74+
);
75+
const droppedMembers = CurrentTeam[0]?.members.filter(
76+
(item: any) => item.status.status === 'suspended',
77+
);
78+
function mapLoginsByDate(team: any) {
79+
if (!team || !Array.isArray(team[0].members)) {
80+
throw new Error('Invalid team object');
81+
}
82+
const loginCounts: any = {};
83+
team[0].members.forEach((member: any) => {
84+
const activities = member.profile?.activity;
85+
86+
if (Array.isArray(activities)) {
87+
activities.forEach((activity) => {
88+
const rawDate = activity.date;
89+
const timestamp = parseInt(rawDate, 10);
90+
if (!Number.isNaN(timestamp)) {
91+
const loginDate = new Date(timestamp).toISOString().split('T')[0];
92+
if (!loginCounts[loginDate]) {
93+
loginCounts[loginDate] = { success: 0, failed: 0 };
94+
}
95+
if (activity.failed === 1) {
96+
loginCounts[loginDate].failed += 1;
97+
} else {
98+
loginCounts[loginDate].success += 1;
99+
}
100+
}
101+
});
102+
}
103+
});
104+
return loginCounts;
105+
}
106+
const loginsbyDate = mapLoginsByDate(CurrentTeam);
107+
const orgName = localStorage.getItem('orgName');
108+
109+
function calculateLoginPercentages(data: any) {
110+
let totalSuccess = 0;
111+
let totalFailed = 0;
112+
113+
// Sum up all successes and failures
114+
Object.values(data).forEach(({ success, failed }: any) => {
115+
totalSuccess += success;
116+
totalFailed += failed;
117+
});
118+
119+
// Calculate percentages
120+
const total = totalSuccess + totalFailed;
121+
const successPercentage = total > 0 ? (totalSuccess / total) * 100 : 0;
122+
const failedPercentage = total > 0 ? (totalFailed / total) * 100 : 0;
123+
124+
return {
125+
successPercentage: successPercentage.toFixed(2),
126+
failedPercentage: failedPercentage.toFixed(2),
127+
totalLogins: total,
128+
};
129+
}
130+
59131
return (
60132
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 backdrop-blur-sm">
61133
<div
@@ -101,15 +173,18 @@ function TeamDetailsModal({
101173
<div>
102174
<div className="grid grid-cols-2 gap-4">
103175
{[
104-
['TTL Name', teamData?.ttlName || 'Sostene'],
105-
['Team Name', teamData?.team || 'Team Name'],
176+
['TTL Name', CurrentTeam[0]?.ttl?.profile?.name || 'Sostene'],
177+
['Team Name', selectedteam?.teams || 'Team Name'],
178+
['Organization', selectedteam?.organization || orgName],
179+
[
180+
'Program',
181+
CurrentTeam[0]?.cohort?.program?.name || 'Program Name',
182+
],
106183
[
107-
'Organization',
108-
teamData?.organization || 'Organization Name',
184+
'Phase',
185+
CurrentTeam[0]?.cohort?.phase?.name || 'Current Phase',
109186
],
110-
['Program', teamData?.program || 'Program Name'],
111-
['Phase', teamData?.phase || 'Current Phase'],
112-
['Cohort', teamData?.cohort || 'Current Cohort'],
187+
['Cohort', CurrentTeam[0]?.cohort.name || 'Current Cohort'],
113188
].map(([label, value], idx) => (
114189
// eslint-disable-next-line react/no-array-index-key
115190
<div key={idx} className="space-y-2">
@@ -132,15 +207,15 @@ function TeamDetailsModal({
132207
Active Members
133208
</p>
134209
<p className="text-xl font-semibold text-green-600 dark:text-green-400">
135-
{teamData?.activeUsers || '0'}
210+
{activeMembers?.length || '0'}
136211
</p>
137212
</div>
138213
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
139214
<p className="text-sm text-gray-600 dark:text-gray-400">
140215
Dropped Members
141216
</p>
142217
<p className="text-xl font-semibold text-red-600 dark:text-red-400">
143-
{teamData?.droppedUsers || '0'}
218+
{droppedMembers?.length || '0'}
144219
</p>
145220
</div>
146221
</div>
@@ -162,13 +237,14 @@ function TeamDetailsModal({
162237
{showAttendanceSummary && (
163238
<div className="absolute z-10 bg-white dark:bg-gray-800 p-4 rounded-lg shadow-lg w-[200px] border border-gray-200 dark:border-gray-700">
164239
<p className="text-gray-800 dark:text-gray-200">
165-
Quality: 1.5
240+
Quality: {CurrentTeam[0]?.avgRatings?.quality || 0}
166241
</p>
167242
<p className="text-gray-800 dark:text-gray-200">
168-
Quantity: 2.3
243+
Quantity: {CurrentTeam[0]?.avgRatings?.quality || 0}
169244
</p>
170245
<p className="text-gray-800 dark:text-gray-200">
171-
Professionalism: 3.1
246+
Professionalism:{' '}
247+
{CurrentTeam[0]?.avgRatings?.professional_Skills || 0}
172248
</p>
173249
</div>
174250
)}
@@ -180,7 +256,7 @@ function TeamDetailsModal({
180256
</label>
181257
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
182258
<p className="text-lg font-semibold text-blue-600 dark:text-blue-400">
183-
{teamData?.rating || '4.5'} / 5.0
259+
{average || '0'} / 5.0
184260
</p>
185261
</div>
186262
</div>
@@ -231,20 +307,30 @@ function TeamDetailsModal({
231307
Logins Attempt Status
232308
</h3>
233309
<ProgressBar
234-
passedPercentage={loginStats[timeframe].passed}
235-
failedPercentage={loginStats[timeframe].failed}
310+
passedPercentage={parseInt(
311+
calculateLoginPercentages(loginsbyDate).successPercentage,
312+
10,
313+
)}
314+
failedPercentage={parseInt(
315+
calculateLoginPercentages(loginsbyDate).failedPercentage,
316+
10,
317+
)}
236318
/>
237319
</div>
238320
<p className="mt-4 ml-[12%]">
239321
Total Logins:{' '}
240322
<span className="font-bold text-primary">
241323
{' '}
242-
{loginStats[timeframe].total}
324+
{calculateLoginPercentages(loginsbyDate).totalLogins}
243325
</span>
244326
</p>
245327
</div>
246328

247-
<TeamChart timeframe={timeframe} />
329+
<TeamChart
330+
timeframe={timeframe}
331+
CurrentTeam={CurrentTeam}
332+
loginsbyDate={loginsbyDate}
333+
/>
248334
</div>
249335
)}
250336
</div>

0 commit comments

Comments
 (0)