Skip to content

Commit 0cd551b

Browse files
RWEMAREMYTuyisenge2Bananayososteneshebz2023JacquelineTuyisenge
authored
main admin dashboard (#626)
* main admin dashboard * new piechart and stats updated * new piechart and stats updated * new piechart and stats updated * ft-admin-dashboard-can-vieww-table-teams * new piechart and stats updated * Bar Chart changes according to teams workrate * user Growth overtime chart * user Growth overtime chart * ft main admin dashboared * ft(626): Admin Dashboard * ft(626): testing Admin Dashboard --------- Co-authored-by: Tuyisenge2 <[email protected]> Co-authored-by: Bananayosostene <[email protected]> Co-authored-by: shebz2023 <[email protected]> Co-authored-by: JacquelineTuyisenge <[email protected]>
1 parent 0c9f0df commit 0cd551b

File tree

74 files changed

+3231
-1204
lines changed

Some content is hidden

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

74 files changed

+3231
-1204
lines changed

Diff for: package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"cloudinary-react": "^1.8.1",
6666
"crypto-browserify": "^3.12.0",
6767
"date-fns": "^2.30.0",
68+
"dayjs": "^1.11.13",
6869
"dotenv": "^16.3.1",
6970
"express": "^4.18.2",
7071
"file-loader": "^6.2.0",

Diff for: public/locales/fr/translation.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -473,4 +473,4 @@
473473
"Director": "Directeur",
474474
"Andela": "Andela",
475475
"University of Rwanda": "Université du Rwanda"
476-
}
476+
}

Diff for: public/locales/kn/translation.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -462,4 +462,4 @@
462462
"Director": "Umuyobozi mukuru",
463463
"Andela": "Andela",
464464
"University of Rwanda": "Kaminuza y' u Rwanda"
465-
}
465+
}

Diff for: src/Chart/BarChart.tsx

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import React from 'react';
2+
import { Bar } from 'react-chartjs-2';
3+
import {
4+
Chart as ChartJS,
5+
CategoryScale,
6+
LinearScale,
7+
BarElement,
8+
Title,
9+
Tooltip,
10+
Legend,
11+
} from 'chart.js';
12+
import { useQuery } from '@apollo/client';
13+
import { GET_ALL_TEAMS } from '../queries/team.queries';
14+
import { FETCH_ALL_RATINGS } from '../queries/ratings.queries';
15+
16+
ChartJS.register(
17+
CategoryScale,
18+
LinearScale,
19+
BarElement,
20+
Title,
21+
Tooltip,
22+
Legend,
23+
);
24+
25+
interface Props {}
26+
27+
// eslint-disable-next-line react/function-component-definition
28+
const BarChart: React.FC<Props> = () => {
29+
const orgToken = localStorage.getItem('orgToken');
30+
const { data, loading, error } = useQuery(GET_ALL_TEAMS, {
31+
variables: {
32+
orgToken,
33+
},
34+
fetchPolicy: 'network-only',
35+
});
36+
37+
const {
38+
data: ratingsData,
39+
loading: ratingsLoading,
40+
error: ratingsError,
41+
} = useQuery(FETCH_ALL_RATINGS, {
42+
variables: {
43+
orgToken,
44+
},
45+
fetchPolicy: 'network-only',
46+
});
47+
48+
if (loading) return <p>Loading...</p>;
49+
if (error) return <p>Error: {error.message}</p>;
50+
51+
if (ratingsLoading) return <p>Loading ratings...</p>;
52+
if (ratingsError) return <p>Error loading ratings: {ratingsError.message}</p>;
53+
54+
const teamNames = data?.getAllTeams?.map(
55+
(team: { name: string }) => team.name,
56+
);
57+
const ratingsArray = ratingsData?.fetchAllRatings || [];
58+
59+
const professionalismData = ratingsArray.map(
60+
(rating: { professional_Skills: string }) =>
61+
parseFloat(rating.professional_Skills),
62+
);
63+
const qualityData = ratingsArray.map((rating: { quality: string }) =>
64+
parseFloat(rating.quality),
65+
);
66+
const quantityData = ratingsArray.map((rating: { quantity: string }) =>
67+
parseFloat(rating.quantity),
68+
);
69+
if (!teamNames || teamNames.length === 0) {
70+
return <p>No team data available.</p>;
71+
}
72+
73+
const datas = {
74+
labels: teamNames,
75+
datasets: [
76+
{
77+
label: 'Professionalism',
78+
data: professionalismData,
79+
backgroundColor: '#5A6ACF',
80+
borderRadius: 20,
81+
barThickness: 14,
82+
},
83+
{
84+
label: 'Quality',
85+
data: qualityData,
86+
backgroundColor: '#fcffa4',
87+
borderRadius: 20,
88+
barThickness: 14,
89+
},
90+
{
91+
label: 'Quantity',
92+
data: quantityData,
93+
backgroundColor: '#9f5233',
94+
borderRadius: 20,
95+
barThickness: 14,
96+
},
97+
],
98+
};
99+
100+
return (
101+
<div className="w-full h-[300px]">
102+
<Bar data={datas} options={{ responsive: true }} className="-ml-2" />
103+
</div>
104+
);
105+
};
106+
107+
export default BarChart;

Diff for: src/Chart/LineChart.tsx

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { useQuery } from '@apollo/client';
2+
import React, { useEffect, useState } from 'react';
3+
import {
4+
LineChart,
5+
Line,
6+
CartesianGrid,
7+
XAxis,
8+
YAxis,
9+
Tooltip,
10+
Legend,
11+
ResponsiveContainer,
12+
} from 'recharts';
13+
import dayjs from 'dayjs';
14+
import isoWeek from 'dayjs/plugin/isoWeek';
15+
import GET_ROLE_QUERY from '../containers/admin-dashBoard/GetRolesQuery';
16+
17+
dayjs.extend(isoWeek);
18+
19+
function UserGrowth() {
20+
const [orgToken, setOrgToken] = useState<string | null>(null);
21+
const [period, setPeriod] = useState<'daily' | 'weekly' | 'monthly'>('daily');
22+
const [selectedYear, setSelectedYear] = useState<string>(
23+
dayjs().year().toString(),
24+
);
25+
26+
useEffect(() => {
27+
const token = localStorage.getItem('orgToken');
28+
setOrgToken(token);
29+
}, []);
30+
31+
const { data, loading, error } = useQuery(GET_ROLE_QUERY, {
32+
variables: { orgToken },
33+
skip: !orgToken,
34+
});
35+
36+
if (loading) {
37+
return <p>Loading...</p>;
38+
}
39+
40+
if (error) {
41+
return <p>Error: {error.message}</p>;
42+
}
43+
44+
const users = data?.getAllUsers || [];
45+
46+
const userGrowth = (users: any[]) => {
47+
const growthData: { [key: string]: number } = {};
48+
49+
users.forEach((user: any) => {
50+
const timestamp = user.createdAt || user.updatedAt;
51+
if (timestamp) {
52+
const date = dayjs(parseInt(timestamp, 10));
53+
const year = date.year().toString();
54+
55+
if (year === selectedYear) {
56+
let periodKey = '';
57+
if (period === 'daily') {
58+
periodKey = date.format('YYYY-MM-DD');
59+
} else if (period === 'weekly') {
60+
periodKey = `${date.year()}-W${date.isoWeek()}`;
61+
} else if (period === 'monthly') {
62+
periodKey = date.format('YYYY-MM');
63+
}
64+
65+
growthData[periodKey] = (growthData[periodKey] || 0) + 1;
66+
}
67+
}
68+
});
69+
70+
const allMonths = Array.from({ length: 12 }, (_, i) =>
71+
dayjs().month(i).format('YYYY-MM'),
72+
);
73+
allMonths.forEach((month) => {
74+
if (!growthData[month]) {
75+
growthData[month] = 0;
76+
}
77+
});
78+
79+
return Object.entries(growthData).map(([date, count]) => ({ date, count }));
80+
};
81+
82+
const growthData = userGrowth(users);
83+
84+
return (
85+
<div className="px-9 py-10 bg-tertiary dark:bg-dark-bg">
86+
<h3 className="text-3xl text-center text-grey-600 font-bold">
87+
User Growth
88+
</h3>
89+
90+
<div className="flex flex-wrap flex-row justify-between">
91+
<div>
92+
<label
93+
htmlFor="year"
94+
style={{ color: '#bdbdbd', marginRight: '10px' }}
95+
>
96+
Year:
97+
</label>
98+
<select
99+
id="year"
100+
value={selectedYear}
101+
onChange={(e) => setSelectedYear(e.target.value)}
102+
style={{
103+
padding: '5px 10px',
104+
borderColor: '#ccc',
105+
borderRadius: '4px',
106+
color: '#bdbdbd',
107+
}}
108+
>
109+
{Array.from({ length: 5 }).map((_, index) => {
110+
const year = dayjs().year() - index;
111+
return (
112+
<option key={year} value={year.toString()}>
113+
{year}
114+
</option>
115+
);
116+
})}
117+
</select>
118+
</div>
119+
120+
<div>
121+
<label
122+
htmlFor="period"
123+
style={{ color: '#bdbdbd', marginRight: '10px' }}
124+
>
125+
Period:
126+
</label>
127+
<select
128+
id="period"
129+
value={period}
130+
onChange={(e) =>
131+
setPeriod(e.target.value as 'daily' | 'weekly' | 'monthly')
132+
}
133+
style={{
134+
padding: '5px 10px',
135+
borderColor: '#ccc',
136+
borderRadius: '4px',
137+
color: '#bdbdbd',
138+
}}
139+
>
140+
<option value="daily">Daily</option>
141+
<option value="weekly">Weekly</option>
142+
<option value="monthly">Monthly</option>
143+
</select>
144+
</div>
145+
</div>
146+
147+
{growthData.length === 0 ? (
148+
<p>No data available</p>
149+
) : (
150+
<div>
151+
<ResponsiveContainer width="100%" height={400}>
152+
<LineChart
153+
data={growthData}
154+
margin={{ top: 20, right: 30, left: 20, bottom: 20 }}
155+
>
156+
<CartesianGrid strokeDasharray="3 3" />
157+
<XAxis
158+
dataKey="date"
159+
tickFormatter={(str) => {
160+
if (period === 'daily') {
161+
return dayjs(str).format('MMM DD');
162+
}
163+
if (period === 'weekly') {
164+
return str.split('-')[1];
165+
}
166+
if (period === 'monthly') {
167+
return dayjs(str).format('MMM YYYY');
168+
}
169+
return str;
170+
}}
171+
/>
172+
<YAxis
173+
label={{
174+
value: 'USERS',
175+
angle: -90,
176+
position: 'insideLeft',
177+
style: { fontSize: 14, fill: '#bdbdbd' },
178+
}}
179+
/>
180+
<Tooltip />
181+
<Legend />
182+
<Line
183+
type="monotone"
184+
dataKey="count"
185+
stroke="#8884d8"
186+
name="User(s)"
187+
dot={false}
188+
/>
189+
</LineChart>
190+
</ResponsiveContainer>
191+
</div>
192+
)}
193+
</div>
194+
);
195+
}
196+
197+
export default UserGrowth;

0 commit comments

Comments
 (0)