# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

**SPGA DMS** — a Laravel 10 / PHP 8.1 Document Management System. Users organize files into nested folders, share folders/files with users or roles, route documents through configurable approval workflows, and receive notifications. Access is gated throughout by named permissions (RBAC). See `TRAINING_GUIDE.md` for the end-user feature map (the `README.md` is the stock Laravel readme and carries no project info).

Runs under XAMPP at `/Applications/XAMPP/htdocs/spga`, backed by a MySQL database named `spga` (`DB_USERNAME=root`, see `.env`).

## Commands

```bash
# Install
composer install
npm install

# Front-end assets (Vite + Tailwind + Alpine). Run dev server while working on Blade/JS:
npm run dev
npm run build          # production bundle

# App
php artisan serve                 # or just hit it via XAMPP/Apache at APP_URL
php artisan migrate               # 32 migrations in database/migrations
php artisan db:seed               # seeds permissions/roles (see Seeders below)
php artisan db:seed --class=PermissionSeeder

# Tests (PHPUnit 10)
php artisan test                          # or: ./vendor/bin/phpunit
php artisan test --filter=PermissionSystemTest       # single test class
./vendor/bin/phpunit tests/Feature/PermissionSystemTest.php

# Lint / format (Laravel Pint)
./vendor/bin/pint            # fix
./vendor/bin/pint --test     # check only
```

Note: `phpunit.xml` has the sqlite/`:memory:` lines **commented out**, so `php artisan test` runs against the configured MySQL connection (`APP_ENV=testing`). Point `DB_DATABASE` at a throwaway database before running the suite, or uncomment the sqlite lines.

## Architecture

### Permissions are the backbone — and there are two enforcement paths
Authorization uses `spatie/laravel-permission` with **human-readable permission name strings** (e.g. `"View Folder"`, `"Share My Files"`, `"My Workflows"`). Almost every route is guarded by a permission. Two mechanisms coexist and both resolve against the same Spatie permissions:

- Custom middleware alias `permission:` → `App\Http\Middleware\CheckPermission` (registered in `app/Http/Kernel.php`). Used by most `routes/web.php` routes. It logs unauthorized attempts and returns 403/redirect.
- Spatie's gate via `can:` → used by the workflow routes (`can:My Workflows`, `can:View System Logs`).

When adding a feature, the permission string must exist in a seeder or the route is unreachable. Permissions/roles are created in `database/seeders/`: `PermissionSeeder.php`, `AddWorkflowPermissionsSeeder.php`, `AddViewMyFolderInfoPermissionSeeder.php`. `User` uses the `HasRoles` trait (and also carries a `role_id` column referenced in some workflow assignment logic).

### Route organization
`routes/web.php` is the hub and `require`s the other pieces: `routes/workflow/workflow.php` (all `/workflow/*` routes, prefixed and named `workflow.`), `routes/workflow/settings.php`, and `routes/auth.php` (Laravel Breeze auth). API routes live in `routes/api.php`. The `App\Workflow\WorkflowServiceProvider` (registered in `config/app.php`) is currently a stub — workflow wiring is done through routes/controllers, not the provider.

### The workflow engine (the most complex subsystem)
A configurable state machine over documents:

- `Workflow` → has ordered `WorkflowStep`s (via `order`) and `WorkflowTransition`s.
- `WorkflowTransition` connects steps (`from_step_id` → `to_step_id`), has an `is_end` flag, and stores assignment targets in `user_ids` / `role_ids` columns that may be **JSON strings or arrays** (see the defensive decoding in `WorkflowTaskController::transition`).
- `WorkflowInstance` is one run of a workflow against a `Document`.
- `WorkflowTask` is a unit of work assigned to a user or role (`assigned_user` / `assigned_role`) at a given step/transition, with a `status` (`in_progress`, `completed`).

The core logic is `App\Http\Controllers\Workflow\WorkflowTaskController::transition()`: it completes the current task, resolves the selected transition, computes the *next* transition/step, decodes assignment IDs, and creates or updates the next `WorkflowTask` (or a final task when `is_end`). It contains deduplication guards for first/next tasks — preserve these when editing. Controllers: `WorkflowController` (CRUD), `WorkflowStepController`, `WorkflowTransitionController`, `WorkflowTaskController`, `MyWorkflowController` (tasks assigned to the current user), `WorkflowDocumentController`, `SettingsController`.

All meaningful workflow mutations should be recorded via `App\Helpers\AuditLogger::log()`, which writes a `WorkflowAudit` row (entity type/id, user, action, before/after) and swallows failures so auditing never breaks the request.

### Files & folders
`Folder` is self-referential (`parent_id` → nested folders) and owned by a creator (`created_by`). A folder `hasMany` `FileMetadata` (its `files()` relation) — **`FileMetadata` is the file record**, not `Document`. Related models: `FileShare` / `FolderShare` logic for sharing with users and roles, `FolderDocument`, and `Document` (used by the workflow side). `FolderController` handles both admin folders (`/folders`) and per-user "My Folders" (`/my-folders`) including share/unshare and folder-contents AJAX. `FileController` handles upload, view, inline PDF preview (`ajaxPreviewPdf`), download, delete, metadata get/search, and sharing. Files are stored on the `local` filesystem disk (`FILESYSTEM_DISK=local`).

### Notifications
Laravel database notifications in `app/Notifications/` (`FileSharedNotification`, `FolderSharedNotification`, `FileUploadedNotification`, `ApprovalRequiredNotification`, `WorkflowStepNotification`, `SystemAlertNotification`). `NotificationController` serves the list and mark-as-read; there's an unauthenticated-safe `/notifications/unread-count` JSON endpoint used by the notification bell.

### Reporting
`ReportController` produces on-screen reports and PDF exports (via `barryvdh/laravel-dompdf`) for folder/file stats, users, workflows, and tasks. The department-setup questionnaire (`DepartmentSetupQuestionnaireController`, `DepartmentSetupSubmission` model) also supports CSV export.

### Front end
Server-rendered Blade views in `resources/views`, styled with Tailwind, with Alpine.js for interactivity and Axios for AJAX. Bundled by Vite (`vite.config.js`); the folder browser, file preview, and sharing dialogs rely on the AJAX endpoints noted above.

## Gotchas

- **Root-level `check_admin.php`, `check_permissions.php`, `debug_folders.php`** are standalone diagnostic scripts, not part of the app request lifecycle — don't treat them as source of truth for behavior.
- `composer.phar` and `composer-setup.php` are committed to the repo; prefer a system `composer` if available.
- `QUEUE_CONNECTION=sync` and `BROADCAST_DRIVER=log` locally — notifications/jobs run inline, not through a queue worker.
