b2in/resources/views/livewire/admin/partners/edit.blade.php
2026-02-20 17:57:50 +01:00

355 lines
15 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\Models\Partner;
use App\Models\Hub;
use Livewire\Volt\Component;
use function Livewire\Volt\{layout, title};
layout('components.layouts.app');
title('Partner bearbeiten');
new class extends Component {
public Partner $partner;
// Basis-Felder
public string $companyName = '';
public string $displayName = '';
public string $street = '';
public string $houseNumber = '';
public string $zip = '';
public string $city = '';
public string $phone = '';
public string $website = '';
public ?int $hubId = null;
public bool $isActive = true;
// Profil-Felder (Phase 1 Migrationen)
public string $storyText = '';
public int|string $foundedYear = '';
public string $specialtiesInput = '';
/**
* Öffnungszeiten als strukturiertes Array.
*
* @var array<string, array{open: string, close: string, closed: bool}>
*/
public array $openingHours = [
'monday' => ['open' => '09:00', 'close' => '18:00', 'closed' => false],
'tuesday' => ['open' => '09:00', 'close' => '18:00', 'closed' => false],
'wednesday' => ['open' => '09:00', 'close' => '18:00', 'closed' => false],
'thursday' => ['open' => '09:00', 'close' => '18:00', 'closed' => false],
'friday' => ['open' => '09:00', 'close' => '18:00', 'closed' => false],
'saturday' => ['open' => '10:00', 'close' => '16:00', 'closed' => false],
'sunday' => ['open' => '', 'close' => '', 'closed' => true],
];
public function mount(int $partnerId): void
{
$this->partner = Partner::findOrFail($partnerId);
$this->authorize('update', $this->partner);
$this->companyName = $this->partner->company_name ?? '';
$this->displayName = $this->partner->display_name ?? '';
$this->street = $this->partner->street ?? '';
$this->houseNumber = $this->partner->house_number ?? '';
$this->zip = $this->partner->zip ?? '';
$this->city = $this->partner->city ?? '';
$this->phone = $this->partner->phone ?? '';
$this->website = $this->partner->website ?? '';
$this->hubId = $this->partner->hub_id;
$this->isActive = $this->partner->is_active;
$this->storyText = $this->partner->story_text ?? '';
$this->foundedYear = $this->partner->founded_year ?? '';
$this->specialtiesInput = $this->partner->specialties
? implode(', ', $this->partner->specialties)
: '';
if ($this->partner->opening_hours) {
$this->openingHours = array_merge($this->openingHours, $this->partner->opening_hours);
}
}
public function save(): void
{
$this->authorize('update', $this->partner);
$this->validate([
'companyName' => 'required|string|max:255',
'displayName' => 'nullable|string|max:255',
'street' => 'nullable|string|max:255',
'houseNumber' => 'nullable|string|max:20',
'zip' => 'nullable|string|max:10',
'city' => 'nullable|string|max:100',
'phone' => 'nullable|string|max:50',
'website' => 'nullable|url|max:255',
'hubId' => 'nullable|exists:hubs,id',
'storyText' => 'nullable|string|max:2000',
'foundedYear' => 'nullable|integer|min:1800|max:' . now()->year,
'specialtiesInput' => 'nullable|string|max:500',
], [
'companyName.required' => __('Bitte geben Sie einen Firmennamen ein.'),
'website.url' => __('Bitte geben Sie eine gültige URL ein (z.B. https://example.de).'),
'foundedYear.integer' => __('Bitte geben Sie eine gültige Jahreszahl ein.'),
'foundedYear.min' => __('Das Gründungsjahr muss nach 1800 liegen.'),
'foundedYear.max' => __('Das Gründungsjahr darf nicht in der Zukunft liegen.'),
]);
$specialties = array_filter(
array_map('trim', explode(',', $this->specialtiesInput))
);
$this->partner->update([
'company_name' => $this->companyName,
'display_name' => $this->displayName ?: null,
'street' => $this->street ?: null,
'house_number' => $this->houseNumber ?: null,
'zip' => $this->zip ?: null,
'city' => $this->city ?: null,
'phone' => $this->phone ?: null,
'website' => $this->website ?: null,
'hub_id' => $this->hubId,
'is_active' => $this->isActive,
'story_text' => $this->storyText ?: null,
'founded_year' => $this->foundedYear ?: null,
'specialties' => array_values($specialties),
'opening_hours' => $this->openingHours,
]);
session()->flash('message', __('Partner-Profil erfolgreich gespeichert.'));
}
/** @return array<string, string> */
protected function dayLabels(): array
{
return [
'monday' => __('Montag'),
'tuesday' => __('Dienstag'),
'wednesday' => __('Mittwoch'),
'thursday' => __('Donnerstag'),
'friday' => __('Freitag'),
'saturday' => __('Samstag'),
'sunday' => __('Sonntag'),
];
}
public function with(): array
{
return [
'hubs' => Hub::orderBy('name')->get(['id', 'name']),
'dayLabels' => $this->dayLabels(),
];
}
}; ?>
<div class="space-y-6 p-6">
{{-- Header --}}
<div class="flex items-center gap-4">
<flux:button href="{{ route('admin.partners.index') }}" variant="ghost" icon="arrow-left" />
<div>
<flux:heading size="xl" class="mb-1">{{ $partner->company_name }}</flux:heading>
<flux:subheading>{{ __('Partner-Profil bearbeiten') }}</flux:subheading>
</div>
</div>
@if (session()->has('message'))
<flux:callout variant="success" icon="check-circle">
{{ session('message') }}
</flux:callout>
@endif
<form wire:submit="save" class="space-y-6">
{{-- Basisdaten --}}
<flux:card class="shadow-elegant">
<div class="mb-4">
<flux:heading size="lg">{{ __('Basisdaten') }}</flux:heading>
</div>
<flux:separator class="mb-6" />
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<flux:field>
<flux:label>{{ __('Firmenname') }}</flux:label>
<flux:input wire:model="companyName" icon="building-office" />
@error('companyName') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<flux:field>
<flux:label>{{ __('Anzeigename (optional)') }}</flux:label>
<flux:description>{{ __('Öffentlich sichtbarer Name, falls abweichend') }}</flux:description>
<flux:input wire:model="displayName" />
@error('displayName') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
</div>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-3">
<flux:field class="md:col-span-2">
<flux:label>{{ __('Straße') }}</flux:label>
<flux:input wire:model="street" />
@error('street') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<flux:field>
<flux:label>{{ __('Hausnummer') }}</flux:label>
<flux:input wire:model="houseNumber" />
@error('houseNumber') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
</div>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-3">
<flux:field>
<flux:label>{{ __('PLZ') }}</flux:label>
<flux:input wire:model="zip" />
@error('zip') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<flux:field class="md:col-span-2">
<flux:label>{{ __('Stadt') }}</flux:label>
<flux:input wire:model="city" />
@error('city') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
</div>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<flux:field>
<flux:label>{{ __('Telefon') }}</flux:label>
<flux:input wire:model="phone" type="tel" icon="phone" />
@error('phone') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<flux:field>
<flux:label>{{ __('Website') }}</flux:label>
<flux:input wire:model="website" type="url" placeholder="https://example.de" icon="globe-alt" />
@error('website') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
</div>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<flux:field>
<flux:label>{{ __('Hub / Region') }}</flux:label>
<flux:select wire:model="hubId">
<flux:select.option :value="null">{{ __(' Kein Hub ') }}</flux:select.option>
@foreach ($hubs as $hub)
<flux:select.option :value="$hub->id">{{ $hub->name }}</flux:select.option>
@endforeach
</flux:select>
@error('hubId') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<flux:field>
<flux:label>{{ __('Status') }}</flux:label>
<flux:switch wire:model="isActive" label="{{ __('Partner ist aktiv') }}" />
</flux:field>
</div>
</flux:card>
{{-- Story & Profil --}}
<flux:card class="shadow-elegant">
<div class="mb-4">
<flux:heading size="lg">{{ __('Story & Profil') }}</flux:heading>
<flux:subheading>{{ __('Erzählen Sie die Geschichte des Partners') }}</flux:subheading>
</div>
<flux:separator class="mb-6" />
<div class="space-y-4">
<flux:field>
<flux:label>{{ __('Story-Text') }}</flux:label>
<flux:description>{{ __('Kurze Geschichte des Unternehmens max. 2.000 Zeichen') }}</flux:description>
<flux:textarea
wire:model="storyText"
placeholder="{{ __('Seit 1950 steht unser Haus für...') }}"
rows="5"
/>
<div class="mt-1 text-right text-xs text-zinc-400">
{{ strlen($storyText) }} / 2000
</div>
@error('storyText') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<flux:field>
<flux:label>{{ __('Gründungsjahr') }}</flux:label>
<flux:input
wire:model="foundedYear"
type="number"
min="1800"
:max="date('Y')"
placeholder="{{ __('z.B. 1985') }}"
icon="calendar"
/>
@error('foundedYear') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
<flux:field>
<flux:label>{{ __('Fachgebiete / Spezialisierungen') }}</flux:label>
<flux:description>{{ __('Kommagetrennt, z.B. Polstermöbel, Küchen, Matratzen') }}</flux:description>
<flux:input
wire:model="specialtiesInput"
placeholder="{{ __('Polstermöbel, Küchen, Matratzen') }}"
/>
@error('specialtiesInput') <flux:error>{{ $message }}</flux:error> @enderror
</flux:field>
</div>
</div>
</flux:card>
{{-- Öffnungszeiten --}}
<flux:card class="shadow-elegant">
<div class="mb-4">
<flux:heading size="lg">{{ __('Öffnungszeiten') }}</flux:heading>
</div>
<flux:separator class="mb-6" />
<div class="space-y-3">
@foreach ($dayLabels as $dayKey => $dayLabel)
<div class="flex items-center gap-4">
<div class="w-28 text-sm font-medium text-zinc-700 dark:text-zinc-300">
{{ $dayLabel }}
</div>
<flux:switch
wire:model.live="openingHours.{{ $dayKey }}.closed"
label="{{ __('Geschlossen') }}"
/>
@unless ($openingHours[$dayKey]['closed'] ?? false)
<div class="flex items-center gap-2">
<flux:input
wire:model="openingHours.{{ $dayKey }}.open"
type="time"
class="w-28"
/>
<span class="text-zinc-500"></span>
<flux:input
wire:model="openingHours.{{ $dayKey }}.close"
type="time"
class="w-28"
/>
</div>
@endunless
</div>
@endforeach
</div>
</flux:card>
{{-- Aktionen --}}
<div class="flex justify-end gap-3">
<flux:button href="{{ route('admin.partners.index') }}" variant="ghost">
{{ __('Abbrechen') }}
</flux:button>
<flux:button
type="submit"
variant="primary"
icon="check"
wire:loading.attr="disabled"
wire:target="save"
>
<span wire:loading.remove wire:target="save">{{ __('Speichern') }}</span>
<span wire:loading wire:target="save">
<flux:icon.arrow-path class="animate-spin inline-block mr-1 h-4 w-4" />
{{ __('Wird gespeichert...') }}
</span>
</flux:button>
</div>
</form>
</div>