seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetUser = User::factory()->create([ 'name' => 'Alt Benutzer', 'email' => 'alt-user@example.com', 'is_active' => true, ]); $targetUser->assignRole('customer'); Profile::factory()->for($targetUser)->create([ 'salutation_key' => 'mr', 'title' => 'Dr.', 'first_name' => 'Alter', 'last_name' => 'Name', 'phone' => '030123', 'address' => 'Alte Strasse 1', 'country_code' => 'DE', 'backlink_url' => 'https://alt.example.com', ]); $companyA = Company::query()->create([ 'name' => 'Alpha GmbH', 'slug' => 'alpha-gmbh', ]); $companyB = Company::query()->create([ 'name' => 'Beta GmbH', 'slug' => 'beta-gmbh', ]); $targetUser->companies()->attach($companyA->id, ['role' => 'member']); $contact = Contact::query()->create([ 'company_id' => $companyA->id, 'first_name' => 'Max', 'last_name' => 'Muster', 'email' => 'max@example.com', 'phone' => '0123', 'responsibility' => 'Presse', ]); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $targetUser->id]) ->set('name', 'Neu Benutzer') ->set('email', 'neu-user@example.com') ->set('profile.salutation_key', 'mrs') ->set('profile.title', 'Prof.') ->set('profile.first_name', 'Erika') ->set('profile.last_name', 'Profil') ->set('profile.phone', '089123456') ->set('profile.address', 'Profilstrasse 5') ->set('profile.country_code', 'AT') ->set('profile.backlink_url', 'https://neu.example.com') ->set('profile.show_stats', true) ->set('profile.tax_id_number', 'ATU12345678') ->set('companyLookup', 'Alpha') ->call('clearCompanyLookup') ->assertSet('companyLookup', '') ->assertSet('selectedLookupCompanyId', null) ->set('contactLookup', 'Max') ->call('clearContactLookup') ->assertSet('contactLookup', '') ->assertSet('selectedLookupContactId', null) ->set('selectedRoles', ['editor']) ->set('linkedCompanyIds', [$companyA->id, $companyB->id]) ->set("companyRoles.$companyA->id", 'responsible') ->set("companyRoles.$companyB->id", 'owner') ->set('contactForms.0.first_name', 'Erika') ->set('contactForms.0.last_name', 'Mustermann') ->set('contactForms.0.email', 'erika@example.com') ->set('contactForms.0.phone', '0999') ->set('contactForms.0.responsibility', 'Leitung PR') ->set('billing.salutation_key', 'mrs') ->set('billing.name', 'Neu Benutzer GmbH') ->set('billing.address1', 'Neue Strasse 10') ->set('billing.postal_code', '80331') ->set('billing.city', 'Muenchen') ->set('billing.country_code', 'DE') ->call('save') ->assertHasNoErrors(); $targetUser->refresh(); $contact->refresh(); expect($targetUser->name)->toBe('Neu Benutzer'); expect($targetUser->email)->toBe('neu-user@example.com'); expect($targetUser->hasRole('editor'))->toBeTrue(); expect($targetUser->hasRole('customer'))->toBeFalse(); $profile = $targetUser->profile()->first(); expect($profile)->not->toBeNull(); expect($profile?->salutation_key)->toBe('mrs'); expect($profile?->title)->toBe('Prof.'); expect($profile?->first_name)->toBe('Erika'); expect($profile?->last_name)->toBe('Profil'); expect($profile?->phone)->toBe('089123456'); expect($profile?->address)->toBe('Profilstrasse 5'); expect($profile?->country_code)->toBe('AT'); expect($profile?->backlink_url)->toBe('https://neu.example.com'); expect($profile?->show_stats)->toBeTrue(); expect($profile?->tax_id_number)->toBe('ATU12345678'); expect($targetUser->companies()->whereKey($companyA->id)->exists())->toBeTrue(); expect($targetUser->companies()->whereKey($companyB->id)->exists())->toBeTrue(); expect($targetUser->companies()->where('companies.id', $companyA->id)->first()?->pivot?->role)->toBe('responsible'); expect($targetUser->companies()->where('companies.id', $companyB->id)->first()?->pivot?->role)->toBe('owner'); expect($contact->first_name)->toBe('Erika'); expect($contact->last_name)->toBe('Mustermann'); expect($contact->email)->toBe('erika@example.com'); expect($contact->phone)->toBe('0999'); expect($contact->responsibility)->toBe('Leitung PR'); $billingAddress = $targetUser->billingAddress()->first(); expect($billingAddress)->not->toBeNull(); expect($billingAddress?->name)->toBe('Neu Benutzer GmbH'); expect($billingAddress?->address1)->toBe('Neue Strasse 10'); expect($billingAddress?->postal_code)->toBe('80331'); expect($billingAddress?->city)->toBe('Muenchen'); expect($billingAddress?->country_code)->toBe('DE'); }); test('admin user edit keeps empty profile date fields empty', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetUser = User::factory()->create([ 'name' => 'Datum Leer', 'email' => 'datum-leer@example.com', ]); $targetUser->assignRole('customer'); Profile::factory()->for($targetUser)->create([ 'birthdate' => null, 'validation_date' => null, 'contract_date' => null, ]); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $targetUser->id]) ->assertSee('Geburtsdatum') ->assertSee('Validierungsdatum') ->assertSee('Vertragsdatum') ->assertSet('profile.birthdate', '') ->assertSet('profile.validation_date', '') ->assertSet('profile.contract_date', '') ->call('save') ->assertHasNoErrors(); $profile = $targetUser->profile()->first(); expect($profile?->birthdate)->toBeNull(); expect($profile?->validation_date)->toBeNull(); expect($profile?->contract_date)->toBeNull(); }); test('admin can create user with roles companies and optional billing address', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $companyA = Company::query()->create([ 'name' => 'Gamma GmbH', 'slug' => 'gamma-gmbh', ]); $companyB = Company::query()->create([ 'name' => 'Delta GmbH', 'slug' => 'delta-gmbh', ]); $this->actingAs($admin); LivewireVolt::test('admin.users.create') ->set('name', 'Neuer User') ->set('email', 'new-user@example.com') ->set('selectedRoles', ['customer']) ->set('linkedCompanyIds', [$companyA->id, $companyB->id]) ->set("companyRoles.$companyA->id", 'member') ->set("companyRoles.$companyB->id", 'owner') ->set('billing.name', 'Neuer User Billing') ->set('billing.address1', 'Hauptstrasse 1') ->set('billing.postal_code', '20095') ->set('billing.city', 'Hamburg') ->set('billing.country_code', 'DE') ->call('save') ->assertHasNoErrors(); $createdUser = User::query()->where('email', 'new-user@example.com')->firstOrFail(); expect($createdUser->name)->toBe('Neuer User'); expect($createdUser->hasRole('customer'))->toBeTrue(); expect($createdUser->companies()->whereKey($companyA->id)->exists())->toBeTrue(); expect($createdUser->companies()->whereKey($companyB->id)->exists())->toBeTrue(); expect($createdUser->companies()->where('companies.id', $companyB->id)->first()?->pivot?->role)->toBe('owner'); $billingAddress = $createdUser->billingAddress()->first(); expect($billingAddress)->not->toBeNull(); expect($billingAddress?->name)->toBe('Neuer User Billing'); }); test('admin can link companies and contacts in user edit via live lookup flow', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $user = User::factory()->create([ 'name' => 'Lookup User', 'email' => 'lookup-user@example.com', ]); $user->assignRole('customer'); $companyA = Company::query()->create([ 'name' => 'Lookup Alpha GmbH', 'slug' => 'lookup-alpha-gmbh', ]); $companyB = Company::query()->create([ 'name' => 'Lookup Beta GmbH', 'slug' => 'lookup-beta-gmbh', ]); $contact = Contact::query()->create([ 'company_id' => $companyA->id, 'first_name' => 'Lena', 'last_name' => 'Lookup', 'email' => 'lena@lookup-alpha.test', ]); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $user->id]) ->set('companyLookup', 'Alpha') ->set('selectedLookupCompanyId', $companyA->id) ->call('addLinkedCompany') ->set('companyLookup', 'Beta') ->set('selectedLookupCompanyId', $companyB->id) ->call('addLinkedCompany') ->set('contactLookup', 'Lena') ->set('selectedLookupContactId', $contact->id) ->call('addLinkedContact') ->set("companyRoles.$companyA->id", 'owner') ->set("companyRoles.$companyB->id", 'member') ->call('save') ->assertHasNoErrors(); $user->refresh(); expect($user->companies()->whereKey($companyA->id)->exists())->toBeTrue(); expect($user->companies()->whereKey($companyB->id)->exists())->toBeTrue(); expect($user->companies()->where('companies.id', $companyA->id)->first()?->pivot?->role)->toBe('owner'); }); test('removing linked company in user edit is persisted immediately', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $user = User::factory()->create(); $user->assignRole('customer'); $companyA = Company::query()->create([ 'name' => 'Persist Remove Alpha', 'slug' => 'persist-remove-alpha', ]); $companyB = Company::query()->create([ 'name' => 'Persist Remove Beta', 'slug' => 'persist-remove-beta', ]); $user->companies()->attach($companyA->id, ['role' => 'member']); $user->companies()->attach($companyB->id, ['role' => 'member']); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $user->id]) ->call('removeLinkedCompany', $companyB->id) ->assertHasNoErrors(); $user->refresh(); expect($user->companies()->whereKey($companyA->id)->exists())->toBeTrue(); expect($user->companies()->whereKey($companyB->id)->exists())->toBeFalse(); }); test('contact lookup in user edit finds results from unlinked companies and auto-links company', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $user = User::factory()->create(); $user->assignRole('customer'); $company = Company::query()->create([ 'name' => 'Lookup Source', 'slug' => 'lookup-source', ]); $contact = Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Sophie', 'last_name' => 'Finder', 'email' => 'sophie@lookup-source.test', ]); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $user->id]) ->set('contactLookup', 'Sophie') ->assertViewHas('contactLookupResults', fn ($results) => $results->pluck('id')->contains($contact->id)) ->set('selectedLookupContactId', $contact->id) ->call('addLinkedContact') ->assertHasNoErrors(); $user->refresh(); expect($user->companies()->whereKey($company->id)->exists())->toBeTrue(); expect($user->contacts()->whereKey($contact->id)->exists())->toBeTrue(); }); test('adding linked company in user edit is persisted immediately', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $user = User::factory()->create(); $user->assignRole('customer'); $company = Company::query()->create([ 'name' => 'Instant Add GmbH', 'slug' => 'instant-add-gmbh', ]); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $user->id]) ->set('companyLookup', 'Instant Add') ->set('selectedLookupCompanyId', $company->id) ->call('addLinkedCompany') ->assertHasNoErrors(); $user->refresh(); expect($user->companies()->whereKey($company->id)->exists())->toBeTrue(); }); test('removing linked contact in user edit is persisted immediately', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $user = User::factory()->create(); $user->assignRole('customer'); $company = Company::query()->create([ 'name' => 'Contact Remove Co', 'slug' => 'contact-remove-co', ]); $contactA = Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Aline', 'last_name' => 'Keep', 'email' => 'aline@remove.test', ]); $contactB = Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Bela', 'last_name' => 'Remove', 'email' => 'bela@remove.test', ]); $user->companies()->attach($company->id, ['role' => 'member']); $user->contacts()->attach($contactA->id); $user->contacts()->attach($contactB->id); $this->actingAs($admin); LivewireVolt::test('admin.users.edit', ['id' => $user->id]) ->call('removeLinkedContact', $contactB->id) ->assertHasNoErrors(); $user->refresh(); expect($user->contacts()->whereKey($contactA->id)->exists())->toBeTrue(); expect($user->contacts()->whereKey($contactB->id)->exists())->toBeFalse(); }); test('admin can view user detail page with linked company and billing data', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetUser = User::factory()->create([ 'name' => 'Detail User', 'email' => 'detail-user@example.com', ]); $targetUser->assignRole('customer'); $company = Company::query()->create([ 'name' => 'Show GmbH', 'slug' => 'show-gmbh', ]); $targetUser->companies()->attach($company->id, ['role' => 'responsible']); Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Lisa', 'last_name' => 'Kontakt', 'email' => 'lisa@show-gmbh.de', 'responsibility' => 'PR', ]); $targetUser->billingAddress()->create([ 'name' => 'Detail Billing', 'address1' => 'Musterweg 2', 'postal_code' => '50667', 'city' => 'Koeln', 'country_code' => 'DE', ]); $this->actingAs($admin); LivewireVolt::test('admin.users.show', ['id' => $targetUser->id]) ->assertSee('Detail User') ->assertSee('Show GmbH') ->assertSee('Lisa Kontakt') ->assertSee('Detail Billing'); }); test('admin can open user details modal from users index and see company link state', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetUser = User::factory()->create([ 'name' => 'Modal User', 'email' => 'modal-user@example.com', ]); $targetUser->assignRole('customer'); $company = Company::query()->create([ 'name' => 'Modal GmbH', 'slug' => 'modal-gmbh', ]); $targetUser->companies()->attach($company->id, ['role' => 'owner']); $contact = Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Mona', 'last_name' => 'Modal', 'email' => 'mona@modal-gmbh.de', 'responsibility' => 'Presse', ]); $publishedPressRelease = PressRelease::factory() ->for($targetUser) ->for($company) ->published() ->create([ 'title' => 'Modal Pressemitteilung', 'hits' => 42, ]); $publishedPressRelease->contacts()->attach($contact->id); PressRelease::factory() ->for($targetUser) ->for($company) ->create([ 'title' => 'Modal Entwurf', ]); $this->actingAs($admin); LivewireVolt::test('admin.users') ->set('search', 'modal-user@example.com') ->assertSee('modal-user@example.com') ->assertSee('1 Firma') ->assertSee('1 veröffentlicht') ->call('showUserDetails', $targetUser->id) ->assertSet('viewingUserId', $targetUser->id) ->assertSee('Firmen, Kontakte und Pressemitteilungen') ->assertSee('Struktur: User') ->assertSee('Modal GmbH') ->assertSee('Mona Modal') ->assertSee('Inhaber') ->assertSee('Modal Pressemitteilung') ->assertSee('Veroeffentlicht') ->assertSee('Hits') ->assertSee('42') ->assertSee('veröffentlichte PM anzeigen') ->assertSee('/admin/press-releases?', false) ->assertSee('status=published', false) ->assertSee('user='.$targetUser->id, false); LivewireVolt::test('admin.press-releases.index') ->assertViewHas('userLookupResults', fn ($results) => $results->isEmpty()) ->assertViewHas('companyLookupResults', fn ($results) => $results->isEmpty()) ->assertViewHas('contactLookupResults', fn ($results) => $results->isEmpty()) ->set('userFilter', (string) $targetUser->id) ->set('statusFilter', 'published') ->assertSee('Modal Pressemitteilung') ->assertDontSee('Modal Entwurf') ->call('clearUserFilter') ->assertSet('userFilter', 'all') ->assertSet('userLookup', '') ->set('userFilter', (string) $targetUser->id) ->set('companyLookup', 'Modal') ->assertViewHas('companyLookupResults', fn ($results) => $results->pluck('id')->contains($company->id)) ->set('companyFilter', (string) $company->id) ->assertSee('Modal Pressemitteilung') ->call('clearCompanyFilter') ->assertSet('companyFilter', 'all') ->assertSet('companyLookup', '') ->set('companyFilter', (string) $company->id) ->set('contactLookup', 'Mona') ->assertViewHas('contactLookupResults', fn ($results) => $results->pluck('id')->contains($contact->id)) ->set('contactFilter', (string) $contact->id) ->assertSee('Modal Pressemitteilung') ->call('clearContactFilter') ->assertSet('contactFilter', 'all') ->assertSet('contactLookup', ''); }); test('admin users index free text search matches names and email parts', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); User::factory()->create([ 'name' => 'Seabstian Einrock', 'email' => 'info@connectar.de', ])->assignRole('customer'); User::factory()->create([ 'name' => 'Barbara Barr', 'email' => 'barbara@example.com', ])->assignRole('customer'); $this->actingAs($admin); LivewireVolt::test('admin.users') ->set('search', 'Seabstian') ->assertSee('info@connectar.de') ->assertDontSee('barbara@example.com') ->set('search', 'Einrock') ->assertSee('info@connectar.de') ->assertDontSee('barbara@example.com') ->set('search', 'connectar.de') ->assertSee('Seabstian Einrock') ->assertDontSee('Barbara Barr') ->set('search', 'Seabstian Einrock info@connectar.de') ->assertSee('info@connectar.de') ->assertDontSee('barbara@example.com') ->set('search', 'Barr') ->assertSee('barbara@example.com') ->assertDontSee('info@connectar.de'); }); test('admin users index uses full count pagination', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); User::factory()->count(51)->create(); $this->actingAs($admin); LivewireVolt::test('admin.users') ->assertViewHas('users', fn ($users): bool => $users instanceof LengthAwarePaginator && $users->perPage() === 50 && $users->total() === 52 && $users->lastPage() === 2); }); test('admin users index supports workflow filters and quality badges', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $withoutCompany = User::factory()->create([ 'name' => 'Ohne Firma User', 'email' => 'ohne-firma@example.com', ]); $withoutCompany->assignRole('customer'); $completeUser = User::factory()->create([ 'name' => 'Kompletter Workflow User', 'email' => 'komplett@example.com', ]); $completeUser->assignRole('editor'); Profile::factory()->for($completeUser)->create([ 'first_name' => 'Komplett', 'last_name' => 'Profil', ]); $company = Company::query()->create([ 'name' => 'Workflow GmbH', 'slug' => 'workflow-gmbh', ]); $contact = Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Willi', 'last_name' => 'Workflow', 'email' => 'willi@workflow-gmbh.de', ]); $completeUser->companies()->attach($company->id, ['role' => 'owner']); $completeUser->contacts()->attach($contact->id); $completeUser->billingAddress()->create([ 'name' => 'Workflow Rechnung', 'address1' => 'Workflowweg 1', 'postal_code' => '10115', 'city' => 'Berlin', 'country_code' => 'DE', ]); PressRelease::factory() ->for($completeUser) ->for($company) ->published() ->create([ 'title' => 'Workflow veroeffentlicht', ]); $this->actingAs($admin); LivewireVolt::test('admin.users') ->assertSee('Alle Datenstände') ->assertSee('Mit Pressemitteilungen') ->assertSee('Mit veröffentlichten PMs') ->assertSee('Profil fehlt') ->assertSee('Profil OK') ->assertSee('Rechnung OK') ->set('qualityFilter', 'without_company') ->assertSee('ohne-firma@example.com') ->assertDontSee('komplett@example.com') ->set('qualityFilter', 'with_profile') ->assertSee('komplett@example.com') ->assertDontSee('ohne-firma@example.com') ->set('qualityFilter', 'with_published_press_releases') ->assertSee('komplett@example.com') ->assertDontSee('ohne-firma@example.com') ->set('qualityFilter', 'without_published_press_releases') ->assertSee('ohne-firma@example.com') ->assertDontSee('komplett@example.com') ->set('qualityFilter', 'all') ->set('roleFilter', 'editor') ->assertSee('komplett@example.com') ->assertDontSee('ohne-firma@example.com') ->set('permissionFilter', 'can_publish_press_releases') ->assertSee('PM-Freigabe') ->assertSee('komplett@example.com') ->assertDontSee('ohne-firma@example.com'); }); test('admin can create and edit contacts via admin contact forms', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $company = Company::query()->create([ 'name' => 'Kontakt GmbH', 'slug' => 'kontakt-gmbh', 'portal' => 'businessportal24', ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.create') ->set('companyId', $company->id) ->set('portal', 'businessportal24') ->set('salutationKey', 'mr') ->set('firstName', 'Paul') ->set('lastName', 'Neumann') ->set('responsibility', 'Pressekontakt') ->set('email', 'paul@kontakt-gmbh.de') ->set('phone', '040123456') ->call('save') ->assertHasNoErrors(); $contact = Contact::query()->where('email', 'paul@kontakt-gmbh.de')->firstOrFail(); expect($contact->company_id)->toBe($company->id); expect($contact->first_name)->toBe('Paul'); LivewireVolt::test('admin.contacts.edit', ['id' => $contact->id]) ->assertSee('Businessportal24') ->set('firstName', 'Paula') ->set('responsibility', 'Leitung PR') ->call('save') ->assertHasNoErrors(); $contact->refresh(); expect($contact->first_name)->toBe('Paula'); expect($contact->responsibility)->toBe('Leitung PR'); LivewireVolt::test('admin.contacts.edit', ['id' => $contact->id]) ->call('deleteContact') ->assertRedirect(route('admin.contacts.index')); expect(Contact::query()->whereKey($contact->id)->exists())->toBeFalse(); expect(Contact::query()->withTrashed()->find($contact->id)?->trashed())->toBeTrue(); }); test('company contact create route prefills company in contact form', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $company = Company::query()->create([ 'name' => 'Prefill GmbH', 'slug' => 'prefill-gmbh', ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.create', ['companyId' => $company->id]) ->assertSet('companyId', $company->id) ->assertSet('isCompanyPrefilled', true); }); test('admin can filter contacts in company detail contacts tab', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $company = Company::query()->create([ 'name' => 'Filter GmbH', 'slug' => 'filter-gmbh', 'portal' => 'businessportal24', ]); Contact::query()->create([ 'company_id' => $company->id, 'portal' => 'businessportal24', 'first_name' => 'Anna', 'last_name' => 'Presse', 'email' => 'anna@filter-gmbh.de', ]); Contact::query()->create([ 'company_id' => $company->id, 'first_name' => 'Bernd', 'last_name' => 'Marketing', 'email' => 'bernd@filter-gmbh.de', ]); $this->actingAs($admin); LivewireVolt::test('admin.companies.show', ['id' => $company->id]) ->assertSee('Businessportal24') ->set('activeTab', 'contacts') ->set('contactSearch', 'anna') ->assertSee('Anna Presse') ->assertSee('Businessportal24') ->assertDontSee('Bernd Marketing'); }); test('admin can live-search and assign existing contacts in company detail with result limit', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetCompany = Company::query()->create([ 'name' => 'Target GmbH', 'slug' => 'target-gmbh', ]); $sourceCompany = Company::query()->create([ 'name' => 'Source GmbH', 'slug' => 'source-gmbh', ]); Contact::query()->create([ 'company_id' => $targetCompany->id, 'first_name' => 'Existing', 'last_name' => 'Target', 'email' => 'existing@target.test', ]); $movableContact = Contact::query()->create([ 'company_id' => $sourceCompany->id, 'first_name' => 'Zora', 'last_name' => 'Finder', 'email' => 'zora@source.test', ]); for ($i = 1; $i <= 45; $i++) { Contact::query()->create([ 'company_id' => $sourceCompany->id, 'first_name' => 'Limit', 'last_name' => sprintf('Result%02d', $i), 'email' => 'limit'.$i.'@source.test', ]); } $this->actingAs($admin); LivewireVolt::test('admin.companies.show', ['id' => $targetCompany->id]) ->set('activeTab', 'contacts') ->set('contactLookup', 'Limit') ->assertViewHas('contactLookupResults', fn ($results) => $results->count() === 45) // 45 Treffer, Limit 50 ->set('contactLookup', 'Zora') ->set('selectedExistingContactId', $movableContact->id) ->call('attachExistingContact') ->assertHasNoErrors(); $movableContact->refresh(); expect($movableContact->company_id)->toBe($targetCompany->id); }); test('admin can filter contacts index by portal and see company quick-jump link', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $presseechoCompany = Company::query()->create([ 'name' => 'Presseecho Firma', 'slug' => 'presseecho-firma', 'portal' => 'presseecho', ]); $bpCompany = Company::query()->create([ 'name' => 'BP Firma', 'slug' => 'bp-firma', 'portal' => 'businessportal24', ]); Contact::query()->create([ 'company_id' => $presseechoCompany->id, 'portal' => 'presseecho', 'first_name' => 'Paula', 'last_name' => 'Echo', 'email' => 'paula@echo.de', ]); Contact::query()->create([ 'company_id' => $bpCompany->id, 'portal' => 'businessportal24', 'first_name' => 'Boris', 'last_name' => 'Portal', 'email' => 'boris@bp.de', ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.index') ->assertSee('Portal') ->set('portalFilter', 'presseecho') ->assertSee('Paula Echo') ->assertSee('Presseecho') ->assertDontSee('Boris Portal') ->assertSee('/admin/companies/'.$presseechoCompany->id, false); }); test('admin contacts index shows press release counts and supports entity filters', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetUser = User::factory()->create([ 'name' => 'Kontakt Filter User', 'email' => 'kontakt-filter-user@example.com', ]); $companyWithPressRelease = Company::query()->create([ 'name' => 'Kontakt PM Firma', 'slug' => 'kontakt-pm-firma', ]); $companyWithoutPressRelease = Company::query()->create([ 'name' => 'Kontakt Ohne PM Firma', 'slug' => 'kontakt-ohne-pm-firma', ]); $contactWithPressRelease = Contact::query()->create([ 'company_id' => $companyWithPressRelease->id, 'first_name' => 'Klara', 'last_name' => 'Pressekontakt', 'email' => 'klara@kontakt-pm.test', ]); $contactWithoutPressRelease = Contact::query()->create([ 'company_id' => $companyWithoutPressRelease->id, 'first_name' => 'Olaf', 'last_name' => 'Ohnepm', 'email' => 'olaf@kontakt-ohne-pm.test', ]); $contactWithPressRelease->users()->attach($targetUser->id); $pressRelease = PressRelease::factory() ->for($targetUser) ->for($companyWithPressRelease) ->published() ->create(['title' => 'Kontakt PM Zaehler']); $pressRelease->contacts()->attach($contactWithPressRelease->id); $this->actingAs($admin); LivewireVolt::test('admin.contacts.index') ->assertSee('Klara') ->assertSee('1 PMs') ->assertSee(route('admin.press-releases.index', ['contact' => $contactWithPressRelease->id]), false) ->assertSee('Alle Datenstände') ->set('qualityFilter', 'with_press_releases') ->assertSee('Klara') ->assertDontSee('Olaf') ->set('qualityFilter', 'without_press_releases') ->assertSee('Olaf') ->assertDontSee('Klara') ->set('qualityFilter', 'all') ->set('userSearch', 'kontakt-filter-user') ->assertViewHas('filterUsers', fn ($results) => $results->pluck('id')->contains($targetUser->id)) ->set('userFilter', (string) $targetUser->id) ->assertSee('Klara') ->assertDontSee('Olaf') ->call('clearUserSearch') ->assertSet('userFilter', 'all') ->assertSet('userSearch', '') ->set('companySearch', 'Kontakt PM') ->assertViewHas('filterCompanies', fn ($results) => $results->pluck('id')->contains($companyWithPressRelease->id)) ->set('companyFilter', (string) $companyWithPressRelease->id) ->assertSee('Klara') ->call('clearCompanySearch') ->assertSet('companyFilter', 'all') ->assertSet('companySearch', ''); }); test('admin can soft delete contact directly from contacts index flow', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $company = Company::query()->create([ 'name' => 'Index Delete GmbH', 'slug' => 'index-delete-gmbh', ]); $contact = Contact::query()->create([ 'company_id' => $company->id, 'portal' => 'both', 'first_name' => 'Delete', 'last_name' => 'Me', 'email' => 'delete-me@index-contact.test', ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.index') ->call('deleteContactFromIndex', $contact->id) ->assertHasNoErrors(); expect(Contact::query()->whereKey($contact->id)->exists())->toBeFalse(); expect(Contact::query()->withTrashed()->find($contact->id)?->trashed())->toBeTrue(); }); test('admin companies index shows relation counts and supports entity filters', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); Storage::fake('public'); $admin = User::factory()->create(); $admin->assignRole('admin'); $targetUser = User::factory()->create([ 'name' => 'Firmen Filter User', 'email' => 'firmen-filter-user@example.com', ]); $companyWithData = Company::query()->create([ 'name' => 'Firmen PM Zaehler GmbH', 'slug' => 'firmen-pm-zaehler', 'email' => 'pm@firma.test', 'portal' => 'businessportal24', 'legacy_portal' => 'businessportal24', 'logo_path' => 'legacy-logo.png', 'is_active' => true, ]); $companyWithoutData = Company::query()->create([ 'name' => 'Firmen Leer GmbH', 'slug' => 'firmen-leer', 'email' => 'leer@firma.test', 'portal' => 'presseecho', 'is_active' => true, ]); Storage::disk('public')->put('businessportal24/company/legacy-logo.png', 'logo'); $companyWithData->users()->attach($targetUser->id); $contact = Contact::query()->create([ 'company_id' => $companyWithData->id, 'first_name' => 'Carla', 'last_name' => 'Firmenkontakt', 'email' => 'carla@firma.test', ]); PressRelease::factory() ->count(2) ->for($targetUser) ->for($companyWithData) ->published() ->create(); $this->actingAs($admin); $legacyLogoUrl = asset('storage/businessportal24/company/legacy-logo.png'); LivewireVolt::test('admin.companies.index') ->assertSee('Portal') ->assertSee('Alle Portale') ->assertSee('Firmen PM Zaehler GmbH') ->assertSee('Businessportal24') ->assertSee($legacyLogoUrl, false) ->assertSee('2 PMs') ->assertSee('1 Kontakte') ->assertSee(route('admin.press-releases.index', ['company' => $companyWithData->id]), false) ->assertSee(route('admin.contacts.index', ['company' => $companyWithData->id]), false) ->set('portalFilter', 'businessportal24') ->assertSee('Firmen PM Zaehler GmbH') ->assertDontSee('Firmen Leer GmbH') ->set('portalFilter', 'all') ->set('qualityFilter', 'with_press_releases') ->assertSee('Firmen PM Zaehler GmbH') ->assertDontSee('Firmen Leer GmbH') ->set('qualityFilter', 'without_press_releases') ->assertSee('Firmen Leer GmbH') ->assertDontSee('Firmen PM Zaehler GmbH') ->set('qualityFilter', 'all') ->set('userLookup', 'firmen-filter-user') ->assertViewHas('userLookupResults', fn ($results) => $results->pluck('id')->contains($targetUser->id)) ->set('userFilter', (string) $targetUser->id) ->assertSee('Firmen PM Zaehler GmbH') ->assertDontSee('Firmen Leer GmbH') ->call('clearUserSearch') ->assertSet('userFilter', 'all') ->assertSet('userLookup', '') ->set('contactLookup', 'Carla') ->assertViewHas('contactLookupResults', fn ($results) => $results->pluck('id')->contains($contact->id)) ->set('contactFilter', (string) $contact->id) ->assertSee('Firmen PM Zaehler GmbH') ->call('clearContactSearch') ->assertSet('contactFilter', 'all') ->assertSet('contactLookup', ''); LivewireVolt::test('admin.companies.show', ['id' => $companyWithData->id]) ->assertSee($legacyLogoUrl, false); LivewireVolt::test('admin.companies.edit', ['id' => $companyWithData->id]) ->assertSet('current_logo_url', $legacyLogoUrl); }); test('admin can save apply and delete contact filter presets', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $presseechoCompany = Company::query()->create([ 'name' => 'Preset Presseecho', 'slug' => 'preset-presseecho', 'portal' => 'presseecho', ]); Contact::query()->create([ 'company_id' => $presseechoCompany->id, 'portal' => 'presseecho', 'first_name' => 'Preset', 'last_name' => 'Kontakt', 'email' => 'preset@example.de', ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.index') ->set('search', 'preset') ->set('companyFilter', (string) $presseechoCompany->id) ->set('portalFilter', 'presseecho') ->set('presetName', 'Nur Presseecho Kontakte') ->call('savePreset') ->assertHasNoErrors(); $preset = UserFilterPreset::query() ->where('user_id', $admin->id) ->where('page', 'admin.contacts.index') ->where('name', 'Nur Presseecho Kontakte') ->firstOrFail(); LivewireVolt::test('admin.contacts.index') ->set('selectedPresetId', $preset->id) ->call('applyPreset') ->assertSet('search', 'preset') ->assertSet('companyFilter', (string) $presseechoCompany->id) ->assertSet('portalFilter', 'presseecho') ->call('deletePreset'); expect(UserFilterPreset::query()->whereKey($preset->id)->exists())->toBeFalse(); }); test('admin can set default preset and it is auto-applied on contacts index load', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $presseechoCompany = Company::query()->create([ 'name' => 'Default Preset Firma', 'slug' => 'default-preset-firma', 'portal' => 'presseecho', ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.index') ->set('search', 'default') ->set('companyFilter', (string) $presseechoCompany->id) ->set('portalFilter', 'presseecho') ->set('presetName', 'Default Kontakte') ->call('savePreset') ->assertHasNoErrors(); $preset = UserFilterPreset::query() ->where('user_id', $admin->id) ->where('page', 'admin.contacts.index') ->where('name', 'Default Kontakte') ->firstOrFail(); LivewireVolt::test('admin.contacts.index') ->set('selectedPresetId', $preset->id) ->call('setDefaultPreset') ->assertHasNoErrors(); $preset->refresh(); expect($preset->is_default)->toBeTrue(); LivewireVolt::test('admin.contacts.index') ->assertSet('selectedPresetId', $preset->id) ->assertSet('search', 'default') ->assertSet('companyFilter', (string) $presseechoCompany->id) ->assertSet('portalFilter', 'presseecho'); }); test('applying a preset updates last used timestamp and affects ordering', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $firstPreset = UserFilterPreset::query()->create([ 'user_id' => $admin->id, 'page' => 'admin.contacts.index', 'name' => 'Aelteres Preset', 'filters' => [ 'search' => '', 'company_filter' => 'all', 'portal_filter' => 'all', ], 'last_used_at' => now()->subDay(), ]); $secondPreset = UserFilterPreset::query()->create([ 'user_id' => $admin->id, 'page' => 'admin.contacts.index', 'name' => 'Neueres Preset', 'filters' => [ 'search' => '', 'company_filter' => 'all', 'portal_filter' => 'all', ], 'last_used_at' => now()->subHours(2), ]); $this->actingAs($admin); LivewireVolt::test('admin.contacts.index') ->set('selectedPresetId', $firstPreset->id) ->call('applyPreset') ->assertHasNoErrors(); $firstPreset->refresh(); expect($firstPreset->last_used_at)->not->toBeNull(); expect($firstPreset->last_used_at?->greaterThan($secondPreset->last_used_at))->toBeTrue(); }); test('admin can create and edit company with persisted data', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $this->actingAs($admin); LivewireVolt::test('admin.companies.create') ->set('portal', 'presseecho') ->set('type', 'company') ->set('company_name', 'Persist GmbH') ->set('email', 'info@persist-gmbh.de') ->set('phone', '04099999') ->set('website', 'https://persist-gmbh.de') ->set('street', 'Testweg 12') ->set('zip', '20095') ->set('city', 'Hamburg') ->set('country', 'DE') ->set('is_active', true) ->call('save') ->assertHasNoErrors(); $company = Company::query()->where('name', 'Persist GmbH')->firstOrFail(); expect($company->portal?->value)->toBe('presseecho'); expect($company->slug)->toBe('persist-gmbh'); expect($company->email)->toBe('info@persist-gmbh.de'); expect($company->address)->toContain('Testweg 12'); LivewireVolt::test('admin.companies.edit', ['id' => $company->id]) ->set('company_name', 'Persist GmbH Updated') ->set('portal', 'businessportal24') ->set('type', 'agency') ->set('email', 'office@persist-gmbh.de') ->set('phone', '04077777') ->set('website', 'https://updated-persist.de') ->set('street', 'Neuer Weg 1') ->set('zip', '50667') ->set('city', 'Koeln') ->set('country', 'DE') ->set('is_active', false) ->call('update') ->assertHasNoErrors(); $company->refresh(); expect($company->name)->toBe('Persist GmbH Updated'); expect($company->portal?->value)->toBe('businessportal24'); expect($company->type?->value)->toBe('agency'); expect($company->email)->toBe('office@persist-gmbh.de'); expect($company->is_active)->toBeFalse(); }); test('admin can soft delete company from edit flow', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(); $admin->assignRole('admin'); $company = Company::query()->create([ 'portal' => 'both', 'type' => 'company', 'name' => 'Delete Me GmbH', 'slug' => 'delete-me-gmbh', 'email' => 'delete@company.test', 'is_active' => true, ]); $this->actingAs($admin); LivewireVolt::test('admin.companies.edit', ['id' => $company->id]) ->call('deleteCompany') ->assertRedirect(route('admin.companies.index')); expect(Company::query()->whereKey($company->id)->exists())->toBeFalse(); expect(Company::query()->withTrashed()->find($company->id)?->trashed())->toBeTrue(); });