92. Case Study: Employee Attendance Application
title: “92. Case Study: Employee Attendance Application” date: 2024-06-12 author: “Andi Putra” tags: [absensi, software engineering, case study, employee management, react, nodejs, diagram]
Case Study: Employee Attendance Application
Employee attendance is one of the essential aspects of human resource management within a company. Recording attendance manually has proven to be inefficient and prone to human error. For that reason, building a modern employee attendance application is an interesting challenge for a software engineer. In this article, I will walk through a case study on developing a simple employee attendance application using web technology. We will discuss the architecture, code examples, simulated user flows, and practical tips for implementing it.
Problem Statement
Imagine an office environment with 50+ employees. Every day, the HR department has to compile attendance, tardiness, and leave/time-off records for monthly reporting. This work is still done manually using Excel, which creates the need for an online attendance application that can:
- Record attendance (check-in/check-out)
- Store employee data
- Record the time and attendance status of each employee
- Provide daily/monthly reports to HR
- Offer a simple web interface
Choosing the Technology Stack
To keep development easy, we will use the following stack:
- Frontend: React.js (SPA)
- Backend: Node.js with Express
- Database: SQLite (for simplicity, can be upgraded to PostgreSQL/MySQL)
- Authentication: JWT (JSON Web Token)
Database Design
Table Structure
Here is an explanation of the main tables:
| Table | Column | Type | Description |
|---|---|---|---|
| employees | id | integer PK | Employee ID |
| name | text | Employee name | |
| text | Unique employee email | ||
| password_hash | text | Password (hashed) | |
| attendances | id | integer PK | Attendance ID |
| employee_id | integer FK | Linked to employees.id | |
| date | date | Attendance date | |
| check_in | time | Clock-in time | |
| check_out | time | Clock-out time (nullable) | |
| status | text | Present/Late/Leave/Absent |
Attendance Flow Diagram
To make things easier to understand, here is a simple attendance flow diagram:
flowchart TD
Start[Mulai: Pegawai Login] --> Input[Input Data Absensi (Check-in)]
Input --> Decision{Sudah Absen?}
Decision -- "Belum" --> Save[Catat waktu 'check-in']
Save --> Sukses[Sukses - Tampil dashboard]
Decision -- "Sudah" --> Error[Tampilkan pesan: Sudah absen hari ini]
Sukses --> ProsesKeluar{Check-out?}
ProsesKeluar -- "Ya" --> SaveOut[Catat waktu 'check-out']
SaveOut --> End[Dashboard updated]
ProsesKeluar -- "Tidak" --> End
User Flow Simulation
Let’s break down the main processes — check-in and check-out — from both the frontend and backend perspectives.
1. Employee Login
Frontend (React):
1// AuthService.js
2async function login(email, password) {
3 const res = await fetch('/api/login', {
4 method: 'POST',
5 headers: { 'Content-Type': 'application/json' },
6 body: JSON.stringify({ email, password })
7 });
8 return res.json();
9}/api/login endpoint will return a JWT if the login is successful.2. Check-in: Clocking In
Frontend (React):
1async function checkIn(token) {
2 const res = await fetch('/api/attendance/check-in', {
3 method: 'POST',
4 headers: { 'Authorization': `Bearer ${token}` }
5 });
6 return res.json();
7}Backend (Node.js + Express):
1// attendanceRoutes.js
2const express = require('express');
3const router = express.Router();
4const { verifyToken } = require('./middleware/auth');
5const db = require('./db');
6
7router.post('/check-in', verifyToken, async (req, res) => {
8 const employeeId = req.user.id; // obtained from the token
9 const today = (new Date()).toISOString().slice(0,10);
10 // Check whether an attendance record already exists for today
11 const existing = await db('attendances')
12 .where({ employee_id: employeeId, date: today }).first();
13 if (existing) {
14 return res.status(400).json({ error: 'Sudah absen hari ini' });
15 }
16 await db('attendances').insert({
17 employee_id: employeeId,
18 date: today,
19 check_in: new Date().toISOString().slice(11,19),
20 status: 'Present'
21 });
22 res.json({ message: 'Check-in sukses' });
23});
1// attendanceRoutes.js
2const express = require('express');
3const router = express.Router();
4const { verifyToken } = require('./middleware/auth');
5const db = require('./db');
6
7router.post('/check-in', verifyToken, async (req, res) => {
8 const employeeId = req.user.id; // obtained from the token
9 const today = (new Date()).toISOString().slice(0,10);
10 // Check whether an attendance record already exists for today
11 const existing = await db('attendances')
12 .where({ employee_id: employeeId, date: today }).first();
13 if (existing) {
14 return res.status(400).json({ error: 'Sudah absen hari ini' });
15 }
16 await db('attendances').insert({
17 employee_id: employeeId,
18 date: today,
19 check_in: new Date().toISOString().slice(11,19),
20 status: 'Present'
21 });
22 res.json({ message: 'Check-in sukses' });
23});3. Check-out: Clocking Out
Frontend:
1async function checkOut(token) {
2 const res = await fetch('/api/attendance/check-out', {
3 method: 'POST',
4 headers: { 'Authorization': `Bearer ${token}` }
5 });
6 return res.json();
7}Backend:
1router.post('/check-out', verifyToken, async (req, res) => {
2 const employeeId = req.user.id;
3 const today = (new Date()).toISOString().slice(0,10);
4 const attendance = await db('attendances')
5 .where({ employee_id: employeeId, date: today }).first();
6 if (!attendance) {
7 return res.status(400).json({ error: 'Belum check-in' });
8 }
9 if (attendance.check_out) {
10 return res.status(400).json({ error: 'Sudah check-out' });
11 }
12 await db('attendances')
13 .where({ id: attendance.id })
14 .update({ check_out: new Date().toISOString().slice(11,19) });
15 res.json({ message: 'Check-out sukses' });
16});
1router.post('/check-out', verifyToken, async (req, res) => {
2 const employeeId = req.user.id;
3 const today = (new Date()).toISOString().slice(0,10);
4 const attendance = await db('attendances')
5 .where({ employee_id: employeeId, date: today }).first();
6 if (!attendance) {
7 return res.status(400).json({ error: 'Belum check-in' });
8 }
9 if (attendance.check_out) {
10 return res.status(400).json({ error: 'Sudah check-out' });
11 }
12 await db('attendances')
13 .where({ id: attendance.id })
14 .update({ check_out: new Date().toISOString().slice(11,19) });
15 res.json({ message: 'Check-out sukses' });
16});Reporting Feature for HR
One of the crucial features is reporting. We want to display a table summarizing attendance and the attendance percentage for each employee.
A simple query for the monthly attendance report:
1SELECT e.name,
2 COUNT(a.id) AS hadir,
3 SUM(CASE WHEN a.status = 'Late' THEN 1 ELSE 0 END) AS telat,
4 SUM(CASE WHEN a.status = 'Absent' THEN 1 ELSE 0 END) AS absen
5FROM employees e
6LEFT JOIN attendances a ON e.id = a.employee_id AND MONTH(a.date) = 6
7GROUP BY e.id, e.name
8ORDER BY e.name;Example report output:
| Employee Name | Present | Late | Absent |
|---|---|---|---|
| Agus Wijaya | 20 | 2 | 0 |
| Budi Santosa | 19 | 0 | 1 |
| Sari Lestari | 21 | 0 | 0 |
This table can be displayed on the HR dashboard.
Simple UI Simulation
(pseudo React code, employee dashboard view)
1function AttendanceDashboard({ user, token }) {
2 const [today, setToday] = useState(null);
3 useEffect(() => {
4 fetch('/api/attendance/today', {
5 headers: { 'Authorization': `Bearer ${token}` }
6 })
7 .then(res => res.json())
8 .then(setToday);
9 }, [token]);
10
11 return (
12 <div>
13 <h2>Hi {user.name}</h2>
14 {today && (
15 <>
16 <p>Tanggal: {today.date}</p>
17 <p>Check-in: {today.check_in || '-'}</p>
18 <p>Check-out: {today.check_out || '-'}</p>
19 {!today.check_in && (
20 <button onClick={() => checkIn(token)}>Absensi Masuk</button>
21 )}
22 {today.check_in && !today.check_out && (
23 <button onClick={() => checkOut(token)}>Absensi Pulang</button>
24 )}
25 </>
26 )}
27 </div>
28 );
29}Best Practices & Challenges
Common challenges:
- User Authentication: Always hash passwords, and use JWT or sessions.
- Concurrency: Prevent double check-in/check-out on the same day.
- Time Validation: Define a check-in cutoff time (for example, >08:00:00 = Late).
- Audit Trail: Log attendance changes whenever edits are needed.
- Sick/Leave/Time-off: Additional statuses and validation are required.
- Data Export: CSV/Excel for monthly reporting.
Improvements
- Add face recognition (using a webcam) to reduce cheating.
- Location monitoring (GPS, for mobile applications).
- Integration with payroll and salary systems.
Conclusion
Through this case study, we learned how to design a robust employee attendance application in a simple way. We used a popular stack (React + Node.js) and designed the tables, endpoints, and user flow ranging from login through to the HR summary. This case study can be developed further to match the complexity of enterprise needs, and it serves as an important foundation for building a culture of work accountability in the modern workplace.
Have experience building digital attendance systems? Let’s discuss in the comments!
References:
Happy engineering! 🚀