# CLAUDE.md

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

## Commands

```bash
# Development server
php spark serve

# Database migrations
php spark migrate           # Run pending migrations
php spark migrate:rollback  # Rollback last batch
php spark db seed CmodQcSeeder  # Seed initial data

# Run tests
./vendor/bin/phpunit                    # All tests
./vendor/bin/phpunit tests/unit/SomeTest.php  # Specific test file
./vendor/bin/phpunit --coverage-html coverage/  # With coverage report
```

## Architecture

This is a CodeIgniter 4 Quality Control management system with:

- **Backend**: PHP 8.1+, CodeIgniter 4
- **Database**: SQL Server (uses `SQLSRV` driver)
- **Frontend**: TailwindCSS + Alpine.js + DaisyUI (CDN-based, no build step)
- **Testing**: PHPUnit 10
- **Icons**: FontAwesome 7

### Key Components

**Models** (`app/Models/`):
- `BaseModel` - Custom base model with automatic camelCase/snake_case conversion
  - `findAll()`, `find()`, `first()` return camelCase keys
  - `insert()`, `update()` accept camelCase, convert to snake_case for DB
- Organized in subdirectories: `Master/`, `Qc/`

**Controllers** (`app/Controllers/`):
- `PageController` - Renders page views with `main_layout`
- `Api\*` - Consolidated entry API controllers (DashboardApi, EntryApi, ReportApi)
- `Master\*` - CRUD for master data (MasterDepts, MasterTests, MasterControls)
- `Qc\*` - QC domain controllers (ControlTests, Results, ResultComments)

**Views** (`app/Views/`):
- PHP templates extending `layout/main_layout`
- Alpine.js components in `x-data` blocks
- DaisyUI components for UI

**Helpers** (`app/Helpers/`):
- `stringcase_helper.php` - `camel_to_snake_array()`, `snake_to_camel_array()`
- The `stringcase` helper is auto-loaded in `BaseController`

### Database Schema

Tables use soft deletes (`deleted_at`) and timestamps (`created_at`, `updated_at`):
- `dict_depts`, `dict_tests`, `dict_controls` - Master data
- `control_tests` - Control-test associations with QC parameters (mean, sd)
- `results` - Daily test results
- `result_comments` - Comments per result

## Conventions

### Case Convention
- **Frontend/JS/API**: camelCase
- **Backend PHP variables**: camelCase
- **Database**: snake_case
- Models handle automatic conversion; use helpers for manual conversions

### API Response Format
```php
return $this->respond([
    'status' => 'success',
    'message' => 'fetch success',
    'data' => $rows
], 200);
```

### Controller Pattern
```php
namespace App\Controllers\Master;

use CodeIgniter\API\ResponseTrait;
use App\Controllers\BaseController;

class DeptsController extends BaseController {
    use ResponseTrait;
    protected $model;
    protected $rules;

    public function __construct() {
        $this->model = new MasterDeptsModel();
        $this->rules = ['name' => 'required|min_length[1]'];
    }

    public function index() {
        $keyword = $this->request->getGet('keyword');
        $rows = $this->model->search($keyword);
        return $this->respond([...], 200);
    }

    public function create() {
        $input = camel_to_snake_array($this->request->getJSON(true));
        if (!$this->validate($this->rules)) {
            return $this->failValidationErrors($this->validator->getErrors());
        }
        $id = $this->model->insert($input, true);
        return $this->respondCreated(['status' => 'success', 'message' => $id]);
    }
}
```

### Model Pattern
```php
namespace App\Models\Master;

use App\Models\BaseModel;

class MasterDeptsModel extends BaseModel {
    protected $table = 'dict_depts';
    protected $primaryKey = 'dept_id';
    protected $allowedFields = ['dept_name', 'deleted_at'];
    protected $useTimestamps = true;
    protected $useSoftDeletes = true;

    public function search(?string $keyword) {
        if ($keyword) {
            $this->like('dept_name', $keyword);
        }
        return $this->findAll();
    }
}
```

### Routes Pattern
- Page routes: `$routes->get('/path', 'PageController::method');`
- API routes: `$routes->group('api', function($routes) { ... });`
- API sub-groups: `api/master`, `api/qc`

## Frontend Patterns

- Alpine.js `x-data` for component state (inline or in `<script>` blocks)
- Fetch API for AJAX (no jQuery)
- DaisyUI components for UI
- Modals with `x-show` and `x-transition`
- `window.BASEURL` available globally for API calls
- Views access page data via `$pageData['title']`, `$pageData['userInitials']`, `$pageData['userName']`, `$pageData['userRole']`

### View Template Pattern
```php
<?= $this->extend("layout/main_layout"); ?>
<?= $this->section("content"); ?>
<main x-data="componentName()">
    <!-- UI content -->
</main>
<?= $this->endSection(); ?>
<?= $this->section("script"); ?>
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data("componentName", () => ({
            // state and methods
        }));
    });
</script>
<?= $this->endSection(); ?>
```

## Things to Avoid

1. Don't skip soft deletes (`deleted_at`)
2. Don't mix concerns - controllers handle HTTP, models handle data
3. Don't forget case conversion - use helpers or BaseModel

## Response Style

- Use emojis in responses where appropriate to add visual appeal 😊
- Keep responses concise and helpful
