Commit graph

41 commits

Author SHA1 Message Date
Thorsten Bus 2ba612072f fix: resolve 17 pre-existing test failures (path refs, Mockery alias mocks)
- Update propresenter ref path from ../propresenter-work/ to ../propresenter/
- Fix ProFileImportTest assertion for CCLI-based upsert behavior
- Replace Mockery alias mocks with testable subclass pattern in
  PlaylistExportTest, eliminating @runInSeparateProcess requirement
- Use DI (app()) for PlaylistExportService in controller for testability
- All 302 tests pass (was 285 pass + 17 fail)
2026-03-30 12:43:50 +02:00
Thorsten Bus 0e3c647cfc feat: probundle export with media, image upscaling, upload dimension warnings
- Fix probundle exports missing images (double slides/ prefix in storage path)
- Replace manual ZipArchive with PresentationBundle + ProBundleWriter from parser plugin
- Add per-agenda-item download route and buttons for songs and slide items
- Remove text layer from image-only slides in .pro generation
- Fix image conversion: upscale small images, black bars on 2 sides max (contain)
- Add upload warnings for non-16:9 and sub-1920x1080 images (German, non-blocking)
- Update SlideFactory and all tests to use slides/ prefix in stored_filename
- Add 11 new tests (agenda download, image conversion, upload warnings)
2026-03-30 10:29:37 +02:00
Thorsten Bus 41426ff25b add better errorhandling for cts sync 2026-03-29 15:24:13 +02:00
Thorsten Bus 4c119b647d feat: add has_agenda flag to services and guard agenda sync
Events without an agenda in ChurchTools now gracefully set has_agenda=false
instead of throwing errors during sync. The edit/finalize buttons are
disabled in the frontend for services without an agenda.

Also fixes missing cts_song_id column on service_songs table.
2026-03-29 15:22:32 +02:00
Thorsten Bus e88079e211 feat(ui): update service list status columns for agenda model 2026-03-29 12:18:50 +02:00
Thorsten Bus 45955b70a2 feat(ui): add slide upload on agenda items 2026-03-29 12:12:58 +02:00
Thorsten Bus de431d29cc feat(export): agenda-ordered playlist export 2026-03-29 11:59:38 +02:00
Thorsten Bus 88661c6bef feat(sync): sync all CTS agenda items (not just songs) 2026-03-29 11:54:50 +02:00
Thorsten Bus 0b671956d6 feat(controller): pass agenda items to edit page 2026-03-29 11:48:28 +02:00
Thorsten Bus 2d70026a20 refactor(model): update finalizationStatus for agenda model 2026-03-29 11:46:54 +02:00
Thorsten Bus 7a71b8b2de chore(debug): add CTS agenda type discovery command 2026-03-29 11:39:24 +02:00
Thorsten Bus 1f367b6f37 feat(settings): add agenda configuration keys 2026-03-29 11:37:33 +02:00
Thorsten Bus 03224ffa06 feat(service): add AgendaMatcherService with wildcard namesmatching 2026-03-29 11:37:06 +02:00
Thorsten Bus 31d7634dbf feat(db): add service_agenda_items table + slides FK migration 2026-03-29 11:34:55 +02:00
Thorsten Bus 044b94b080 refactor(export): use in-memory content for zip bundle and playlist entries
Replace file-path-based zip entries with in-memory content via
file_get_contents. Rename .pro entry to 'data' (raw protobuf),
add addStoredEntry() helper with CM_STORE compression, and remove
temp directory management.
2026-03-06 10:25:21 +01:00
Thorsten Bus af0c72ebcc feat(ui): improve arrangement configurator, song preview, and downloads
Simplify ArrangementConfigurator: replace color pickers with compact
pills, add click-to-add from pool, use watcher-based auto-select for
new/cloned arrangements, remove group_colors from save payload.

Enhance SongsBlock preview: color-coded group headers with tinted
backgrounds, PDF download button inside preview modal, .pro download
link per matched song, show DB ccli_id with fallback to CTS ccli_id.

Fix Modal z-index for nested dialogs. Fix SlideUploader duplicate
upload on watch by adding deep option and upload guard. Expand API
log detail sections by default and increase JSON tree depth. Convert
song download button from emit to direct .pro download link.
2026-03-02 23:02:51 +01:00
Thorsten Bus b40c371edc feat(export): embed slide blocks in playlist and add roundtrip test
Add information, moderation, and sermon slide presentations as .pro
files in the generated .proplaylist bundle. Each block queries slides
by type/service, converts stored images, and generates a ProPresenter
presentation via ProFileGenerator.

Add test_download_pro_roundtrip_preserves_content that imports a .pro
file, exports it, re-reads with the parser, and asserts song name,
groups, slides, translations, arrangements, and CCLI metadata survive
the round-trip.
2026-03-02 23:02:30 +01:00
Thorsten Bus bef910b126 feat(slides): add sort_order column, bulk delete, and reorder endpoints
Add sort_order to slides table with migration and model fillable.
Add destroyBulk() for batch soft-delete by type/service_id and
reorder() for drag-and-drop slide ordering. Auto-assign sort_order
on image and zip uploads.
2026-03-02 23:02:19 +01:00
Thorsten Bus 04d271f96a style: apply Laravel Pint formatting across codebase
Auto-formatted by Laravel Pint (default Laravel preset): string
concatenation spacing, anonymous class brace placement, constructor
body shorthand, import ordering, and assertion indentation.
2026-03-02 23:02:03 +01:00
Thorsten Bus 5b35afb31d feat(export): add probundle export for service slide blocks
- Create ProBundleExportService with generateBundle() method
- Generate flat ZIP with .pro file + image files at root level
- Add downloadBundle() method to ServiceController
- Add services.download-bundle route
- Add .probundle download buttons to Information, Moderation, Sermon blocks
- Add 3 tests verifying ZIP structure and validation
- All tests pass (206/206, 1129 assertions)
2026-03-02 22:18:33 +01:00
Thorsten Bus fefa761748 feat(settings): add macro configuration infrastructure
- Migration: settings table with key (unique), value (text), timestamps
- Model: Setting with static get/set helpers using DB upsert
- Controller: SettingsController with index (Inertia) and update (JSON)
- Vue: Settings page with 4 macro fields, auto-save on blur
- Navigation: Einstellungen link in desktop + mobile nav after API-Log
- Shared props: macroSettings added to HandleInertiaRequests
- Integration: ProExportService injects macro into COPYRIGHT group slides
- Gracefully skips macro injection when settings empty/null
2026-03-02 22:00:19 +01:00
Thorsten Bus 6543133713 feat(songs): auto-select default arrangement on song match 2026-03-02 21:22:30 +01:00
Thorsten Bus a36841f920 feat(songs): add CTS song ID matching, info slide date filter, arrangement ordering, translation defaults
- Add cts_song_id column to songs and service_songs for CCLI-free matching fallback
- Filter information slides by uploaded_at <= service date (not shown before upload)
- New arrangements use song's default group ordering instead of cloning
- Auto-set use_translation=true when matched song has translation
- Update syncSongs/syncServiceAgendaSongs to store and use cts_song_id
- Add tests for CTS song ID fallback, upload date filtering, and translation defaults
2026-03-02 14:10:40 +01:00
Thorsten Bus bb25b3b98d feat(logs): store and lazy-load actual API response body in request log
- Add response_body longText column to api_request_logs table
- Store serialized response (max 500KB) in ChurchToolsService::logApiCall
- Add GET /api-logs/{log}/response-body endpoint for lazy loading
- Replace static 'Array mit X Einträgen' with collapsible JSON viewer
- Response body fetched on-demand when user clicks to expand
2026-03-02 13:25:45 +01:00
Thorsten Bus 22f1829132 fix(slides): show information slides without expire_date in service edit
- Add whereNull('expire_date') as alternative condition so info slides
  without an expiration date appear in all services
- Fix test assertion ordering by setting explicit uploaded_at timestamps
2026-03-02 13:25:22 +01:00
Thorsten Bus 747d2c3c07 feat(services): implement .proplaylist export for finalized services
- Add PlaylistExportService that generates .proplaylist with embedded .pro files
- Update ServiceController::download() with real playlist export (replaces placeholder)
- Return 403 for non-finalized services, 422 when no exportable songs found
- Update frontend downloadService() to handle binary file blob response
- Replace obsolete placeholder test with proper 403 and 422 behavior tests
2026-03-02 12:27:55 +01:00
Thorsten Bus ca7160068e feat(songs): implement .pro file download/export from SongDB 2026-03-02 12:22:48 +01:00
Thorsten Bus 77d47f4b73 feat(songs): implement .pro file import with SongDB mapping 2026-03-02 12:21:01 +01:00
Thorsten Bus e2e1723b99 feat(logs): add expandable request/response details in API log 2026-03-02 12:14:34 +01:00
Thorsten Bus a5a520c754 feat(sync): limit CTS fetch to next 10 services 2026-03-02 12:04:24 +01:00
Thorsten Bus c3fa352adb feat(sync): add hourly CTS sync schedule 2026-03-02 12:03:52 +01:00
Thorsten Bus 85111c70e7 feat: add CTS API request logging with searchable frontend UI 2026-03-02 11:01:48 +01:00
Thorsten Bus d5abff0d82 fix: propagate actual sync error messages to frontend 2026-03-02 10:44:20 +01:00
Thorsten Bus 5459529c3a fix: remove duplicate opening brace in ServiceController index method 2026-03-02 10:43:35 +01:00
Thorsten Bus 3a1ba1fc7d feat(auth): add dummy test login for local dev + update env for Herd
- Update .env.example for Herd (APP_URL, CHURCHTOOLS_REDIRECT_URI)
- Add POST /dev-login route (local/testing only)
- Add "Test-Anmeldung" button to Login.vue
- Update UserFactory with OAuth fields (churchtools_id, avatar, groups, roles)
2026-03-01 22:33:29 +01:00
Thorsten Bus 27f8402ae8 feat: Wave 4 - Song DB Management + Finalization (T20-T24)
T20: Song DB Page
- Songs/Index.vue with search, action buttons, pagination
- Upload area for .pro files (calls T23 placeholder)
- Song-Datenbank nav link added to AuthenticatedLayout
- Tests: 9 new (44 assertions)

T21: Song DB Edit Popup
- SongEditModal.vue with metadata + ArrangementConfigurator
- Auto-save with fetch (500ms debounce for text, immediate on blur)
- Tests: 11 new (53 assertions)

T22: Song DB Translate Page
- Songs/Translate.vue with two-column editor
- URL fetch or manual paste, line-count constraints
- Group headers with colors, save marks has_translation=true
- Tests: 1 new (12 assertions)

T23: .pro File Placeholders
- ProParserNotImplementedException with HTTP 501
- ProFileController with importPro/downloadPro placeholders
- German error messages
- Tests: 5 new (7 assertions)

T24: Service Finalization + Status
- Two-step finalization with warnings (unmatched songs, missing slides)
- Download placeholder toast
- isReadyToFinalize accessor on Service model
- Tests: 11 new (30 assertions)

All tests passing: 174/174 (905 assertions)
Build: ✓ Vite production build successful
German UI: All user-facing text in German with 'Du' form
2026-03-01 20:30:07 +01:00
Thorsten Bus d75d748417 feat: T19 - Song Preview Modal + PDF Download
- SongPreviewModal.vue: Teleport modal with song text in arrangement order
  - Group headers with group.color background
  - Side-by-side translations when use_translation=true
  - Copyright footer from song metadata
  - Close button + click-outside/Escape dismiss
  - PDF download button

- SongPdfController.php: PDF generation endpoint
  - GET /songs/{song}/arrangements/{arrangement}/pdf
  - Uses barryvdh/laravel-dompdf with DejaVu Sans font
  - Old-school CSS only (NO Tailwind)
  - Handles German umlauts correctly

- resources/views/pdf/song.blade.php: PDF template
  - Title header, groups with colored headers, slide text, copyright footer
  - Includes translation text when present
  - Simple CSS: font-family, color, background-color, margin, padding

- Tests: 9 new (25 assertions)
  - PDF content type verification
  - Filename includes song title
  - Groups in correct arrangement order
  - Translation text included when present
  - Copyright footer present
  - German umlauts render correctly
  - Auth guard
  - 404 when arrangement doesn't belong to song
  - Empty arrangement handling

All tests passing: 133/133 (728 assertions)
Build: ✓ Vite production build successful
Fix: Removed duplicate content from SongPdfTest.php (parse error)
2026-03-01 20:15:02 +01:00
Thorsten Bus b2d230e991 feat: Wave 3 (partial) - Service Edit page + 4 blocks (T14-T18)
T14: Service Edit Page Layout + Routing
- ServiceController::edit() with eager-loaded relationships
- Services/Edit.vue with 4 collapsible accordion blocks
- Route: GET /services/{service}/edit
- Information slides query: global + service-specific with expire_date filtering
- Tests: 2 new (edit page render + auth guard)

T15: Information Block (Slides + Expire Dates)
- InformationBlock.vue with dynamic expire_date filtering
- Shows slides where type='information' AND expire_date >= service.date
- Global visibility across services (not service-specific)
- SlideUploader with showExpireDate=true
- SlideGrid with prominent expire date + inline editing
- Badge showing slide count + 'expiring soon' warning (within 3 days)
- Tests: 7 new (105 assertions)

T16: Moderation Block (Service-Specific)
- ModerationBlock.vue (service-specific slides)
- Filters: type='moderation' AND service_id = current_service
- No expire date field (unlike Information block)
- Service isolation (slides from Service A don't appear in Service B)
- Tests: 5 new (14 assertions)

T17: Sermon Block (Service-Specific)
- SermonBlock.vue (identical to Moderation but type='sermon')
- Service-specific slides, no expire date
- Tests: 5 new (14 assertions)

T18: Songs Block (Matching + Arrangement + Translation)
- SongsBlock.vue with conditional UI (unmatched vs matched states)
- Unmatched: 'Erstellung anfragen' button + searchable select for manual assign
- Matched: ArrangementConfigurator + translation checkbox + preview/download buttons
- ServiceSongController::update() for use_translation and song_arrangement_id
- ArrangementConfigurator emits 'arrangement-selected' for auto-save
- ServiceController::edit() provides songsCatalog for matching search
- Tests: 2 new (45 assertions)

T19: Song PDF (INCOMPLETE - timeout)
- SongPdfController.php created (partial)
- resources/views/pdf/song.blade.php created (partial)
- SongPreviewModal.vue MISSING
- Tests MISSING
- Will be completed in next commit

All tests passing: 124/124 (703 assertions)
Build: ✓ Vite production build successful
German UI: All user-facing text in German with 'Du' form
Dependencies: barryvdh/laravel-dompdf added for PDF generation
2026-03-01 20:09:47 +01:00
Thorsten Bus d915f8cfc2 feat: Wave 2 - Service list, Song CRUD, Slide upload, Arrangements, Song matching, Translation
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
2026-03-01 19:55:37 +01:00
Thorsten Bus 57d54ec06b feat: Wave 1 Foundation - Database, OAuth, Sync, Files, Layout, Email (T2-T7)
T2: Database Schema + All Migrations
- 10 migrations: users extension, services, songs, song_groups, song_slides,
  song_arrangements, song_arrangement_groups, service_songs, slides, cts_sync_log
- 9 Eloquent models with relationships and casts
- 9 factory classes for testing
- Tests: DatabaseSchemaTest (2 tests, 26 assertions) 

T3: ChurchTools OAuth Provider
- Custom Socialite provider for ChurchTools OAuth2
- AuthController with redirect/callback/logout
- Replaced Breeze login with OAuth-only (German UI)
- Removed all Breeze register/password-reset pages
- Tests: OAuthTest (9 tests, 54 assertions) 

T4: CTS API Service + Sync Command
- ChurchToolsService wrapping 5pm-HDH/churchtools-api
- SyncChurchToolsCommand (php artisan cts:sync)
- SyncController for refresh button
- CCLI-based song matching
- Tests: ChurchToolsSyncTest (2 tests) 

T5: File Conversion Service
- FileConversionService with letterbox/pillarbox to 1920×1080
- ConvertPowerPointJob (queued) with LibreOffice + spatie/pdf-to-image
- ZIP extraction and recursive processing
- Thumbnail generation (320×180)
- Tests: FileConversionTest (2 tests, 21 assertions) 

T6: Shared Vue Components
- AuthenticatedLayout with nav, user info, refresh button
- useAutoSave composable (500ms debounce)
- FlashMessage, ConfirmDialog, LoadingSpinner components
- HandleInertiaRequests middleware with shared props
- Tests: SharedPropsTest (7 tests) 

T7: Email Configuration
- MissingSongRequest mailable (German)
- Email template with song info and service link
- SONG_REQUEST_EMAIL config
- Tests: MissingSongMailTest (2 tests, 10 assertions) 

All tests passing: 30/30 (233 assertions)
All UI text in German with 'Du' form
Wave 1 complete: 7/7 tasks 
2026-03-01 19:39:26 +01:00
Thorsten Bus 1756473701 feat: Laravel 12 scaffolding with Breeze Vue + Docker setup
- Install Laravel 12 with Breeze (Vue stack + Inertia.js)
- Configure Pest testing framework (5 tests passing)
- Add Docker multi-stage build (PHP 8.3 + LibreOffice + ImageMagick)
- Create docker-compose.yml with app + node services
- Configure Vite for Docker hot-reload
- Set app locale to 'de' (German)
- Add Vue packages: @vueuse/core, vue-draggable-plus, vue3-dropzone
- Update .env.example with all project vars
- Relocate spike files: src/Cts/ → app/Cts/ (Laravel autoload)
- Tests: 5 passed (14 assertions)
- Vite build: successful
- Docker: app container running

Task: T1 - Laravel Scaffolding + Breeze Vue + Docker
2026-03-01 19:25:32 +01:00