259 lines
9.4 KiB
PHP
259 lines
9.4 KiB
PHP
<?php
|
|
|
|
use App\Enums\Portal;
|
|
use App\Enums\PressReleaseStatus;
|
|
use App\Models\Category;
|
|
use App\Models\Company;
|
|
use App\Models\PressRelease;
|
|
use App\Services\PressRelease\PressReleaseHtmlSanitizer;
|
|
use Illuminate\Validation\Rule;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Locked;
|
|
use Livewire\Attributes\Title;
|
|
use Livewire\Volt\Component;
|
|
|
|
new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] class extends Component
|
|
{
|
|
#[Locked]
|
|
public int $id;
|
|
|
|
public string $portal = '';
|
|
|
|
public string $language = 'de';
|
|
|
|
public int|string|null $companyId = null;
|
|
|
|
public int|string|null $categoryId = null;
|
|
|
|
public string $title = '';
|
|
|
|
public string $text = '';
|
|
|
|
public string $keywords = '';
|
|
|
|
public string $backlinkUrl = '';
|
|
|
|
public function mount(int $id): void
|
|
{
|
|
$this->id = $id;
|
|
|
|
$pr = $this->getMyPR();
|
|
$this->authorize('update', $pr);
|
|
|
|
abort_unless(
|
|
in_array($pr->status->value, ['draft', 'rejected']),
|
|
403,
|
|
__('Nur Entwürfe und abgelehnte Pressemitteilungen können bearbeitet werden.')
|
|
);
|
|
|
|
$this->portal = $pr->portal->value;
|
|
$this->language = $pr->language;
|
|
$this->companyId = $pr->company_id;
|
|
$this->categoryId = $pr->category_id;
|
|
$this->title = $pr->title;
|
|
$this->text = $pr->text;
|
|
$this->keywords = $pr->keywords ?? '';
|
|
$this->backlinkUrl = $pr->backlink_url ?? '';
|
|
}
|
|
|
|
public function updatedCompanyId(): void
|
|
{
|
|
$company = $this->selectedCompany();
|
|
|
|
if ($company?->portal) {
|
|
$this->portal = $company->portal->value;
|
|
}
|
|
}
|
|
|
|
public function save(): void
|
|
{
|
|
$this->validate([
|
|
'language' => ['required', Rule::in(['de', 'en'])],
|
|
'companyId' => ['required', 'integer'],
|
|
'categoryId' => ['required', 'integer', Rule::exists('categories', 'id')],
|
|
'title' => ['required', 'string', 'min:5', 'max:255'],
|
|
'text' => ['required', 'string', 'min:50'],
|
|
'keywords' => ['nullable', 'string', 'max:255'],
|
|
'backlinkUrl' => ['nullable', 'url', 'max:255'],
|
|
]);
|
|
|
|
$pr = $this->getMyPR();
|
|
$this->authorize('update', $pr);
|
|
|
|
$company = $this->selectedCompany();
|
|
|
|
if (! $company) {
|
|
$this->addError('companyId', __('Die gewählte Firma ist nicht Ihrem Account zugeordnet.'));
|
|
|
|
return;
|
|
}
|
|
|
|
$this->portal = $company->portal?->value ?? Portal::Presseecho->value;
|
|
|
|
$cleanText = app(PressReleaseHtmlSanitizer::class)->clean($this->text);
|
|
|
|
$pr->update([
|
|
'portal' => $this->portal,
|
|
'language' => $this->language,
|
|
'company_id' => (int) $this->companyId,
|
|
'category_id' => (int) $this->categoryId,
|
|
'title' => $this->title,
|
|
'text' => $cleanText,
|
|
'keywords' => $this->keywords ?: null,
|
|
'backlink_url' => $this->backlinkUrl ?: null,
|
|
]);
|
|
|
|
session()->flash('success', __('Pressemitteilung gespeichert.'));
|
|
$this->redirect(route('me.press-releases.show', $pr->id), navigate: true);
|
|
}
|
|
|
|
public function with(): array
|
|
{
|
|
$user = auth()->user();
|
|
$myCompanies = $user->companies()->orderBy('name')->get(['companies.id', 'companies.name', 'companies.portal']);
|
|
$selectedCompany = $this->selectedCompany();
|
|
|
|
$categories = Category::query()
|
|
->with('translations')
|
|
->where('is_active', true)
|
|
->orderBy('id')
|
|
->get();
|
|
|
|
return [
|
|
'myCompanies' => $myCompanies,
|
|
'categories' => $categories,
|
|
'selectedPortalLabel' => $selectedCompany?->portal?->label() ?? __('Wird aus der Firma übernommen'),
|
|
];
|
|
}
|
|
|
|
private function getMyPR(): PressRelease
|
|
{
|
|
return PressRelease::withoutGlobalScopes()
|
|
->where('user_id', auth()->id())
|
|
->findOrFail($this->id);
|
|
}
|
|
|
|
private function selectedCompany(): ?Company
|
|
{
|
|
return auth()->user()
|
|
->companies()
|
|
->whereKey((int) $this->companyId)
|
|
->first(['companies.id', 'companies.portal']);
|
|
}
|
|
}; ?>
|
|
|
|
<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">{{ __('User Backend') }}</span>
|
|
<span class="eyebrow muted">{{ __('Mein Bereich · Bearbeiten') }}</span>
|
|
<span class="badge hub">ID {{ $id }}</span>
|
|
</div>
|
|
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
|
|
{{ __('Pressemitteilung bearbeiten') }}
|
|
</h1>
|
|
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
|
|
{{ __('Inhalt und Metadaten der Pressemitteilung aktualisieren.') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2 flex-shrink-0">
|
|
<flux:button variant="ghost" icon="arrow-left" href="{{ route('me.press-releases.show', $id) }}" wire:navigate>
|
|
{{ __('Zurück') }}
|
|
</flux:button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="grid gap-6 lg:grid-cols-[1fr,300px]">
|
|
<div class="space-y-6">
|
|
<article class="panel">
|
|
<div class="panel-head">
|
|
<span class="section-eyebrow">{{ __('Inhalt') }}</span>
|
|
</div>
|
|
<div class="p-5 space-y-4">
|
|
<flux:field>
|
|
<flux:label>{{ __('Titel') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>
|
|
<flux:input wire:model="title" />
|
|
<flux:error name="title" />
|
|
</flux:field>
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Text') }} <span class="text-[color:var(--color-err)]">*</span></flux:label>
|
|
<flux:editor
|
|
wire:model="text"
|
|
toolbar="heading | bold italic | bullet ordered blockquote | link | undo redo"
|
|
/>
|
|
<flux:error name="text" />
|
|
</flux:field>
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Stichwörter') }}</flux:label>
|
|
<flux:input wire:model="keywords" />
|
|
</flux:field>
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Backlink-URL') }}</flux:label>
|
|
<flux:input wire:model="backlinkUrl" type="url" placeholder="https://…" />
|
|
<flux:error name="backlinkUrl" />
|
|
</flux:field>
|
|
</div>
|
|
</article>
|
|
|
|
<livewire:components.press-release-images-manager :press-release-id="$id" :wire:key="'pr-images-'.$id" />
|
|
</div>
|
|
|
|
<aside class="space-y-4">
|
|
<article class="panel">
|
|
<div class="panel-head">
|
|
<span class="section-eyebrow">{{ __('Metadaten') }}</span>
|
|
</div>
|
|
<div class="p-5 space-y-4">
|
|
<flux:field>
|
|
<flux:label>{{ __('Firma') }}</flux:label>
|
|
<flux:select wire:model="companyId">
|
|
@foreach ($myCompanies as $c)
|
|
<option value="{{ $c->id }}">{{ $c->name }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
<flux:error name="companyId" />
|
|
</flux:field>
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Kategorie') }}</flux:label>
|
|
<flux:select wire:model="categoryId">
|
|
<option value="">{{ __('Bitte wählen…') }}</option>
|
|
@foreach ($categories as $cat)
|
|
@php($catName = $cat->translations->firstWhere('locale', 'de')?->name ?? $cat->id)
|
|
<option value="{{ $cat->id }}">{{ $catName }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
<flux:error name="categoryId" />
|
|
</flux:field>
|
|
|
|
<flux:input :label="__('Portal')" :value="$selectedPortalLabel" disabled />
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Sprache') }}</flux:label>
|
|
<flux:select wire:model="language">
|
|
<option value="de">Deutsch</option>
|
|
<option value="en">English</option>
|
|
</flux:select>
|
|
</flux:field>
|
|
</div>
|
|
</article>
|
|
|
|
<article class="panel">
|
|
<div class="panel-head">
|
|
<span class="section-eyebrow">{{ __('Aktionen') }}</span>
|
|
</div>
|
|
<div class="p-5">
|
|
<flux:button type="button" variant="primary" class="w-full" wire:click="save">
|
|
{{ __('Speichern') }}
|
|
</flux:button>
|
|
</div>
|
|
</article>
|
|
</aside>
|
|
</div>
|
|
</div>
|