Embed clips in admin review
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { CheckCircle2, Film, Layers3, PlayCircle } from '@lucide/vue'
|
||||
|
||||
import { buildClipEmbed, type ClipEmbed } from '../../lib/clipEmbeds'
|
||||
import { useAwardsStore } from '../../stores/awards'
|
||||
import type { AdminClipSubmissionItem } from '../../types/awards'
|
||||
|
||||
@@ -37,6 +38,9 @@ export function useAdminClipManager() {
|
||||
(!search || [clip.title, clip.creator, clip.platform, clip.submittedByTwitchId, clip.clipUrl].join(' ').toLowerCase().includes(search)),
|
||||
)
|
||||
})
|
||||
const clipEmbeds = computed<Record<number, ClipEmbed | null>>(() =>
|
||||
Object.fromEntries(submissions.value.map((clip) => [clip.id, buildClipEmbed(clip.clipUrl)])),
|
||||
)
|
||||
const duplicateUrls = computed(() => {
|
||||
const counts = new Map<string, number>()
|
||||
for (const clip of submissions.value) {
|
||||
@@ -150,6 +154,7 @@ export function useAdminClipManager() {
|
||||
submissions,
|
||||
categoryName,
|
||||
clips,
|
||||
clipEmbeds,
|
||||
stats,
|
||||
statusFilters,
|
||||
platformFilters,
|
||||
|
||||
@@ -22,6 +22,7 @@ const {
|
||||
submissions,
|
||||
categoryName,
|
||||
clips,
|
||||
clipEmbeds,
|
||||
stats,
|
||||
statusFilters,
|
||||
platformFilters,
|
||||
@@ -95,39 +96,59 @@ const {
|
||||
<p v-if="adminError" class="border-b border-rose-100 bg-rose-50 px-5 py-3 text-sm text-rose-700">{{ adminError }}</p>
|
||||
|
||||
<div class="divide-y divide-violet-50">
|
||||
<div v-for="clip in clips" :key="clip.id" class="flex flex-col gap-3 px-5 py-4 lg:flex-row lg:items-center lg:gap-4">
|
||||
<div class="grid h-12 w-12 shrink-0 place-items-center rounded-2xl bg-[linear-gradient(135deg,#ece2ff,#fff2dd)] text-violet-600">
|
||||
<Film class="h-5 w-5" />
|
||||
<div v-for="clip in clips" :key="clip.id" class="grid gap-4 px-5 py-5 xl:grid-cols-[minmax(0,1fr)_minmax(320px,420px)_320px] xl:items-start">
|
||||
<div class="flex min-w-0 gap-4">
|
||||
<div class="grid h-12 w-12 shrink-0 place-items-center rounded-2xl bg-[linear-gradient(135deg,#ece2ff,#fff2dd)] text-violet-600">
|
||||
<Film class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate font-semibold text-slate-900">{{ clip.title || 'Ohne Titel' }}</p>
|
||||
<p class="truncate text-sm text-slate-500">
|
||||
{{ clip.creator || 'Unbekannt' }}
|
||||
<span v-if="clip.categoryId"> · {{ categoryName[clip.categoryId] }}</span>
|
||||
· von {{ clip.submittedByTwitchId }}
|
||||
</p>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<span :class="['rounded-full border px-3 py-1 text-xs font-semibold', platformClass(clip.platform)]">{{ clip.platform }}</span>
|
||||
<span :class="['rounded-full border px-3 py-1 text-xs font-semibold', statusClass(clip.status)]">{{ statusLabel(clip.status) }}</span>
|
||||
<a
|
||||
:href="clip.clipUrl"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
referrerpolicy="no-referrer"
|
||||
class="inline-flex items-center gap-1.5 rounded-full border border-violet-200 px-3 py-1 text-xs font-semibold text-violet-700 transition hover:bg-violet-50"
|
||||
>
|
||||
<ExternalLink class="h-3.5 w-3.5" /> Clip öffnen
|
||||
</a>
|
||||
</div>
|
||||
<p v-if="duplicateUrlCount(clip.clipUrl) > 1" class="mt-3 text-xs font-semibold text-amber-700">
|
||||
Mögliches Duplikat: diese URL wurde {{ duplicateUrlCount(clip.clipUrl) }}x eingereicht.
|
||||
</p>
|
||||
<p v-if="creatorClipCount(clip) > 1" class="mt-1 text-xs font-semibold text-violet-700">
|
||||
Sammelpunkt: {{ creatorClipCount(clip) }} Clips für diese Person oder diesen Kandidaten.
|
||||
</p>
|
||||
<p v-if="clip.reviewedAt" class="mt-1 text-xs text-slate-500">
|
||||
Zuletzt geprüft von {{ clip.reviewedByTwitchId || 'Admin' }} · {{ new Date(clip.reviewedAt).toLocaleString('de-DE') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate font-semibold text-slate-900">{{ clip.title || 'Ohne Titel' }}</p>
|
||||
<p class="truncate text-sm text-slate-500">
|
||||
{{ clip.creator || 'Unbekannt' }}
|
||||
<span v-if="clip.categoryId"> · {{ categoryName[clip.categoryId] }}</span>
|
||||
· von {{ clip.submittedByTwitchId }}
|
||||
</p>
|
||||
<p v-if="duplicateUrlCount(clip.clipUrl) > 1" class="mt-1 text-xs font-semibold text-amber-700">
|
||||
Mögliches Duplikat: diese URL wurde {{ duplicateUrlCount(clip.clipUrl) }}x eingereicht.
|
||||
</p>
|
||||
<p v-if="creatorClipCount(clip) > 1" class="mt-1 text-xs font-semibold text-violet-700">
|
||||
Sammelpunkt: {{ creatorClipCount(clip) }} Clips für diese Person oder diesen Kandidaten.
|
||||
</p>
|
||||
<p v-if="clip.reviewedAt" class="mt-1 text-xs text-slate-500">
|
||||
Zuletzt geprüft von {{ clip.reviewedByTwitchId || 'Admin' }} · {{ new Date(clip.reviewedAt).toLocaleString('de-DE') }}
|
||||
</p>
|
||||
<div class="min-w-0">
|
||||
<div v-if="clipEmbeds[clip.id]" class="overflow-hidden rounded-2xl border border-violet-100 bg-slate-950 shadow-sm">
|
||||
<iframe
|
||||
class="aspect-video w-full"
|
||||
:src="clipEmbeds[clip.id]?.src"
|
||||
:title="clip.title || `${clip.platform} Clip ${clip.id}`"
|
||||
loading="lazy"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture; web-share"
|
||||
allowfullscreen
|
||||
referrerpolicy="strict-origin-when-cross-origin"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="rounded-2xl border border-dashed border-amber-200 bg-amber-50 px-4 py-5 text-sm font-medium leading-6 text-amber-800">
|
||||
Dieser Clip kann nicht eingebettet werden. Öffne ihn über den Clip-Link zur manuellen Prüfung.
|
||||
</div>
|
||||
</div>
|
||||
<span :class="['shrink-0 rounded-full border px-3 py-1 text-xs font-semibold', platformClass(clip.platform)]">{{ clip.platform }}</span>
|
||||
<span :class="['shrink-0 rounded-full border px-3 py-1 text-xs font-semibold', statusClass(clip.status)]">{{ statusLabel(clip.status) }}</span>
|
||||
<a
|
||||
:href="clip.clipUrl"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
referrerpolicy="no-referrer"
|
||||
class="inline-flex shrink-0 items-center gap-1.5 rounded-full border border-violet-200 px-3 py-1.5 text-xs font-semibold text-violet-700 transition hover:bg-violet-50"
|
||||
>
|
||||
<ExternalLink class="h-3.5 w-3.5" /> Clip öffnen
|
||||
</a>
|
||||
<div class="w-full lg:w-[320px]">
|
||||
<div class="w-full">
|
||||
<textarea
|
||||
v-model="reviewNotes[clip.id]"
|
||||
rows="2"
|
||||
|
||||
Reference in New Issue
Block a user