tinyqc/CLAUDE.md
mahdahar 0a96b04bdf feat: Implement Monthly Entry interface and consolidate Entry API controller
- Implement Monthly Entry interface with full data entry grid
    - Add batch save with validation and statistics for monthly results
    - Support daily comments per day per test
    - Add result status indicators and validation summaries
  - Consolidate Entry API controller
    - Refactor EntryApiController to handle both daily/monthly operations
    - Add batch save endpoints with comprehensive validation
    - Implement statistics calculation for result entries
  - Add Control Test master data management
    - Create MasterControlsController for CRUD operations
    - Add dialog forms for control test configuration
    - Implement control-test associations with QC parameters
  - Refactor Report API and views
    - Implement new report index with Levey-Jennings charts placeholder
    - Add monthly report functionality with result statistics
    - Include QC summary with mean, SD, and CV calculations
  - UI improvements
    - Overhaul dashboard with improved layout
    - Update daily entry interface with inline editing
    - Enhance master data management with DaisyUI components
    - Add proper modal dialogs and form validation
  - Database and seeding
    - Update migration for control_tests table schema
    - Remove redundant migration and seed files
    - Update seeders with comprehensive test data
  - Documentation
    - Update CLAUDE.md with comprehensive project documentation
    - Add architecture overview and conventions

  BREAKING CHANGES:
  - Refactored Entry API endpoints structure
  - Removed ReportApiController::view() - consolidated into new report index
2026-01-21 13:41:37 +07:00

179 lines
5.2 KiB
Markdown

# 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