Employee Attendance Management System in React
Employee Attendance Management System in React
js
Here's a comprehensive implementation of an Employee Attendance
Management System using React.js. This system includes features like
employee registration, attendance marking, reporting, and admin
dashboard.
System Architecture
1. Frontend (React.js)
Components structure
State management (Context API or Redux)
Routing (React Router)
UI Framework (Material-UI or Bootstrap)
API integration (Axios)
Implementation
1
2. Folder Structure
Copy
Download
src/
├── components/
│ ├── AttendanceCalendar.js
│ ├── AttendanceForm.js
│ ├── Dashboard.js
│ ├── EmployeeForm.js
│ ├── EmployeeList.js
│ ├── Login.js
│ ├── Navbar.js
│ ├── Reports.js
│ └── Settings.js
├── context/
│ └── AuthContext.js
├── pages/
│ ├── AdminPage.js
│ ├── EmployeePage.js
│ └── HomePage.js
├── services/
│ ├── attendanceService.js
│ └── employeeService.js
├── App.js
├── App.css
├── index.js
└── index.css
src/context/AuthContext.js
jsx
Copy
Download
2
import { createContext, useState, useEffect } from 'react';
useEffect(() => {
// Check if user is logged in from localStorage
const storedUser = localStorage.getItem('user');
if (storedUser) {
setUser(JSON.parse(storedUser));
}
setLoading(false);
}, []);
return (
<AuthContext.Provider value={{ user, login, logout, loading }}>
{children}
</AuthContext.Provider>
);
};
src/components/Navbar.js
3
jsx
Copy
Download
import React, { useContext } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { AuthContext } from '../context/AuthContext';
import { AppBar, Toolbar, Typography, Button, Box } from
'@mui/material';
return (
<AppBar position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1
}}>
Attendance System
</Typography>
<Box sx={{ display: 'flex', gap: 2 }}>
{user && (
<>
<Button color="inherit" component={Link} to="/dashboard">
Dashboard
</Button>
{user.role === 'admin' && (
<Button color="inherit" component={Link} to="/employees">
Employees
</Button>
4
)}
<Button color="inherit" component={Link} to="/reports">
Reports
</Button>
<Button color="inherit" onClick={handleLogout}>
Logout
</Button>
</>
)}
</Box>
</Toolbar>
</AppBar>
);
};
src/components/Login.js
jsx
Copy
Download
import React, { useState, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { AuthContext } from '../context/AuthContext';
import { TextField, Button, Container, Typography, Box } from
'@mui/material';
5
const handleSubmit = (e) => {
e.preventDefault();
setError('');
return (
<Container maxWidth="xs">
<Box sx={{ mt: 8, display: 'flex', flexDirection: 'column',
alignItems: 'center' }}>
<Typography variant="h5" component="h1" gutterBottom>
Attendance System Login
</Typography>
6
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1
}}>
<TextField
margin="normal"
required
fullWidth
label="Email Address"
autoComplete="email"
autoFocus
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<TextField
margin="normal"
required
fullWidth
label="Password"
type="password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{error && (
<Typography color="error" variant="body2">
{error}
</Typography>
)}
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Sign In
</Button>
7
</Box>
</Box>
</Container>
);
};
src/components/Dashboard.js
jsx
Copy
Download
import React, { useContext } from 'react';
import { AuthContext } from '../context/AuthContext';
import { Box, Typography, Card, CardContent, Grid } from
'@mui/material';
import AttendanceCalendar from './AttendanceCalendar';
return (
<Box sx={{ p: 3 }}>
<Typography variant="h4" gutterBottom>
Welcome, {user?.name}
</Typography>
8
<AttendanceCalendar />
</CardContent>
</Card>
</Grid>
src/components/AttendanceCalendar.js
jsx
Copy
Download
9
import React, { useState } from 'react';
import { format, startOfMonth, endOfMonth, eachDayOfInterval,
isToday } from 'date-fns';
import { Box, Typography, Button, Grid, Paper } from '@mui/material';
return (
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2
}}>
<Button onClick={handlePrevMonth}>Previous</Button>
<Typography variant="h6">
{format(currentDate, 'MMMM yyyy')}
</Typography>
<Button onClick={handleNextMonth}>Next</Button>
</Box>
{isToday(currentDate) && (
<Box sx={{ mb: 2 }}>
<Typography variant="subtitle1" gutterBottom>
Mark Today's Attendance
</Typography>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button
variant="contained"
color="success"
onClick={() => handleMarkAttendance('present')}
>
Present
</Button>
<Button
variant="contained"
color="error"
onClick={() => handleMarkAttendance('absent')}
11
>
Absent
</Button>
<Button
variant="contained"
color="warning"
onClick={() => handleMarkAttendance('half-day')}
>
Half Day
</Button>
<Button
variant="contained"
color="info"
onClick={() => handleMarkAttendance('leave')}
>
Leave
</Button>
</Box>
</Box>
)}
{daysInMonth.map(day => {
const dayKey = format(day, 'yyyy-MM-dd');
const status = attendanceData[dayKey];
return (
12
<Grid item xs={1} key={dayKey}>
<Paper
sx={{
p: 1,
height: '100%',
backgroundColor: status ? getStatusColor(status) :
'background.paper',
textAlign: 'center'
}}
>
<Typography>{format(day, 'd')}</Typography>
{status && (
<Typography variant="caption">
{status}
</Typography>
)}
</Paper>
</Grid>
);
})}
</Grid>
</Box>
);
};
src/components/EmployeeList.js
jsx
Copy
Download
import React, { useState, useEffect } from 'react';
import {
Table,
13
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
Button,
TextField,
Box,
Typography
} from '@mui/material';
import { Edit, Delete } from '@mui/icons-material';
useEffect(() => {
// Mock data - in real app, this would be from an API
const mockEmployees = [
{ id: 1, name: 'John Doe', email: '[email protected]', position:
'Developer', department: 'IT' },
{ id: 2, name: 'Jane Smith', email: '[email protected]', position:
'Designer', department: 'Creative' },
{ id: 3, name: 'Bob Johnson', email: '[email protected]', position:
'Manager', department: 'Operations' },
];
setEmployees(mockEmployees);
}, []);
14
const handleDelete = (id) => {
setEmployees(employees.filter(emp => emp.id !== id));
};
return (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Employee Management
</Typography>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Position</TableCell>
<TableCell>Department</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
15
</TableHead>
<TableBody>
{filteredEmployees.map((employee) => (
<TableRow key={employee.id}>
<TableCell>{employee.id}</TableCell>
<TableCell>{employee.name}</TableCell>
<TableCell>{employee.email}</TableCell>
<TableCell>{employee.position}</TableCell>
<TableCell>{employee.department}</TableCell>
<TableCell>
<Button size="small" color="primary">
<Edit fontSize="small" />
</Button>
<Button
size="small"
color="error"
onClick={() => handleDelete(employee.id)}
>
<Delete fontSize="small" />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>
);
};
src/components/EmployeeForm.js
jsx
16
Copy
Download
import React, { useState } from 'react';
import {
TextField,
Button,
Box,
Typography,
FormControl,
InputLabel,
Select,
MenuItem,
Grid
} from '@mui/material';
return (
<Box component="form" onSubmit={handleSubmit} sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom>
{employee ? 'Edit Employee' : 'Add New Employee'}
</Typography>
src/components/Reports.js
jsx
Copy
Download
import React, { useState } from 'react';
import {
Box,
Typography,
TextField,
Button,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
FormControl,
InputLabel,
Select,
MenuItem
} from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { LocalizationProvider } from
'@mui/x-date-pickers/LocalizationProvider';
21
import { AdapterDateFns } from
'@mui/x-date-pickers/AdapterDateFns';
return (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Attendance Reports
</Typography>
<Button
variant="contained"
onClick={generateReport}
disabled={reportType === 'custom' && (!startDate || !endDate)}
>
Generate Report
</Button>
</Box>
23
<TableRow>
<TableCell>Date</TableCell>
<TableCell align="right">Present</TableCell>
<TableCell align="right">Absent</TableCell>
<TableCell align="right">Late</TableCell>
<TableCell align="right">% Attendance</TableCell>
</TableRow>
</TableHead>
<TableBody>
{reportData.map((row) => (
<TableRow key={row.date}>
<TableCell>{row.date}</TableCell>
<TableCell align="right">{row.present}</TableCell>
<TableCell align="right">{row.absent}</TableCell>
<TableCell align="right">{row.late}</TableCell>
<TableCell align="right">
{Math.round((row.present / (row.present + row.absent)) *
100}%
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</Box>
);
};
24
Download
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from
'react-router-dom';
import { AuthProvider, AuthContext } from './context/AuthContext';
import Navbar from './components/Navbar';
import Login from './components/Login';
import Dashboard from './components/Dashboard';
import EmployeeList from './components/EmployeeList';
import Reports from './components/Reports';
import EmployeeForm from './components/EmployeeForm';
Features Implemented
1. Authentication System
o Login for admin and employees
o Role-based access control
o Protected routes
2. Employee Management
o Add/edit employee details
o View employee list
o Search functionality
3. Attendance Tracking
o Mark daily attendance (Present/Absent/Half-day/Leave)
o Calendar view of attendance
4. Reporting
o Daily/weekly/monthly reports
o Custom date range reports
o Attendance statistics
5. User Interface
o Responsive design with Material-UI
o Intuitive navigation
27
o Data visualization
o Database integration
2. Additional Features
o Leave management system
o Shift scheduling
3. Enhancements
o Biometric integration
o Mobile app version
o Geolocation tracking for attendance
o Face recognition for attendance marking
28
Backend Server Setup
server.js
javascript
Copy
Download
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { v4: uuidv4 } = require('uuid');
const multer = require('multer');
const path = require('path');
// Middleware
app.use(cors());
app.use(express.json());
app.use('/uploads', express.static('uploads'));
// Database connection
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));
// Models
const User = require('./models/User');
const Attendance = require('./models/Attendance');
29
const Leave = require('./models/Leave');
const Shift = require('./models/Shift');
// Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/employees', require('./routes/employees'));
app.use('/api/attendance', require('./routes/attendance'));
app.use('/api/leaves', require('./routes/leaves'));
app.use('/api/shifts', require('./routes/shifts'));
app.use('/api/reports', require('./routes/reports'));
routes/auth.js
javascript
Copy
Download
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/User');
// Register
router.post('/register', async (req, res) => {
try {
const { name, email, password, role } = req.body;
// Hash password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
// Create JWT
const payload = {
user: {
id: user.id,
role: user.role
}
};
jwt.sign(
payload,
process.env.JWT_SECRET,
{ expiresIn: '5h' },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
} catch (err) {
31
console.error(err.message);
res.status(500).send('Server error');
}
});
// Login
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
// Check password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: 'Invalid credentials' });
}
// Create JWT
const payload = {
user: {
id: user.id,
role: user.role
}
};
jwt.sign(
payload,
process.env.JWT_SECRET,
{ expiresIn: '5h' },
(err, token) => {
32
if (err) throw err;
res.json({
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
}
);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
module.exports = router;
Database Models
models/User.js
javascript
Copy
Download
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
33
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['admin', 'employee', 'manager'],
default: 'employee'
},
department: {
type: String
},
position: {
type: String
},
joiningDate: {
type: Date,
default: Date.now
},
faceData: {
type: String // For face recognition
},
createdAt: {
type: Date,
default: Date.now
}
});
models/Attendance.js
javascript
Copy
Download
const mongoose = require('mongoose');
35
checkOut: {
type: Date
},
location: {
type: {
type: String,
enum: ['Point'],
default: 'Point'
},
coordinates: {
type: [Number],
required: true
}
},
notes: {
type: String
}
}, { timestamps: true });
models/Leave.js
javascript
Copy
Download
const mongoose = require('mongoose');
routes/leaves.js
javascript
Copy
Download
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Leave = require('../models/Leave');
await leave.save();
res.json(leave);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
38
router.get('/', auth, async (req, res) => {
try {
if (req.user.role !== 'admin') {
return res.status(403).json({ msg: 'Unauthorized' });
}
// Approve/reject leave
router.put('/:id', auth, async (req, res) => {
try {
if (req.user.role !== 'admin') {
return res.status(403).json({ msg: 'Unauthorized' });
}
leave.status = status;
leave.notes = notes;
leave.approvedBy = req.user.id;
await leave.save();
res.json(leave);
} catch (err) {
console.error(err.message);
39
res.status(500).send('Server error');
}
});
module.exports = router;
Shift Scheduling
models/Shift.js
javascript
Copy
Download
const mongoose = require('mongoose');
40
enum: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday',
'sunday']
}],
createdAt: {
type: Date,
default: Date.now
}
});
routes/shifts.js
javascript
Copy
Download
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Shift = require('../models/Shift');
const User = require('../models/User');
// Create shift
router.post('/', auth, async (req, res) => {
try {
if (req.user.role !== 'admin') {
return res.status(403).json({ msg: 'Unauthorized' });
}
await shift.save();
res.json(shift);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
module.exports = router;
services/notificationService.js
42
javascript
Copy
Download
const nodemailer = require('nodemailer');
const User = require('../models/User');
const Leave = require('../models/Leave');
const Attendance = require('../models/Attendance');
await transporter.sendMail(mailOptions);
} catch (err) {
console.error('Email sending error:', err);
}
};
if (pendingLeaves.length > 0) {
const admins = await User.find({ role: 'admin' });
44
});
if (!attendance) {
await sendEmailNotification(
employee.email,
'Attendance Reminder',
'Please remember to mark your attendance for today.'
);
}
}
} catch (err) {
console.error('Attendance reminder error:', err);
}
};
module.exports = {
sendEmailNotification,
checkPendingLeaves,
sendAttendanceReminders
};
services/reportService.js
javascript
Copy
Download
const ExcelJS = require('exceljs');
const PDFDocument = require('pdfkit');
const fs = require('fs');
const path = require('path');
const Attendance = require('../models/Attendance');
const User = require('../models/User');
// Add headers
worksheet.columns = [
{ header: 'Employee ID', key: 'employeeId', width: 15 },
{ header: 'Name', key: 'name', width: 25 },
{ header: 'Date', key: 'date', width: 15 },
{ header: 'Status', key: 'status', width: 15 },
{ header: 'Check In', key: 'checkIn', width: 15 },
{ header: 'Check Out', key: 'checkOut', width: 15 }
];
// Add rows
attendanceData.forEach(record => {
worksheet.addRow({
employeeId: record.user.employeeId,
name: record.user.name,
date: record.date.toISOString().split('T')[0],
status: record.status,
checkIn: record.checkIn ? record.checkIn.toTimeString().split(' ')[0]
: 'N/A',
checkOut: record.checkOut ?
record.checkOut.toTimeString().split(' ')[0] : 'N/A'
});
});
// Generate file
46
const fileName = `attendance_report_${Date.now()}.xlsx`;
const filePath = path.join(__dirname, '../reports', fileName);
await workbook.xlsx.writeFile(filePath);
return filePath;
} catch (err) {
console.error('Excel report generation error:', err);
throw err;
}
};
// Add content
doc.fontSize(20).text('Attendance Report', { align: 'center' });
doc.fontSize(12).text(`From: ${startDate} To: ${endDate}`, { align:
'center' });
doc.moveDown(2);
// Create table
let y = doc.y;
const rowHeight = 20;
const colWidths = [80, 150, 80, 80, 80, 80];
47
// Header row
doc.font('Helvetica-Bold');
['Employee ID', 'Name', 'Date', 'Status', 'Check In', 'Check
Out'].forEach((text, i) => {
doc.text(text, 50 + colWidths.slice(0, i).reduce((a, b) => a + b, 0), y,
{
width: colWidths[i],
align: 'left'
});
});
doc.font('Helvetica');
// Data rows
attendanceData.forEach(record => {
y += rowHeight;
[
record.user.employeeId,
record.user.name,
record.date.toISOString().split('T')[0],
record.status,
record.checkIn ? record.checkIn.toTimeString().split(' ')[0] : 'N/A',
record.checkOut ? record.checkOut.toTimeString().split(' ')[0] :
'N/A'
].forEach((text, i) => {
doc.text(text, 50 + colWidths.slice(0, i).reduce((a, b) => a + b, 0),
y, {
width: colWidths[i],
align: 'left'
});
});
});
doc.end();
48
return new Promise((resolve, reject) => {
doc.on('finish', () => resolve(filePath));
doc.on('error', reject);
});
} catch (err) {
console.error('PDF report generation error:', err);
throw err;
}
};
module.exports = {
generateExcelReport,
generatePDFReport
};
3. Enhancements Implementation
services/biometricService.js
javascript
Copy
Download
const mongoose = require('mongoose');
const User = require('../models/User');
const Attendance = require('../models/Attendance');
49
if (!user || !user.fingerprintData) {
return false;
}
if (!user) {
return { success: false, message: 'Fingerprint not recognized' };
}
50
// Mark attendance
const today = new Date();
today.setHours(0, 0, 0, 0);
if (!attendance) {
attendance = new Attendance({
user: user._id,
date: today,
status
});
}
await attendance.save();
module.exports = {
51
matchFingerprint,
registerFingerprint,
markAttendanceWithFingerprint
};
MobileAttendanceScreen.js
javascript
Copy
Download
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-
native';
import { useNavigation } from '@react-navigation/native';
import * as Location from 'expo-location';
import axios from 'axios';
import jwtDecode from 'jwt-decode';
useEffect(() => {
// Get user from token
const token = AsyncStorage.getItem('token');
if (token) {
const decoded = jwtDecode(token);
setUser(decoded.user);
}
52
// Get location
(async () => {
let { status } = await
Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
return (
<View style={styles.container}>
<Text style={styles.title}>Mark Attendance</Text>
<TouchableOpacity
style={[styles.button, styles.presentButton]}
onPress={() => markAttendance('present')}
>
<Text style={styles.buttonText}>Check In</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.absentButton]}
onPress={() => markAttendance('check-out')}
>
<Text style={styles.buttonText}>Check Out</Text>
</TouchableOpacity>
{location && (
<Text style={styles.location}>
Location: {location.coords.latitude.toFixed(4)},
{location.coords.longitude.toFixed(4)}
</Text>
)}
</View>
);
};
54
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 30
},
button: {
padding: 15,
borderRadius: 8,
width: '100%',
alignItems: 'center',
marginBottom: 15
},
presentButton: {
backgroundColor: '#4CAF50'
},
absentButton: {
backgroundColor: '#F44336'
},
buttonText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold'
},
error: {
color: 'red',
marginBottom: 20
},
location: {
55
marginTop: 20,
color: '#666'
}
});
56
Copy
Download
const geolib = require('geolib');
const OfficeLocation = {
latitude: 12.9716, // Example coordinates
longitude: 77.5946
};
const MAX_DISTANCE_METERS = 100; // 100m radius
if (!verifyLocation(location)) {
return res.status(400).json({ msg: 'You must be within office
premises to mark attendance' });
}
57
Face Recognition for Attendance Marking
class FaceRecognitionService {
constructor() {
this.initialized = false;
}
async initialize() {
await faceapi.nets.ssdMobilenetv1.loadFromDisk('./models');
await faceapi.nets.faceLandmark68Net.loadFromDisk('./models');
await faceapi.nets.faceRecognitionNet.loadFromDisk('./models');
this.initialized = true;
}
if (!detections) {
throw new Error('No face detected in the image');
}
58
// Save descriptor to user in database
await User.findByIdAndUpdate(userId, {
faceDescriptor: Array.from(detections.descriptor)
});
return true;
}
async recognizeFace(imageBuffer) {
if (!this.initialized) await this.initialize();
if (!detections) {
throw new Error('No face detected in the image');
}
return bestMatch;
}
}
if (!image) {
return res.status(400).json({ success: false, message: 'Image is
required' });
}
60
// Recognize face
const user = await faceService.recognizeFace(imageBuffer);
if (!user) {
return res.status(400).json({ success: false, message: 'Face not
recognized' });
}
// Mark attendance
const today = new Date();
today.setHours(0, 0, 0, 0);
if (!attendance) {
attendance = new Attendance({
user: user._id,
date: today,
status: 'present'
});
}
res.json({
success: true,
message: 'Attendance marked successfully',
user: {
id: user._id,
61
name: user.name
}
});
} catch (err) {
console.error('Face attendance error:', err);
res.status(500).json({ success: false, message: 'Error marking
attendance' });
}
});
module.exports = router;
Implementation Notes
1. Backend Integration:
o Uses JWT for secure authentication
o MongoDB for data storage with proper schemas
2. Additional Features:
o Comprehensive leave management system
o Shift scheduling with employee assignments
3. Enhancements:
o Biometric integration (fingerprint and face recognition)
o Mobile app component (React Native)
o Geolocation verification for attendance
o Face recognition using TensorFlow.js
63