# CLAUDE.md

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

## Project Overview

TTO (Települési Tervező és Összefoglaló) is a Hungarian municipal planning CMS. It allows planners to publish planning phases, events, workshops, surveys, and galleries for public transparency. Built on a custom PHP MVC framework with no external web framework (no Laravel/Slim).

## Commands

```bash
# Install dependencies
composer install

# Run all tests
./vendor/bin/phpunit

# Run a single test file
./vendor/bin/phpunit tests/AuthControllerTest.php

# Run a single test method
./vendor/bin/phpunit --filter testMethodName tests/SomeTest.php

# Run cron jobs manually
php cron/publish_scheduled.php
php cron/process_survey_jobs.php
```

No build step, linter, or formatter is configured. The app is served via Apache with `public/` as document root.

## Architecture

### Request Lifecycle

`public/index.php` → `App::boot()` → loads `.env` + config → starts session → runs `CsrfMiddleware` → dispatches `Router` → controller method → view render or JSON response.

### Custom Framework (app/Core/)

- **Router** — Pattern matching with `{param}` placeholders, GET/POST only. Routes defined in `config/routes.php`.
- **Model** — Base ORM class providing `findAll`, `findById`, `create`, `update`, `delete`, `updateSortOrders`. All queries use PDO prepared statements.
- **Database** — PDO singleton via `Database::getInstance()`.

### Multi-Tenancy

Every content table has a `settlement_id` column. The base `Model` class has `$usesSettlementScope = true` which automatically filters all queries by the active settlement stored in `$_SESSION['active_settlement_id']`. Only `User` and `Settlement` models disable this.

### Content Status Workflow

Content entities support `draft` → `scheduled` → `published` statuses. The `cron/publish_scheduled.php` job promotes scheduled items to published when `publish_at <= NOW()`.

### Survey Processing Pipeline

CSV import → `survey_imports` table → creates `SurveyJob` → `cron/process_survey_jobs.php` picks up jobs → closed questions get local percentage calculation, open questions go to GPT-4o-mini via `AiSurveyService` → results stored in `survey_results` → `PersonalDataFilterService` checks for PII.

### API Layer

Public read-only JSON endpoints under `/api/settlements/{slug}/*`. No auth required. Response format: `{success, data, meta}`. CORS enabled (`Access-Control-Allow-Origin: *`). API controllers extend `BaseApiController`.

### Admin Controllers

Standard CRUD pattern: index/create/store/edit/update/destroy. Most also support `reorder` (drag-and-drop sort) and `toggleHighlight`. Views are PHP templates rendered via `ViewHelper` with layout wrapping (`layouts/admin.php`).

### Image Processing

Uploads go to `storage/uploads/`, then `ImageProcessingService` eagerly generates 7 responsive width variants (130px–3200px) in `storage/generated/` using intervention/image.

## Key Conventions

- **Language**: UI text, flash messages, and comments are in Hungarian. Code identifiers, table/column names are English.
- **Autoloading**: PSR-4 with `App\` → `app/`.
- **PHP version**: 8.2+ required.
- **Database**: MySQL 8.0+, utf8mb4_unicode_ci, InnoDB with foreign keys. Migrations are raw SQL files in `database/migrations/` (no migration runner).
- **Auth**: Session-based with bcrypt passwords, 8-hour inactivity timeout, CSRF tokens on all POST requests.
- **Views**: Plain PHP templates using `extract($data)`, two layouts (`admin.php`, `auth.php`), flash messages for user feedback.
