Iterate admin pages with smarter workflows

This commit is contained in:
AzuTear
2026-06-18 01:00:58 +02:00
parent 0fa763667d
commit a2d6a5edc0
10 changed files with 422 additions and 36 deletions
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { BarChart3, CheckCircle2, Tags, Users, Vote } from '@lucide/vue'
import { RouterLink } from 'vue-router'
import AdminPageHeader from '../../components/admin/AdminPageHeader.vue'
import AdminSeasonToolbar from '../../components/admin/AdminSeasonToolbar.vue'
@@ -22,12 +23,33 @@ const votingReadiness = computed(() =>
}),
)
const readyCount = computed(() => votingReadiness.value.filter((category) => category.ready).length)
const notReadyCategories = computed(() => votingReadiness.value.filter((category) => !category.ready))
const stats = computed(() => [
{ label: 'Stimmen gesamt', value: totalVotes.value, icon: Vote },
{ label: 'Voting-ready', value: readyCount.value, icon: CheckCircle2 },
{ label: 'Kategorien', value: seasonDetail.value.categories.length, icon: Tags },
{ label: 'Kandidaten', value: seasonDetail.value.candidates.length, icon: Users },
])
const votingChecklist = computed(() => [
{
label: 'Voting-Phase aktiv',
done: seasonDetail.value.currentPhase.toLowerCase().includes('voting'),
note: seasonDetail.value.currentPhase || 'Keine Phase gesetzt',
to: '/admin/settings',
},
{
label: 'Alle Kategorien haben Kandidaten',
done: notReadyCategories.value.every((category) => category.candidateCount > 0) && seasonDetail.value.categories.length > 0,
note: `${notReadyCategories.value.filter((category) => category.candidateCount === 0).length} Kategorien ohne Kandidaten`,
to: '/admin/categories',
},
{
label: 'Offene Reviews niedrig',
done: seasonDetail.value.pendingNominations.length === 0,
note: `${seasonDetail.value.pendingNominations.length} offene Reviews`,
to: '/admin/reviews',
},
])
</script>
<template>
@@ -102,5 +124,29 @@ const stats = computed(() => [
</div>
</Card>
</section>
<Card class="p-6">
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-violet-500">Voting Checkliste</p>
<h2 class="mt-2 font-[Cormorant_Garamond] text-4xl text-violet-800">Vor dem Public Push</h2>
<div class="mt-5 grid gap-3 lg:grid-cols-3">
<RouterLink
v-for="item in votingChecklist"
:key="item.label"
:to="item.to"
class="rounded-[22px] border p-4 transition hover:-translate-y-0.5 hover:bg-violet-50/50"
:class="item.done ? 'border-emerald-100 bg-emerald-50/40' : 'border-amber-100 bg-amber-50/50'"
>
<div class="flex items-start justify-between gap-3">
<div>
<p class="font-semibold text-slate-900">{{ item.label }}</p>
<p class="mt-1 text-sm leading-5 text-slate-500">{{ item.note }}</p>
</div>
<span class="rounded-full px-3 py-1 text-xs font-semibold" :class="item.done ? 'bg-emerald-100 text-emerald-700' : 'bg-amber-100 text-amber-700'">
{{ item.done ? 'ok' : 'pruefen' }}
</span>
</div>
</RouterLink>
</div>
</Card>
</div>
</template>