T8: Service List Page - ServiceController with index, finalize, reopen actions - Services/Index.vue with status indicators (songs mapped/arranged, slides uploaded) - German UI with finalize/reopen toggle buttons - Status aggregation via SQL subqueries for efficiency - Tests: 3 passing (46 assertions) T9: Song CRUD Backend - SongController with full REST API (index, store, show, update, destroy) - SongService for default groups/arrangements creation - SongRequest validation (title required, ccli_id unique) - Search by title and CCLI ID - last_used_in_service accessor via service_songs join - Tests: 20 passing (85 assertions) T10: Slide Upload Component - SlideController with store, destroy, updateExpireDate - SlideUploader.vue with vue3-dropzone drag-and-drop - SlideGrid.vue with thumbnail grid and inline expire date editing - Multi-format support: images (sync), PPT (async job), ZIP (extract) - Type validation: information (global), moderation/sermon (service-specific) - Tests: 15 passing (37 assertions) T11: Arrangement Configurator - ArrangementController with store, clone, update, destroy - ArrangementConfigurator.vue with vue-draggable-plus - Drag-and-drop arrangement editor with colored group pills - Clone from default or existing arrangement - Color picker for group customization - Prevent deletion of last arrangement - Tests: 4 passing (17 assertions) T12: Song Matching Service - SongMatchingService with autoMatch, manualAssign, requestCreation, unassign - ServiceSongController API endpoints for song assignment - Auto-match by CCLI ID during CTS sync - Manual assignment with searchable song select - Email request for missing songs (MissingSongRequest mailable) - Tests: 14 passing (33 assertions) T13: Translation Service - TranslationService with fetchFromUrl, importTranslation, removeTranslation - TranslationController API endpoints - URL scraping (best-effort HTTP fetch with strip_tags) - Line-count distribution algorithm (match original slide line counts) - Mark song as translated, remove translation - Tests: 18 passing (18 assertions) All tests passing: 103/103 (488 assertions) Build: ✓ Vite production build successful German UI: All user-facing text in German with 'Du' form
17 lines
3.4 KiB
Markdown
17 lines
3.4 KiB
Markdown
- 2026-03-01: Fuer 1920x1080 Slide-Output ohne Upscaling funktioniert in Intervention Image v3 die Kombination aus schwarzer Canvas (`create()->fill('000000')`), `scaleDown(width: 1920, height: 1080)` und zentriertem `place(...)` stabil.
|
|
- 2026-03-01: Bei Fake-Storage in Tests muessen Zielordner vor direktem Intervention-`save()` explizit erstellt werden (`makeDirectory`/`mkdir`), sonst wirft Intervention `NotWritableException`.
|
|
- 2026-03-01: Fuer Testverifikation von Letterbox/Pillarbox sind farbige PNG-Testbilder sinnvoller als `UploadedFile::fake()->image(...)`, weil Fake-Bilder sonst komplett schwarz sein koennen.
|
|
- 2026-03-01: CTS-Sync laeuft stabil mit `EventRequest::where("from", heute)` + `EventAgendaRequest::fromEvent(...)->get()`, wenn Services per `cts_event_id` und Agenda-Songs per (`service_id`,`order`) upserted werden; CCLI-Matching bleibt strikt auf `songs.ccli_id` und setzt nur dann `song_id`/`matched_at`.
|
|
- 2026-03-01: SongController CRUD nutzt `auth:sanctum` Middleware; `actingAs()` in Tests funktioniert damit problemlos (Sanctum unterstuetzt Session-Auth in Tests).
|
|
- 2026-03-01: SQLite gibt `date`-Spalten als `YYYY-MM-DD 00:00:00` zurueck statt `YYYY-MM-DD` — Accessor muss `substr($date, 0, 10)` nutzen fuer saubere Date-Only Werte.
|
|
- 2026-03-01: `Attribute::get()` in Laravel 12 fuer berechnete Accessors statt altem `get{Name}Attribute()` Pattern. Snake_case `last_used_in_service` mapped automatisch auf `lastUsedInService()` Methode.
|
|
- 2026-03-01: Default-Gruppen (Strophe 1=#3B82F6, Refrain=#10B981, Bridge=#F59E0B) und Default-Arrangement 'Normal' werden automatisch bei Song-Erstellung via SongService erzeugt.
|
|
- 2026-03-01: `Rule::unique('songs', 'ccli_id')->ignore($songId)->whereNull('deleted_at')` stellt sicher, dass Soft-Deleted Songs die Unique-Constraint nicht blockieren.
|
|
- 2026-03-01: `bootstrap/app.php` braucht explizit `api: __DIR__.'/../routes/api.php'` in `withRouting()` — ist nicht automatisch registriert in Laravel 12.
|
|
- 2026-03-01: Service-Listenstatus laesst sich performant in einem Query aggregieren via `withCount(...)` fuer Song-Metriken plus `addSelect`-Subqueries fuer `has_sermon_slides` und datumsabhaengige `info_slides_count` (inkl. globaler `information`-Slides mit `service_id = null`).
|
|
- 2026-03-01: TranslationService line-count distribution: iterate groups (by order) → slides (by order), for each slide count lines in `text_content`, then slice that many lines from the translated text array. `array_slice` + offset tracking works cleanly.
|
|
- 2026-03-01: URL scraping is best-effort only: `Http::timeout(10)->get($url)` + `strip_tags()` + `trim()`. Return null on any failure — no exceptions bubble up. PHP 8.1+ allows `catch (\Exception)` without variable capture.
|
|
- 2026-03-01: Translation routes: `POST /api/translation/fetch-url` (preview), `POST /api/songs/{song}/translation/import` (save), `DELETE /api/songs/{song}/translation` (remove). All under `auth:sanctum` middleware.
|
|
- 2026-03-01: `removeTranslation` uses a two-step approach: collect slide IDs via `SongSlide::whereIn('song_group_id', $song->groups()->pluck('id'))` then bulk-update `text_content_translated = null`, avoiding N+1 queries.
|
|
- 2026-03-01: Der Arrangement-Konfigurator bleibt stabil bei mehrfachen Gruppeninstanzen, wenn die Sequenz mit Vue-Keys im Muster `${group.id}-${index}` gerendert und die Reihenfolge nach jedem Drag-End sofort per `router.put(..., { preserveScroll: true })` gespeichert wird.
|