diff --git a/.sisyphus/evidence/task-7-nav-link.png b/.sisyphus/evidence/task-7-nav-link.png new file mode 100644 index 0000000..6f1d9a8 Binary files /dev/null and b/.sisyphus/evidence/task-7-nav-link.png differ diff --git a/.sisyphus/evidence/task-7-settings-save.png b/.sisyphus/evidence/task-7-settings-save.png new file mode 100644 index 0000000..50e950f Binary files /dev/null and b/.sisyphus/evidence/task-7-settings-save.png differ diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php new file mode 100644 index 0000000..833a558 --- /dev/null +++ b/app/Http/Controllers/SettingsController.php @@ -0,0 +1,43 @@ + $settings, + ]); + } + + public function update(Request $request): JsonResponse + { + $validated = $request->validate([ + 'key' => ['required', 'string', 'in:'.implode(',', self::MACRO_KEYS)], + 'value' => ['nullable', 'string', 'max:500'], + ]); + + Setting::set($validated['key'], $validated['value']); + + return response()->json(['success' => true]); + } +} diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 55d3fe8..4125518 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -3,6 +3,7 @@ namespace App\Http\Middleware; use App\Models\CtsSyncLog; +use App\Models\Setting; use Illuminate\Http\Request; use Inertia\Middleware; @@ -46,6 +47,12 @@ public function share(Request $request): array ], 'last_synced_at' => CtsSyncLog::latest()->first()?->synced_at, 'app_name' => config('app.name'), + 'macroSettings' => [ + 'name' => Setting::get('macro_name'), + 'uuid' => Setting::get('macro_uuid'), + 'collectionName' => Setting::get('macro_collection_name', '--MAIN--'), + 'collectionUuid' => Setting::get('macro_collection_uuid', '8D02FC57-83F8-4042-9B90-81C229728426'), + ], ]; } } diff --git a/app/Models/Setting.php b/app/Models/Setting.php new file mode 100644 index 0000000..372e878 --- /dev/null +++ b/app/Models/Setting.php @@ -0,0 +1,29 @@ +where('key', $key)->first(); + + return $setting?->value ?? $default; + } + + public static function set(string $key, ?string $value): void + { + DB::table('settings')->updateOrInsert( + ['key' => $key], + ['value' => $value, 'updated_at' => now()], + ); + } +} diff --git a/app/Services/ProExportService.php b/app/Services/ProExportService.php index c47813a..45f5c24 100644 --- a/app/Services/ProExportService.php +++ b/app/Services/ProExportService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Models\Setting; use App\Models\Song; use ProPresenter\Parser\ProFileGenerator; @@ -15,7 +16,7 @@ public function generateProFile(Song $song): string $arrangements = $this->buildArrangements($song); $ccli = $this->buildCcliMetadata($song); - $tempPath = sys_get_temp_dir() . '/' . uniqid('pro-export-') . '.pro'; + $tempPath = sys_get_temp_dir().'/'.uniqid('pro-export-').'.pro'; ProFileGenerator::generateAndWrite($tempPath, $song->title, $groups, $arrangements, $ccli); @@ -25,9 +26,11 @@ public function generateProFile(Song $song): string private function buildGroups(Song $song): array { $groups = []; + $macroData = $this->buildMacroData(); foreach ($song->groups->sortBy('order') as $group) { $slides = []; + $isCopyrightGroup = strcasecmp($group->name, 'COPYRIGHT') === 0; foreach ($group->slides->sortBy('order') as $slide) { $slideData = ['text' => $slide->text_content ?? '']; @@ -36,6 +39,10 @@ private function buildGroups(Song $song): array $slideData['translation'] = $slide->text_content_translated; } + if ($isCopyrightGroup && $macroData) { + $slideData['macro'] = $macroData; + } + $slides[] = $slideData; } @@ -49,6 +56,23 @@ private function buildGroups(Song $song): array return $groups; } + private function buildMacroData(): ?array + { + $name = Setting::get('macro_name'); + $uuid = Setting::get('macro_uuid'); + + if (! $name || ! $uuid) { + return null; + } + + return [ + 'name' => $name, + 'uuid' => $uuid, + 'collectionName' => Setting::get('macro_collection_name', '--MAIN--'), + 'collectionUuid' => Setting::get('macro_collection_uuid', '8D02FC57-83F8-4042-9B90-81C229728426'), + ]; + } + private function buildArrangements(Song $song): array { $arrangements = []; diff --git a/database/migrations/2026_03_02_200000_create_settings_table.php b/database/migrations/2026_03_02_200000_create_settings_table.php new file mode 100644 index 0000000..5cc2756 --- /dev/null +++ b/database/migrations/2026_03_02_200000_create_settings_table.php @@ -0,0 +1,23 @@ +id(); + $table->string('key')->unique(); + $table->text('value')->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('settings'); + } +}; diff --git a/resources/js/Layouts/AuthenticatedLayout.vue b/resources/js/Layouts/AuthenticatedLayout.vue index 5db0d83..0564992 100644 --- a/resources/js/Layouts/AuthenticatedLayout.vue +++ b/resources/js/Layouts/AuthenticatedLayout.vue @@ -115,6 +115,13 @@ function triggerSync() { > API-Log + + Einstellungen + API-Log + + Einstellungen + diff --git a/resources/js/Pages/Settings.vue b/resources/js/Pages/Settings.vue new file mode 100644 index 0000000..e787374 --- /dev/null +++ b/resources/js/Pages/Settings.vue @@ -0,0 +1,155 @@ + + + + + + + + + Einstellungen + + + + + + + + + ProPresenter Makro-Konfiguration + + + Diese Einstellungen werden beim Export auf Copyright-Folien als Makro-Aktion angewendet. + + + + + + + {{ field.label }} + + + + + + + + + + + + + + + + + + + + + {{ errors[field.key] }} + + + + Standard: {{ field.defaultValue }} + + + + + + + + diff --git a/routes/web.php b/routes/web.php index b5bd2bc..21b8db2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,16 +1,15 @@ group(function () { Route::get('/login', [AuthController::class, 'showLogin'])->name('login'); @@ -33,6 +32,7 @@ ] ); Auth::login($user); + return redirect()->route('dashboard'); })->name('dev-login'); }); @@ -66,6 +66,9 @@ Route::get('/api-logs', [ApiLogController::class, 'index'])->name('api-logs.index'); Route::get('/api-logs/{log}/response-body', [ApiLogController::class, 'responseBody'])->name('api-logs.response-body'); + Route::get('/settings', [SettingsController::class, 'index'])->name('settings.index'); + Route::patch('/settings', [SettingsController::class, 'update'])->name('settings.update'); + Route::post('/songs/{song}/arrangements', '\\App\\Http\\Controllers\\ArrangementController@store')->name('arrangements.store'); Route::post('/arrangements/{arrangement}/clone', '\\App\\Http\\Controllers\\ArrangementController@clone')->name('arrangements.clone'); Route::put('/arrangements/{arrangement}', '\\App\\Http\\Controllers\\ArrangementController@update')->name('arrangements.update'); @@ -81,6 +84,8 @@ |-------------------------------------------------------------------------- */ Route::post('/slides', '\\App\\Http\\Controllers\\SlideController@store')->name('slides.store'); + Route::delete('/slides/bulk', '\\App\\Http\\Controllers\\SlideController@destroyBulk')->name('slides.bulk-destroy'); + Route::post('/slides/reorder', '\\App\\Http\\Controllers\\SlideController@reorder')->name('slides.reorder'); Route::delete('/slides/{slide}', '\\App\\Http\\Controllers\\SlideController@destroy')->name('slides.destroy'); Route::patch('/slides/{slide}/expire-date', '\\App\\Http\\Controllers\\SlideController@updateExpireDate')->name('slides.update-expire-date'); });
+ Diese Einstellungen werden beim Export auf Copyright-Folien als Makro-Aktion angewendet. +
+ {{ errors[field.key] }} +
+ Standard: {{ field.defaultValue }} +