310 lines
12 KiB
PHP
310 lines
12 KiB
PHP
<?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="grid items-end gap-8" style="grid-template-columns:1fr auto;">
|
|
<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="ghost" 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:input type="file" wire:model="logo" accept="image/*" />
|
|
<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="ghost" href="{{ route('admin.companies.index') }}" wire:navigate>
|
|
{{ __('Abbrechen') }}
|
|
</flux:button>
|
|
<flux:button type="submit" variant="primary">
|
|
{{ __('Firma erstellen') }}
|
|
</flux:button>
|
|
</div>
|
|
</article>
|
|
</form>
|
|
</div>
|