Files
lms-v2/CLAUDE.md
T

68 lines
5.6 KiB
Markdown
Raw Normal View History

2026-05-30 22:15:16 +07:00
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
An LMS / CBT (Computer-Based Test) application for employee training compliance, built on **Laravel 13** with **Livewire 3 + Volt**. The codebase is a rewrite ("v2") of an older CodeIgniter 3 system; data is migrated in from the legacy database via Artisan commands (see Data Migration below). Most code comments and many domain terms are in Indonesian.
## Commands
```bash
composer dev # Run the full dev stack concurrently: php artisan serve + queue:listen + pail (logs) + vite
npm run dev # Vite dev server only (HMR for assets)
npm run build # Production asset build
composer test # Clears config cache, then runs the full PHPUnit suite
php artisan test # Run tests directly
php artisan test --filter=AuthenticationTest # Run a single test class
php artisan test tests/Feature/Auth/PasswordResetTest.php # Run a single file
vendor/bin/pint # Format PHP (Laravel Pint, default ruleset — no config file)
```
Tests run against an in-memory **SQLite** database (configured in `phpunit.xml`), independent of the MySQL dev database.
## Architecture
### Routing & access control (read `routes/web.php` first)
Authorization is layered through nested route-group middleware, not per-controller checks:
1. `auth` — must be logged in.
2. `force.password` (`App\Http\Middleware\ForcePasswordChange`, aliased in `bootstrap/app.php`) — if `User->must_change_password` is true, every request is redirected to `password.force-change` until the user sets a new password. This implements mandatory first-login password rotation for migrated accounts.
3. Spatie role middleware splits the app into areas:
- `role:admin|trainer``/admin` (prefix `admin.`) and `/reports` — management UI, master data, question bank, exports/imports.
- `role:karyawan``/portal` (route names prefixed `cbt.`) — the employee test-taking portal.
`bootstrap/app.php` is where middleware aliases and the global `web` middleware stack are registered (Laravel 11+ style — there is no `Http/Kernel.php`).
### Three user roles
`admin`, `trainer`, `karyawan` (employee). Roles are managed by **spatie/laravel-permission** (`role:` middleware, `HasRoles` trait on `User`). Note: `User` also has a legacy `role` string column that older migration code writes to directly — prefer the Spatie role API for new authorization logic.
### Volt page routing
`VoltServiceProvider` mounts both `resources/views/livewire` and `resources/views/pages` as Volt component roots. Auth screens (`routes/auth.php`, Laravel Breeze + Volt) and the password-reset flow are single-file Volt components under `resources/views/pages/auth`. Controllers in `app/Http/Controllers/{Admin,Cbt}` mostly return Blade views (`resources/views/pages/...`) that embed Livewire components for interactivity.
### Domain model
- **Master data:** `Department``Position` (many-to-many via `department_position`); a `User` belongs to one department and one position.
- **TrainingMatrix** ties a `Department` + `Position` + `Sop` together — it defines which SOP/training a role must complete (compliance requirement).
- **Sop** — standard operating procedure / training material. Uses `spatie/laravel-activitylog` (`getActivitylogOptions`).
- **Exam / QuestionBank / Question** — `QuestionBank` is scoped by department/position/training_matrix; exams produce `ExamResult` records (`is_passed`, `user_id`, `exam_id`).
- **Compliance** is computed (not stored): `App\Livewire\Matrix\ComplianceMonitor` derives the pass rate as `karyawan` users with ≥1 passing `ExamResult` ÷ total `karyawan`.
- `UserDocument` — uploaded employee documents.
When relating to `User`, note the legacy migration columns `old_id` and `old_password_hash` used to map/verify accounts carried over from the CI3 system.
### Activity logging
Login/logout are logged via `Event::listen` in `AppServiceProvider::boot()` (category `autentikasi`). Models use spatie/activitylog for audit trails, surfaced through `AuditTrailController`.
### File storage — Nextcloud over WebDAV
`AppServiceProvider::boot()` registers a custom `webdav` Storage driver (Sabre DAV client + Flysystem). The `nextcloud` disk in `config/filesystems.php` uses it, configured via `NEXTCLOUD_*` env vars. Use `Storage::disk('nextcloud')` for employee document/photo storage.
### Imports / exports
Employee, department, and position data export to Excel (**maatwebsite/excel**, `app/Exports`) and PDF (**barryvdh/laravel-dompdf**). Employee import has a preview→process two-step flow (`app/Imports`, see `EmployeeController` routes). Custom export/import routes are declared **before** `Route::resource(...)` in `web.php` so they aren't shadowed by resource wildcards — preserve that ordering when adding routes.
## Data Migration (legacy CI3 → v2)
A second MySQL connection **`mysql_old`** (env `DB_*_OLD`, default database `lmsv2-old`) holds the legacy data. The migration is run by `php artisan lms:migrate-data` (`app/Console/Commands/MigrateLmsData.php`), which orchestrates, in strict order: departments (from `sections`) → positions → department-position links → staff/students → user dept/position sync → questions → exam history. Additional one-off commands exist: `lms:migrate-*` (`MigrateOldQuestions`, `MigrateStaffToTrainer`, `MigrateUserInitials`, `MigrateUserKtp`). These read the old DB directly with `DB::table('lmsv2-old.<table>')`.
> Note: the repo is **not** a git repository. There is a stray file named ``elect('SHOW COLUMNS FROM `lmsv2-old`.classes');`` in the project root — a scratch artifact, not source.