# 21 · FRONTEND — Phase 2 (HRM)

> Follows `20-FRONTEND.md §1` architecture rules verbatim (Next.js App Router + TypeScript, Tailwind + shadcn/ui only, plain React hooks, dependency-free client validation, single `lib/api-client.ts`, types mirror API DTOs). Not repeated here. This file adds only HRM-specific screens/flows.

---

## 1. Screens (new, under `app/dashboard/hrm/`)

```
app/dashboard/hrm/
├── employees/                 list + create/edit + detail (bank details, documents, salary history tabs)
├── attendance/                batch list + upload wizard (upload → preview → confirm)
├── leave/                     leave request list + approval inbox
├── payroll/                   payroll run list + generate → review → approve → lock → payslips
├── reports/                   attendance summary / OT / late-arrival / payroll register / salary history / document expiry
└── settings/                  departments, designations, employment types, work shifts, document types,
                                leave types, salary components, statutory settings, tax slabs
```

Mirrors the existing `app/dashboard/settings/{roles,users}` admin-screen pattern for master-data CRUD; `components/auth/RolePermissionTree.tsx`-style list/detail layout is the closest existing analog for the Employees list+detail screen.

## 2. Flows → API mapping

- **Create Employee** — form posts `POST /employees`. On blur of the email field, call `GET /users/email-lookup?email=`; if a match is returned, show a non-blocking suggestion chip ("System user 'kasun.p' matches this email — link instead?") that sets `linkUserId` on submit if accepted. Never auto-link.
- **Create User** (existing `/dashboard/settings/users` screen, extended) — same pattern in reverse: on email blur, call `GET /employees/email-lookup?email=`, offer `linkEmployeeId`.
- **Attendance upload wizard** — three-step client flow over one `AttendanceUploadBatch`:
  1. Upload (file picker + "Download template" link) → `POST /attendance-batches`, batch created `Draft`, server-computed preview returned.
  2. Preview/Confirm — table of `AttendanceRecord`s with computed Working Hours/Late/Early/OT/Status columns; inline edit (`PUT .../records/{id}`) and duplicate-resolution actions; `POST .../validate` then `POST .../confirm`.
  3. Confirmed state is read-only in the UI (matches the server 409 on edit); an "Unlock" action is only shown to an HR Administrator.
- **Payroll run** — `Generate` (`POST /payroll-runs`) shows a Preview list (`Employee | Basic | OT | Allowances | Deductions | Net`); clicking a row opens the detailed breakdown (`GET .../lines/{lineId}`) rendered as the exact Basic/Allowances/OT/Gross/Late/No-Pay/Loan/EPF/ETF/Tax/Net layout from the spec. `Approve`/`Lock`/`Unlock` buttons gated on current `Status`; `Generate Payslips` only enabled once `Locked`. Payslip view opens `GET /payslips/{id}/view` in a print-friendly page (browser print → PDF is the user's own path in this phase, per the confirmed "HTML first" decision).
- **Leave approval inbox** — list of `Submitted` `LeaveRequest`s for the current approver's team; approve/reject inline.

## 3. Validation posture (per `20-FRONTEND.md §3`, applied to HRM specifics)

Client-side (format/required/range only, for UX):
- Employee: required fields present, email format, date ranges sane (hire date not in future).
- Attendance upload: file extension/size check before upload (fast feedback), mirrored server-side as authoritative.

Server-authoritative (never assumed client-side):
- Employee code uniqueness, email-lookup match existence, one-User-per-Employee constraint.
- Attendance duplicate detection (within-batch and cross-batch), employee-code resolution, batch lock state.
- Payroll: attendance-confirmed precondition, salary-structure overlap, lock/unlock authorization, all calculated amounts (Gross/Net/Tax/EPF/ETF) — the client never recomputes or previews these independently of what the server returns.

## 4. Error & empty states

Same posture as `20-FRONTEND.md §4`: surface server `ProblemDetails.code` directly (e.g. a friendly message keyed off `ATTENDANCE_BATCH_LOCKED`/`PAYROLL_PERIOD_LOCKED`/`EMPLOYEE_CODE_DUPLICATE`), empty states for "no employees yet" / "no attendance batches this period" / "no payroll runs yet" following the existing master-data screen convention (§2.2 there).

---

*End of 21-FRONTEND-HRM.md.*
