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:
Kevin Adametz 2026-07-09 15:38:11 +00:00
parent c6b5ec8479
commit fd8e050cb7
60 changed files with 3963 additions and 327 deletions

View file

@ -0,0 +1,79 @@
<?php
namespace App\Models;
use App\Enums\ContactMessageStatus;
use App\Enums\ContactMessageTopic;
use Database\Factories\ContactMessageFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* Kontakt-Anliegen aus dem schwebenden Kontakt-Widget (Kontaktaufnahme,
* Fehlermeldung, PM-Meldung, Feedback). Bewusst OHNE PortalScope: die
* Admin-Queue „Kontakt" sieht alle Portale; das Portal wird zur Anzeige/
* Filterung als eigene Spalte gehalten.
*/
class ContactMessage extends Model
{
/** @use HasFactory<ContactMessageFactory> */
use HasFactory;
protected $fillable = [
'portal',
'topic',
'status',
'name',
'email',
'message',
'page_url',
'payload',
'requester_ip',
'user_id',
'resolved_by_user_id',
'admin_note',
'resolved_at',
];
protected function casts(): array
{
return [
'topic' => ContactMessageTopic::class,
'status' => ContactMessageStatus::class,
'payload' => 'array',
'resolved_at' => 'datetime',
];
}
/**
* Öffentliche Vorgangsnummer — analog zu Legal-Requests ("M-{id}").
*/
public function reference(): string
{
return 'KT-'.$this->id;
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function resolver(): BelongsTo
{
return $this->belongsTo(User::class, 'resolved_by_user_id');
}
/**
* @param Builder<ContactMessage> $query
* @return Builder<ContactMessage>
*/
public function scopeOpen(Builder $query): Builder
{
return $query->whereIn('status', [
ContactMessageStatus::Open->value,
ContactMessageStatus::InProgress->value,
]);
}
}