PM-Detailseite: Inhaltspruefung (Gruen/Gelb/Rot) + Qualitaets-Score sichtbar
- Neue Kachel "Inhaltspruefung" in Status & Verlauf (Badge je Ergebnis, "Ausstehend" ohne Pruefung); bei Gelb neutraler Hinweis, dass die Boost-Platzierung gruen gepruefte Meldungen voraussetzt - Content-Score-Panel des Editors in gemeinsame Komponente x-content-score-panel extrahiert und auf der Detailseite wiederverwendet (Score/100, Stufe, Abstand zur naechsten Schwelle) - PressReleaseClassification::badgeClass() (ok/warn/err) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4f2be1e0a8
commit
6f96e4a4cc
5 changed files with 126 additions and 32 deletions
|
|
@ -24,4 +24,13 @@ enum PressReleaseClassification: string
|
|||
self::Red => 'Rot',
|
||||
};
|
||||
}
|
||||
|
||||
public function badgeClass(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Green => 'ok',
|
||||
self::Yellow => 'warn',
|
||||
self::Red => 'err',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
42
resources/views/components/content-score-panel.blade.php
Normal file
42
resources/views/components/content-score-panel.blade.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{{-- Content-Score-Panel (Qualitäts-Feedback): Score 0–100 + Stufe samt
|
||||
Abstand zur nächsten Schwelle. Gemeinsam genutzt von PM-Editor und
|
||||
PM-Detailseite (13.07.); `note` trägt den kontextspezifischen Fußtext. --}}
|
||||
@props(['score', 'tier' => null, 'note' => null])
|
||||
|
||||
@php
|
||||
$tierEnum = $tier instanceof \App\Enums\PressReleaseContentTier
|
||||
? $tier
|
||||
: ($tier ? \App\Enums\PressReleaseContentTier::from($tier) : null);
|
||||
$tiers = config('scoring.content_score.tiers');
|
||||
$nextThreshold = $score < (int) $tiers['gepruft']
|
||||
? (int) $tiers['gepruft']
|
||||
: ($score < (int) $tiers['hochwertig'] ? (int) $tiers['hochwertig'] : null);
|
||||
@endphp
|
||||
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Qualität') }}</span>
|
||||
@if ($tierEnum)
|
||||
<span class="badge {{ $tierEnum === \App\Enums\PressReleaseContentTier::Hochwertig ? 'ok' : ($tierEnum === \App\Enums\PressReleaseContentTier::Geprueft ? 'hub' : 'muted') }}">{{ $tierEnum->label() }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="text-[26px] font-bold leading-none text-[color:var(--color-ink)]">
|
||||
{{ $score }}<span class="text-[14px] font-medium text-[color:var(--color-ink-3)]">/100</span>
|
||||
</div>
|
||||
@if ($nextThreshold)
|
||||
<p class="text-[12px] text-[color:var(--color-ink-2)] mt-2 m-0">
|
||||
{{ __('Noch :points Punkte bis zur nächsten Stufe.', ['points' => $nextThreshold - $score]) }}
|
||||
</p>
|
||||
@else
|
||||
<p class="text-[12px] text-[color:var(--color-ink-2)] mt-2 m-0">
|
||||
{{ __('Höchste Stufe erreicht.') }}
|
||||
</p>
|
||||
@endif
|
||||
@if ($note)
|
||||
<p class="text-[11px] text-[color:var(--color-ink-3)] mt-2 leading-[1.45] m-0">
|
||||
{{ $note }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</article>
|
||||
|
|
@ -809,38 +809,8 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
|
||||
{{-- Content-Score (Qualitäts-Feedback während des Schreibens) --}}
|
||||
@if (! is_null($contentScore))
|
||||
@php
|
||||
$tierEnum = $contentTier ? \App\Enums\PressReleaseContentTier::from($contentTier) : null;
|
||||
$tiers = config('scoring.content_score.tiers');
|
||||
$nextThreshold = $contentScore < (int) $tiers['gepruft']
|
||||
? (int) $tiers['gepruft']
|
||||
: ($contentScore < (int) $tiers['hochwertig'] ? (int) $tiers['hochwertig'] : null);
|
||||
@endphp
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Qualität') }}</span>
|
||||
@if ($tierEnum)
|
||||
<span class="badge {{ $tierEnum === \App\Enums\PressReleaseContentTier::Hochwertig ? 'ok' : ($tierEnum === \App\Enums\PressReleaseContentTier::Geprueft ? 'hub' : 'muted') }}">{{ $tierEnum->label() }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="text-[26px] font-bold leading-none text-[color:var(--color-ink)]">
|
||||
{{ $contentScore }}<span class="text-[14px] font-medium text-[color:var(--color-ink-3)]">/100</span>
|
||||
</div>
|
||||
@if ($nextThreshold)
|
||||
<p class="text-[12px] text-[color:var(--color-ink-2)] mt-2 m-0">
|
||||
{{ __('Noch :points Punkte bis zur nächsten Stufe.', ['points' => $nextThreshold - $contentScore]) }}
|
||||
</p>
|
||||
@else
|
||||
<p class="text-[12px] text-[color:var(--color-ink-2)] mt-2 m-0">
|
||||
{{ __('Höchste Stufe erreicht.') }}
|
||||
</p>
|
||||
@endif
|
||||
<p class="text-[11px] text-[color:var(--color-ink-3)] mt-2 leading-[1.45] m-0">
|
||||
{{ __('Der Score wird nach dem Speichern automatisch neu berechnet.') }}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
<x-content-score-panel :score="$contentScore" :tier="$contentTier"
|
||||
:note="__('Der Score wird nach dem Speichern automatisch neu berechnet.')" />
|
||||
@endif
|
||||
|
||||
{{-- Portal (Read-only, Badge in Portal-Farbe) --}}
|
||||
|
|
|
|||
|
|
@ -928,6 +928,23 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
<span @class(['badge', $statusClass])>{{ $pr->status->label() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{-- Ergebnis der KI-Inhaltsprüfung (13.07.): erklärt sichtbar,
|
||||
warum die Boost-Platzierung (nur Grün) verfügbar ist oder nicht. --}}
|
||||
<div class="rounded-[5px] border border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)] p-3">
|
||||
<div class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold">{{ __('Inhaltsprüfung') }}</div>
|
||||
<div class="mt-1.5">
|
||||
@if ($pr->classification)
|
||||
<span class="badge {{ $pr->classification->badgeClass() }} dot">{{ $pr->classification->label() }}</span>
|
||||
@else
|
||||
<span class="badge muted">{{ __('Ausstehend') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if ($pr->classification === \App\Enums\PressReleaseClassification::Yellow)
|
||||
<div class="text-[11px] text-[color:var(--color-ink-3)] mt-1.5 leading-[1.45]">
|
||||
{{ __('Die Boost-Platzierung ist grün geprüften Meldungen vorbehalten.') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="rounded-[5px] border border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)] p-3">
|
||||
<div class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold">{{ __('Erstellt') }}</div>
|
||||
<div class="text-[13px] font-semibold text-[color:var(--color-ink)] mt-1">
|
||||
|
|
@ -1047,6 +1064,11 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
@endif
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{-- Content-Score wie im Editor (gemeinsame Komponente, 13.07.). --}}
|
||||
@if (! is_null($pr->content_score))
|
||||
<x-content-score-panel :score="$pr->content_score" :tier="$pr->content_tier" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- ============== TITELBILD (Hero) ============== --}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\PressReleaseClassification;
|
||||
use App\Enums\PressReleaseContentTier;
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
|
|
@ -211,3 +213,52 @@ test('admin show zeigt Boilerplate-Override nicht wenn leer', function () {
|
|||
LivewireVolt::test('admin.press-releases.show', ['id' => $pr->id])
|
||||
->assertDontSee('Eigener Abbinder (Boilerplate)');
|
||||
});
|
||||
|
||||
test('customer show zeigt das Pruefergebnis und den Qualitaets-Score', function () {
|
||||
/** @var TestCase $this */
|
||||
['customer' => $customer, 'pr' => $pr] = makeCustomerForShowPhase8a([
|
||||
'status' => PressReleaseStatus::Published->value,
|
||||
'published_at' => now(),
|
||||
'classification' => PressReleaseClassification::Green->value,
|
||||
'content_score' => 87,
|
||||
'content_tier' => PressReleaseContentTier::Hochwertig->value,
|
||||
]);
|
||||
$this->actingAs($customer);
|
||||
|
||||
LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id])
|
||||
->assertSee('Inhaltsprüfung')
|
||||
->assertSee('Grün')
|
||||
->assertSee('Qualität')
|
||||
->assertSee('87')
|
||||
->assertSee('Hochwertig')
|
||||
->assertSee('Höchste Stufe erreicht.');
|
||||
});
|
||||
|
||||
test('customer show erklaert bei Gelb den fehlenden Boost neutral', function () {
|
||||
/** @var TestCase $this */
|
||||
['customer' => $customer, 'pr' => $pr] = makeCustomerForShowPhase8a([
|
||||
'status' => PressReleaseStatus::Published->value,
|
||||
'published_at' => now(),
|
||||
'classification' => PressReleaseClassification::Yellow->value,
|
||||
]);
|
||||
$this->actingAs($customer);
|
||||
|
||||
LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id])
|
||||
->assertSee('Inhaltsprüfung')
|
||||
->assertSee('Gelb')
|
||||
->assertSee('Die Boost-Platzierung ist grün geprüften Meldungen vorbehalten.');
|
||||
});
|
||||
|
||||
test('customer show zeigt ohne Pruefung Ausstehend und kein Qualitaets-Panel', function () {
|
||||
/** @var TestCase $this */
|
||||
['customer' => $customer, 'pr' => $pr] = makeCustomerForShowPhase8a([
|
||||
'classification' => null,
|
||||
'content_score' => null,
|
||||
]);
|
||||
$this->actingAs($customer);
|
||||
|
||||
LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id])
|
||||
->assertSee('Inhaltsprüfung')
|
||||
->assertSee('Ausstehend')
|
||||
->assertDontSee('Höchste Stufe erreicht.');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue