Skip to content

Commit ed52623

Browse files
committed
(fix): add coordinator dashboard page
1 parent 2e47213 commit ed52623

File tree

5 files changed

+1836
-2
lines changed

5 files changed

+1836
-2
lines changed

src/components/DataTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function DataTable({ data, columns, title, loading, className }: TableData) {
6868

6969
return (
7070
<div
71-
className={`relative font-serif bg-indigo-100 dark:bg-dark-bg shadow-lg h-fit px-5 py-8 rounded-md w-full lg:w-auto mx-auto mb-10 ${className}`}
71+
className={`relative font-serif bg-indigo-100 dark:bg-dark-bg shadow-lg h-fit px-5 py-8 rounded-md w-[100%] overflow-scroll "lg:ml-60 mx-auto"} mb-10`}
7272
>
7373
<div className="flex flex-col md:flex-row items-center justify-between pb-6 space-y-4 md:space-y-0">
7474
<div>

src/pages/CoordinatorDashboard.tsx

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/* eslint-disable jsx-a11y/no-redundant-roles */
2+
3+
import React, { useContext, useEffect, useState } from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
// eslint-disable-next-line import/no-useless-path-segments
6+
import { Link } from 'react-router-dom';
7+
import { useQuery } from '@apollo/client';
8+
import { use } from 'i18next';
9+
import { UserContext } from '../hook/useAuth';
10+
import AdminTraineeDashboard from './AdminTraineeDashboard';
11+
import CoordinatorTraineeDashboard from './coordinatorTraineDashboard';
12+
import { FETCH_ALL_RATINGS } from '../queries/ratings.queries';
13+
import InvitationCardSkeleton from '../Skeletons/InvitationCardSkeleton';
14+
15+
interface Trainee {
16+
user: {
17+
id: string;
18+
email: string;
19+
profile: {
20+
avatar: string;
21+
firstName: string;
22+
};
23+
};
24+
}
25+
26+
function CoordinatorDashboard() {
27+
const organizationToken = localStorage.getItem('orgToken');
28+
29+
const { user } = useContext(UserContext);
30+
const { t }: any = useTranslation();
31+
32+
const { loading, error, data } = useQuery(FETCH_ALL_RATINGS, {
33+
variables: { orgToken: organizationToken },
34+
skip: !organizationToken,
35+
});
36+
37+
if (loading) {
38+
return (
39+
<div className="flex flex-col items-center w-[80%] mx-auto mdl:w-[100%] mdl:justify-between gap-5 xmd:flex-row xmd:flex-wrap bg-light-bg dark:bg-dark-frame-bg py-4 font-serif">
40+
<InvitationCardSkeleton />
41+
<InvitationCardSkeleton />
42+
<InvitationCardSkeleton />
43+
</div>
44+
);
45+
}
46+
if (error) {
47+
return <p>Error loading data</p>;
48+
}
49+
50+
const sortedRatings = [...(data?.fetchAllRatings || [])]
51+
.sort((a, b) => b.average - a.average)
52+
.filter(
53+
(rating, index, self) =>
54+
index === self.findIndex((r) => r.user.id === rating.user.id),
55+
);
56+
const topThree = sortedRatings.slice(0, 3);
57+
58+
const lastThree = [];
59+
const seenIds = new Set();
60+
61+
// eslint-disable-next-line no-plusplus
62+
for (let i = sortedRatings.length - 1; i >= 0 && lastThree.length < 3; i--) {
63+
// eslint-disable-line no-plusplus
64+
const rating = sortedRatings[i];
65+
if (!seenIds.has(rating.user.id)) {
66+
seenIds.add(rating.user.id);
67+
lastThree.push(rating);
68+
}
69+
}
70+
71+
const ratings = data?.fetchAllRatings || [];
72+
73+
const cohorts = ratings.reduce((acc: any, rating: any) => {
74+
const cohortName = rating.cohort.name;
75+
const averageRating = parseFloat(rating.average);
76+
77+
if (!acc[cohortName]) {
78+
acc[cohortName] = {
79+
totalRating: 0,
80+
traineeCount: 0,
81+
coordinator: rating.coordinator,
82+
};
83+
}
84+
acc[cohortName].totalRating += averageRating;
85+
acc[cohortName].traineeCount += 1;
86+
87+
return acc;
88+
}, {});
89+
90+
const cohortPerformances = Object.keys(cohorts).map((cohortName) => ({
91+
cohortName,
92+
averageRating:
93+
cohorts[cohortName].totalRating / cohorts[cohortName].traineeCount,
94+
coordinator: cohorts[cohortName].coordinator,
95+
}));
96+
97+
const topCohorts = cohortPerformances
98+
.sort((a, b) => b.averageRating - a.averageRating)
99+
.slice(0, 3);
100+
101+
return (
102+
<div className="flex flex-col grow bg-light-bg dark:bg-dark-frame-bg">
103+
<div className="flex flex-row">
104+
<div className="flex flex-col items-center w-[80%] mx-auto mdl:w-[100%] mdl:justify-between gap-5 xmd:flex-row xmd:flex-wrap bg-light-bg dark:bg-dark-frame-bg py-4 font-serif">
105+
<div className="w-[20rem] p-4 bg-indigo-100 border border-gray-200 rounded-lg shadow sm:p-8 dark:bg-gray-800 dark:border-gray-700">
106+
<div className="flex items-center justify-between mb-4 h-8">
107+
<h5 className="text-xl font-bold leading-none text-gray-900 dark:text-white">
108+
Top Performing Cohorts
109+
</h5>
110+
</div>
111+
<div className="flow-root">
112+
<ul
113+
role="list"
114+
className="divide-y h-[12.5rem] divide-gray-200 dark:divide-gray-700"
115+
>
116+
{topCohorts.map((cohort) => (
117+
<li
118+
key={cohort.cohortName}
119+
className="py-3 sm:py-4 border-t border-t-black dark:border-t-gray-200"
120+
>
121+
<div className="flex items-center">
122+
<div className="flex-shrink-0">
123+
<div className="w-8 h-8 rounded-full bg-white text-black flex items-center justify-center">
124+
{cohort.cohortName.charAt(0).toUpperCase()}
125+
</div>
126+
</div>
127+
<div className="flex-1 min-w-0 ms-4">
128+
<p className="text-sm font-medium text-gray-900 truncate dark:text-white">
129+
{cohort.cohortName}
130+
</p>
131+
<p className="text-sm text-gray-500 truncate dark:text-gray-400">
132+
Average Rating: {cohort.averageRating.toFixed(2)}
133+
</p>
134+
</div>
135+
</div>
136+
</li>
137+
))}
138+
</ul>
139+
</div>
140+
</div>
141+
142+
<div className="w-[20rem] p-4 bg-indigo-100 border border-gray-200 rounded-lg shadow sm:p-8 dark:bg-gray-800 dark:border-gray-700">
143+
<div className="flex items-center justify-between mb-4 h-8">
144+
<h5 className="text-xl font-bold leading-none text-gray-900 dark:text-white">
145+
Top Performing Trainees
146+
</h5>
147+
</div>
148+
<div className="flow-root">
149+
<ul
150+
role="list"
151+
className="divide-y h-[12.5rem] divide-gray-200 dark:divide-gray-700"
152+
>
153+
{topThree.map((rating) => (
154+
<li
155+
key={rating.user.id}
156+
className="py-3 sm:py-4 border-t border-t-black dark:border-t-gray-200"
157+
>
158+
<div className="flex items-center">
159+
<div className="flex-shrink-0">
160+
<div className="w-8 h-8 rounded-full bg-white text-black flex items-center justify-center">
161+
<p className="text-lg">
162+
{rating.user.profile?.firstName
163+
.charAt(0)
164+
.toUpperCase()}
165+
</p>
166+
</div>
167+
</div>
168+
<div className="flex-1 min-w-0 ms-4">
169+
<p className="text-sm font-medium text-gray-900 truncate dark:text-white">
170+
{`${rating.user.profile?.firstName || ''} ${
171+
rating.user.profile?.lastName || ''
172+
}`}
173+
</p>
174+
<p className="text-sm text-gray-500 truncate dark:text-gray-400">
175+
sprint: {rating.sprint}
176+
</p>
177+
</div>
178+
</div>
179+
</li>
180+
))}
181+
</ul>
182+
</div>
183+
</div>
184+
185+
<div className="w-[20rem] p-4 bg-indigo-100 border border-gray-200 rounded-lg shadow sm:p-8 dark:bg-gray-800 dark:border-gray-700">
186+
<div className="flex items-center justify-between mb-4 h-8">
187+
<h5 className="text-xl font-bold leading-none text-gray-900 dark:text-white">
188+
Last Performing Trainees
189+
</h5>
190+
</div>
191+
<div className="flow-root">
192+
<ul
193+
role="list"
194+
className="divide-y h-[12.5rem] divide-gray-200 dark:divide-gray-700"
195+
>
196+
{lastThree.map((rating) => (
197+
<li
198+
key={rating.user.id}
199+
className="py-3 sm:py-4 border-t border-t-black dark:border-t-gray-200"
200+
>
201+
<div className="flex items-center">
202+
<div className="flex-shrink-0">
203+
<div className="w-8 h-8 rounded-full bg-white text-black flex items-center justify-center">
204+
<p className="text-lg">
205+
{rating.user.profile?.firstName
206+
.charAt(0)
207+
.toUpperCase()}
208+
</p>
209+
</div>
210+
</div>
211+
<div className="flex-1 min-w-0 ms-4">
212+
<p className="text-sm font-medium text-gray-900 truncate dark:text-white">
213+
{`${rating.user.profile?.firstName || ''} ${
214+
rating.user.profile?.lastName || ''
215+
}`}
216+
</p>
217+
<p className="text-sm text-gray-500 truncate dark:text-gray-400">
218+
sprint: {rating.sprint}
219+
</p>
220+
</div>
221+
</div>
222+
</li>
223+
))}
224+
</ul>
225+
</div>
226+
</div>
227+
</div>
228+
</div>
229+
<div className=" fex-row justify-ceter w-[100%] pb8">
230+
<CoordinatorTraineeDashboard />
231+
</div>
232+
</div>
233+
);
234+
}
235+
236+
export default CoordinatorDashboard;

src/pages/Dashboard.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SupAdDashboard from './SupAdDashboard';
44
import AdminDashboard from './AdminDashboard';
55
import TraineeDashboard from './TraineeDashboard';
66
import ManagerCard from '../components/ManagerCard';
7+
import CoordinatorDashboard from './CoordinatorDashboard';
78

89
export function Dashboard() {
910
return (
@@ -21,7 +22,7 @@ export function Dashboard() {
2122
<TraineeDashboard />
2223
</CheckRole>
2324
<CheckRole roles={['coordinator']}>
24-
<AdminDashboard />
25+
<CoordinatorDashboard />
2526
</CheckRole>
2627
<CheckRole roles={['manager']}>
2728
<ManagerCard />

0 commit comments

Comments
 (0)