Admin-PM-Liste: KI-Pruefungs-Modal + geschaerfte KI-Prompts
- Spalten umsortiert (Aktionen, Datum, KI-Pruefung zuerst); Pruefen-Button nur noch Icon, oeffnet Detail-Modal mit Klassifikation, Begruendung, Content-Score inkl. Faktor-Breakdown und Re-Check (wire:poll bis Queue durch) - Content-Score: Bild-Punkte (10) systemseitig statt KI (nur ein Upload-Bild moeglich), Quellen 10->5, Pressestil 20->25; Breakdown wandert ins Audit-Log - Klassifikations-Prompt prueft gegen aktive Ablehnungskategorien (K1-K6, rejection_categories): klarer Verstoss => direkt rot mit Kategorie-Code Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
9cb53b1e17
commit
b26dc9057c
7 changed files with 613 additions and 89 deletions
|
|
@ -9,6 +9,7 @@ use App\Enums\PressReleaseStatus;
|
|||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Contact;
|
||||
use App\Models\KiAudit;
|
||||
use App\Models\PressRelease;
|
||||
use App\Models\User;
|
||||
use App\Services\Admin\AdminPerformanceCache;
|
||||
|
|
@ -164,17 +165,41 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
);
|
||||
}
|
||||
|
||||
/** PM-ID, deren KI-Prüfdetails aktuell im Modal angezeigt werden. */
|
||||
public ?int $aiCheckId = null;
|
||||
|
||||
/** Zeitpunkt (ISO 8601) des zuletzt angestoßenen Laufs — steuert die „läuft…"-Anzeige. */
|
||||
public ?string $aiCheckStartedAt = null;
|
||||
|
||||
public function openAiCheck(int $id): void
|
||||
{
|
||||
$this->aiCheckId = $id;
|
||||
$this->aiCheckStartedAt = null;
|
||||
|
||||
Flux::modal('ai-check-details')->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manuelle KI-Prüfung aus der Liste — bewusst ohne Routing: bewertet
|
||||
* und protokolliert, ändert aber nie den Status (Admin entscheidet).
|
||||
*/
|
||||
public function runAiCheck(int $id): void
|
||||
public function runAiCheck(?int $id = null): void
|
||||
{
|
||||
$id ??= $this->aiCheckId;
|
||||
|
||||
if ($id === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pr = $this->findAdminPR($id);
|
||||
|
||||
ClassifyPressRelease::dispatch($pr->id, route: false)->onQueue('classification');
|
||||
ScorePressRelease::dispatch($pr->id)->onQueue('classification');
|
||||
|
||||
if ($id === $this->aiCheckId) {
|
||||
$this->aiCheckStartedAt = now()->toIso8601String();
|
||||
}
|
||||
|
||||
Flux::toast(__('KI-Prüfung angestoßen — Ergebnis erscheint nach dem nächsten Queue-Lauf.'), variant: 'success');
|
||||
}
|
||||
|
||||
|
|
@ -278,6 +303,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
|
||||
return [
|
||||
'pressReleases' => $query,
|
||||
'aiCheck' => $this->aiCheckDetails(),
|
||||
'stats' => $this->pressReleaseStats(),
|
||||
'statusOptions' => PressReleaseStatus::cases(),
|
||||
'portalOptions' => Portal::cases(),
|
||||
|
|
@ -288,6 +314,74 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Daten für das KI-Prüfungs-Modal: PM, jüngste Audit-Einträge und ob
|
||||
* gerade ein angestoßener Lauf noch aussteht (Queue noch nicht durch).
|
||||
*
|
||||
* @return array{pr: PressRelease, classificationAudit: KiAudit|null, scoreAudit: KiAudit|null, pending: bool}|null
|
||||
*/
|
||||
private function aiCheckDetails(): ?array
|
||||
{
|
||||
if ($this->aiCheckId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pr = PressRelease::withoutGlobalScopes()
|
||||
->with(['kiAudits' => fn ($q) => $q->limit(10)])
|
||||
->find($this->aiCheckId);
|
||||
|
||||
if ($pr === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classificationAudit = $pr->kiAudits->firstWhere('type', KiAudit::TYPE_CLASSIFICATION);
|
||||
$scoreAudit = $pr->kiAudits->firstWhere('type', KiAudit::TYPE_CONTENT_SCORE);
|
||||
|
||||
$startedAt = $this->aiCheckStartedAt !== null
|
||||
? \Illuminate\Support\Carbon::parse($this->aiCheckStartedAt)
|
||||
: null;
|
||||
|
||||
$pending = $startedAt !== null && (
|
||||
$classificationAudit === null
|
||||
|| $classificationAudit->created_at->lt($startedAt)
|
||||
|| $scoreAudit === null
|
||||
|| $scoreAudit->created_at->lt($startedAt)
|
||||
);
|
||||
|
||||
return [
|
||||
'pr' => $pr,
|
||||
'classificationAudit' => $classificationAudit,
|
||||
'scoreAudit' => $scoreAudit,
|
||||
'scoreBreakdown' => $this->scoreBreakdown($scoreAudit),
|
||||
'pending' => $pending,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiert die Faktor-Aufschlüsselung des Content-Scores aus der
|
||||
* Audit-Roh-Antwort. Deterministischer Treiber legt sie direkt ab,
|
||||
* OpenAI liefert sie im JSON der Message.
|
||||
*
|
||||
* @return array<string, int|float>
|
||||
*/
|
||||
private function scoreBreakdown(?KiAudit $scoreAudit): array
|
||||
{
|
||||
if ($scoreAudit === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$raw = $scoreAudit->raw_response ?? [];
|
||||
$breakdown = $raw['breakdown'] ?? null;
|
||||
|
||||
if (! is_array($breakdown)) {
|
||||
$content = data_get($raw, 'choices.0.message.content');
|
||||
$parsed = is_string($content) ? json_decode($content, true) : null;
|
||||
$breakdown = is_array($parsed['breakdown'] ?? null) ? $parsed['breakdown'] : [];
|
||||
}
|
||||
|
||||
return array_filter($breakdown, static fn ($value): bool => is_numeric($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{total: int, published: int, review: int, draft: int, rejected: int, archived: int}
|
||||
*/
|
||||
|
|
@ -846,6 +940,10 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
</div>
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.column class="w-[90px]">{{ __('Aktionen') }}</flux:table.column>
|
||||
<flux:table.column class="w-[130px]" sortable :sorted="$sortBy === 'created_at'"
|
||||
:direction="$sortDir" wire:click="sort('created_at')">{{ __('Datum') }}</flux:table.column>
|
||||
<flux:table.column class="w-[120px]">{{ __('KI-Prüfung') }}</flux:table.column>
|
||||
<flux:table.column class="w-[200px]" sortable :sorted="$sortBy === 'status'" :direction="$sortDir"
|
||||
wire:click="sort('status')">{{ __('Status') }}</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'title'" :direction="$sortDir"
|
||||
|
|
@ -853,12 +951,8 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
<flux:table.column class="w-[200px]" sortable :sorted="$sortBy === 'portal'"
|
||||
:direction="$sortDir" wire:click="sort('portal')">{{ __('Portal') }}</flux:table.column>
|
||||
<flux:table.column class="w-[180px]">{{ __('Kategorie') }}</flux:table.column>
|
||||
<flux:table.column class="w-[130px]" sortable :sorted="$sortBy === 'created_at'"
|
||||
:direction="$sortDir" wire:click="sort('created_at')">{{ __('Datum') }}</flux:table.column>
|
||||
<flux:table.column class="w-[80px]" sortable :sorted="$sortBy === 'hits'" :direction="$sortDir"
|
||||
wire:click="sort('hits')">{{ __('Hits') }}</flux:table.column>
|
||||
<flux:table.column class="w-[120px]">{{ __('KI-Prüfung') }}</flux:table.column>
|
||||
<flux:table.column class="w-[90px]">{{ __('Aktionen') }}</flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
@forelse($pressReleases as $pr)
|
||||
|
|
@ -886,6 +980,59 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
: $pr->created_at;
|
||||
@endphp
|
||||
<flux:table.row wire:key="{{ $pr->id }}" class="{{ $rowClass }}">
|
||||
<flux:table.cell>
|
||||
<div class="flex items-center gap-1">
|
||||
<flux:button size="sm" variant="filled" icon="eye"
|
||||
href="{{ route('admin.press-releases.show', $pr->id) }}" wire:navigate
|
||||
title="{{ __('Ansehen') }}" />
|
||||
<flux:button size="sm" variant="filled" icon="pencil"
|
||||
href="{{ route('admin.press-releases.edit', $pr->id) }}" wire:navigate
|
||||
title="{{ __('Bearbeiten') }}" />
|
||||
</div>
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
<div class="font-mono text-[12px] text-[color:var(--color-ink-2)] tabular-nums">
|
||||
{{ $primaryDate?->format('d.m.Y') }}
|
||||
</div>
|
||||
<div class="text-[10.5px] text-[color:var(--color-ink-4)] mt-0.5">
|
||||
{{ $dateSubLabel }} · {{ $primaryDate?->format('H:i') }}
|
||||
</div>
|
||||
@if ($status === 'review' && $pr->scheduled_at && $pr->scheduled_at->isFuture())
|
||||
<div class="mt-1 inline-flex items-center gap-1 text-[10.5px] font-mono tabular-nums text-[color:var(--color-accent-deep)]">
|
||||
<flux:icon.calendar variant="micro" class="size-3" />
|
||||
<span>{{ __('geplant') }} · {{ $pr->scheduledAtLocal()->format('d.m. H:i') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if ($pr->embargo_at && $pr->embargo_at->isFuture())
|
||||
<div class="mt-1 inline-flex items-center gap-1 text-[10.5px] font-mono tabular-nums text-[color:var(--color-accent-deep)]">
|
||||
<flux:icon.lock-closed variant="micro" class="size-3" />
|
||||
<span>{{ __('Embargo bis') }} {{ $pr->embargoAtLocal()->format('d.m.') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<flux:button size="sm" variant="filled" icon="sparkles"
|
||||
wire:click="openAiCheck({{ $pr->id }})"
|
||||
title="{{ __('KI-Prüfung öffnen') }}" />
|
||||
@if ($pr->classification)
|
||||
@php
|
||||
$kiBadgeClass = match ($pr->classification) {
|
||||
\App\Enums\PressReleaseClassification::Green => 'published',
|
||||
\App\Enums\PressReleaseClassification::Yellow => 'review',
|
||||
\App\Enums\PressReleaseClassification::Red => 'rejected',
|
||||
};
|
||||
@endphp
|
||||
<button type="button" wire:click="openAiCheck({{ $pr->id }})"
|
||||
class="cursor-pointer" title="{{ __('KI-Prüfung öffnen') }}">
|
||||
<span @class(['badge', $kiBadgeClass])>{{ $pr->classification->label() }}</span>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
<div class="flex items-center gap-1.5 flex-wrap">
|
||||
<span class="badge {{ $badgeClass }} dot">{{ $pr->status->label() }}</span>
|
||||
|
|
@ -968,59 +1115,9 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
</span>
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
<div class="font-mono text-[12px] text-[color:var(--color-ink-2)] tabular-nums">
|
||||
{{ $primaryDate?->format('d.m.Y') }}
|
||||
</div>
|
||||
<div class="text-[10.5px] text-[color:var(--color-ink-4)] mt-0.5">
|
||||
{{ $dateSubLabel }} · {{ $primaryDate?->format('H:i') }}
|
||||
</div>
|
||||
@if ($status === 'review' && $pr->scheduled_at && $pr->scheduled_at->isFuture())
|
||||
<div class="mt-1 inline-flex items-center gap-1 text-[10.5px] font-mono tabular-nums text-[color:var(--color-accent-deep)]">
|
||||
<flux:icon.calendar variant="micro" class="size-3" />
|
||||
<span>{{ __('geplant') }} · {{ $pr->scheduledAtLocal()->format('d.m. H:i') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if ($pr->embargo_at && $pr->embargo_at->isFuture())
|
||||
<div class="mt-1 inline-flex items-center gap-1 text-[10.5px] font-mono tabular-nums text-[color:var(--color-accent-deep)]">
|
||||
<flux:icon.lock-closed variant="micro" class="size-3" />
|
||||
<span>{{ __('Embargo bis') }} {{ $pr->embargoAtLocal()->format('d.m.') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
<span
|
||||
class="font-mono text-[12px] text-[color:var(--color-ink-2)] tabular-nums">{{ number_format($pr->hits) }}</span>
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
@if ($pr->classification)
|
||||
@php
|
||||
$kiBadgeClass = match ($pr->classification) {
|
||||
\App\Enums\PressReleaseClassification::Green => 'published',
|
||||
\App\Enums\PressReleaseClassification::Yellow => 'review',
|
||||
\App\Enums\PressReleaseClassification::Red => 'rejected',
|
||||
};
|
||||
@endphp
|
||||
<span @class(['badge', $kiBadgeClass])>{{ $pr->classification->label() }}</span>
|
||||
@else
|
||||
<flux:button size="sm" variant="filled" icon="sparkles" wire:click="runAiCheck({{ $pr->id }})"
|
||||
wire:loading.attr="disabled" title="{{ __('KI-Prüfung starten (ohne Statusänderung)') }}">
|
||||
{{ __('Prüfen') }}
|
||||
</flux:button>
|
||||
@endif
|
||||
</flux:table.cell>
|
||||
|
||||
<flux:table.cell>
|
||||
<div class="flex items-center gap-1">
|
||||
<flux:button size="sm" variant="filled" icon="eye"
|
||||
href="{{ route('admin.press-releases.show', $pr->id) }}" wire:navigate
|
||||
title="{{ __('Ansehen') }}" />
|
||||
<flux:button size="sm" variant="filled" icon="pencil"
|
||||
href="{{ route('admin.press-releases.edit', $pr->id) }}" wire:navigate
|
||||
title="{{ __('Bearbeiten') }}" />
|
||||
</div>
|
||||
|
||||
@if ($pr->status === \App\Enums\PressReleaseStatus::Review)
|
||||
<flux:modal name="confirm-index-publish-{{ $pr->id }}" class="max-w-lg">
|
||||
|
|
@ -1085,7 +1182,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
</flux:table.row>
|
||||
@empty
|
||||
<flux:table.row>
|
||||
<flux:table.cell colspan="7">
|
||||
<flux:table.cell colspan="8">
|
||||
@if ($hasAnyFilter)
|
||||
<x-portal.empty-state
|
||||
variant="warm"
|
||||
|
|
@ -1127,4 +1224,159 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten
|
|||
</article>
|
||||
|
||||
{{ $pressReleases->links('components.portal.pagination') }}
|
||||
|
||||
{{-- ============== KI-PRÜFUNGS-MODAL ============== --}}
|
||||
<flux:modal name="ai-check-details" class="max-w-2xl">
|
||||
@if ($aiCheck)
|
||||
@php
|
||||
$aiPr = $aiCheck['pr'];
|
||||
$classificationAudit = $aiCheck['classificationAudit'];
|
||||
$scoreAudit = $aiCheck['scoreAudit'];
|
||||
@endphp
|
||||
<div class="space-y-6" @if ($aiCheck['pending']) wire:poll.3s @endif>
|
||||
<div>
|
||||
<flux:heading size="lg">{{ __('KI-Prüfung') }}</flux:heading>
|
||||
<flux:subheading>
|
||||
PM-{{ $aiPr->id }} · {{ \Illuminate\Support\Str::limit($aiPr->title, 90) }}
|
||||
</flux:subheading>
|
||||
</div>
|
||||
|
||||
@if ($aiCheck['pending'])
|
||||
<div class="flex items-center gap-2 rounded-[6px] border border-[color:var(--color-warn)]/40 bg-[color:var(--color-warn-soft)] px-3 py-2.5 text-[12.5px] text-[color:var(--color-accent-deep)]">
|
||||
<flux:icon.arrow-path class="size-4 animate-spin" />
|
||||
{{ __('Prüfung läuft — das Ergebnis erscheint hier automatisch, sobald die Queue durchgelaufen ist.') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
{{-- Klassifikation (Rot/Gelb/Grün) --}}
|
||||
<section class="rounded-[6px] border border-[color:var(--color-line)] p-4 space-y-2">
|
||||
<span class="section-eyebrow">{{ __('Klassifikation') }}</span>
|
||||
<div>
|
||||
@if ($aiPr->classification)
|
||||
@php
|
||||
$kiBadge = match ($aiPr->classification) {
|
||||
\App\Enums\PressReleaseClassification::Green => 'ok',
|
||||
\App\Enums\PressReleaseClassification::Yellow => 'warn',
|
||||
\App\Enums\PressReleaseClassification::Red => 'err',
|
||||
};
|
||||
@endphp
|
||||
<span class="badge {{ $kiBadge }} dot">{{ $aiPr->classification->label() }}</span>
|
||||
@else
|
||||
<span class="badge muted">{{ __('Noch nicht geprüft') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if ($classificationAudit)
|
||||
@if ($classificationAudit->reason)
|
||||
<p class="m-0 text-[12.5px] leading-[1.5] text-[color:var(--color-ink-2)] whitespace-pre-line">
|
||||
{{ $classificationAudit->reason }}
|
||||
</p>
|
||||
@elseif ($aiPr->classification === \App\Enums\PressReleaseClassification::Green)
|
||||
<p class="m-0 text-[12.5px] leading-[1.5] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Keine Auffälligkeiten — bei Grün liefert die KI keine Begründung.') }}
|
||||
</p>
|
||||
@endif
|
||||
<p class="m-0 text-[11px] text-[color:var(--color-ink-3)]">
|
||||
{{ $classificationAudit->created_at->format('d.m.Y H:i') }}
|
||||
· {{ $classificationAudit->provider }}
|
||||
@if ($classificationAudit->model)
|
||||
· {{ $classificationAudit->model }}
|
||||
@endif
|
||||
</p>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
{{-- Content-Score --}}
|
||||
<section class="rounded-[6px] border border-[color:var(--color-line)] p-4 space-y-2">
|
||||
<span class="section-eyebrow">{{ __('Content-Score') }}</span>
|
||||
<div>
|
||||
@if (! is_null($aiPr->content_score) && $aiPr->content_tier)
|
||||
@php
|
||||
$tierBadge = match ($aiPr->content_tier) {
|
||||
\App\Enums\PressReleaseContentTier::Hochwertig => 'ok',
|
||||
\App\Enums\PressReleaseContentTier::Geprueft => 'hub',
|
||||
\App\Enums\PressReleaseContentTier::Standard => 'muted',
|
||||
};
|
||||
@endphp
|
||||
<span class="badge {{ $tierBadge }}">{{ $aiPr->content_score }} / 100 · {{ $aiPr->content_tier->label() }}</span>
|
||||
@else
|
||||
<span class="badge muted">{{ __('Noch nicht bewertet') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if ($aiCheck['scoreBreakdown'] !== [])
|
||||
@php
|
||||
$breakdownMeta = $scoreAudit?->provider === 'deterministic'
|
||||
? [
|
||||
'base' => [__('Basiswert'), 40],
|
||||
'length' => [__('Textlänge'), 20],
|
||||
'subtitle' => [__('Untertitel'), 6],
|
||||
'image' => [__('Bildmaterial'), 10],
|
||||
'source' => [__('Quelle/Link'), 8],
|
||||
'headline' => [__('Headline'), 8],
|
||||
'keywords' => [__('Keywords'), 4],
|
||||
'contact' => [__('Pressekontakt'), 4],
|
||||
]
|
||||
: [
|
||||
'pressestil' => [__('Pressestil'), 25],
|
||||
'struktur' => [__('Struktur'), 15],
|
||||
'lesbarkeit' => [__('Lesbarkeit'), 10],
|
||||
'vollstaendigkeit' => [__('Vollständigkeit'), 15],
|
||||
'bild' => [__('Bildmaterial'), 10],
|
||||
'quellen' => [__('Quellen/Belege'), 5],
|
||||
'headline' => [__('Headline'), 10],
|
||||
'originalitaet' => [__('Originalität'), 10],
|
||||
];
|
||||
@endphp
|
||||
<dl class="m-0 space-y-1.5 pt-1">
|
||||
@foreach ($aiCheck['scoreBreakdown'] as $factor => $points)
|
||||
@php
|
||||
[$factorLabel, $factorMax] = $breakdownMeta[$factor] ?? [ucfirst($factor), null];
|
||||
$factorRatio = $factorMax ? min(100, (int) round(($points / $factorMax) * 100)) : null;
|
||||
@endphp
|
||||
<div class="flex items-center gap-2" wire:key="ai-breakdown-{{ $factor }}">
|
||||
<dt class="w-[110px] flex-shrink-0 text-[11px] text-[color:var(--color-ink-3)]">
|
||||
{{ $factorLabel }}
|
||||
</dt>
|
||||
<dd class="m-0 flex-1">
|
||||
@if ($factorRatio !== null)
|
||||
<div class="h-1.5 rounded-full bg-[color:var(--color-line)] overflow-hidden">
|
||||
<div class="h-full rounded-full bg-[color:var(--color-hub)]" style="width: {{ $factorRatio }}%"></div>
|
||||
</div>
|
||||
@endif
|
||||
</dd>
|
||||
<dd class="m-0 w-[46px] flex-shrink-0 text-right font-mono text-[11px] tabular-nums text-[color:var(--color-ink-2)]">
|
||||
{{ $points }}{{ $factorMax !== null ? ' / '.$factorMax : '' }}
|
||||
</dd>
|
||||
</div>
|
||||
@endforeach
|
||||
</dl>
|
||||
@endif
|
||||
@if ($scoreAudit)
|
||||
<p class="m-0 text-[11px] text-[color:var(--color-ink-3)]">
|
||||
{{ $scoreAudit->created_at->format('d.m.Y H:i') }}
|
||||
· {{ $scoreAudit->provider }}
|
||||
@if ($scoreAudit->model)
|
||||
· {{ $scoreAudit->model }}
|
||||
@endif
|
||||
</p>
|
||||
@endif
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<p class="m-0 text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Die manuelle Prüfung bewertet und protokolliert nur — der Status der PM bleibt unverändert.') }}
|
||||
</p>
|
||||
|
||||
<div class="flex justify-end space-x-2 rtl:space-x-reverse">
|
||||
<flux:modal.close>
|
||||
<flux:button variant="filled">{{ __('Schließen') }}</flux:button>
|
||||
</flux:modal.close>
|
||||
<flux:button variant="primary" icon="sparkles" wire:click="runAiCheck"
|
||||
wire:loading.attr="disabled" :disabled="$aiCheck['pending']">
|
||||
{{ $aiPr->classification ? __('Erneut prüfen') : __('Prüfung starten') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</flux:modal>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue