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:
@@ -6,6 +6,7 @@ import type {
|
||||
AdminSeasonDetailResponse,
|
||||
ApproveNominationPayload,
|
||||
AdminSeasonListItem,
|
||||
CreateClipPayload,
|
||||
CreateNominationPayload,
|
||||
CreateVotePayload,
|
||||
OverviewResponse,
|
||||
@@ -31,9 +32,9 @@ const fallbackOverview: OverviewResponse = {
|
||||
{ key: 'show', title: 'Award Show', startsAt: '2026-07-20', endsAt: '2026-07-20', state: 'upcoming' },
|
||||
],
|
||||
featuredCategories: [
|
||||
{ id: 1, groupName: 'Main Awards', name: 'VTuber des Jahres', description: 'Die groesste Auszeichnung des Jahres.', maxNomineesPerUser: 3 },
|
||||
{ id: 2, groupName: 'Performance', name: 'Bestes Live Event', description: 'Events, Konzerte und Showformate.', maxNomineesPerUser: 3 },
|
||||
{ id: 3, groupName: 'Clips & Highlights', name: 'Clip des Jahres', description: 'Der lustigste oder emotionalste Clip.', maxNomineesPerUser: 3 },
|
||||
{ id: 1, groupName: 'Main Awards', name: 'VTuber des Jahres', description: 'Die VTuberin oder der VTuber, der dieses Jahr einfach alle verzaubert hat.', maxNomineesPerUser: 3 },
|
||||
{ id: 2, groupName: 'Performance', name: 'Bestes Live Event', description: 'Das Event, das die Community zum Beben gebracht hat – Konzert, Watchalong oder Mega-Stream.', maxNomineesPerUser: 3 },
|
||||
{ id: 3, groupName: 'Clips & Highlights', name: 'Clip des Jahres', description: 'Der eine Clip, den du seit Monaten in jeden Chat spammst.', maxNomineesPerUser: 3 },
|
||||
],
|
||||
winnersPreview: [
|
||||
{ year: 2025, category: 'VTuber des Jahres', winnerName: 'Hoshimi Miyu', winnerSlug: '@hoshimimiyu' },
|
||||
@@ -41,9 +42,10 @@ const fallbackOverview: OverviewResponse = {
|
||||
{ year: 2024, category: 'Clip des Jahres', winnerName: 'Pyonkichi Kingdom', winnerSlug: '@pyonkichikingdom' },
|
||||
],
|
||||
faq: [
|
||||
{ question: 'Wer kann nominieren und voten?', answer: 'Jede Person mit Twitch Login. Das Konto wird beim ersten Login implizit erstellt.' },
|
||||
{ question: 'Wie werden Gewinner bestimmt?', answer: 'Aktuell rein community-basiert. Eine Mischlogik kann spaeter aktiviert werden.' },
|
||||
{ question: 'Wer verwaltet Kategorien und Unterkategorien?', answer: 'Das Team pflegt diese pro Jahr im Admin-Bereich.' },
|
||||
{ question: 'Wer darf mitmachen?', answer: 'Jede:r mit einem Twitch-Account. Einmal einloggen genügt – kein extra Konto, kein Papierkram.' },
|
||||
{ question: 'Wie werden die Gewinner bestimmt?', answer: 'Komplett durch eure Stimmen. Die Community entscheidet, wer auf die Bühne darf – kein Jury-Geheimnis.' },
|
||||
{ question: 'Kann ich meine Wahl noch ändern?', answer: 'Klar! Bis zum Ende der Voting-Phase kannst du Nominierungen und Stimmen jederzeit anpassen.' },
|
||||
{ question: 'Wer kuratiert die Kategorien?', answer: 'Das Jayuhime-Team stellt die Kategorien jedes Jahr frisch zusammen, passend zur Community.' },
|
||||
],
|
||||
}
|
||||
|
||||
@@ -55,23 +57,23 @@ const fallbackCategories: SeasonCategoriesResponse = {
|
||||
id: 1,
|
||||
name: 'VTuber des Jahres',
|
||||
groupName: 'Main Awards',
|
||||
description: 'Die Hauptkategorie fuer die praegendste Creator-Praesenz des Jahres.',
|
||||
description: 'Die Hauptkategorie – für die prägendste Creator-Präsenz des ganzen Jahres.',
|
||||
maxNomineesPerUser: 3,
|
||||
candidates: [
|
||||
{ id: 1, displayName: 'Hoshimi Miyu', channelSlug: '@hoshimimiyu', platform: 'Twitch' },
|
||||
{ id: 2, displayName: 'Kurainu', channelSlug: '@kurainu', platform: 'Twitch' },
|
||||
{ id: 3, displayName: 'Shiro Ch.', channelSlug: '@shiroch', platform: 'Twitch' },
|
||||
{ id: 1, displayName: 'Hoshimi Miyu', channelSlug: '@hoshimimiyu', platform: 'Twitch', clipUrl: 'https://clips.twitch.tv/HoshimiHighlight' },
|
||||
{ id: 2, displayName: 'Kurainu', channelSlug: '@kurainu', platform: 'Twitch', clipUrl: 'https://www.youtube.com/watch?v=kurainu' },
|
||||
{ id: 3, displayName: 'Shiro Ch.', channelSlug: '@shiroch', platform: 'Twitch', clipUrl: 'https://clips.twitch.tv/ShiroMoment' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Bestes Live Event',
|
||||
groupName: 'Performance',
|
||||
description: 'Konzerte, Sonderformate und grosse Community-Shows.',
|
||||
description: 'Konzerte, Sonderformate und große Community-Shows, die in Erinnerung bleiben.',
|
||||
maxNomineesPerUser: 3,
|
||||
candidates: [
|
||||
{ id: 4, displayName: 'Kurainu 3D Live', channelSlug: '@kurainu', platform: 'Twitch' },
|
||||
{ id: 5, displayName: 'Aoi Sakura Showcase', channelSlug: '@aoisakura', platform: 'YouTube' },
|
||||
{ id: 4, displayName: 'Kurainu 3D Live', channelSlug: '@kurainu', platform: 'Twitch', clipUrl: 'https://www.youtube.com/watch?v=kurainu3d' },
|
||||
{ id: 5, displayName: 'Aoi Sakura Showcase', channelSlug: '@aoisakura', platform: 'YouTube', clipUrl: 'https://www.youtube.com/watch?v=aoisakura' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -277,6 +279,9 @@ export const useAwardsStore = defineStore('awards', {
|
||||
submitVote(payload: CreateVotePayload) {
|
||||
return api.submitVote(payload)
|
||||
},
|
||||
submitClip(payload: CreateClipPayload) {
|
||||
return api.submitClip(payload)
|
||||
},
|
||||
async updateAdminSeason(seasonId: number, payload: UpdateSeasonPayload) {
|
||||
const result = await api.updateAdminSeason(seasonId, payload)
|
||||
await this.loadAdmin()
|
||||
@@ -302,6 +307,16 @@ export const useAwardsStore = defineStore('awards', {
|
||||
await this.loadAdminSeasonDetail(seasonId)
|
||||
return result
|
||||
},
|
||||
async deleteAdminCandidate(candidateId: number, seasonId: number) {
|
||||
const result = await api.deleteAdminCandidate(candidateId)
|
||||
await this.loadAdminSeasonDetail(seasonId)
|
||||
return result
|
||||
},
|
||||
async deleteAdminCategory(categoryId: number, seasonId: number) {
|
||||
const result = await api.deleteAdminCategory(categoryId)
|
||||
await this.loadAdminSeasonDetail(seasonId)
|
||||
return result
|
||||
},
|
||||
async approveAdminNomination(nominationId: number, seasonId: number, payload: ApproveNominationPayload) {
|
||||
const result = await api.approveAdminNomination(nominationId, payload)
|
||||
await this.loadAdminSeasonDetail(seasonId)
|
||||
|
||||
Reference in New Issue
Block a user