Backend-WIP: Kontakt-Queue, Page-Cache, Validierungs-Feedback, i18n
Gebuendelt, da ueber gemeinsame lang-/Service-Dateien verschraenkt: - Kontakt-Nachrichten: contact_messages-Tabelle + Model/Enums/Factory, Admin-Queue (livewire/admin/contact-messages), ContactController persistiert das Anliegen zusaetzlich zur Brand-Mail - Page-Data-Cache (Services/Web/*PageData) + web-archive-Indizes auf press_releases - Validierungs-Feedback + Company-Portal-Restriction (Feature-Tests) - i18n-Auslagerung lang/de|en (auth, passwords, validation, static) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c6b5ec8479
commit
fd8e050cb7
60 changed files with 3963 additions and 327 deletions
|
|
@ -15,7 +15,8 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
|
|||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public string $portal = 'both';
|
||||
#[Validate('required|in:'.Portal::Presseecho->value.','.Portal::Businessportal24->value)]
|
||||
public string $portal = Portal::Presseecho->value;
|
||||
|
||||
#[Validate('required|min:3|max:255')]
|
||||
public string $company_name = '';
|
||||
|
|
@ -86,7 +87,7 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
|
|||
if ($this->logo) {
|
||||
$stored = $imageService->storeCompanyLogo(
|
||||
$this->logo,
|
||||
$this->portal === Portal::Both->value ? Portal::Presseecho->value : $this->portal,
|
||||
$this->portal,
|
||||
$company->id,
|
||||
);
|
||||
|
||||
|
|
@ -111,7 +112,13 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
|
|||
['code' => 'GB', 'name' => 'Großbritannien'],
|
||||
['code' => 'US', 'name' => 'USA'],
|
||||
]),
|
||||
'portalOptions' => Portal::cases(),
|
||||
// „Beide Portale" ist bei der Anlage nicht wählbar, damit neue
|
||||
// Firmen ihre PMs nicht automatisch auf beiden Portalen
|
||||
// veröffentlichen.
|
||||
'portalOptions' => array_filter(
|
||||
Portal::cases(),
|
||||
fn (Portal $portalCase): bool => $portalCase !== Portal::Both,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use App\Enums\Portal;
|
|||
use App\Models\Company;
|
||||
use App\Services\Image\ImageService;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Attributes\Validate;
|
||||
|
|
@ -18,6 +19,12 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
|
|||
|
||||
public string $portal = 'both';
|
||||
|
||||
/**
|
||||
* Bestandsschutz: „Beide Portale" wird nur angeboten, wenn die Firma
|
||||
* den Wert bereits trägt — neu zuweisen lässt er sich nicht mehr.
|
||||
*/
|
||||
public bool $allowBothPortal = false;
|
||||
|
||||
#[Validate('required|min:3|max:255')]
|
||||
public string $company_name = '';
|
||||
|
||||
|
|
@ -78,6 +85,7 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
|
|||
}
|
||||
|
||||
$this->portal = $company->portal?->value ?? Portal::Both->value;
|
||||
$this->allowBothPortal = $company->portal === Portal::Both || $company->portal === null;
|
||||
$this->company_name = $company->name;
|
||||
$this->description = '';
|
||||
$this->email = $company->email ?? '';
|
||||
|
|
@ -105,6 +113,18 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
|
|||
return;
|
||||
}
|
||||
|
||||
$allowedPortals = [Portal::Presseecho->value, Portal::Businessportal24->value];
|
||||
|
||||
if ($company->portal === Portal::Both || $company->portal === null) {
|
||||
$allowedPortals[] = Portal::Both->value;
|
||||
}
|
||||
|
||||
if (! in_array($this->portal, $allowedPortals, true)) {
|
||||
throw ValidationException::withMessages([
|
||||
'portal' => __('„Beide Portale" kann nicht mehr neu zugewiesen werden.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$slug = $company->generateUniqueSlug($this->company_name, ['portal' => $this->portal]);
|
||||
$logoPath = $company->logo_path;
|
||||
$logoVariants = $company->logo_variants;
|
||||
|
|
@ -157,7 +177,10 @@ new #[Layout('components.layouts.app'), Title('Firma bearbeiten')] class extends
|
|||
['code' => 'GB', 'name' => 'Großbritannien'],
|
||||
['code' => 'US', 'name' => 'USA'],
|
||||
]),
|
||||
'portalOptions' => Portal::cases(),
|
||||
'portalOptions' => array_filter(
|
||||
Portal::cases(),
|
||||
fn (Portal $portalCase): bool => $portalCase !== Portal::Both || $this->allowBothPortal,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
185
resources/views/livewire/admin/contact-messages/index.blade.php
Normal file
185
resources/views/livewire/admin/contact-messages/index.blade.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\ContactMessageStatus;
|
||||
use App\Enums\ContactMessageTopic;
|
||||
use App\Models\ContactMessage;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Volt\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
new #[Layout('components.layouts.app'), Title('Kontakt')] class extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
|
||||
#[Url(as: 'status', except: 'open')]
|
||||
public string $statusFilter = 'open';
|
||||
|
||||
#[Url(as: 'topic', except: 'all')]
|
||||
public string $topicFilter = 'all';
|
||||
|
||||
public function updatedStatusFilter(): void
|
||||
{
|
||||
$this->resetPage();
|
||||
}
|
||||
|
||||
public function updatedTopicFilter(): void
|
||||
{
|
||||
$this->resetPage();
|
||||
}
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
$messages = ContactMessage::query()
|
||||
->when($this->statusFilter === 'open', fn ($query) => $query->open())
|
||||
->when(! in_array($this->statusFilter, ['open', 'all'], true), fn ($query) => $query->where('status', $this->statusFilter))
|
||||
->when($this->topicFilter !== 'all', fn ($query) => $query->where('topic', $this->topicFilter))
|
||||
->latest()
|
||||
->paginate(50);
|
||||
|
||||
return [
|
||||
'messages' => $messages,
|
||||
'topicOptions' => ContactMessageTopic::cases(),
|
||||
'openCount' => ContactMessage::query()->open()->count(),
|
||||
'totalCount' => ContactMessage::query()->count(),
|
||||
];
|
||||
}
|
||||
|
||||
public function statusBadgeClass(ContactMessageStatus $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
ContactMessageStatus::Open => 'warn',
|
||||
ContactMessageStatus::InProgress => 'hub',
|
||||
ContactMessageStatus::Resolved => 'ok',
|
||||
ContactMessageStatus::Rejected => 'err',
|
||||
};
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="space-y-8">
|
||||
{{-- ============== PAGE HEADER ============== --}}
|
||||
<x-portal.page-header
|
||||
:badge="__('Admin Backend')"
|
||||
:eyebrow="__('Administration · Kontakt')"
|
||||
:title="__('Kontakt')"
|
||||
:description="__('Anliegen aus dem Kontakt-Widget und dem Kontaktformular: Kontaktaufnahmen, Fehlermeldungen, PM-Meldungen und Feedback – manuelle Bearbeitung.')"
|
||||
/>
|
||||
|
||||
{{-- ============== KPI-Reihe ============== --}}
|
||||
<section class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<x-portal.stat-card variant="primary" :label="__('Offen')" :value="number_format($openCount)">
|
||||
<x-slot:meta>{{ __('zu bearbeiten') }}</x-slot:meta>
|
||||
<x-slot:trend>{{ __('offen + in Bearbeitung') }}</x-slot:trend>
|
||||
</x-portal.stat-card>
|
||||
<x-portal.stat-card variant="muted" :label="__('Gesamt')" :value="number_format($totalCount)">
|
||||
<x-slot:meta>{{ __('alle Anliegen') }}</x-slot:meta>
|
||||
<x-slot:trend>{{ __('inkl. erledigt') }}</x-slot:trend>
|
||||
</x-portal.stat-card>
|
||||
</section>
|
||||
|
||||
{{-- ============== FILTER ============== --}}
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Filter') }}</span>
|
||||
</div>
|
||||
<div class="p-5 flex flex-col gap-4 sm:flex-row">
|
||||
<flux:select wire:model.live="statusFilter" class="w-full sm:w-56">
|
||||
<option value="open">{{ __('Offen & in Bearbeitung') }}</option>
|
||||
<option value="all">{{ __('Alle Status') }}</option>
|
||||
<option value="resolved">{{ __('Erledigt') }}</option>
|
||||
<option value="rejected">{{ __('Abgelehnt') }}</option>
|
||||
</flux:select>
|
||||
|
||||
<flux:select wire:model.live="topicFilter" class="w-full sm:w-56">
|
||||
<option value="all">{{ __('Alle Anliegen') }}</option>
|
||||
@foreach ($topicOptions as $topicOption)
|
||||
<option value="{{ $topicOption->value }}">{{ $topicOption->label() }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{-- ============== TABELLE ============== --}}
|
||||
<article class="panel overflow-hidden">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Anliegen') }}</span>
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ __(':count Einträge', ['count' => $messages->total()]) }}
|
||||
</span>
|
||||
</div>
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.column>{{ __('Anliegen') }}</flux:table.column>
|
||||
<flux:table.column>{{ __('Status') }}</flux:table.column>
|
||||
<flux:table.column>{{ __('Absender') }}</flux:table.column>
|
||||
<flux:table.column>{{ __('Portal') }}</flux:table.column>
|
||||
<flux:table.column>{{ __('Eingegangen') }}</flux:table.column>
|
||||
<flux:table.column>{{ __('Aktion') }}</flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@forelse ($messages as $message)
|
||||
<flux:table.row :key="$message->id">
|
||||
<flux:table.cell>
|
||||
<span class="text-[13px] font-semibold text-[color:var(--color-ink)]">
|
||||
{{ $message->topic->label() }}
|
||||
</span>
|
||||
<span class="block text-[11px] font-mono text-[color:var(--color-ink-4)]">
|
||||
{{ $message->reference() }}
|
||||
</span>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<span class="badge dot {{ $this->statusBadgeClass($message->status) }}">
|
||||
{{ $message->status->label() }}
|
||||
</span>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<span class="text-[12.5px] text-[color:var(--color-ink)]">
|
||||
{{ $message->name ?: __('anonym') }}
|
||||
</span>
|
||||
<span class="block text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ $message->email }}
|
||||
</span>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<span class="text-[12.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ $message->portal ?: '—' }}
|
||||
</span>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<span class="text-[12px] text-[color:var(--color-ink-3)]">
|
||||
{{ $message->created_at?->format('d.m.Y H:i') ?? '-' }}
|
||||
</span>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<flux:button size="sm" variant="filled" icon="eye"
|
||||
href="{{ route('admin.contact-messages.show', $message->id) }}" wire:navigate>
|
||||
{{ __('Öffnen') }}
|
||||
</flux:button>
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@empty
|
||||
<flux:table.row>
|
||||
<flux:table.cell colspan="6">
|
||||
<div class="flex flex-col items-center justify-center px-4 py-10 text-center">
|
||||
<div class="w-14 h-14 rounded-[6px] flex items-center justify-center mb-3
|
||||
bg-[color:var(--color-hub-soft)] border border-[color:var(--color-hub-soft-2)] text-[color:var(--color-hub)]">
|
||||
<flux:icon.chat-bubble-left-right class="size-6" />
|
||||
</div>
|
||||
<div class="text-[14px] font-semibold text-[color:var(--color-ink)] mb-1">
|
||||
{{ __('Keine Anliegen in dieser Ansicht') }}
|
||||
</div>
|
||||
<p class="text-[12.5px] text-[color:var(--color-ink-3)] m-0 max-w-[420px]">
|
||||
{{ __('Anliegen aus dem Kontakt-Widget (Kontaktaufnahme, Fehlermeldung, PM-Meldung, Feedback) laufen hier auf.') }}
|
||||
</p>
|
||||
</div>
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforelse
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
|
||||
<x-portal.table-pagination :paginator="$messages" />
|
||||
</article>
|
||||
</div>
|
||||
217
resources/views/livewire/admin/contact-messages/show.blade.php
Normal file
217
resources/views/livewire/admin/contact-messages/show.blade.php
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\ContactMessageStatus;
|
||||
use App\Models\ContactMessage;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new #[Layout('components.layouts.app'), Title('Anliegen · Kontakt')] class extends Component
|
||||
{
|
||||
#[Locked]
|
||||
public int $id;
|
||||
|
||||
public string $adminNote = '';
|
||||
|
||||
public string $notification = '';
|
||||
|
||||
public function mount(int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->adminNote = (string) ($this->message()->admin_note ?? '');
|
||||
}
|
||||
|
||||
public function markInProgress(): void
|
||||
{
|
||||
$this->message()->update(['status' => ContactMessageStatus::InProgress->value]);
|
||||
$this->notification = __('Status auf „In Bearbeitung" gesetzt.');
|
||||
}
|
||||
|
||||
public function resolve(): void
|
||||
{
|
||||
$this->finish(ContactMessageStatus::Resolved, __('Anliegen als erledigt markiert.'));
|
||||
}
|
||||
|
||||
public function reject(): void
|
||||
{
|
||||
$this->finish(ContactMessageStatus::Rejected, __('Anliegen abgelehnt.'));
|
||||
}
|
||||
|
||||
public function reopen(): void
|
||||
{
|
||||
$this->message()->update([
|
||||
'status' => ContactMessageStatus::Open->value,
|
||||
'resolved_by_user_id' => null,
|
||||
'resolved_at' => null,
|
||||
]);
|
||||
$this->notification = __('Anliegen wieder geöffnet.');
|
||||
}
|
||||
|
||||
private function finish(ContactMessageStatus $status, string $notification): void
|
||||
{
|
||||
$this->message()->update([
|
||||
'status' => $status->value,
|
||||
'admin_note' => $this->adminNote !== '' ? $this->adminNote : null,
|
||||
'resolved_by_user_id' => auth()->id(),
|
||||
'resolved_at' => now(),
|
||||
]);
|
||||
|
||||
$this->notification = $notification;
|
||||
}
|
||||
|
||||
private function message(): ContactMessage
|
||||
{
|
||||
return ContactMessage::query()->with(['user', 'resolver'])->findOrFail($this->id);
|
||||
}
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
return ['message' => $this->message()];
|
||||
}
|
||||
|
||||
public function statusBadgeClass(ContactMessageStatus $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
ContactMessageStatus::Open => 'warn',
|
||||
ContactMessageStatus::InProgress => 'hub',
|
||||
ContactMessageStatus::Resolved => 'ok',
|
||||
ContactMessageStatus::Rejected => 'err',
|
||||
};
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="space-y-8">
|
||||
{{-- ============== PAGE HEADER ============== --}}
|
||||
<x-portal.page-header
|
||||
size="sm"
|
||||
:badge="__('Admin Backend')"
|
||||
:eyebrow="__('Kontakt · Anliegen')"
|
||||
:title="$message->topic->label()"
|
||||
>
|
||||
<x-slot:meta>
|
||||
<span class="badge dot {{ $this->statusBadgeClass($message->status) }}">{{ $message->status->label() }}</span>
|
||||
</x-slot:meta>
|
||||
<x-slot:actions>
|
||||
<flux:button icon="arrow-left" variant="filled" href="{{ route('admin.contact-messages.index') }}" wire:navigate>
|
||||
{{ __('Zurück') }}
|
||||
</flux:button>
|
||||
</x-slot:actions>
|
||||
</x-portal.page-header>
|
||||
|
||||
@if ($notification)
|
||||
<div x-data="{ show: true }" x-init="setTimeout(() => show = false, 3000)" x-show="show" x-transition
|
||||
class="px-4 py-3 rounded-[5px] border text-[12.5px] flex items-center gap-2
|
||||
bg-[color:var(--color-ok-soft)] border-[color:var(--color-ok)]/30 text-[color:var(--color-gain-deep)]">
|
||||
<flux:icon.check-circle class="size-[16px] flex-shrink-0" />
|
||||
{{ $notification }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-6 xl:grid-cols-3">
|
||||
{{-- ============== ANLIEGEN ============== --}}
|
||||
<article class="panel xl:col-span-2">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Anliegen') }}</span>
|
||||
</div>
|
||||
<div class="p-5 space-y-4">
|
||||
<div class="rounded-[5px] border border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)] p-4 text-[13px] text-[color:var(--color-ink)] whitespace-pre-line">
|
||||
{{ $message->message ?: __('Keine Nachricht hinterlegt.') }}
|
||||
</div>
|
||||
|
||||
<dl class="grid gap-3 sm:grid-cols-2 text-[12.5px]">
|
||||
<div>
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Vorgangsnummer') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)] font-mono">{{ $message->reference() }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Name') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">{{ $message->name ?: __('anonym') }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Absender') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)] break-all">
|
||||
@if ($message->email)
|
||||
<a href="mailto:{{ $message->email }}"
|
||||
class="text-[color:var(--color-hub)] underline underline-offset-2">{{ $message->email }}</a>
|
||||
@else
|
||||
{{ __('anonym') }}
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Eingegangen') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">{{ $message->created_at?->format('d.m.Y H:i') ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Portal') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">{{ $message->portal ?: '—' }}</dd>
|
||||
</div>
|
||||
@if ($message->user)
|
||||
<div>
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Benutzerkonto') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">
|
||||
{{ $message->user->name }}
|
||||
@if (\Illuminate\Support\Facades\Route::has('admin.users.show'))
|
||||
<a href="{{ route('admin.users.show', $message->user->id) }}" wire:navigate
|
||||
class="ml-2 text-[12px] text-[color:var(--color-hub)] underline underline-offset-2">{{ __('öffnen') }}</a>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if ($message->page_url)
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold mb-1">{{ __('Abgesendet von Seite') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">
|
||||
<a href="{{ $message->page_url }}" target="_blank" rel="noopener"
|
||||
class="text-[color:var(--color-hub)] underline underline-offset-2 break-all">{{ $message->page_url }}</a>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{-- ============== BEARBEITUNG ============== --}}
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Bearbeitung') }}</span>
|
||||
</div>
|
||||
<div class="p-5 space-y-4">
|
||||
<flux:field>
|
||||
<flux:textarea wire:model="adminNote" :label="__('Interne Notiz')" rows="4"
|
||||
:placeholder="__('Wie wurde das Anliegen bearbeitet?')" />
|
||||
</flux:field>
|
||||
|
||||
@if ($message->resolver)
|
||||
<p class="text-[12px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Zuletzt bearbeitet von :name am :date', [
|
||||
'name' => $message->resolver->name,
|
||||
'date' => $message->resolved_at?->format('d.m.Y H:i') ?? '-',
|
||||
]) }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<div class="flex flex-col gap-2 pt-2 border-t border-[color:var(--color-bg-rule)]">
|
||||
@if ($message->status->isOpen())
|
||||
@if ($message->status !== \App\Enums\ContactMessageStatus::InProgress)
|
||||
<flux:button variant="filled" icon="clock" wire:click="markInProgress">
|
||||
{{ __('In Bearbeitung nehmen') }}
|
||||
</flux:button>
|
||||
@endif
|
||||
<flux:button variant="primary" icon="check" wire:click="resolve">
|
||||
{{ __('Als erledigt markieren') }}
|
||||
</flux:button>
|
||||
<flux:button variant="danger" icon="x-mark" wire:click="reject">
|
||||
{{ __('Ablehnen') }}
|
||||
</flux:button>
|
||||
@else
|
||||
<flux:button variant="filled" icon="arrow-path" wire:click="reopen">
|
||||
{{ __('Wieder öffnen') }}
|
||||
</flux:button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue