Skip to content

Commit ff8ed10

Browse files
author
“kebean”
committed
implement attendance
1 parent ea1e1e3 commit ff8ed10

File tree

6 files changed

+223
-23
lines changed

6 files changed

+223
-23
lines changed

package-lock.json

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

src/components/AttendanceCard.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
3+
type AttendanceCardProps = {
4+
name: string;
5+
email: string;
6+
score: 'present' | 'late' | 'absent';
7+
};
8+
9+
const scoreColors = {
10+
present: 'text-green-500',
11+
late: 'text-yellow-500',
12+
absent: 'text-red-500',
13+
};
14+
15+
const scoreIcons = {
16+
present: '✅',
17+
late: '⚠️',
18+
absent: '❌',
19+
};
20+
21+
function AttendanceCard({ name, email, score }: AttendanceCardProps) {
22+
return (
23+
<tr className="bg-white dark:bg-gray-800">
24+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">
25+
{name}
26+
</td>
27+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300">
28+
{email}
29+
</td>
30+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
31+
<span className={scoreColors[score]}>{scoreIcons[score]}</span>
32+
</td>
33+
</tr>
34+
);
35+
}
36+
37+
export default AttendanceCard;

src/components/ModalAttendance.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React from "react";
2+
3+
interface ModalProps {
4+
isVisible: boolean;
5+
onClose: () => void;
6+
}
7+
8+
const ModalAttendance: React.FC<ModalProps> = ({ isVisible, onClose }) => {
9+
const dummyTrainees = [
10+
{ name: "Ngoma Chris", attendance: 1 },
11+
{ name: "Ngoma Chris", attendance: 0 },
12+
{ name: "Ngoma Chris", attendance: 2 },
13+
];
14+
15+
if (!isVisible) return null;
16+
17+
return (
18+
<div className="fixed inset-0 bg-gray-900 bg-opacity-75 flex justify-center items-center z-50">
19+
<div className="bg-gray-800 text-white rounded-lg w-96 p-6 shadow-lg">
20+
<h2 className="text-xl font-bold text-center text-purple-400 mb-4">
21+
Take Attendance
22+
</h2>
23+
<div className="text-center">
24+
<h3 className="font-semibold text-lg">Week 4</h3>
25+
<p className="text-sm">Monday, 27th November 2023</p>
26+
</div>
27+
28+
<div className="mt-4 grid grid-cols-2 gap-2">
29+
{dummyTrainees.map((trainee, index) => (
30+
<div
31+
key={index}
32+
className="flex justify-between items-center bg-gray-700 rounded-lg p-2"
33+
>
34+
<span>{trainee.name}</span>
35+
<div className="flex items-center space-x-2">
36+
<span>[{trainee.attendance}]</span>
37+
<button type="button" className="text-red-500"></button>
38+
</div>
39+
</div>
40+
))}
41+
</div>
42+
43+
<div className="mt-4">
44+
<label htmlFor="trainee" className="block text-sm font-medium">
45+
Trainee Name
46+
</label>
47+
<div className="flex mt-1">
48+
<input
49+
id="trainee"
50+
type="text"
51+
className="bg-gray-700 text-white w-full py-2 px-3 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500"
52+
placeholder="Ngoma Chris"
53+
/>
54+
<button type="button" className="ml-2 bg-purple-500 text-white px-4 py-2 rounded-md hover:bg-purple-600">
55+
Search
56+
</button>
57+
</div>
58+
</div>
59+
60+
<div className="mt-4 flex items-center justify-center space-x-4">
61+
<button type="button" className="text-green-500">✔️</button>
62+
<button type="button" className="text-yellow-500">~</button>
63+
<button type="button" className="text-red-500"></button>
64+
</div>
65+
66+
<div className="mt-6 flex justify-between">
67+
<button
68+
type="button"
69+
onClick={onClose}
70+
className="bg-gray-500 text-white py-2 px-4 rounded-md hover:bg-gray-600"
71+
>
72+
Cancel
73+
</button>
74+
<button type="submit" className="bg-purple-500 text-white py-2 px-4 rounded-md hover:bg-purple-600">
75+
Submit
76+
</button>
77+
</div>
78+
</div>
79+
</div>
80+
);
81+
};
82+
83+
export default ModalAttendance;

src/components/Sidebar.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ function Sidebar({ style, toggle }: { style: string; toggle: () => void }) {
8181
<UserGroupIcon className="w-5" />
8282
</SideNavLink>
8383
</CheckRole>
84+
<CheckRole roles={['admin', 'coordinator']}>
85+
<SideNavLink onClick={toggle} to="/Attendance" name="Attendance">
86+
<UserGroupIcon className="w-5" />
87+
</SideNavLink>
88+
</CheckRole>
8489
{/* INVITATION */}
8590
<CheckRole roles={['admin', 'coordinator']}>
8691
<SideNavLink onClick={toggle} to="/invitation" name="Invitation">

src/containers/DashRoutes.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function DashRoutes() {
126126
<Route path="/settings" element={<Settings />} />
127127
<Route path="/performance" element={<TraineePerfomance />} />
128128
{/* <Route path="/attendance" element={<TraineeAttendance />} /> */}
129-
<Route path="/attendance" element={<Attendance />} />
129+
<Route path="/attendance" element={<Attendance/>} />
130130
<Route path="/attendance-details" element={<AttendanceDetails />} />
131131
<Route path="/teams" element={<AdminTeams />} />
132132
<Route path="/cohorts" element={<AdminCohorts />} />

src/pages/Attendance.tsx

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,99 @@
1-
import React from 'react';
2-
import CheckRole from '../utils/CheckRoles';
1+
import React, { useState } from 'react';
2+
import AttendanceCard from '../components/AttendanceCard';
3+
import Modal from '../components/ModalAttendance';
34

4-
const TraineeAttendanceTracker = React.lazy(
5-
() => import('../pages/TraineeAttendance'),
6-
);
7-
const TraineeAttendance = React.lazy(
8-
() => import('../components/TraineeAttendance'),
9-
);
5+
const dummyData = [
6+
{ name: 'Ngoma Chris', email: '[email protected]', score: 'present' },
7+
{ name: 'Ngoma Chris', email: '[email protected]', score: 'late' },
8+
{ name: 'Ngoma Chris', email: '[email protected]', score: 'absent' },
9+
{ name: 'Ngoma Chris', email: '[email protected]', score: 'present' },
10+
{ name: 'Ngoma Chris', email: '[email protected]', score: 'late' },
11+
];
12+
const AttendancePage: React.FC = () => {
13+
const [isModalOpen, setIsModalOpen] = useState(false);
14+
15+
const openModal = () => {
16+
setIsModalOpen(true);
17+
};
18+
19+
const closeModal = () => {
20+
setIsModalOpen(false);
21+
};
1022

11-
function Attendance() {
1223
return (
13-
<>
14-
<CheckRole roles={['coordinator']}>
15-
<TraineeAttendanceTracker />
16-
</CheckRole>
17-
<CheckRole roles={['trainee']}>
18-
<TraineeAttendance />
19-
</CheckRole>
20-
</>
24+
<div className="min-h-screen bg-gray-900 text-white">
25+
<div className="max-w-4xl mx-auto p-6">
26+
<h1 className="text-2xl font-bold mb-4">Attendance</h1>
27+
28+
<div className="flex justify-between items-center mb-6">
29+
<div>
30+
<label className="block text-sm font-medium">Team</label>
31+
<select className="mt-1 block w-full bg-gray-700 border border-gray-600 text-white py-2 px-3 rounded-md">
32+
<option>Steppers</option>
33+
<option>Nova</option>
34+
<option>Fighters</option>
35+
</select>
36+
</div>
37+
<button
38+
onClick={openModal}
39+
className="bg-purple-500 text-white py-2 px-4 rounded-md hover:bg-purple-600"
40+
type="submit"
41+
>
42+
Submit Attendance
43+
</button>
44+
</div>
45+
46+
<div className="flex justify-between items-center mb-4">
47+
<div className="flex space-x-4">
48+
<button className="text-sm font-medium px-3 py-1 border-b-2 border-gray-400">
49+
Phase 1
50+
</button>
51+
<button type="button" className="text-sm font-medium px-3 py-1">Phase 2</button>
52+
<button type="button" className="text-sm font-medium px-3 py-1">Phase 3</button>
53+
</div>
54+
55+
<div className="flex items-center space-x-2">
56+
<label className="text-sm">Week:</label>
57+
<select className="bg-gray-700 border border-gray-600 text-white py-2 px-3 rounded-md">
58+
<option>01</option>
59+
<option>02</option>
60+
<option>03</option>
61+
</select>
62+
</div>
63+
</div>
64+
65+
<div className="overflow-x-auto">
66+
<table className="min-w-full table-auto divide-y divide-gray-200">
67+
<thead className="bg-gray-100 dark:bg-gray-700">
68+
<tr>
69+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
70+
Names
71+
</th>
72+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
73+
Email
74+
</th>
75+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
76+
Score
77+
</th>
78+
</tr>
79+
</thead>
80+
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200">
81+
{dummyData.map((entry, index) => (
82+
<AttendanceCard
83+
key={index}
84+
name={entry.name}
85+
email={entry.email}
86+
score={entry.score as 'present' | 'late' | 'absent'}
87+
/>
88+
))}
89+
</tbody>
90+
</table>
91+
</div>
92+
93+
<Modal isVisible={isModalOpen} onClose={closeModal} />
94+
</div>
95+
</div>
2196
);
22-
}
97+
};
2398

24-
export default Attendance;
99+
export default AttendancePage;

0 commit comments

Comments
 (0)