Refine admin workflows and team access

This commit is contained in:
AzuTear
2026-06-26 18:09:29 +02:00
parent b7804e10ea
commit 8b69dfbafb
71 changed files with 5066 additions and 751 deletions
+56 -9
View File
@@ -4,9 +4,12 @@ import { CheckCircle2, ExternalLink, Film, Search, Trash2, TriangleAlert, Undo2,
import AdminPageHeader from '../../components/admin/AdminPageHeader.vue'
import AdminSeasonToolbar from '../../components/admin/AdminSeasonToolbar.vue'
import { useAdminClipManager } from '../../components/admin/useAdminClipManager'
import { watchAdminToast } from '../../composables/useAdminToast'
import Button from '../../components/ui/Button.vue'
import Card from '../../components/ui/Card.vue'
import Modal from '../../components/ui/Modal.vue'
import NativeSelect from '../../components/ui/NativeSelect.vue'
import PaginationFooter from '../../components/ui/PaginationFooter.vue'
const {
query,
@@ -15,6 +18,7 @@ const {
categoryFilter,
deleting,
statusSaving,
bulkSaving,
adminMessage,
adminError,
clipToDelete,
@@ -22,6 +26,11 @@ const {
submissions,
categoryName,
clips,
page,
totalPages,
pagedClips,
rangeStart,
rangeEnd,
clipEmbeds,
stats,
statusFilters,
@@ -33,8 +42,11 @@ const {
duplicateUrlCount,
creatorClipCount,
updateClipStatus,
bulkUpdateStatus,
confirmDelete,
} = useAdminClipManager()
watchAdminToast(adminMessage, adminError)
</script>
<template>
@@ -71,12 +83,14 @@ const {
placeholder="Titel, Creator, Plattform, URL oder User suchen …"
/>
</label>
<select v-model="platformFilter" class="h-12 rounded-2xl border border-violet-200 bg-white px-4 text-sm outline-none transition focus:border-violet-400 focus:ring-4 focus:ring-violet-100">
<option v-for="filter in platformFilters" :key="filter.key" :value="filter.key">{{ filter.label }} · {{ filter.count }}</option>
</select>
<select v-model="categoryFilter" class="h-12 rounded-2xl border border-violet-200 bg-white px-4 text-sm outline-none transition focus:border-violet-400 focus:ring-4 focus:ring-violet-100">
<option v-for="filter in categoryFilters" :key="filter.id" :value="filter.id">{{ filter.label }} · {{ filter.count }}</option>
</select>
<NativeSelect
v-model="platformFilter"
:options="platformFilters.map((filter) => ({ label: `${filter.label} · ${filter.count}`, value: filter.key }))"
/>
<NativeSelect
v-model="categoryFilter"
:options="categoryFilters.map((filter) => ({ label: `${filter.label} · ${filter.count}`, value: filter.id }))"
/>
</div>
<div class="flex flex-wrap gap-2">
<button
@@ -92,11 +106,35 @@ const {
</div>
</div>
<p v-if="adminMessage" class="border-b border-emerald-100 bg-emerald-50 px-5 py-3 text-sm text-emerald-700">{{ adminMessage }}</p>
<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 v-if="clips.length > 0" class="flex flex-wrap items-center justify-between gap-3 border-b border-violet-100 bg-violet-50/40 px-5 py-3">
<p class="text-sm text-slate-600">
<strong class="text-violet-800">{{ clips.length }}</strong>
{{ clips.length === 1 ? 'Clip' : 'Clips' }} in dieser Ansicht
</p>
<div class="flex flex-wrap gap-2">
<Button
class="gap-1.5 !bg-emerald-600 hover:!bg-emerald-500"
size="sm"
:disabled="bulkSaving || statusSaving !== null"
@click="bulkUpdateStatus('approved')"
>
<CheckCircle2 class="h-3.5 w-3.5" />
{{ bulkSaving ? 'Verarbeite …' : 'Alle freigeben' }}
</Button>
<Button
class="gap-1.5 !bg-rose-600 hover:!bg-rose-500"
size="sm"
:disabled="bulkSaving || statusSaving !== null"
@click="bulkUpdateStatus('rejected')"
>
<XCircle class="h-3.5 w-3.5" />
Alle ablehnen
</Button>
</div>
</div>
<div class="divide-y divide-violet-50">
<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 v-for="clip in pagedClips" :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" />
@@ -186,6 +224,15 @@ const {
</p>
</div>
</div>
<PaginationFooter
:page="page"
:total-pages="totalPages"
:range-start="rangeStart"
:range-end="rangeEnd"
:filtered-count="clips.length"
@update:page="page = $event"
/>
</Card>
<Modal :open="!!clipToDelete" title="Clip entfernen?" @close="clipToDelete = null">