presseportale/resources/views/livewire/admin/companies/create.blade.php
Kevin Adametz 036a53499f Responsive-Härtung: Seiten-Header, Kontextleiste, Stat-Cards
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 14:08:08 +00:00

317 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use App\Enums\CompanyType;
use App\Enums\Portal;
use App\Models\Company;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Validate;
use Livewire\Volt\Component;
use Livewire\WithFileUploads;
new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Component
{
use WithFileUploads;
public string $portal = 'both';
public string $type = 'company';
#[Validate('required|min:3|max:255')]
public string $company_name = '';
#[Validate('nullable|max:500')]
public string $description = '';
#[Validate('required|email')]
public string $email = '';
#[Validate('nullable|max:50')]
public string $phone = '';
#[Validate('nullable|url')]
public string $website = '';
#[Validate('nullable|max:255')]
public string $street = '';
#[Validate('nullable|max:20')]
public string $zip = '';
#[Validate('nullable|max:255')]
public string $city = '';
#[Validate('nullable|max:255')]
public string $state = '';
#[Validate('required|max:2')]
public string $country = 'DE';
#[Validate('nullable|image|max:1024')]
public $logo;
#[Validate('nullable|max:255')]
public ?string $tax_id = null;
#[Validate('nullable|max:255')]
public ?string $registration_number = null;
public bool $is_verified = false;
public bool $is_active = true;
public function save(): void
{
$this->validate();
$slug = (new Company)->generateUniqueSlug($this->company_name, ['portal' => $this->portal]);
$logoPath = $this->logo
? $this->logo->store('company-logos', 'public')
: null;
Company::query()->create([
'portal' => $this->portal,
'type' => $this->type,
'name' => $this->company_name,
'slug' => $slug,
'address' => $this->composeAddress(),
'country_code' => strtoupper($this->country),
'phone' => $this->phone ?: null,
'email' => $this->email ?: null,
'website' => $this->website ?: null,
'logo_path' => $logoPath,
'is_active' => $this->is_active,
]);
session()->flash('success', 'Firma erfolgreich erstellt.');
$this->redirect(route('admin.companies.index'), navigate: true);
}
public function with(): array
{
return [
'countries' => collect([
['code' => 'DE', 'name' => 'Deutschland'],
['code' => 'AT', 'name' => 'Österreich'],
['code' => 'CH', 'name' => 'Schweiz'],
['code' => 'FR', 'name' => 'Frankreich'],
['code' => 'GB', 'name' => 'Großbritannien'],
['code' => 'US', 'name' => 'USA'],
]),
'portalOptions' => Portal::cases(),
'typeOptions' => CompanyType::cases(),
];
}
protected function composeAddress(): ?string
{
$lineOne = trim($this->street);
$lineTwo = trim(trim($this->zip).' '.trim($this->city));
$lineThree = trim($this->state);
$parts = array_filter([$lineOne, $lineTwo, $lineThree], fn ($value) => $value !== '');
return $parts !== [] ? implode(', ', $parts) : null;
}
}; ?>
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-nowrap whitespace-nowrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Stammdaten · Neue Firma') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Neue Firma') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
{{ __('Stammdaten, Adresse, Logo und Rechtsangaben einer neuen Firma erfassen.') }}
</p>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<flux:button variant="filled" icon="arrow-left" href="{{ route('admin.companies.index') }}" wire:navigate>
{{ __('Zurück') }}
</flux:button>
</div>
</header>
<form wire:submit="save" class="space-y-6">
<article class="panel">
<div class="panel-head">
<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>{{ __('Firmenname') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>
<flux:input wire:model="company_name" placeholder="{{ __('Vollständiger Firmenname...') }}" />
<flux:error name="company_name" />
</flux:field>
<flux:field>
<flux:label>{{ __('Beschreibung') }}</flux:label>
<flux:textarea wire:model="description" rows="4" placeholder="{{ __('Kurze Beschreibung der Firma (optional)...') }}" />
<flux:error name="description" />
</flux:field>
<div class="grid gap-4 sm:grid-cols-2">
<flux:field>
<flux:label>{{ __('E-Mail') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>
<flux:input wire:model="email" type="email" placeholder="{{ __('kontakt@firma.de') }}" icon="envelope" />
<flux:error name="email" />
</flux:field>
<flux:field>
<flux:label>{{ __('Telefon') }}</flux:label>
<flux:input wire:model="phone" type="tel" placeholder="{{ __('+49 123 456789') }}" icon="phone" />
<flux:error name="phone" />
</flux:field>
</div>
<flux:field>
<flux:label>{{ __('Website') }}</flux:label>
<flux:input wire:model="website" type="url" placeholder="{{ __('https://www.firma.de') }}" icon="globe-alt" />
<flux:error name="website" />
</flux:field>
</div>
</article>
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Adresse') }}</span>
</div>
<div class="p-5 space-y-4">
<flux:field>
<flux:label>{{ __('Straße & Hausnummer') }}</flux:label>
<flux:input wire:model="street" placeholder="{{ __('Musterstraße 123') }}" />
<flux:error name="street" />
</flux:field>
<div class="grid gap-4 sm:grid-cols-3">
<flux:field>
<flux:label>{{ __('PLZ') }}</flux:label>
<flux:input wire:model="zip" placeholder="{{ __('12345') }}" />
<flux:error name="zip" />
</flux:field>
<flux:field class="sm:col-span-2">
<flux:label>{{ __('Stadt') }}</flux:label>
<flux:input wire:model="city" placeholder="{{ __('Berlin') }}" />
<flux:error name="city" />
</flux:field>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<flux:field>
<flux:label>{{ __('Bundesland / Region') }}</flux:label>
<flux:input wire:model="state" placeholder="{{ __('Bayern') }}" />
<flux:error name="state" />
</flux:field>
<flux:field>
<flux:label>{{ __('Land') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>
<flux:select wire:model="country">
@foreach ($countries as $country)
<option value="{{ $country['code'] }}">{{ $country['name'] }}</option>
@endforeach
</flux:select>
<flux:error name="country" />
</flux:field>
</div>
</div>
</article>
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Rechtliche Daten') }}</span>
</div>
<div class="p-5 grid gap-4 sm:grid-cols-2">
<flux:field>
<flux:label>{{ __('Steuernummer / USt-IdNr.') }}</flux:label>
<flux:input wire:model="tax_id" placeholder="{{ __('DE123456789') }}" />
<flux:error name="tax_id" />
</flux:field>
<flux:field>
<flux:label>{{ __('Handelsregisternummer') }}</flux:label>
<flux:input wire:model="registration_number" placeholder="{{ __('HRB 12345') }}" />
<flux:error name="registration_number" />
</flux:field>
</div>
</article>
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Logo & Status') }}</span>
</div>
<div class="p-5 space-y-4">
<flux:field>
<flux:label>{{ __('Firmenlogo') }}</flux:label>
<flux:file-upload wire:model="logo" accept="image/*">
<flux:file-upload.dropzone
:heading="__('Logo hierher ziehen oder klicken')"
:text="__('Bilddatei · empfohlen quadratisch, min. 400×400 px')"
with-progress
inline
/>
</flux:file-upload>
<flux:description>{{ __('Maximal 1 MB. Empfohlen: quadratisch, min. 400x400px') }}</flux:description>
<flux:error name="logo" />
@if ($logo)
<div class="mt-4">
<div class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-2">
{{ __('Vorschau:') }}
</div>
<img src="{{ $logo->temporaryUrl() }}" width="128" height="128"
class="h-32 max-h-32 w-32 max-w-32 rounded-[6px] border border-[color:var(--color-bg-rule)] object-contain bg-[color:var(--color-bg-elev)]">
</div>
@endif
</flux:field>
<div class="flex gap-6 pt-2 border-t border-[color:var(--color-bg-rule)]">
<flux:checkbox wire:model="is_verified" label="{{ __('Firma ist verifiziert') }}" />
<flux:checkbox wire:model="is_active" label="{{ __('Firma ist aktiv') }}" />
</div>
</div>
</article>
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Aktionen') }}</span>
</div>
<div class="p-5 flex justify-end gap-3">
<flux:button variant="filled" href="{{ route('admin.companies.index') }}" wire:navigate>
{{ __('Abbrechen') }}
</flux:button>
<flux:button type="submit" variant="primary">
{{ __('Firma erstellen') }}
</flux:button>
</div>
</article>
</form>
</div>