// case-study.md

Enterprise HRIS
Modern HR information system on Laravel 12 + Vuestic Admin (Vite)

A full HR information system covering the entire employee lifecycle — organizational units, employee records, time + attendance, leave, payroll, memos with workflow, performance evaluations, official business / overtime / offset requests, and a granular OU-scoped RBAC. Modern stack: Laravel 12 API with TOTP 2FA, Vuestic Admin frontend on Vite + Pinia + TypeScript, AG Grid Enterprise tables, CI/CD on Jenkins + Buddy.

client Confidential · under NDA
last touched
role Full-Stack Developer
period 2024 — Present
status In production
scope ~54 models · ~41 controllers · ~57 migrations · ~95 pages
Laravel 12 Vue 3 Vite Pinia TypeScript Vuestic Admin AG Grid Enterprise Sanctum TOTP 2FA Storybook Playwright
// receipts · tl;dr
domain models
~54
api controllers
~41
ui pages
~95
2FA
TOTP ✓
// outcome · what changed
BEFORE

HR ran on shared spreadsheets + email threads for every employee change.

AFTER

95-page consolidated system with role-aware access across 54 domain models.

BEFORE

Password-only auth gating sensitive employee + payroll data.

AFTER

TOTP 2FA-gated access with per-role permissions enforced server-side.

BEFORE

Documentation drifted faster than code; onboarding hit stale instructions.

AFTER

Docs co-located with code under /docs; loose root-level .md ages out within 3 months by rule.

What it is

A modern HRIS used by an enterprise HR + operations team to run the complete employee lifecycle — from hire (records, contacts, documents, certifications, training history) through daily ops (attendance, leave, official-business, overtime, offset, memos) to payroll (periods, records, government contributions, tax tables, 13th-month) and performance evaluations — under OU-scoped RBAC with TOTP 2FA.

Built on Laravel 12 (PHP 8.2+) with Sanctum auth and Google Authenticator 2FA (pragmarx/google2fa-laravel + QR codes via bacon/bacon-qr-code). Frontend is Vuestic Admin on Vite, Vue 3 + TypeScript, Pinia state, Vue Router, Vue i18n, AG Grid Enterprise. Ships with Storybook for design-system discipline, a Playwright e2e workspace, and CI/CD on both Jenkins and Buddy.

The bottleneck

HR ran on spreadsheets and standalone tools per process — a payroll workbook, a leave-tracker, a time-sheet exporter, a Word-template memo flow, a separate sheet for offsets. Nothing talked to anything. Specific friction:

HR needed one platform where the time logs, the schedule, the approvals, the payroll record, and the audit trail all live on the same row of the same table — with per-OU access and real auth security.

How I broke it down

What I built

Domain modules shipped:

Payroll Period 2026-04 — anonymized payroll record table with employee numbers, OU, salary grade, days worked, gross / deductions / net, status pills, and per-row 2FA verification badges.
MOCKPayroll Period 2026-04 (processing) · 6 summary tiles (active employees, gross/net, deductions, approver chain, OT hours), per-employee record table with multi-OU scoping and 2FA verification stamps. Employee names anonymized; payroll figures illustrative.

Example: memo-number allocation — the kind of subtle concurrency problem that bites in production. Two HR officers click “publish” on draft memos in the same OU at the same instant; both need a sequential number; the database must hand out exactly one per request:

~/app/Services/MemoNumberService.php
class MemoNumberService
{
    public function allocate(OrganizationalUnit $ou, int $year): string
    {
        $lockKey = "memo_seq:{$ou->code}:{$year}";

        // MySQL advisory lock — guarantees no two requests share a number,
        // even across web nodes. Times out after 5s rather than hang.
        DB::selectOne("SELECT GET_LOCK(?, 5) AS got", [$lockKey]);

        try {
            $next = EmployeeMemo::where('ou_id', $ou->id)
                ->whereYear('published_at', $year)
                ->max('sequence') + 1;

            return sprintf('%s-%d-%04d', $ou->code, $year, $next);
        } finally {
            DB::selectOne("SELECT RELEASE_LOCK(?)", [$lockKey]);
        }
    }
}
Payroll run · dynamic menus · org chart screen captures on request

Tech

Results

domain models
~54
api controllers
~41
ui pages
~95
2FA
TOTP ✓

The HRIS replaced a patchwork of spreadsheets, Word templates, and biometric exports with one platform where time logs, schedules, approved exceptions, payroll records, and the audit trail share the same row of the same table. Memo numbering is concurrency-safe, payroll PDFs generate on demand, authentication is gated by TOTP 2FA enrolled via QR. Built on a modern toolchain (Vite + TypeScript + Pinia + Storybook + Playwright) with two CI/CD pipelines wired up.

Specific business figures (employee count, payroll volumes, days-saved per HR cycle) stay with the client; happy to discuss specifics on request.

What I’d do again — and differently

Worked well:

Would tighten: