diff --git a/AGENTS.md b/AGENTS.md index 7fa0ed8..101a845 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,7 +20,7 @@ ## General - There should be Button in the Top Bar, to refresh the Data from the CTS API and a timestamp with the latest refresh. - LoggedIn User should be visible in the Top Bar - every action should be immediately persistent, no separate "save" button required, unless explicitly described. -- parser and generator of song files (.pro) are added later, to just add simple placeholder and wait for the detailed spec to implement +- ProPresenter `.pro` file parser/generator is implemented as a separate composer package (`propresenter/parser`) linked via path repository ## The Plan @@ -98,112 +98,160 @@ ## SongDB Import --- +## Repository Structure + +Two git repositories, both local (no remote): + +| Repo | Path | Branch | Purpose | +|------|------|--------|---------| +| **cts-work** | `/Users/thorsten/AI/cts-work` | `cts-presenter-app` | Laravel app (main codebase) | +| **propresenter-work** | `/Users/thorsten/AI/propresenter-work/php` | `propresenter-parser` | ProPresenter .pro/.proplaylist parser (composer path dependency) | + +The parser is linked via `composer.json` path repository: `"url": "../propresenter-work/php"`. + ## Build, Test, Lint Commands -```bash -# Setup (first time) -composer setup +### cts-work (Laravel App) -# Dev server (Laravel + Vite + Queue + Logs via concurrently) +```bash +# First-time setup +composer setup # install, .env, key:generate, migrate, npm install, npm build + +# Dev server (Laravel + Vite + Queue + Logs) composer dev # Build frontend npm run build -# Run all PHP tests (clears config cache first) +# Run ALL PHP tests (206 tests, clears config cache first) composer test -# or directly: php artisan test -# Run a single PHP test file +# Single test file php artisan test tests/Feature/ServiceControllerTest.php -# Run a single test method +# Single test method php artisan test --filter=test_service_kann_abgeschlossen_werden -# Run only Unit or Feature suite -php artisan test --testsuite=Unit +# Test suite php artisan test --testsuite=Feature +php artisan test --testsuite=Unit -# PHP code formatting (Laravel Pint - default Laravel preset) +# PHP formatting (Laravel Pint, default preset — no pint.json) ./vendor/bin/pint -# Check only (no changes): -./vendor/bin/pint --test +./vendor/bin/pint --test # check only -# Run e2e tests (requires running dev server at http://cts-work.test) +# E2E tests (requires dev server at http://cts-work.test) npx playwright test -# Single e2e file: npx playwright test tests/e2e/service-list.spec.ts # Migrations php artisan migrate ``` -## Architecture Overview +### propresenter-work (Parser Module) -``` -app/ - Http/Controllers/ # Inertia controllers, return Inertia::render() or JSON - Http/Requests/ # Form request validation - Http/Middleware/ # HandleInertiaRequests shares props - Models/ # Eloquent models with factories in database/factories/ - Services/ # Business logic (ChurchToolsService, SongService, etc.) - Jobs/ # Queue jobs (PowerPoint conversion) - Mail/ # Mailable classes - Cts/ # CTS API spike/sync utilities -resources/js/ - Pages/ # Vue page components (mapped via Inertia::render) - Components/ # Reusable Vue components - Composables/ # Vue composables (useAutoSave) - Layouts/ # AuthenticatedLayout, GuestLayout, MainLayout -tests/ - Feature/ # HTTP/integration tests (class-based, PHPUnit style) - Unit/ # Unit tests - e2e/ # Playwright browser tests (TypeScript) +```bash +cd /Users/thorsten/AI/propresenter-work/php + +# Run all tests (230 tests) +./vendor/bin/phpunit + +# Single test file +./vendor/bin/phpunit tests/ProFileReaderTest.php ``` -## Code Style — PHP +## Architecture + +``` +cts-work/ + app/Http/Controllers/ # Inertia controllers (Inertia::render or JSON) + app/Models/ # Eloquent models (factories in database/factories/) + app/Services/ # Business logic (ChurchToolsService, ProExportService, etc.) + app/Jobs/ # Queue jobs (PowerPoint conversion) + app/Mail/ # Mailable classes (German content) + resources/js/Pages/ # Vue page components (mapped via Inertia::render) + resources/js/Components/ # Reusable Vue components + resources/js/Composables/ # Vue composables (useAutoSave) + resources/js/Layouts/ # AuthenticatedLayout, GuestLayout + tests/Feature/ # Pest v4 / PHPUnit feature tests + tests/e2e/ # Playwright browser tests (TypeScript) + +propresenter-work/php/ + src/ # ProFileReader, ProFileGenerator, ProPlaylistGenerator, Song, Group, Slide, Arrangement + tests/ # PHPUnit 11 tests with #[Test] attributes + ref/ # .pro fixture files for testing +``` + +## Code Style -- PHP - **Formatter**: Laravel Pint (default Laravel preset, no custom config) -- **Indentation**: 4 spaces -- **Imports**: Fully qualified, one per line, grouped (PHP classes, then Laravel, then app) -- **Models**: Use `$fillable` array (not `$guarded`). Use `casts()` method (not `$casts` property). Relationships return typed `HasMany`/`BelongsTo`/etc. -- **Controllers**: Return type hints (`Response`, `JsonResponse`, `RedirectResponse`). Use route-model binding. Use `Inertia::render()` for page responses. -- **Migrations**: Anonymous class style: `return new class () extends Migration { ... }` -- **Error messages**: German. Flash via `->with('success', '...')`. JSON errors use `message` key. -- **Null safety**: Use nullsafe operator `?->` and null coalescing `??` -- **DB operations**: Prefer Eloquent, fall back to `DB::table()` for bulk upserts in sync code -- **SoftDeletes**: Used on `Song` model. Use `whereNull('deleted_at')` in manual queries. +- **Imports**: Fully qualified, one per line, alphabetical. App\ first, then Illuminate\, then external. +- **Constructors**: Promoted properties with `private readonly`. Empty body: `{}` on same line. + ```php + public function __construct( + private readonly SongMatchingService $songMatchingService, + ) {} + ``` +- **Return types**: Always present. Use union types for multiple returns: `Response|JsonResponse`. +- **String concat**: No spaces around `.` operator: `'Fehler: '.$e->getMessage()` +- **Null safety**: Nullsafe `?->` and null coalescing `??`. Never suppress with `@`. +- **Models**: `$fillable` array (never `$guarded`). `casts()` method (never `$casts` property). Typed relationship returns (`HasMany`, `BelongsTo`). `Attribute::get()` for computed accessors. +- **Migrations**: Anonymous class: `return new class extends Migration {`. Methods: `up(): void`, `down(): void`. +- **Error messages**: German, Du-form. Flash: `->with('success', '...')`. JSON: `'message'` key. +- **Validation**: Inline `$request->validate([...])` with `Rule::in()` for enums. +- **Transactions**: `DB::transaction(function () use (...): void { ... })` for multi-step writes. +- **Constants**: `private const NAME = [...]` for fixed value sets. -## Code Style — Vue / Frontend +## Code Style -- Vue / Frontend -- **Vue 3 Composition API** only, always `