Add winner archive, host image upload and live demo data
CI - Build & Verify / Build, Typecheck & Hygiene (push) Successful in 1m1s
CI - Build & Verify / Deploy to award.noveria.net (push) Successful in 1m30s

Deliver the demo-ready feature set and seed data so the live site can be
presented end to end:

- Winner archive: ArchivedWinner domain, admin CRUD endpoints/view/manager
  and public archive surface, backed by AddArchivedWinners migration.
- Host presentation: host image upload and artist name on SiteSettings with
  public image endpoint and supporting migrations.
- Clip submissions: idempotent table-ensure migration plus current-season
  demo clips for review workflows.
- Demo seed data: sponsors, share links and 2025 archived winners, with a
  guarded RemoveDemoSeasons cleanup; all seeds guard against real data.
- EnsureRuntimeSchemaParity migration to align runtime schema defensively.
- Admin/home UI refinements; remove unused team role permissions modal and
  dead share-quick-links code.

All seed and schema migrations are idempotent (IF NOT EXISTS / ON CONFLICT)
and skip when real season data is present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
AzuTear
2026-06-29 21:47:52 +02:00
parent f323e5a82c
commit 6b3b0360e7
92 changed files with 7412 additions and 583 deletions
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { Ban, CheckCircle2, Link2, Loader2, ShieldAlert, Trash2, UserRoundCheck, WandSparkles } from '@lucide/vue'
import { RouterLink } from 'vue-router'
import Button from '../ui/Button.vue'
import NativeSelect from '../ui/NativeSelect.vue'
@@ -25,7 +26,6 @@ const props = defineProps<{
platforms: string[]
} | null
trackingReviewNotes?: string
selectedNominationHasBlockingFlag?: boolean
streamUrl?: string
canApproveSelected: boolean
blacklistSaving: boolean
@@ -159,12 +159,23 @@ function avgViewerWindowLabel(nomination: AdminNominationReviewGroup) {
class="rounded-[24px] border p-4"
:class="nomination.trackingReviewStatus === 'overridden' ? 'border-emerald-100 bg-emerald-50/70' : nomination.requiresManualReview ? 'border-amber-100 bg-amber-50/70' : 'border-violet-100 bg-white'"
>
<div class="flex items-center gap-2">
<ShieldAlert class="h-4 w-4" :class="nomination.requiresManualReview ? 'text-amber-700' : 'text-violet-600'" />
<p class="text-[11px] font-semibold uppercase tracking-[0.18em]" :class="nomination.requiresManualReview ? 'text-amber-800' : 'text-violet-500'">Tracking Review</p>
<div class="flex flex-wrap items-center justify-between gap-3">
<div class="flex items-center gap-2">
<ShieldAlert class="h-4 w-4" :class="nomination.requiresManualReview ? 'text-amber-700' : 'text-violet-600'" />
<p class="text-[11px] font-semibold uppercase tracking-[0.18em]" :class="nomination.requiresManualReview ? 'text-amber-800' : 'text-violet-500'">Tracking Review</p>
</div>
<RouterLink
to="/admin/tracking-rules"
class="inline-flex items-center rounded-full border border-violet-200 bg-white px-3 py-1.5 text-xs font-semibold text-violet-700 transition hover:border-violet-300 hover:bg-violet-50"
>
Zur Konfiguration
</RouterLink>
</div>
<p class="mt-2 text-sm font-semibold text-slate-900">{{ props.trackerStatusMeta(nomination.trackerStatus).hint }}</p>
<p class="mt-2 text-xs leading-5 text-slate-500">
Diese Tracking-Metriken blockieren keine Annahme oder Verwerfung. Sie zeigen nur, welche Review-Regel und welches Zeitfenster aktuell angewandt werden.
</p>
<div v-if="nomination.trackingFlags.length" class="mt-3 flex flex-wrap gap-2">
<span
@@ -172,7 +183,7 @@ function avgViewerWindowLabel(nomination: AdminNominationReviewGroup) {
:key="flag.key"
class="rounded-full border border-amber-200 bg-white px-3 py-1 text-xs font-semibold text-amber-800"
>
{{ flag.label }}<span v-if="flag.blocksApproval"> · blockiert</span>
{{ flag.label }}
</span>
</div>
@@ -190,7 +201,7 @@ function avgViewerWindowLabel(nomination: AdminNominationReviewGroup) {
</span>
</div>
<strong class="mt-1 block text-sm text-violet-900">{{ metric.value }}</strong>
<p class="mt-1 text-xs text-slate-500">{{ metric.required ? 'Pflichtmetrik' : 'Optionale Metrik' }} · {{ metric.windowLabel }}</p>
<p class="mt-1 text-xs text-slate-500">Review-Regel · {{ metric.required ? 'Pflichtmetrik' : 'Optionale Metrik' }} · {{ metric.windowLabel }}</p>
</div>
</div>
@@ -271,12 +282,7 @@ function avgViewerWindowLabel(nomination: AdminNominationReviewGroup) {
</p>
<p v-if="!canApproveSelected" class="mt-3 rounded-2xl border border-rose-100 bg-rose-50 px-4 py-3 text-sm text-rose-700">
<span v-if="selectedNominationHasBlockingFlag && nomination.trackingReviewStatus !== 'overridden'">
Mindestens ein Tracking-Flag blockiert die Freigabe. Bitte zuerst einen Override setzen.
</span>
<span v-else>
Anzeigename, Handle, Plattform und Tier sind Pflicht.
</span>
Anzeigename, Handle, Plattform und Tier sind Pflicht.
</p>
</section>