UI: Agentur-Angaben im Profil, Firmen-Typ-Auswahl entfernt

A5 des Umbaus Agentur-Attribution → User-Profil: Kunden-Profil erhält
den Abschnitt „Agentur-Angaben" (Feld Agentur-/Verteilername, öffentlich
als Übermittler sichtbar). Firma-Anlegen (Kunde + Admin) und
Admin-Firma-Bearbeiten kommen ohne Typ-Feld aus — Neuanlagen sind immer
type=company. Firmen-Übersicht zeigt kein Typ-Label mehr. Admin-User-
Formulare pflegen agency_name; die Detailseite zeigt den Übermittler
als Badge. CompanyFactory erzeugt keine zufälligen Agentur-Typen mehr.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-08 09:44:17 +00:00
parent 59ac67a4c8
commit 3ff95bc93a
13 changed files with 98 additions and 90 deletions

View file

@ -29,6 +29,7 @@ trait ProvidesUserFormRules
return [
'name' => ['required', 'string', 'max:255'],
'agencyName' => ['nullable', 'string', 'max:190'],
'email' => ['required', 'email', $emailUnique],
'portal' => ['required', Rule::in(array_map(static fn (Portal $portal): string => $portal->value, Portal::cases()))],
'registrationType' => ['required', Rule::in(array_map(static fn (RegistrationType $type): string => $type->value, RegistrationType::cases()))],

View file

@ -21,7 +21,9 @@ class CompanyFactory extends Factory
return [
'portal' => fake()->randomElement([Portal::Presseecho->value, Portal::Businessportal24->value]),
'type' => fake()->randomElement(CompanyType::cases())->value,
// Neuanlagen sind immer Newsrooms; der Legacy-Typ „agency" wird
// nur noch in Tests der Daten-Commands explizit gesetzt.
'type' => CompanyType::Company->value,
'name' => $name,
'slug' => Str::slug($name).'-'.fake()->unique()->numberBetween(1000, 9999),
'address' => fake()->streetAddress(),

View file

@ -17,8 +17,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
public string $portal = 'both';
public string $type = 'company';
#[Validate('required|min:3|max:255')]
public string $company_name = '';
@ -70,7 +68,9 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
$company = Company::query()->create([
'portal' => $this->portal,
'type' => $this->type,
// Firmen sind ausschließlich Newsrooms; der Übermittler
// („Agentur") ist eine Profil-Eigenschaft des Accounts.
'type' => CompanyType::Company->value,
'name' => $this->company_name,
'slug' => $slug,
'address' => $this->composeAddress(),
@ -112,7 +112,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
['code' => 'US', 'name' => 'USA'],
]),
'portalOptions' => Portal::cases(),
'typeOptions' => CompanyType::cases(),
];
}
@ -149,25 +148,14 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
<span class="section-eyebrow">{{ __('Basisinformationen') }}</span>
</div>
<div class="p-5 space-y-4">
<div class="grid gap-4 sm:grid-cols-2">
<flux:field>
<flux:label>{{ __('Portal') }}</flux:label>
<flux:select wire:model="portal">
@foreach ($portalOptions as $portalOption)
<option value="{{ $portalOption->value }}">{{ $portalOption->label() }}</option>
@endforeach
</flux:select>
</flux:field>
<flux:field>
<flux:label>{{ __('Typ') }}</flux:label>
<flux:select wire:model="type">
@foreach ($typeOptions as $typeOption)
<option value="{{ $typeOption->value }}">{{ $typeOption->label() }}</option>
@endforeach
</flux:select>
</flux:field>
</div>
<flux:field>
<flux:label>{{ __('Portal') }}</flux:label>
<flux:select wire:model="portal">
@foreach ($portalOptions as $portalOption)
<option value="{{ $portalOption->value }}">{{ $portalOption->label() }}</option>
@endforeach
</flux:select>
</flux:field>
<flux:field>
<flux:label>{{ __('Firmenname') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>

View file

@ -1,6 +1,5 @@
<?php
use App\Enums\CompanyType;
use App\Enums\Portal;
use App\Models\Company;
use App\Services\Image\ImageService;
@ -19,8 +18,6 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
public string $portal = 'both';
public string $type = 'company';
#[Validate('required|min:3|max:255')]
public string $company_name = '';
@ -81,7 +78,6 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
}
$this->portal = $company->portal?->value ?? Portal::Both->value;
$this->type = $company->type?->value ?? CompanyType::Company->value;
$this->company_name = $company->name;
$this->description = '';
$this->email = $company->email ?? '';
@ -134,7 +130,6 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
$company->update([
'portal' => $this->portal,
'type' => $this->type,
'name' => $this->company_name,
'slug' => $slug,
'address' => $this->composeAddress(),
@ -163,7 +158,6 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
['code' => 'US', 'name' => 'USA'],
]),
'portalOptions' => Portal::cases(),
'typeOptions' => CompanyType::cases(),
];
}
@ -219,25 +213,14 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
<span class="section-eyebrow">{{ __('Basisinformationen') }}</span>
</div>
<div class="p-5 space-y-4">
<div class="grid gap-4 sm:grid-cols-2">
<flux:field>
<flux:label>{{ __('Portal') }}</flux:label>
<flux:select wire:model="portal">
@foreach ($portalOptions as $portalOption)
<option value="{{ $portalOption->value }}">{{ $portalOption->label() }}</option>
@endforeach
</flux:select>
</flux:field>
<flux:field>
<flux:label>{{ __('Typ') }}</flux:label>
<flux:select wire:model="type">
@foreach ($typeOptions as $typeOption)
<option value="{{ $typeOption->value }}">{{ $typeOption->label() }}</option>
@endforeach
</flux:select>
</flux:field>
</div>
<flux:field>
<flux:label>{{ __('Portal') }}</flux:label>
<flux:select wire:model="portal">
@foreach ($portalOptions as $portalOption)
<option value="{{ $portalOption->value }}">{{ $portalOption->label() }}</option>
@endforeach
</flux:select>
</flux:field>
<flux:field>
<flux:label>{{ __('Firmenname') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>

View file

@ -18,6 +18,8 @@ new #[Layout('components.layouts.app'), Title('Benutzer anlegen')] class extends
public string $name = '';
public string $agencyName = '';
public string $email = '';
public string $portal = '';
@ -116,6 +118,7 @@ new #[Layout('components.layouts.app'), Title('Benutzer anlegen')] class extends
$user = User::query()->create([
'name' => $validated['name'],
'agency_name' => $validated['agencyName'] ?: null,
'email' => $validated['email'],
'portal' => $validated['portal'],
'registration_type' => $validated['registrationType'],
@ -236,6 +239,13 @@ new #[Layout('components.layouts.app'), Title('Benutzer anlegen')] class extends
<flux:error name="email" />
</flux:field>
<flux:field class="sm:col-span-2">
<flux:label>{{ __('Agentur-/Verteilername') }}</flux:label>
<flux:input wire:model="agencyName" />
<flux:description>{{ __('Erscheint öffentlich als Übermittler („übermittelt von …") unter den Mitteilungen dieses Accounts.') }}</flux:description>
<flux:error name="agencyName" />
</flux:field>
<flux:field>
<flux:label>{{ __('Portal') }}</flux:label>
<flux:select wire:model="portal">

View file

@ -24,6 +24,8 @@ new #[Layout('components.layouts.app'), Title('Benutzer bearbeiten')] class exte
public string $name = '';
public string $agencyName = '';
public string $email = '';
public string $portal = '';
@ -149,6 +151,7 @@ new #[Layout('components.layouts.app'), Title('Benutzer bearbeiten')] class exte
->findOrFail($id);
$this->name = $user->name;
$this->agencyName = (string) ($user->agency_name ?? '');
$this->email = $user->email;
$this->portal = $user->portal?->value ?? Portal::Both->value;
$this->registrationType = $user->registration_type?->value ?? RegistrationType::ExistingLegacy->value;
@ -410,6 +413,7 @@ new #[Layout('components.layouts.app'), Title('Benutzer bearbeiten')] class exte
$user->update([
'name' => $validated['name'],
'agency_name' => $validated['agencyName'] ?: null,
'email' => $validated['email'],
'portal' => $validated['portal'],
'registration_type' => $validated['registrationType'],
@ -819,6 +823,13 @@ new #[Layout('components.layouts.app'), Title('Benutzer bearbeiten')] class exte
<flux:error name="email" />
</flux:field>
<flux:field class="sm:col-span-2">
<flux:label>{{ __('Agentur-/Verteilername') }}</flux:label>
<flux:input wire:model="agencyName" />
<flux:description>{{ __('Erscheint öffentlich als Übermittler („übermittelt von …") unter den Mitteilungen dieses Accounts.') }}</flux:description>
<flux:error name="agencyName" />
</flux:field>
<flux:field>
<flux:label>{{ __('Portal') }}</flux:label>
<flux:select wire:model="portal">

View file

@ -80,6 +80,11 @@ new #[Layout('components.layouts.app'), Title('Benutzer anzeigen')] class extend
>
<x-slot:meta>
<span class="badge hub">ID #{{ $user->id }}</span>
@if (filled($user->agency_name))
<span class="badge hub" title="{{ __('Erscheint öffentlich als Übermittler unter den Mitteilungen dieses Accounts.') }}">
{{ __('Übermittler') }}: {{ $user->agency_name }}
</span>
@endif
@if ($user->is_active)
<span class="badge ok dot">{{ __('Aktiv') }}</span>
@else

View file

@ -16,8 +16,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
public string $portal = '';
public string $type = '';
public string $address = '';
public string $email = '';
@ -32,7 +30,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
public function mount(): void
{
$this->type = CompanyType::Company->value;
$this->countryCode = (string) config('countries.default', 'DE');
}
@ -46,7 +43,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
Portal::Businessportal24->value,
Portal::Both->value,
])],
'type' => ['required', Rule::in([CompanyType::Company->value, CompanyType::Agency->value])],
'address' => ['nullable', 'string', 'max:1000'],
'email' => ['nullable', 'email', 'max:190'],
'phone' => ['nullable', 'string', 'max:40'],
@ -72,7 +68,9 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
$company = new Company([
'portal' => $validated['portal'],
'owner_user_id' => $user->id,
'type' => $validated['type'],
// Firmen sind ausschließlich Newsrooms; der Übermittler
// („Agentur") ist eine Profil-Eigenschaft des Accounts.
'type' => CompanyType::Company->value,
'name' => $validated['name'],
'address' => $validated['address'] ?: null,
'country_code' => $validated['countryCode'] ?: null,
@ -110,10 +108,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
Portal::Businessportal24->value => Portal::Businessportal24->label(),
Portal::Both->value => Portal::Both->label(),
],
'types' => [
CompanyType::Company->value => CompanyType::Company->label(),
CompanyType::Agency->value => CompanyType::Agency->label(),
],
'countries' => (array) config('countries.items', []),
];
}
@ -162,15 +156,6 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten
<flux:error name="portal" />
</flux:field>
<flux:field>
<flux:select wire:model="type" :label="__('Typ')" required>
@foreach ($types as $value => $label)
<flux:select.option value="{{ $value }}">{{ $label }}</flux:select.option>
@endforeach
</flux:select>
<flux:error name="type" />
</flux:field>
<flux:field>
<flux:input wire:model="email" :label="__('E-Mail')" type="email" />
<flux:error name="email" />

View file

@ -259,29 +259,23 @@ new #[Layout('components.layouts.app'), Title('Meine Firmen')] class extends Com
}
/**
* Liefert eine kompakte Meta-Line: Stadt · Typ.
* Liefert eine kompakte Meta-Line: die Stadt aus der Adresse.
* (Kein Typ-Label mehr — seit dem Umbau der Agentur-Attribution
* gibt es nur noch einen Firmen-Typ.)
*/
public function metaLine(Company $company): string
{
$parts = [];
$address = trim((string) ($company->address ?? ''));
if (filled($address)) {
$lastLine = collect(preg_split('/\r?\n/', $address))
->map(fn ($line) => trim((string) $line))
->filter()
->last();
if (is_string($lastLine) && filled($lastLine)) {
$parts[] = $lastLine;
}
if (blank($address)) {
return '';
}
$type = $company->type?->label();
if (is_string($type) && filled($type)) {
$parts[] = $type;
}
$lastLine = collect(preg_split('/\r?\n/', $address))
->map(fn ($line) => trim((string) $line))
->filter()
->last();
return implode(' · ', $parts);
return is_string($lastLine) ? $lastLine : '';
}
/**
@ -330,7 +324,6 @@ new #[Layout('components.layouts.app'), Title('Meine Firmen')] class extends Com
'companies.id',
'companies.owner_user_id',
'companies.portal',
'companies.type',
'companies.name',
'companies.address',
'companies.logo_path',

View file

@ -12,6 +12,8 @@ new #[Layout('components.layouts.app'), Title('Mein Profil')] class extends Comp
{
public string $name = '';
public string $agencyName = '';
public string $language = 'de';
public string $salutationKey = 'none';
@ -64,6 +66,7 @@ new #[Layout('components.layouts.app'), Title('Mein Profil')] class extends Comp
$profile = $user->profile;
$this->name = (string) $user->name;
$this->agencyName = (string) ($user->agency_name ?? '');
$this->language = $user->language ?? 'de';
$this->salutationKey = (string) ($profile?->salutation_key ?? 'none');
@ -146,6 +149,7 @@ new #[Layout('components.layouts.app'), Title('Mein Profil')] class extends Comp
{
$rules = [
'name' => ['required', 'string', 'max:120'],
'agencyName' => ['nullable', 'string', 'max:190'],
'language' => ['required', Rule::in(['de', 'en'])],
'salutationKey' => ['required', Rule::in(array_keys((array) config('salutations.items', [])))],
'firstName' => ['nullable', 'string', 'max:80'],
@ -204,6 +208,7 @@ new #[Layout('components.layouts.app'), Title('Mein Profil')] class extends Comp
$user->forceFill([
'name' => $validated['name'],
'agency_name' => $validated['agencyName'] ?: null,
'language' => $validated['language'],
])->save();
@ -495,7 +500,29 @@ new #[Layout('components.layouts.app'), Title('Mein Profil')] class extends Comp
</div>
</article>
{{-- ============== 3) EINSTELLUNGEN ============== --}}
{{-- ============== 3) AGENTUR-ANGABEN ============== --}}
<article class="panel" id="agentur">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Agentur-Angaben') }}</span>
</div>
<div class="p-5 grid gap-6 lg:grid-cols-[0.8fr_1.2fr]">
<p class="text-[13px] leading-[1.55] text-[color:var(--color-ink-2)] m-0">
{{ __('Falls Sie Pressemitteilungen im Auftrag Dritter einreichen, erscheint diese Angabe öffentlich als Übermittler unter Ihren Mitteilungen.') }}
</p>
<flux:input
wire:model="agencyName"
:label="__('Agentur-/Verteilername')"
:description="__('Leer lassen, wenn Sie nur eigene Mitteilungen veröffentlichen.')"
/>
</div>
<div class="px-5 py-4 border-t border-[color:var(--color-bg-rule)] flex justify-end">
<flux:button type="submit" variant="primary">
{{ __('Speichern') }}
</flux:button>
</div>
</article>
{{-- ============== 4) EINSTELLUNGEN ============== --}}
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Einstellungen') }}</span>

View file

@ -1242,7 +1242,6 @@ test('admin can create and edit company with persisted data', function () {
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')
@ -1264,7 +1263,6 @@ test('admin can create and edit company with persisted data', function () {
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')
@ -1279,7 +1277,8 @@ test('admin can create and edit company with persisted data', function () {
$company->refresh();
expect($company->name)->toBe('Persist GmbH Updated');
expect($company->portal?->value)->toBe('businessportal24');
expect($company->type?->value)->toBe('agency');
// Kein Typ-Feld mehr: Firmen sind seit dem Attributions-Umbau immer Newsrooms.
expect($company->type?->value)->toBe('company');
expect($company->email)->toBe('office@persist-gmbh.de');
expect($company->is_active)->toBeFalse();
});

View file

@ -45,7 +45,6 @@ test('save erstellt eine Firma und ordnet sie dem User als Owner zu', function (
->test('customer.press-kits.create')
->set('name', 'Neue Brauerei AG')
->set('portal', 'presseecho')
->set('type', 'company')
->set('email', 'kontakt@brauerei.de')
->call('save')
->assertHasNoErrors()
@ -57,6 +56,9 @@ test('save erstellt eine Firma und ordnet sie dem User als Owner zu', function (
expect($company)->not->toBeNull()
->and($company->owner_user_id)->toBe($customer->id)
->and($company->portal->value)->toBe('presseecho')
// Ohne Typ-Feld immer Newsroom: der Übermittler („Agentur") lebt
// seit dem Attributions-Umbau im Profilfeld users.agency_name.
->and($company->type?->value)->toBe('company')
->and($company->email)->toBe('kontakt@brauerei.de')
->and($company->is_active)->toBeTrue();
@ -96,7 +98,6 @@ test('save akzeptiert Portal Both', function () {
->test('customer.press-kits.create')
->set('name', 'Beide Portale GmbH')
->set('portal', 'both')
->set('type', 'company')
->call('save')
->assertHasNoErrors();

View file

@ -30,7 +30,9 @@ test('customer can update own profile fields', function () {
LivewireVolt::test('customer.profile')
->assertSee('Rechnungsadresse')
->assertSee('Agentur-Angaben')
->set('name', 'Neuer Anzeigename')
->set('agencyName', 'Muster Presseverteiler')
->set('firstName', 'Max')
->set('lastName', 'Mustermann')
->set('language', 'en')
@ -51,6 +53,7 @@ test('customer can update own profile fields', function () {
$customer->refresh();
expect($customer->name)->toBe('Neuer Anzeigename');
expect($customer->agency_name)->toBe('Muster Presseverteiler');
expect($customer->language)->toBe('en');
expect($customer->profile?->first_name)->toBe('Max');
expect($customer->profile?->last_name)->toBe('Mustermann');