# Backend User Module

## 1. Pengenalan

Module untuk manage admin users (backend users) — CRUD, profile, roles. Column email verification wujud, tetapi admin routes tidak enforce `verified` middleware.

## 2. Database

**Table:** `backend_users`

| Column | Type | Description |
|--------|------|-------------|
| id | bigint (PK) | Primary key |
| username | string(255) (unique) | Username login |
| email | string(255) (unique) | Email login |
| password | string(255) | Hashed password |
| first_name | string(255) (nullable) | Nama pertama |
| last_name | string(255) (nullable) | Nama akhir |
| profile_picture | string(255) | Path gambar profile |
| is_active | boolean | Status aktif |
| must_change_password | boolean | Wajib tukar password pada login seterusnya |
| must_change_password_at | timestamp (nullable) | Mula grace period reminder |
| email_verified_at | timestamp | Bila email diverifikasi |
| expires_at | datetime | Tempoh account |
| two_factor_code | string(255) | Column 2FA disediakan — flow MFA belum diimplementasikan |
| two_factor_expires_at | datetime | Tempoh 2FA (belum digunakan) |
| last_login_at | datetime | Last login |
| remember_token | string(100) | Remember me token |
| timestamps | - | created_at, updated_at |

## 3. Routes

| Method | URI | Permission | Name |
|--------|-----|------------|------|
| GET | `/admin/backend-user` | `backend-user.view` | `backend-user.index` |
| GET | `/admin/backend-user/create` | `backend-user.create` | `backend-user.create` |
| POST | `/admin/backend-user` | `backend-user.create` | `backend-user.store` |
| GET | `/admin/backend-user/{id}` | `backend-user.view` | `backend-user.show` |
| GET | `/admin/backend-user/{id}/edit` | `backend-user.update` | `backend-user.edit` |
| PUT | `/admin/backend-user/{id}` | `backend-user.update` | `backend-user.update` |
| POST | `/admin/backend-user/{id}/unlock-password-change` | `backend-user.update` | `backend-user.unlock-password-change` |
| POST | `/admin/backend-user/{id}/send-reset-link` | `backend-user.update` | `backend-user.send-reset-link` |
| DELETE | `/admin/backend-user/{id}` | `backend-user.delete` | `backend-user.destroy` |
| GET | `/admin/profile/edit` | (none) | `admin.profile.edit` |
| PUT | `/admin/profile` | (none) | `admin.profile.update` |

## 4. Controller — `BackendUserController`

**Functions:**
- `index()` — List users with search (username, email, first/last name). Filter `?locked=1` by `must_change_password` dan `?role=...` by role name. Eager load roles. Sort super-admin first, then by created_at desc.
- `create()` — Return form dengan roles dropdown dan active email templates untuk pilihan activation/temporary password.
- `store(UserRequest)` — Create user. `manual` dan `temporary` arm `must_change_password`; `invite` cipta user inactive tanpa flag. Temporary password dijana 12 aksara dan memenuhi complexity. Mode `invite` menghantar `AdminActivateAccount` dengan optional `activation_template_id`; mode `temporary` menghantar `AdminTemporaryPassword` dengan optional `temporary_password_template_id`. Mode `manual` tidak menghantar email automatik.
- `show($id)` — Display user detail.
- `edit($id)` — Return edit form with user data + roles.
- `update(UserRequest, $id)` — Update user; jika password diisi, re-arm `must_change_password` + timestamp.
- `destroy($id)` — Delete user.
- `editProfile()` — Profile edit untuk current authenticated user.
- `updateProfile(Request)` — Update profile current user; password optional, jika diisi mesti 8–12 + complexity + confirmation, kemudian clear flag.
- `unlockPasswordChange($id)` — Admin escape hatch; clear `must_change_password` + timestamp. Button guna SweetAlert confirmation dan tidak reset password.

## 5. Models

### `BackendUser`
- Table: `backend_users`
- Implements: `MustVerifyEmail`
- Traits: `HasFactory, Notifiable, HasRoles`
- Email notifications:
  - `sendPasswordResetNotification()` → `AdminResetPassword` (DB template `admin.password_reset`, fallback Laravel reset)
  - `sendEmailVerificationNotification()` → `TemplatedVerifyEmail('admin.verify_email')`
- Fillable: username, email, email_verified_at, password, first_name, last_name, profile_picture, expires_at, two_factor_expires_at, two_factor_code, is_active, must_change_password, must_change_password_at, last_login_at
- Hidden: password, two_factor_code, remember_token
- Audit Trail records web-request CRUD changes for this model; see [Audit Trail](audit-trail.md).

## 6. Views

- `index.blade.php` — Table: No, Username, Email, Full Name, Roles, Last Login, Actions (reset link, clear forced password change, edit/delete). Search, Role filter (`All Roles` + role names, onchange submit), pagination, SweetAlert untuk delete/reset/status/unlock. Filter `?locked=1` dan `?role=...`.
- `create.blade.php` — Form: Username, Email, password mode (`manual` / `temporary` / `invite`), Password + Confirm Password dengan eye toggle berasingan, First Name, Last Name, Profile Picture (FilePond), Roles (Select2 multi). Untuk mode `invite`, dropdown `activation_template_id` dipaparkan. Untuk mode `temporary`, dropdown `temporary_password_template_id` dipaparkan. Kedua-duanya optional dan hanya senaraikan template active. Tiada HTML `required`; validation dibuat server-side.
- `edit.blade.php` — Same as create dengan pre-filled values; password/confirm optional.
- `show.blade.php` — Detail view (read-only).

## 7. Form Request — `UserRequest`

Guna form request yang sama untuk BackendUser dan FrontendUser. Validation rules:
- username: required, string, max:255, unique (ignore current on update)
- email: required, email, unique (ignore current on update)
- password: `required_if:password_mode,manual` pada create, nullable pada update; `between:8,12`; wajib ada huruf besar, huruf kecil, nombor dan simbol
- password_confirmation: nullable; `required_with:password`; `same:password`
- password_mode: `manual`, `temporary`, atau `invite`
- first_name, last_name: required, string
- profile_picture: nullable, image, mimes:jpg,jpeg,png,webp, max:15360
- roles: nullable, array
- roles.*: string, exists:roles,name
- activation_template_id: nullable, integer, exists dalam `email_templates` dan active
- temporary_password_template_id: nullable, integer, exists dalam `email_templates` dan active

Nota: mismatch confirmation dilekatkan pada `password_confirmation` (bukan `password`) supaya error muncul pada field Confirm Password. FrontendUser create menghantar hidden `password_mode=manual` kerana form request ini dikongsi.

## 8. Authentication & Throttle

| Perkara | Nilai |
|---|---|
| Login throttle | Named `admin-login` — 5 percubaan/minit per username + IP |
| Forgot password throttle | 5 request/minit per IP |
| Reset password throttle | 5 request/minit per IP |
| Account lockout | Belum diimplementasikan |
| MFA | Belum diimplementasikan; column `two_factor_*` masih belum digunakan |
| `?locked=1` | Filter `must_change_password = true`, bukan lockout login |

Password minimum 8 dan maksimum 12 aksara. Untuk admin CMS, pertimbangkan naik ke 12 atau tambah MFA pada fasa seterusnya.

## 9. Seeder — `BackendUserSeeder`

- Seeders run in console and do not generate web-request Audit Trail entries.
- Create superadmin demo: `superadmin@example.com` / `password` — **tukar/buang untuk production**
- Mark email as verified
- Assign `super-admin` role

## 10. Permissions

| Permission | Description |
|------------|-------------|
| `backend-user.view` | View list & detail |
| `backend-user.create` | Create new user |
| `backend-user.update` | Edit user |
| `backend-user.delete` | Delete user |
