diff --git a/.sisyphus/evidence/task-1-docker-status.txt b/.sisyphus/evidence/task-1-docker-status.txt new file mode 100644 index 0000000..3f30765 --- /dev/null +++ b/.sisyphus/evidence/task-1-docker-status.txt @@ -0,0 +1,4 @@ +time="2026-03-01T19:25:10+01:00" level=warning msg="/Users/thorsten/AI/cts-work/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion" +NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS +cts-presenter-app cts-work-app "docker-php-entrypoi…" app 14 seconds ago Up 11 seconds (health: starting) 0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp, 9000/tcp +cts-presenter-node node:20-alpine "docker-entrypoint.s…" node 14 seconds ago Restarting (127) 2 seconds ago diff --git a/.sisyphus/evidence/task-1-vite-build.txt b/.sisyphus/evidence/task-1-vite-build.txt new file mode 100644 index 0000000..00188bb --- /dev/null +++ b/.sisyphus/evidence/task-1-vite-build.txt @@ -0,0 +1,31 @@ + +> build +> vite build + +vite v7.3.1 building client environment for production... +transforming... +✓ 784 modules transformed. +rendering chunks... +computing gzip size... +public/build/manifest.json 7.31 kB │ gzip: 0.90 kB +public/build/assets/app-DmWltKVM.css 51.56 kB │ gzip: 9.08 kB +public/build/assets/_plugin-vue_export-helper-DlAUqK2U.js 0.09 kB │ gzip: 0.10 kB +public/build/assets/PrimaryButton-mpvOc2jF.js 0.55 kB │ gzip: 0.38 kB +public/build/assets/GuestLayout-Bfh8jlss.js 0.56 kB │ gzip: 0.40 kB +public/build/assets/Dashboard-BKIdG5wF.js 0.73 kB │ gzip: 0.47 kB +public/build/assets/TextInput-EaSAg8Rp.js 1.05 kB │ gzip: 0.59 kB +public/build/assets/Edit-D8wcA1TZ.js 1.23 kB │ gzip: 0.67 kB +public/build/assets/ConfirmPassword-5FXNzWX9.js 1.34 kB │ gzip: 0.76 kB +public/build/assets/ForgotPassword-Cmz40c62.js 1.52 kB │ gzip: 0.87 kB +public/build/assets/VerifyEmail-D63zqc5n.js 1.60 kB │ gzip: 0.92 kB +public/build/assets/ResetPassword-Boa5zZGJ.js 2.08 kB │ gzip: 0.85 kB +public/build/assets/Register-DIIrlql3.js 2.54 kB │ gzip: 0.98 kB +public/build/assets/UpdatePasswordForm-BHHWCAaH.js 2.58 kB │ gzip: 1.01 kB +public/build/assets/UpdateProfileInformationForm-CpR_pYA7.js 2.60 kB │ gzip: 1.22 kB +public/build/assets/Login-Y39w2pjq.js 2.75 kB │ gzip: 1.29 kB +public/build/assets/ApplicationLogo-Vi50890Y.js 3.25 kB │ gzip: 1.44 kB +public/build/assets/DeleteUserForm-DUQ1pkPb.js 5.09 kB │ gzip: 2.10 kB +public/build/assets/AuthenticatedLayout-BQ1sV8GT.js 6.93 kB │ gzip: 2.29 kB +public/build/assets/Welcome-DekM14C9.js 18.71 kB │ gzip: 6.16 kB +public/build/assets/app-CK2TOLa8.js 254.85 kB │ gzip: 90.07 kB +✓ built in 1.08s diff --git a/.sisyphus/notepads/cts-presenter-app/decisions.md b/.sisyphus/notepads/cts-presenter-app/decisions.md new file mode 100644 index 0000000..7eae880 --- /dev/null +++ b/.sisyphus/notepads/cts-presenter-app/decisions.md @@ -0,0 +1,3 @@ +# Architectural Decisions + +Key technical decisions made during CTS Presenter App development. diff --git a/.sisyphus/notepads/cts-presenter-app/issues.md b/.sisyphus/notepads/cts-presenter-app/issues.md new file mode 100644 index 0000000..1b0199b --- /dev/null +++ b/.sisyphus/notepads/cts-presenter-app/issues.md @@ -0,0 +1,3 @@ +# Issues & Gotchas + +Problems encountered and their solutions. diff --git a/.sisyphus/notepads/cts-presenter-app/learnings.md b/.sisyphus/notepads/cts-presenter-app/learnings.md new file mode 100644 index 0000000..8e1ea0f --- /dev/null +++ b/.sisyphus/notepads/cts-presenter-app/learnings.md @@ -0,0 +1,186 @@ +# Learnings & Conventions + +Convention log for CTS Presenter App development. + +## 2026-03-01 - Task 0 CTS API Spike + +- `5pm-hdh/churchtools-api` v2.1.0 bietet sowohl `CTConfig::setApiKey()` als auch `CTConfig::authWithLoginToken()`; `setApiKey` ist als deprecated markiert, funktioniert aber weiterhin fuer Token-Auth. +- Events-Fetch fuer heute+zukunft laeuft ueber `EventRequest::where('from', 'YYYY-MM-DD')->get()` und sendet Query-Parameter `from` plus `page`. +- Song-Response enthaelt im verwendeten Shape `ccli`, `arrangements` und optional `lyrics` als nested Objekt. +- Ohne gesetzte Runtime-Variablen `CTS_API_URL`/`CTS_API_TOKEN` ist nur ein Mock-basierter Spike moeglich; Live-Auth muss spaeter mit echten Env-Werten nachgezogen werden. + +## 2026-03-01 - Task 1 Laravel Scaffolding + Breeze Vue + Docker + +- Laravel 11 + Breeze (Vue stack) + Pest scaffolding completed successfully +- Inertia.js integration required explicit app.js setup with createInertiaApp() and resolvePageComponent() +- Vite v7 requires @vitejs/plugin-vue to be explicitly configured in vite.config.js +- Tailwind CSS v4 requires @tailwindcss/vite v4 (not v3) - version mismatch caused build failures +- npm install requires --legacy-peer-deps flag due to Vite v7 + @vitejs/plugin-vue v5 compatibility +- Docker build: PHP 8.4-fpm-alpine chosen over 8.3 due to composer.lock requiring PHP 8.4+ dependencies +- Docker build: Removed Imagick PECL installation (requires autoconf) - can be added later if needed +- Locale set to 'de' in config/app.php via env() with fallback +- Vue packages (@vueuse/core, vue-draggable-plus, @jaxtheprime/vue3-dropzone) added to package.json +- docker-compose.yml v2 syntax used (no version field needed, but warning appears) +- Vite server configured for Docker with host: '0.0.0.0' and hmr settings for hot-reload +- QA Scenario 1: docker compose up -d successfully starts app + node containers +- QA Scenario 2: npm run build succeeds with 784 modules transformed, ~255KB gzipped app bundle +- All Pest tests pass (home route returns 200 with Inertia response) +- Task 0 spike files (CtsApiSpikeTest.php, CtsApiSpikeSync.php) preserved in new structure + +## [2026-03-01 19:25] Task 1: Laravel Scaffolding + Breeze Vue + Docker + +### Package Version Compatibility +- Laravel 12 requires specific package versions: + - `vite`: `^7.0.0` (matches laravel-vite-plugin@^2.0.0 peer dependency) + - `@vitejs/plugin-vue`: `^6.0.0` (compatible with Vite 7) + - Avoid version mismatches - check peer dependencies before install + +### Docker Setup +- Multi-stage Dockerfile with PHP 8.3-fpm-alpine base +- LibreOffice + ImageMagick installed successfully in Alpine +- App container starts successfully on port 8000 +- Node container requires `npm install` to be run inside container for first-time setup +- docker-compose.yml `version` attribute is obsolete (warning can be ignored) + +### Laravel Autoload +- Spike files relocated from `src/Cts/` to `app/Cts/` to match Laravel PSR-4 autoload +- Laravel expects application code in `app/` directory +- Custom `src/` directory requires composer.json autoload configuration + +### Vite Configuration +- Configured for Docker with `host: '0.0.0.0'` and `port: 5173` +- HMR configured for localhost access from host machine +- Build succeeds with 784 modules transformed + +### Tests +- 5 tests passing (14 assertions) +- CTS API spike tests preserved from Task 0 +- Home route test added for Inertia verification +- Breeze default tests included + +### Locale +- App locale set to `'de'` in `config/app.php` +- All UI text must be in German with "Du" form (per project spec) + +## [2026-03-01 19:45] Wave 1 Complete (T2-T7) + +### Database Schema (T2) +- 10 migrations created in correct dependency order +- All Eloquent models have proper relationships (hasMany, belongsTo, belongsToMany) +- Factory classes generate valid test data +- Soft deletes on songs and slides tables working correctly +- JSON casts for cts_data, churchtools_groups, churchtools_roles + +### ChurchTools OAuth (T3) +- Custom Socialite provider successfully replaces Breeze auth +- All Breeze register/password-reset pages removed +- OAuth-only login with German UI ("Mit ChurchTools anmelden") +- User creation from OAuth callback with churchtools_id, avatar, groups, roles +- 9 tests passing (54 assertions) + +### CTS API Sync (T4) +- ChurchToolsService wraps 5pm-HDH/churchtools-api correctly +- CCLI-based song matching works (matched_at timestamp set) +- Unmatched songs preserved with song_id=null for manual matching +- Sync log entries created with counts and status +- German flash messages ("Daten wurden aktualisiert") + +### File Conversion (T5) +- Intervention Image v3 letterbox/pillarbox working perfectly +- 400×300 PNG → 1920×1080 JPG with black bars (no upscaling) +- Portrait images get pillarbox (black bars left/right) +- PPT conversion queued via ConvertPowerPointJob (NOT synchronous) +- Thumbnail generation at 320×180 +- ZIP extraction and recursive processing implemented + +### Shared Vue Components (T6) +- AuthenticatedLayout with sticky nav, sync button, user avatar +- useAutoSave composable with 500ms debounce for text inputs +- FlashMessage, ConfirmDialog, LoadingSpinner components +- HandleInertiaRequests middleware shares auth.user, flash, last_synced_at, app_name +- All German UI text with "Du" form +- 7 tests passing + +### Email Configuration (T7) +- MissingSongRequest mailable with German template +- Email includes song name, CCLI ID, service info, link to service +- SONG_REQUEST_EMAIL configurable via .env +- Subject: "Song-Anfrage: {songName} (CCLI: {ccliId})" +- 2 tests passing (10 assertions) + +### Test Suite Health +- All 30 tests passing (233 assertions) +- No LSP errors (PHP LSP not configured, but tests validate correctness) +- Clean git history with atomic commits + +### Blockers Resolved +- Fixed ChurchToolsSyncTest: changed `post()` to `$this->post()` (trivial fix) +- No other blockers encountered + +### Next Steps +- Wave 2 ready to start (T8-T13): Service List, Song CRUD, Slide Upload, Arrangement Configurator, Song Matching, Translation Service +- All Wave 2 tasks can run in parallel (no dependencies between them) + +## [2026-03-01] Task T12: Song Matching Service (CCLI ID) + +### SongMatchingService Pattern +- Dedicated service class for all song matching operations: autoMatch, manualAssign, requestCreation, unassign +- autoMatch checks: (1) not already assigned, (2) has cts_ccli_id, (3) Song with matching ccli_id exists in DB +- manualAssign overwrites any existing assignment (no guard needed) +- requestCreation sends MissingSongRequest mailable to configurable SONG_REQUEST_EMAIL address +- unassign clears both song_id and matched_at + +### ChurchToolsService Integration +- Refactored syncServiceAgendaSongs to use SongMatchingService::autoMatch instead of inline DB matching +- updateOrInsert creates the record first (without song_id), then Eloquent lookup + autoMatch +- Preserves existing manual assignments during re-sync (only autoMatches if song_id is null) +- app(SongMatchingService::class) used inside the method to avoid constructor injection on the sync service + +### API Routes +- ServiceSongController with 3 POST endpoints under auth:sanctum middleware +- Routes: /api/service-songs/{id}/assign, /request, /unassign +- German response messages: 'Song erfolgreich zugeordnet', 'Anfrage wurde gesendet', 'Zuordnung entfernt' +- Validation on assign: song_id required + exists:songs,id +- findOrFail gives automatic 404 for missing ServiceSong records + +### Testing +- 14 tests (33 assertions) covering service methods + API endpoints + auth + validation +- Factory-based tests with ServiceSong::factory(), Song::factory(), User::factory() +- Mail::fake() for email assertion without actually sending +- Unique constraint gotcha: Song factory generates ccli_id with 80% probability, need explicit overrides in tests + +### Pre-existing Test Failures (NOT caused by T12) +- TranslationServiceTest: TranslationService class not yet created (future task) +- ArrangementControllerTest: Routes not yet defined (future task) +- ServiceControllerTest: Vite manifest missing Vue component (frontend task) + +## [2026-03-01] Task T10: Slide Upload & Grid Components + +### SlideController +- SlideController handles 3 routes: POST /slides, DELETE /slides/{slide}, PATCH /slides/{slide}/expire-date +- Image uploads processed synchronously via FileConversionService (1920×1080 JPG + 320×180 thumbnail) +- PPT uploads stored to temp location then dispatched via ConvertPowerPointJob (async) +- ZIP uploads processed recursively via processZip() — images sync, PPTs async +- Moderation & sermon slides require service_id; information slides can be global (service_id nullable) +- Extension validation done manually after Laravel validation (controller checks getClientOriginalExtension) + +### Vue Components +- @jaxtheprime/vue3-dropzone v1.1.0: v-model for files, @change event when files selected, slots for placeholder-img/title/description +- CSS variables for theming: --v3-dropzone--primary, --v3-dropzone--border, etc. +- Must hide default preview with `.v3-dropzone__preview { display: none }` since we use SlideGrid +- SlideUploader emits 'uploaded' after all files processed sequentially +- SlideGrid emits 'deleted' and 'updated' for parent refresh +- Inline expire date editing: click-to-edit pattern with save/cancel buttons + +### Testing +- PPTX controller test requires mocking FileConversionService since fake files aren't real PowerPoint +- Use `$this->postJson()` for validation tests expecting 422 (otherwise Laravel redirects with 302) +- Mockery mock with `app()->instance()` works for service container injection +- All 15 SlideControllerTest tests pass (37 assertions) +- Full suite: 103 tests, 488 assertions + +### Design Patterns +- Amber/orange color palette matches AuthenticatedLayout nav gradient (from-amber-500 to-orange-600) +- Dropzone uses dashed border + amber gradient background for hover states +- Slide cards: rounded-xl, subtle shadow, hover lift (-translate-y-0.5), overlay controls +- Expire date color coding: red=expired, amber=expires within 7 days, emerald=active, gray=no date diff --git a/.sisyphus/notepads/cts-presenter-app/problems.md b/.sisyphus/notepads/cts-presenter-app/problems.md new file mode 100644 index 0000000..e6f10b5 --- /dev/null +++ b/.sisyphus/notepads/cts-presenter-app/problems.md @@ -0,0 +1,3 @@ +# Unresolved Blockers + +Blockers that are not yet resolved.