Titelbild-Upload: Bildrechte in 5 Schritten + große Bildvorschau
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
25ea91d85b
commit
a7c30d4ecc
2 changed files with 167 additions and 90 deletions
|
|
@ -5,6 +5,22 @@
|
|||
|
||||
---
|
||||
|
||||
## 2026-06-12 · Titelbild-Upload: Struktur + große Vorschau ✅
|
||||
|
||||
- **Was**: Das Bildrechte-Formular im Titelbild-Upload (gemeinsame
|
||||
Komponente `press-release-images-manager`, Admin + Customer) war eine
|
||||
lange flache Feldliste — jetzt fünf nummerierte Schritte mit
|
||||
Trennlinien: 1 Bild auswählen, 2 Bildinformationen (öffentlich),
|
||||
3 Herkunft & Lizenz (Pflicht-Badges, 2-Spalten-Grid), 4 Personen &
|
||||
Rechte Dritter (Radio-Gruppen nebeneinander), 5 Bestätigung. Nach der
|
||||
Bildauswahl erscheint statt des Mini-Thumbnails eine **große
|
||||
16:9-Vorschau** im Titelbild-Format (mit Dateiname/Größe und „Anderes
|
||||
Bild wählen"); die Dropzone weicht der Vorschau, bis das Bild
|
||||
verworfen wird.
|
||||
- **Dateien**: `resources/views/livewire/components/press-release-images-manager.blade.php`.
|
||||
- **Build/Test**: PressReleaseImageLicenseTest 12 passed, Suite 552
|
||||
passed / 4 skipped, Pint clean.
|
||||
|
||||
## 2026-06-12 · Verlinkung & Backlinks (Decision-Update 11.06.) ✅
|
||||
|
||||
- **Was**: Systemseitige `rel`-Auszeichnung für Links in PMs umgesetzt:
|
||||
|
|
|
|||
|
|
@ -355,9 +355,17 @@ new class extends Component {
|
|||
</flux:button>
|
||||
</div>
|
||||
@else
|
||||
<form wire:submit="saveImage" class="mt-4 space-y-3 rounded-md border border-zinc-200 p-4 dark:border-zinc-700">
|
||||
<form wire:submit="saveImage" class="mt-4 space-y-6 rounded-md border border-zinc-200 p-4 dark:border-zinc-700 sm:p-5">
|
||||
<flux:heading size="xs">{{ __('Titelbild hochladen') }}</flux:heading>
|
||||
|
||||
{{-- ===== Schritt 1 · Bild auswählen ===== --}}
|
||||
<section class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="flex size-5 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-[11px] font-bold text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">1</span>
|
||||
<span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-500">{{ __('Bild auswählen') }}</span>
|
||||
</div>
|
||||
|
||||
@if (! $newImage)
|
||||
<flux:file-upload wire:model="newImage" accept="image/jpeg,image/png,image/webp"
|
||||
:description="__('JPG/PNG/WebP, max. 16 MB. Wird als Titelbild gespeichert und ersetzt den Platzhalter.')">
|
||||
<flux:file-upload.dropzone :heading="__('Bild hierher ziehen oder klicken')"
|
||||
|
|
@ -368,30 +376,64 @@ new class extends Component {
|
|||
class="rounded-md border border-amber-200 bg-amber-50 p-3 text-xs leading-relaxed text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100">
|
||||
{{ __('Bitte laden Sie nur Bilder hoch, für die Sie die erforderlichen Nutzungsrechte besitzen. Bilder aus Google, Social Media, Messenger-Gruppen oder fremden Websites dürfen nicht ohne ausdrückliche Erlaubnis verwendet werden.') }}
|
||||
</div>
|
||||
|
||||
@if ($newImage)
|
||||
<flux:file-item :heading="$newImage->getClientOriginalName()" :image="$this->newImagePreviewUrl()"
|
||||
:size="$newImage->getSize()">
|
||||
<x-slot name="actions">
|
||||
<flux:file-item.remove wire:click="removeNewImage" :aria-label="__('Bild entfernen')" />
|
||||
</x-slot>
|
||||
</flux:file-item>
|
||||
@else
|
||||
{{-- Große Vorschau im Titelbild-Format, damit das Motiv
|
||||
vor dem Hochladen wirklich beurteilbar ist. --}}
|
||||
<div class="overflow-hidden rounded-md border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="relative aspect-[16/9] bg-zinc-50 dark:bg-zinc-800">
|
||||
@if ($this->newImagePreviewUrl())
|
||||
<img src="{{ $this->newImagePreviewUrl() }}" alt="{{ __('Vorschau des gewählten Titelbilds') }}"
|
||||
class="absolute inset-0 size-full object-cover" />
|
||||
@else
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<flux:icon.photo class="size-10 text-zinc-400" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 border-t border-zinc-200 px-3 py-2 dark:border-zinc-700">
|
||||
<div class="min-w-0 text-xs text-zinc-500">
|
||||
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $newImage->getClientOriginalName() }}</span>
|
||||
<span class="mx-1">·</span>
|
||||
<span>{{ number_format($newImage->getSize() / 1048576, 2, ',', '.') }} MB</span>
|
||||
</div>
|
||||
<flux:button size="xs" variant="filled" icon="arrow-path" wire:click="removeNewImage">
|
||||
{{ __('Anderes Bild wählen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<flux:error name="newImage" />
|
||||
</section>
|
||||
|
||||
{{-- ===== Schritt 2 · Öffentliche Bildinfos ===== --}}
|
||||
<section class="space-y-3 border-t border-zinc-200 pt-5 dark:border-zinc-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="flex size-5 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-[11px] font-bold text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">2</span>
|
||||
<span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-500">{{ __('Bildinformationen (öffentlich sichtbar)') }}</span>
|
||||
</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<flux:input wire:model="newTitle" :label="__('Titel / Alt-Text (optional)')" />
|
||||
<flux:input wire:model="newCopyright" :label="__('Öffentlicher Bildnachweis')"
|
||||
:description="__('Wird öffentlich angezeigt, z. B. Foto: Max Mustermann / Beispiel GmbH.')" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<flux:separator variant="subtle" :text="__('Bildrechte')" />
|
||||
|
||||
<flux:input wire:model="newAuthor" :label="__('Urheber / Fotograf / Rechteinhaber (Pflichtfeld)')"
|
||||
{{-- ===== Schritt 3 · Herkunft & Lizenz ===== --}}
|
||||
<section class="space-y-3 border-t border-zinc-200 pt-5 dark:border-zinc-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="flex size-5 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-[11px] font-bold text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">3</span>
|
||||
<span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-500">{{ __('Herkunft & Lizenz') }}</span>
|
||||
</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<flux:input wire:model="newAuthor" :label="__('Urheber / Fotograf / Rechteinhaber')" :badge="__('Pflicht')"
|
||||
required />
|
||||
<flux:select wire:model.live="newLicenseType" :label="__('Lizenztyp')" required>
|
||||
<flux:select wire:model.live="newLicenseType" :label="__('Lizenztyp')" :badge="__('Pflicht')" required>
|
||||
<flux:select.option value="" disabled>{{ __('Bitte wählen…') }}</flux:select.option>
|
||||
@foreach ($licenseTypeOptions as $value => $label)
|
||||
<flux:select.option :value="$value">{{ $label }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
</div>
|
||||
|
||||
@if ($newLicenseType === \App\Enums\ImageLicenseType::CreativeCommons->value)
|
||||
<flux:select wire:model.live="newLicenseDetail" :label="__('Creative-Commons-Lizenz')" required>
|
||||
|
|
@ -410,14 +452,23 @@ new class extends Component {
|
|||
:description="__('Bitte kurz erklären, warum die Nutzung erlaubt ist.')" required />
|
||||
@endif
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<flux:input wire:model="newLicenseUrl" type="url"
|
||||
:label="$licenseUrlRequired ? __('Quelle oder Lizenznachweis-URL') : __(
|
||||
'Quelle oder Lizenznachweis-URL (optional)')"
|
||||
:required="$licenseUrlRequired" placeholder="https://…" />
|
||||
|
||||
<flux:input wire:model="newSourceUrl" type="url" :label="__('Weitere Quelle / Fundstelle (optional)')"
|
||||
placeholder="https://…" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- ===== Schritt 4 · Personen & Rechte Dritter ===== --}}
|
||||
<section class="space-y-3 border-t border-zinc-200 pt-5 dark:border-zinc-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="flex size-5 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-[11px] font-bold text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">4</span>
|
||||
<span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-500">{{ __('Personen & Rechte Dritter') }}</span>
|
||||
</div>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<flux:radio.group wire:model.live="newPeopleRightsStatus"
|
||||
:label="__('Sind erkennbare Personen abgebildet?')" required>
|
||||
@foreach ($peopleRightsOptions as $value => $label)
|
||||
|
|
@ -425,6 +476,14 @@ new class extends Component {
|
|||
@endforeach
|
||||
</flux:radio.group>
|
||||
|
||||
<flux:radio.group wire:model.live="newPropertyRightsStatus"
|
||||
:label="__('Sind Marken, Kunstwerke, geschützte Werke oder private Orte sichtbar?')" required>
|
||||
@foreach ($propertyRightsOptions as $value => $label)
|
||||
<flux:radio :value="$value" :label="$label" />
|
||||
@endforeach
|
||||
</flux:radio.group>
|
||||
</div>
|
||||
|
||||
@if (in_array($newPeopleRightsStatus, ['consent', 'public_event'], true))
|
||||
<div
|
||||
class="rounded-md border border-amber-200 bg-amber-50 p-3 text-xs leading-relaxed text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100">
|
||||
|
|
@ -432,19 +491,20 @@ new class extends Component {
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<flux:radio.group wire:model.live="newPropertyRightsStatus"
|
||||
:label="__('Sind Marken, Kunstwerke, geschützte Werke oder private Orte sichtbar?')" required>
|
||||
@foreach ($propertyRightsOptions as $value => $label)
|
||||
<flux:radio :value="$value" :label="$label" />
|
||||
@endforeach
|
||||
</flux:radio.group>
|
||||
|
||||
@if ($showsRightsWarning)
|
||||
<div
|
||||
class="rounded-md border border-amber-200 bg-amber-50 p-3 text-xs leading-relaxed text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-100">
|
||||
{{ __('Diese Auswahl kann Einschränkungen enthalten. Bitte laden Sie das Bild nur hoch, wenn die Nutzung, Bearbeitung und Veröffentlichung als Titelbild erlaubt ist.') }}
|
||||
</div>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
{{-- ===== Schritt 5 · Bestätigung ===== --}}
|
||||
<section class="space-y-3 border-t border-zinc-200 pt-5 dark:border-zinc-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="flex size-5 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-[11px] font-bold text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">5</span>
|
||||
<span class="text-[11px] font-semibold uppercase tracking-wider text-zinc-500">{{ __('Bestätigung') }}</span>
|
||||
</div>
|
||||
|
||||
<flux:textarea wire:model="newRightsNotes"
|
||||
:label="__('Interne Hinweise zu Rechten / Freigaben (optional)')" rows="3" />
|
||||
|
|
@ -452,10 +512,11 @@ new class extends Component {
|
|||
<flux:switch wire:model="newRightsConfirmed" align="right" :label="__('Rechte bestätigt')"
|
||||
:description="__('Ich bestätige, dass ich über die erforderlichen Rechte zur Veröffentlichung dieses Bildes verfüge und die Verantwortung für die Richtigkeit meiner Angaben übernehme. Dies umfasst Urheberrechte, Nutzungsrechte, Persönlichkeitsrechte abgebildeter Personen sowie gegebenenfalls Marken-, Eigentums- oder sonstige Rechte Dritter. Mir ist bewusst, dass ich für fehlerhafte oder unvollständige Angaben verantwortlich bin.')" />
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<div class="flex justify-end gap-2 border-t border-zinc-200 pt-4 dark:border-zinc-700">
|
||||
<flux:button type="button" variant="filled" wire:click="closeUploadForm">{{ __('Abbrechen') }}</flux:button>
|
||||
<flux:button type="submit" variant="primary" icon="arrow-up-tray">{{ __('Hochladen') }}</flux:button>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
@endif
|
||||
@else
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue