Refine admin workflows and team access
This commit is contained in:
@@ -32,6 +32,7 @@ export function useAdminAnalyticsManager() {
|
||||
reviews,
|
||||
hasWinner,
|
||||
votes,
|
||||
votePct: totalVotes.value > 0 ? Math.round((votes / totalVotes.value) * 100) : 0,
|
||||
status,
|
||||
statusClass: candidates === 0
|
||||
? 'border-rose-100 bg-rose-50 text-rose-700'
|
||||
@@ -40,20 +41,37 @@ export function useAdminAnalyticsManager() {
|
||||
: hasWinner
|
||||
? 'border-emerald-100 bg-emerald-50 text-emerald-700'
|
||||
: 'border-sky-100 bg-sky-50 text-sky-700',
|
||||
statusDot: candidates === 0 ? 'bg-rose-400' : reviews > 0 ? 'bg-amber-400' : hasWinner ? 'bg-emerald-400' : 'bg-sky-400',
|
||||
}
|
||||
})
|
||||
.sort((a, b) => b.reviews - a.reviews || a.candidates - b.candidates || b.votes - a.votes),
|
||||
)
|
||||
|
||||
const categoryGroups = computed(() => {
|
||||
const groups = new Map<string, typeof categoryHealth.value>()
|
||||
for (const cat of categoryHealth.value) {
|
||||
const key = cat.groupName || 'Ohne Gruppe'
|
||||
if (!groups.has(key)) groups.set(key, [])
|
||||
groups.get(key)!.push(cat)
|
||||
}
|
||||
return [...groups.entries()].map(([name, cats]) => ({ name, cats }))
|
||||
})
|
||||
|
||||
const emptyCategories = computed(() => categoryHealth.value.filter((category) => category.candidates === 0))
|
||||
const categoriesWithReviews = computed(() => categoryHealth.value.filter((category) => category.reviews > 0))
|
||||
const categoriesWithoutWinner = computed(() => categoryHealth.value.filter((category) => !category.hasWinner))
|
||||
const categoriesReady = computed(() => categoryHealth.value.filter((c) => c.status === 'Bereit' || c.status === 'Gewinner gesetzt'))
|
||||
const pendingClipCount = computed(() => seasonDetail.value.clipSubmissions.filter((clip) => clip.status === 'pending').length)
|
||||
const winnerCoveragePct = computed(() => {
|
||||
const categoryCount = seasonDetail.value.categories.length
|
||||
if (categoryCount === 0) return 0
|
||||
return Math.round((seasonDetail.value.results.length / categoryCount) * 100)
|
||||
})
|
||||
const readinessPct = computed(() => {
|
||||
const total = categoryHealth.value.length
|
||||
if (total === 0) return 0
|
||||
return Math.round((categoriesReady.value.length / total) * 100)
|
||||
})
|
||||
|
||||
const metricCards = computed(() => [
|
||||
{ label: 'Nominierungen', value: totalNominations.value, note: 'eingereicht im Jahr', icon: Sparkles, tone: 'text-fuchsia-700 bg-fuchsia-50 border-fuchsia-100' },
|
||||
@@ -140,14 +158,37 @@ export function useAdminAnalyticsManager() {
|
||||
},
|
||||
])
|
||||
|
||||
// Top categories enriched with vote share %
|
||||
const topCategoriesEnriched = computed(() =>
|
||||
topCategories.value.map((cat) => ({
|
||||
...cat,
|
||||
pct: totalVotes.value > 0 ? Math.round((cat.votes / totalVotes.value) * 100) : 0,
|
||||
barWidth: totalVotes.value > 0 ? Math.max(2, Math.round((cat.votes / maxVotes.value) * 100)) : 2,
|
||||
})),
|
||||
)
|
||||
|
||||
// Health counts for status summary
|
||||
const healthSummary = computed(() => ({
|
||||
leer: emptyCategories.value.length,
|
||||
reviewOffen: categoriesWithReviews.value.length,
|
||||
bereit: categoryHealth.value.filter((c) => c.status === 'Bereit').length,
|
||||
gewinner: categoryHealth.value.filter((c) => c.status === 'Gewinner gesetzt').length,
|
||||
total: categoryHealth.value.length,
|
||||
}))
|
||||
|
||||
return {
|
||||
categoryHealth,
|
||||
categoryGroups,
|
||||
metricCards,
|
||||
readinessCards,
|
||||
insightCards,
|
||||
attentionItems,
|
||||
topCategories,
|
||||
topCategoriesEnriched,
|
||||
maxVotes,
|
||||
winnerCoveragePct,
|
||||
readinessPct,
|
||||
healthSummary,
|
||||
totalVotes,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user