Update admin PWA events, branding, and packages
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-19 11:35:38 +01:00
parent 926bc7d070
commit fbff2afa3e
43 changed files with 6846 additions and 6323 deletions

View File

@@ -8,6 +8,8 @@ use App\Models\EventType;
use App\Models\Package;
use App\Services\EventJoinTokenService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Tests\TestCase;
class EventBrandingResponseTest extends TestCase
@@ -131,4 +133,19 @@ class EventBrandingResponseTest extends TestCase
$response->assertJsonPath('branding.secondary_color', '#fedcba');
$response->assertJsonPath('branding.buttons.radius', 8);
}
public function test_branding_asset_uses_public_disk(): void
{
Storage::fake('public');
Storage::disk('public')->put('branding/logos/test.png', 'logo');
$url = URL::temporarySignedRoute('api.v1.branding.asset', now()->addMinutes(5), [
'path' => 'branding/logos/test.png',
]);
$response = $this->get($url);
$response->assertOk();
$this->assertSame('logo', $response->streamedContent());
}
}

View File

@@ -214,12 +214,16 @@ class EventControllerTest extends TenantTestCase
$response = $this->authenticatedRequest('PUT', "/api/v1/tenant/events/{$event->slug}", [
'settings' => [
'engagement_mode' => 'photo_only',
'guest_downloads_enabled' => false,
'guest_sharing_enabled' => true,
],
]);
$response->assertOk();
$event->refresh();
$this->assertSame('photo_only', data_get($event->settings, 'engagement_mode'));
$this->assertFalse((bool) data_get($event->settings, 'guest_downloads_enabled'));
$this->assertTrue((bool) data_get($event->settings, 'guest_sharing_enabled'));
}
public function test_create_event_rejects_unavailable_service_tier_for_partner_kontingent(): void
@@ -299,6 +303,94 @@ class EventControllerTest extends TenantTestCase
$this->assertSame('blur_last', data_get($settings, 'live_show.background_mode'));
}
public function test_update_event_accepts_svg_watermark_data_url(): void
{
Storage::fake('public');
$eventType = EventType::factory()->create();
$event = Event::factory()->for($this->tenant)->create([
'event_type_id' => $eventType->id,
'name' => 'SVG Watermark Event',
'slug' => 'svg-watermark',
'date' => now()->addDays(2),
]);
$svg = '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="20"></svg>';
$dataUrl = 'data:image/svg+xml;base64,'.base64_encode($svg);
$response = $this->authenticatedRequest('PUT', "/api/v1/tenant/events/{$event->slug}", [
'settings' => [
'watermark' => [
'mode' => 'custom',
'asset_data_url' => $dataUrl,
'position' => 'bottom-right',
],
],
]);
$response->assertOk();
$event->refresh();
$path = data_get($event->settings, 'watermark.asset');
$this->assertSame("branding/watermarks/event-{$event->id}.svg", $path);
Storage::disk('public')->assertExists($path);
}
public function test_show_event_includes_signed_watermark_asset_url(): void
{
Storage::fake('public');
$eventType = EventType::factory()->create();
$event = Event::factory()->for($this->tenant)->create([
'event_type_id' => $eventType->id,
'name' => 'Watermark Preview Event',
'slug' => 'watermark-preview',
'date' => now()->addDays(2),
'settings' => [
'watermark' => [
'mode' => 'custom',
'asset' => 'branding/watermarks/event-123.png',
],
],
]);
Storage::disk('public')->put('branding/watermarks/event-123.png', 'asset');
$response = $this->authenticatedRequest('GET', "/api/v1/tenant/events/{$event->slug}");
$response->assertOk();
$url = (string) $response->json('data.settings.watermark.asset_url');
$this->assertNotSame('', $url);
$this->assertStringContainsString('/api/v1/branding/asset/branding/watermarks/event-123.png', $url);
$this->assertStringContainsString('signature=', $url);
}
public function test_show_event_includes_base_watermark_asset_url_when_missing_settings(): void
{
Storage::fake('public');
config(['watermark.base.asset' => 'branding/watermarks/base-watermark.png']);
Storage::disk('public')->put('branding/watermarks/base-watermark.png', 'asset');
$eventType = EventType::factory()->create();
$event = Event::factory()->for($this->tenant)->create([
'event_type_id' => $eventType->id,
'name' => 'Base Watermark Preview',
'slug' => 'base-watermark-preview',
'date' => now()->addDays(2),
'settings' => [],
]);
$response = $this->authenticatedRequest('GET', "/api/v1/tenant/events/{$event->slug}");
$response->assertOk();
$this->assertSame('base', $response->json('data.settings.watermark.mode'));
$url = (string) $response->json('data.settings.watermark.asset_url');
$this->assertNotSame('', $url);
$this->assertStringContainsString('/api/v1/branding/asset/branding/watermarks/base-watermark.png', $url);
$this->assertStringContainsString('signature=', $url);
}
public function test_update_event_uploads_branding_logo_data_url(): void
{
Storage::fake('public');

View File

@@ -14,7 +14,7 @@ class SeedDemoSwitcherTenantsTest extends TestCase
{
use RefreshDatabase;
public function testStarterEmptyTenantHasOneRemainingEvent(): void
public function test_starter_empty_tenant_has_one_remaining_event(): void
{
config(['app.env' => 'local']);
app()->detectEnvironment(fn () => 'local');
@@ -32,7 +32,7 @@ class SeedDemoSwitcherTenantsTest extends TestCase
$this->assertSame(1, $starterPackage->package->max_events_per_year - $starterPackage->used_events);
}
public function testStarterWeddingTenantConsumesEvent(): void
public function test_standard_wedding_tenant_consumes_event(): void
{
config(['app.env' => 'local']);
app()->detectEnvironment(fn () => 'local');
@@ -42,12 +42,12 @@ class SeedDemoSwitcherTenantsTest extends TestCase
Artisan::call('demo:seed-switcher');
$starterWedding = Tenant::where('slug', 'demo-starter-wedding')->firstOrFail();
$starterPackage = TenantPackage::where('tenant_id', $starterWedding->id)->with('package')->firstOrFail();
$standardWedding = Tenant::where('slug', 'demo-starter-wedding')->firstOrFail();
$standardPackage = TenantPackage::where('tenant_id', $standardWedding->id)->with('package')->firstOrFail();
$this->assertSame('starter', $starterPackage->package->slug);
$this->assertSame(1, $starterPackage->used_events);
$this->assertSame(0, $starterPackage->package->max_events_per_year - $starterPackage->used_events);
$this->assertSame('standard', $standardPackage->package->slug);
$this->assertSame(1, $standardPackage->used_events);
$this->assertSame(4, $standardPackage->package->max_events_per_year - $standardPackage->used_events);
}
private function seedDemoPackages(): void

View File

@@ -70,4 +70,28 @@ class TenantPackageOverviewTest extends TenantTestCase
$this->assertSame(10, $payload['active_package']['package_limits']['used_gallery_days']);
$this->assertSame(9, $payload['active_package']['remaining_events']);
}
public function test_active_package_falls_back_to_active_endcustomer_package(): void
{
$package = Package::factory()->endcustomer()->create([
'features' => ['custom_branding'],
'branding_allowed' => true,
'watermark_allowed' => false,
]);
TenantPackage::factory()->create([
'tenant_id' => $this->tenant->id,
'package_id' => $package->id,
'active' => true,
]);
$request = Request::create('/api/v1/tenant/packages', 'GET');
$request->attributes->set('tenant', $this->tenant);
$response = app(TenantPackageController::class)->index($request);
$payload = $response->getData(true);
$this->assertSame($package->id, $payload['active_package']['package_id']);
$this->assertSame(['custom_branding'], $payload['active_package']['package_limits']['features']);
}
}