R.RAFI BAKHTIAR
SOFTWARE ENGINEER
OPEN TO WORK CV ↗
← BACK TO PROJECTS

PROJECT 01 · FULL-STACK APP

Attenda.
Attendance, organized.

attenda / overview

Overview

EMPLOYEESTeam overview
ATTENDANCEDaily records
MANAGEMENTAccount status
EMPLOYEEDATESTATUS
PROJECT TYPEFull-stack learning project
ROLEFull-stack developer
STACKReact · Go · Echo · GORM · MySQL

01 / WHY

Attendance needs some structure.

Recording attendance is only part of the problem. Employees need a simple way to log their time, and administrators need one place to manage access and review the records. I built Attenda to practice authentication, role-based access, and a clean separation between frontend and backend.

02 / WHAT IT DOES

Two roles,
clear responsibilities.

01

Clock in and out

Employees record one clock-in and one clock-out per day. The status, on time up to 08:00 WIB and late after, is set automatically.

02

Personal dashboard

On-time rate, recent attendance, and a searchable attendance history.

03

Employee management

Admins create employee accounts and activate or deactivate them. Deactivated accounts lose access at once.

04

Attendance records

Admins see a summary of today and browse all records, searching by employee name and filtering by status.

05

Safe by default

bcrypt passwords, HS256 JWT, role middleware, input validation, and rate limiting on login and register.

06

Tests and CI

Go tests for the JWT, the late rule, validation, and the admin middleware. GitHub Actions runs format, vet, test, lint, and build.

SCREENSHOTS

How it looks.

Attenda employee dashboard showing the on-time rate and recent attendance
Employee dashboard: clock in and out, on-time rate, and recent attendance.
Attenda attendance history page with totals and a search box
Attendance history with totals and search.
Attenda admin dashboard with today's attendance summary
Admin dashboard: today's attendance summary.
Attenda employee management page where admins search, add, and deactivate accounts
Employee management: search, add, and deactivate accounts.
Attenda attendance records page filtered by name and status
All attendance records, filtered by name and status.
Attenda sign-in page
Sign-in page.

API ENDPOINTS

The API at a glance.

METHODPATHACCESSNOTE
GET /health Public API and database health check.
POST /register Public Create an employee account. The role cannot be chosen by the client.
POST /login Public Returns a JWT. Rate limited per IP.
GET /profile Login required The logged-in user.
POST /clock-in Login required Record today's clock-in. Status is on_time or late, set by the server.
POST /clock-out Login required Record today's clock-out.
GET /attendance/history Login required Your own attendance history, newest first.
GET /admin/employees Admin only List employees.
POST /admin/employees Admin only Create an employee account.
PATCH /admin/employees/:id/status Admin only Activate or deactivate an employee.
GET /admin/attendances Admin only All attendance records.

DESIGN DECISIONS

Choices I made, and why.

01

The account is checked on every request

The auth middleware loads the user from the database each time. A deactivated account, or one whose role changed, loses access immediately instead of keeping it until the JWT expires.

02

The database prevents double clock-ins

A unique index on user and date rejects a second clock-in for the same day, even when two requests arrive at the same moment. The handler turns MySQL duplicate-entry error 1062 into a clear response.

03

Roles are never chosen by the client

Public registration always creates an employee. Admin accounts come only from a seed command, and admin routes sit behind both the auth and the admin middleware.

04

Late is decided by the server, in Jakarta time

A clock-in after 08:00 WIB is late. The cutoff is computed in the Asia/Jakarta time zone on the server, so it does not depend on the device clock, and a unit test covers the rule.

05

Authentication basics are locked down

Passwords use bcrypt. Tokens must be HS256 and are rejected otherwise, the server refuses to run auth without a JWT secret, login and register are rate limited, and the server has read, write, and idle timeouts.

03 / NEXT IMPROVEMENTS

What I would do next.

Store the JWT in an httpOnly cookie with refresh tokens instead of localStorage, paginate the admin lists, use versioned migrations instead of AutoMigrate, and add integration tests that run against a real database.

DISCUSS THIS PROJECT ↗ NEXT PROJECT: TASK API ↗