pp-planer/AGENTS.md
Thorsten Bus fa3162b2b7 docs: add build commands and architecture overview to AGENTS.md
Add build/test/lint commands, architecture overview, PHP/Vue/test
code style conventions, and key project constraints. Include dompdf
config, vite HMR note, and sisyphus evidence files.
2026-03-02 23:03:14 +01:00

11 KiB

ChurchService presenter software show creator

Description

There is tool (churchtools - called cts from now on) to plan a church service, that contains all parts of the service (information, songs, sermon, prayer, etc.).

Use CTS API for services: https://api.church.tools/package-CT.ChurchService.html.

All the wording in the frontend and communication has to be in German with Du, not Sie.

TechStack

  • Laravel (Vue+Inertia) App, with Sqlite (switchable to MySQL)
  • use DB for caching the API data and just "Update" the DB from the API
  • use CTS API with existing env CTS_API_TOKEN for auth - ONLY DO READS, NO WRITES OR CHANGES ARE ALLOWED

General

  • Login should be done via OAUTH for all churchtools users (https://churchtools.academy/de/help/system-einstellungen/oauth-login-systemeinstellungen/oauth-zwischen-zwei-churchtools-systemen/) and linked to an automatically created local user.
  • 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

The Plan

We use the data from the API, to create a form to finalize the service setup and to create a file for the presenter software at the end.

  1. Show all today or future services in a list, with details (Title, Preacher, 'beamer technican', qty of songs, last changed, finalized_at) and state of the setup (No or Yes at...):

    • x/y songs found and mapped
    • x/y songs verified arrangement
    • sermon slides uploaded
    • X info slides uploaded
    • finalized at
  2. for every service, show Action Buttons:

    • if finalized: ReOpen and Download
    • if NOT finalized: Edit and Finalize
    • ReOpen and Finalize just change the status of the service
    • Edit shows a form, with these Blocks to edit (details of these blocks are below)
      • information
      • moderation
      • sermon
      • songs

Form Blocks

Block: Information

  • show list of thumbnails for all uploaded slides with a muted upload date field with uploader name, and a prominent Expiredate field
  • each thumbnail could be delete (softdelete) or can inline change the date with a datepicker
  • add big plus icon/area for drag'n'drop or click for fileupload new files with a datepicker for a date, that was added to all files as expire date
  • automatically show these files to all future services, till the expire date is after the service date

Block: Moderation

Same features as Block Information but without the datepicker and only relevant for this service.

Block: Sermon

Same features as Block Moderation.

Block: Songs

  • Show all songs (Name, CCLI ID, has Translation ..) from the service in the right order.
  • on every update from the CTS API, try to match the song with the CCLI-ID to an existing song from the DB
    • if NOT matched
      • show a button "request creation", which causes an EMAIL to a configured mail address, with the song and the CCLI Id and the ask for create the song
      • and show a searchable select field of all songs in the DB (CCLI ID included and searchable) to manually assign a song from the DB to this service song
    • if song found:
      • if song is translated, show checkbox (default:true) for use the version with translation
      • every song has a body for the arrangement selection/configuration
        • select field with all existing arrangements
        • "add" Button to create a new arangmengt (clone from master order) and ask for a name
        • "clone" Button to clone the selected arragenement and ask for a name
        • show the groups of the arrangement and make possible to rearange or add a group to the arrangement via drag'n'drop, like rev/form-song-arangment-config.jpg
  • every song with a selected arrangement ("normal" should selected always as default) should have two buttons:
    • preview: show the text of the song in the order of the arrangement configuration, prominent highlighted which textpart was with group
    • download: download the preview as a nice pdf with header/footer and copyright footer from the Song DB.

File Upload

  • could be a zip file, contained multiple other files of types below, handle as mass upload
  • could me multiple files, so handle each one as a single file for types below
  • could be an image file (png, jpg, jpeg) -> always convert to jpg 1920x1080 (dont cut parts of an image, keep orientation and ratio)
  • could be a ppt or pptx (powerpoint) -> convert to multiple JPG with 1920x1080 (see jpg convert)

SongDB Import

There should be a menu item for songDB in the Top.

  • it shows all songs from the DB, with created, last update, ccliID, last_used_in_service every song has a delete (soft_delete), download, translate and an edit button
  • edit: shows a popup with Name, CCLI and copyright text (all that is available from song metadata) and the arrangement configurator from the service->song block
  • download: download generated .pro file from the songDB for this song
  • translate: allow add a full text or an URL to the Full text, and then start an editor, that shows two columns for every slide of every group. Left the original text, right a texteditor, with the imported text - always the same line qty of text from the original is used from the given translated text. Save this as translation for this song, and mark it as with translation.
  • UploadArea for drag'n'drop and click for upload, to upload a .pro file, a zip file with multiple .pro files, or a bunch of .pro files, which should be parsed (this module was integrated later, so show an Exception here till this was finalized) and added into the song DB.

Build, Test, Lint Commands

# Setup (first time)
composer setup

# Dev server (Laravel + Vite + Queue + Logs via concurrently)
composer dev

# Build frontend
npm run build

# Run all PHP tests (clears config cache first)
composer test
# or directly:
php artisan test

# Run a single PHP test file
php artisan test tests/Feature/ServiceControllerTest.php

# Run a single test method
php artisan test --filter=test_service_kann_abgeschlossen_werden

# Run only Unit or Feature suite
php artisan test --testsuite=Unit
php artisan test --testsuite=Feature

# PHP code formatting (Laravel Pint - default Laravel preset)
./vendor/bin/pint
# Check only (no changes):
./vendor/bin/pint --test

# Run e2e tests (requires running 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

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)

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.

Code Style — Vue / Frontend

  • Vue 3 Composition API only, always <script setup>. No Options API.
  • Props: defineProps({ propName: { type: Type, default: value } })
  • Emits: defineEmits(['event-name'])
  • Imports: Use @/ alias for resources/js/. Vue imports from 'vue', Inertia from '@inertiajs/vue3'.
  • Functions: Prefer function name() {} declarations in components (not const name = () => {})
  • Styling: Tailwind CSS v4 utility classes inline. Scoped <style> only when necessary (e.g. drag-and-drop).
  • State: ref() for reactive state, computed() for derived. Use watch() for side effects.
  • Routing: Use route('name', params) (Ziggy) for URL generation. Use router.post/get/delete from Inertia.
  • Testing attributes: Add data-testid="..." on interactive elements for Playwright e2e tests.
  • All user-facing text must be German (Du-form, not Sie).

Code Style — Tests

  • Framework: Pest v4 (wraps PHPUnit). Feature tests are class-based extending TestCase with RefreshDatabase.
  • Naming: test_snake_case_german_description (e.g. test_service_kann_abgeschlossen_werden)
  • Auth: $this->actingAs(User::factory()->create())
  • Vite: Call $this->withoutVite() before testing Inertia page renders
  • Time: Use Carbon::setTestNow('2026-03-01 10:00:00') for deterministic time
  • Assertions: assertInertia(fn ($page) => $page->component('...')->has('...')->where('...')) for Inertia responses
  • DB: Tests use in-memory SQLite (configured in phpunit.xml)
  • e2e: Playwright (TypeScript), tests/e2e/, baseURL http://cts-work.test, auth via auth.setup.ts

Key Conventions

  • CTS API is READ-ONLY — never write/modify data via ChurchTools API
  • Immediate persistence — all user actions save instantly, no separate "save" button
  • German localeAPP_LOCALE=de, all UI text in German, Du-form
  • File uploads — images convert to JPG 1920x1080 (maintain aspect ratio, no cropping); PPT/PPTX convert to multiple JPGs
  • Named routes — all routes have names, use route('name') everywhere
  • ProPresenter (.pro) parser — placeholder only, not yet implemented