Redesign public site, add phase gating and clip submission

- Rebuild landing page and Nominations/Voting/Winners views in the
  Jayuhime brand style (purple/gold/pastel-rainbow, stars, PageHero banner)
- Gate participation by season phase (nominate/clip share the nomination
  window, vote in the voting window); hide closed/locked pages from nav
- Add login-gated clip submission flow (link-based) + voting clip links
- Make candidate admin scalable: searchable, filterable, paginated table
  with modal create/edit and confirm-delete; add delete API/store actions
- New reusable Modal and PageHero components, usePhases composable
- Segmented top nav and full-size header on admin routes
- vite: honor PORT env for preview

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
AzuTear
2026-06-18 06:46:41 +02:00
parent a2d6a5edc0
commit cca297a4eb
17 changed files with 1735 additions and 691 deletions
+84
View File
@@ -0,0 +1,84 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, watch } from 'vue'
import { X } from '@lucide/vue'
const props = defineProps<{
open: boolean
title?: string
subtitle?: string
}>()
const emit = defineEmits<{ (e: 'close'): void }>()
function onKey(event: KeyboardEvent) {
if (event.key === 'Escape' && props.open) emit('close')
}
watch(
() => props.open,
(open) => {
if (typeof document !== 'undefined') {
document.body.style.overflow = open ? 'hidden' : ''
}
},
)
onMounted(() => window.addEventListener('keydown', onKey))
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKey)
if (typeof document !== 'undefined') document.body.style.overflow = ''
})
</script>
<template>
<Teleport to="body">
<div
v-if="open"
class="modal-overlay fixed inset-0 z-50 flex items-center justify-center p-4"
@click.self="emit('close')"
>
<div class="absolute inset-0 bg-violet-950/30 backdrop-blur-sm" @click="emit('close')" />
<div class="modal-panel relative z-10 w-full max-w-lg overflow-hidden rounded-[28px] border border-violet-200/70 bg-white shadow-[0_40px_90px_rgba(76,40,160,0.28)]">
<div class="flex items-start justify-between gap-4 border-b border-violet-100 bg-[linear-gradient(135deg,#f3edff,#fff4e6)] px-6 py-5">
<div>
<h3 v-if="title" class="font-[Cormorant_Garamond] text-3xl text-violet-800">{{ title }}</h3>
<p v-if="subtitle" class="mt-1 text-sm text-slate-500">{{ subtitle }}</p>
</div>
<button
type="button"
class="grid h-9 w-9 shrink-0 place-items-center rounded-full text-slate-400 transition hover:bg-white/70 hover:text-violet-700"
@click="emit('close')"
>
<X class="h-5 w-5" />
</button>
</div>
<div class="max-h-[70vh] overflow-y-auto px-6 py-6">
<slot />
</div>
<div v-if="$slots.footer" class="flex justify-end gap-3 border-t border-violet-100 bg-violet-50/40 px-6 py-4">
<slot name="footer" />
</div>
</div>
</div>
</Teleport>
</template>
<style scoped>
.modal-overlay {
animation: modal-fade 0.16s ease;
}
.modal-panel {
animation: modal-pop 0.18s ease;
}
@keyframes modal-fade {
from {
opacity: 0;
}
}
@keyframes modal-pop {
from {
opacity: 0;
transform: translateY(8px) scale(0.98);
}
}
</style>