feat: optional fixed viewport background on public pages
Adds background_fixed column, admin checkbox, fixed-position layers on the public layout, Dutch strings, and store tests. Made-with: Cursor
This commit is contained in:
@@ -23,6 +23,7 @@ trait ValidatesPreregistrationPageInput
|
|||||||
'post_submit_redirect_url' => ['nullable', 'string', 'url:http,https', 'max:500'],
|
'post_submit_redirect_url' => ['nullable', 'string', 'url:http,https', 'max:500'],
|
||||||
'background_overlay_color' => ['nullable', 'string', 'regex:/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'],
|
'background_overlay_color' => ['nullable', 'string', 'regex:/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'],
|
||||||
'background_overlay_opacity' => ['nullable', 'integer', 'min:0', 'max:100'],
|
'background_overlay_opacity' => ['nullable', 'integer', 'min:0', 'max:100'],
|
||||||
|
'background_fixed' => ['sometimes', 'boolean'],
|
||||||
'page_background' => ['nullable', 'file', 'image', 'mimes:jpeg,png,jpg,webp', 'max:5120'],
|
'page_background' => ['nullable', 'file', 'image', 'mimes:jpeg,png,jpg,webp', 'max:5120'],
|
||||||
'remove_page_background' => ['sometimes', 'boolean'],
|
'remove_page_background' => ['sometimes', 'boolean'],
|
||||||
'start_date' => ['required', 'date'],
|
'start_date' => ['required', 'date'],
|
||||||
@@ -69,6 +70,7 @@ trait ValidatesPreregistrationPageInput
|
|||||||
|
|
||||||
$this->merge([
|
$this->merge([
|
||||||
'is_active' => $this->boolean('is_active'),
|
'is_active' => $this->boolean('is_active'),
|
||||||
|
'background_fixed' => $this->boolean('background_fixed'),
|
||||||
'remove_page_background' => $this->boolean('remove_page_background'),
|
'remove_page_background' => $this->boolean('remove_page_background'),
|
||||||
'ticketshop_url' => $ticketshopNormalized,
|
'ticketshop_url' => $ticketshopNormalized,
|
||||||
'post_submit_redirect_url' => $redirectNormalized,
|
'post_submit_redirect_url' => $redirectNormalized,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class PreregistrationPage extends Model
|
|||||||
'background_image',
|
'background_image',
|
||||||
'background_overlay_color',
|
'background_overlay_color',
|
||||||
'background_overlay_opacity',
|
'background_overlay_opacity',
|
||||||
|
'background_fixed',
|
||||||
'logo_image',
|
'logo_image',
|
||||||
'is_active',
|
'is_active',
|
||||||
];
|
];
|
||||||
@@ -42,6 +43,7 @@ class PreregistrationPage extends Model
|
|||||||
'start_date' => 'datetime',
|
'start_date' => 'datetime',
|
||||||
'end_date' => 'datetime',
|
'end_date' => 'datetime',
|
||||||
'phone_enabled' => 'boolean',
|
'phone_enabled' => 'boolean',
|
||||||
|
'background_fixed' => 'boolean',
|
||||||
'is_active' => 'boolean',
|
'is_active' => 'boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('preregistration_pages', function (Blueprint $table): void {
|
||||||
|
$table->boolean('background_fixed')->default(false)->after('background_overlay_opacity');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('preregistration_pages', function (Blueprint $table): void {
|
||||||
|
$table->dropColumn('background_fixed');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -22,5 +22,7 @@
|
|||||||
"Subscriber removed.": "Abonnee verwijderd.",
|
"Subscriber removed.": "Abonnee verwijderd.",
|
||||||
"Delete this subscriber? This cannot be undone.": "Deze abonnee verwijderen? Dit kan niet ongedaan worden gemaakt.",
|
"Delete this subscriber? This cannot be undone.": "Deze abonnee verwijderen? Dit kan niet ongedaan worden gemaakt.",
|
||||||
"Remove": "Verwijderen",
|
"Remove": "Verwijderen",
|
||||||
"Actions": "Acties"
|
"Actions": "Acties",
|
||||||
|
"Fix background to viewport": "Achtergrond vastzetten op het scherm",
|
||||||
|
"When enabled, the background image and overlay stay fixed while visitors scroll long content.": "Als dit aan staat, blijven de achtergrondafbeelding en de overlay stilstaan terwijl bezoekers door lange inhoud scrollen."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,19 @@
|
|||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<label class="inline-flex cursor-pointer items-center gap-2 text-sm text-slate-800">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="background_fixed"
|
||||||
|
value="1"
|
||||||
|
class="rounded border-slate-300 text-indigo-600 focus:ring-indigo-500"
|
||||||
|
@checked(old('background_fixed', $page?->background_fixed ?? false))
|
||||||
|
/>
|
||||||
|
{{ __('Fix background to viewport') }}
|
||||||
|
</label>
|
||||||
|
<p class="mt-1 text-xs text-slate-600">{{ __('When enabled, the background image and overlay stay fixed while visitors scroll long content.') }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-6 sm:grid-cols-2">
|
<div class="grid gap-6 sm:grid-cols-2">
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
$formButtonLabel = (string) (data_get($formContent, 'button_label') ?: __('public.register_button'));
|
$formButtonLabel = (string) (data_get($formContent, 'button_label') ?: __('public.register_button'));
|
||||||
$formButtonColor = (string) data_get($formContent, 'button_color', '#F47B20');
|
$formButtonColor = (string) data_get($formContent, 'button_color', '#F47B20');
|
||||||
$formButtonTextColor = (string) data_get($formContent, 'button_text_color', '#FFFFFF');
|
$formButtonTextColor = (string) data_get($formContent, 'button_text_color', '#FFFFFF');
|
||||||
|
$bgFixed = $page->background_fixed;
|
||||||
|
$bgLayerPosition = $bgFixed ? 'fixed inset-0 pointer-events-none z-0' : 'absolute inset-0';
|
||||||
|
$overlayPosition = $bgFixed ? 'fixed inset-0 pointer-events-none z-[1]' : 'absolute inset-0';
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@extends('layouts.public')
|
@extends('layouts.public')
|
||||||
@@ -28,19 +31,19 @@
|
|||||||
<div class="relative min-h-screen w-full overflow-x-hidden">
|
<div class="relative min-h-screen w-full overflow-x-hidden">
|
||||||
@if ($bgUrl !== null)
|
@if ($bgUrl !== null)
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
class="{{ $bgLayerPosition }} bg-cover bg-center bg-no-repeat"
|
||||||
style="background-image: url('{{ e($bgUrl) }}')"
|
style="background-image: url('{{ e($bgUrl) }}')"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></div>
|
></div>
|
||||||
@else
|
@else
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950"
|
class="{{ $bgLayerPosition }} bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></div>
|
></div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0"
|
class="{{ $overlayPosition }}"
|
||||||
style="background-color: {{ e($overlayColor) }}; opacity: {{ $overlayOpacity }}"
|
style="background-color: {{ e($overlayColor) }}; opacity: {{ $overlayOpacity }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></div>
|
></div>
|
||||||
|
|||||||
@@ -32,9 +32,31 @@ class StorePreregistrationPageTest extends TestCase
|
|||||||
$page = PreregistrationPage::query()->first();
|
$page = PreregistrationPage::query()->first();
|
||||||
$response->assertRedirect(route('admin.pages.edit', $page));
|
$response->assertRedirect(route('admin.pages.edit', $page));
|
||||||
$this->assertSame('Summer Fest', $page?->title);
|
$this->assertSame('Summer Fest', $page?->title);
|
||||||
|
$this->assertFalse($page?->background_fixed);
|
||||||
$this->assertGreaterThanOrEqual(4, PageBlock::query()->where('preregistration_page_id', $page?->id)->count());
|
$this->assertGreaterThanOrEqual(4, PageBlock::query()->where('preregistration_page_id', $page?->id)->count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_store_can_enable_fixed_background(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create(['role' => 'user']);
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->post(route('admin.pages.store'), [
|
||||||
|
'title' => 'Winter Fest',
|
||||||
|
'thank_you_message' => null,
|
||||||
|
'expired_message' => null,
|
||||||
|
'ticketshop_url' => null,
|
||||||
|
'start_date' => '2026-06-01T10:00',
|
||||||
|
'end_date' => '2026-06-30T18:00',
|
||||||
|
'is_active' => true,
|
||||||
|
'background_fixed' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$page = PreregistrationPage::query()->where('title', 'Winter Fest')->first();
|
||||||
|
$response->assertRedirect(route('admin.pages.edit', $page));
|
||||||
|
$this->assertNotNull($page);
|
||||||
|
$this->assertTrue($page->background_fixed);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_validation_failure_redirects_back_with_input(): void
|
public function test_validation_failure_redirects_back_with_input(): void
|
||||||
{
|
{
|
||||||
$user = User::factory()->create(['role' => 'user']);
|
$user = User::factory()->create(['role' => 'user']);
|
||||||
|
|||||||
Reference in New Issue
Block a user