249 lines
7.7 KiB
TypeScript
249 lines
7.7 KiB
TypeScript
import type {
|
||
BalanceCandidate,
|
||
Bracket,
|
||
MixEvent,
|
||
Player,
|
||
Registration,
|
||
Series,
|
||
Session,
|
||
Team,
|
||
} from './client'
|
||
import type { RankOrdinal } from '../ranks'
|
||
|
||
// Fixtures keep local development and visual review useful without a backend.
|
||
// Production builds always use the real API.
|
||
export const demoMode =
|
||
import.meta.env.DEV && import.meta.env.VITE_DEMO_MODE !== 'false'
|
||
|
||
const now = new Date()
|
||
const daysFromNow = (days: number, hour = 19) => {
|
||
const date = new Date(now)
|
||
date.setDate(date.getDate() + days)
|
||
date.setHours(hour, 0, 0, 0)
|
||
return date.toISOString()
|
||
}
|
||
|
||
const makePlayer = (
|
||
id: string,
|
||
displayName: string,
|
||
tank: RankOrdinal,
|
||
damage: RankOrdinal,
|
||
support: RankOrdinal,
|
||
): Player => ({
|
||
id,
|
||
displayName,
|
||
battleTag: `${displayName.replace(/\s/g, '')}#${1000 + Number(id.replace(/\D/g, '') || 0)}`,
|
||
ratings: { tank, damage, support, updatedAt: daysFromNow(-4) },
|
||
preferredRoles: tank >= damage && tank >= support ? ['tank'] : damage >= support ? ['damage'] : ['support'],
|
||
preferredPlayerIds: [],
|
||
})
|
||
|
||
export const demoProfile: Player = {
|
||
...makePlayer('p1', 'Lemintare', 30, 33, 27),
|
||
preferredRoles: ['damage', 'tank'],
|
||
preferredPlayerIds: ['p2', 'p5'],
|
||
}
|
||
|
||
export const demoSession: Session = {
|
||
account: {
|
||
id: 'account-1',
|
||
discordId: '125661705720381444',
|
||
displayName: 'Lemintare',
|
||
role: 'admin',
|
||
},
|
||
player: demoProfile,
|
||
}
|
||
|
||
export const demoEvents: MixEvent[] = [
|
||
{
|
||
id: 'event-1',
|
||
title: 'Saturday Night Scrim',
|
||
description: 'Community draft night · 4 teams · Bo3 single elimination',
|
||
startsAt: daysFromNow(2),
|
||
endsAt: daysFromNow(2, 23),
|
||
registrationDeadline: daysFromNow(1, 21),
|
||
status: 'registration',
|
||
rulesetName: 'Community Bo3 v2',
|
||
counts: { going: 18, maybe: 4, not_going: 3 },
|
||
myRsvp: 'going',
|
||
},
|
||
{
|
||
id: 'event-2',
|
||
title: 'Midweek Mix',
|
||
description: 'Fast queue with two balanced teams',
|
||
startsAt: daysFromNow(5),
|
||
endsAt: daysFromNow(5, 23),
|
||
registrationDeadline: daysFromNow(4),
|
||
status: 'registration',
|
||
rulesetName: 'Quick Mix v1',
|
||
counts: { going: 9, maybe: 6, not_going: 2 },
|
||
myRsvp: 'maybe',
|
||
},
|
||
{
|
||
id: 'event-3',
|
||
title: 'Summer Clash #4',
|
||
description: 'Live playoff bracket',
|
||
startsAt: daysFromNow(0, 20),
|
||
endsAt: daysFromNow(1, 1),
|
||
registrationDeadline: daysFromNow(-1),
|
||
status: 'live',
|
||
rulesetName: 'Tournament Bo3 v2',
|
||
counts: { going: 22, maybe: 0, not_going: 4 },
|
||
myRsvp: 'going',
|
||
},
|
||
]
|
||
|
||
const players = [
|
||
demoProfile,
|
||
makePlayer('p2', 'Nova', 36, 28, 29),
|
||
makePlayer('p3', 'Kairo', 24, 35, 26),
|
||
makePlayer('p4', 'MercyMain', 18, 27, 37),
|
||
makePlayer('p5', 'Pulse', 25, 34, 29),
|
||
makePlayer('p6', 'Astra', 31, 25, 36),
|
||
makePlayer('p7', 'Hex', 35, 29, 23),
|
||
makePlayer('p8', 'Orbit', 22, 34, 30),
|
||
makePlayer('p9', 'Miko', 27, 30, 36),
|
||
makePlayer('p10', 'Rook', 37, 26, 24),
|
||
makePlayer('p11', 'Echo', 26, 32, 31),
|
||
makePlayer('p12', 'Zenith', 28, 30, 35),
|
||
]
|
||
players[1].preferredPlayerIds = ['p1']
|
||
players[4].preferredPlayerIds = ['p1']
|
||
|
||
export const demoRegistrations: Registration[] = players.map((player, index) => ({
|
||
id: `reg-${index + 1}`,
|
||
player,
|
||
status: index < 9 ? 'going' : index < 11 ? 'maybe' : 'not_going',
|
||
updatedAt: daysFromNow(-(index % 4)),
|
||
changedBy:
|
||
index === 4
|
||
? { id: 'account-1', displayName: 'Lemintare', source: 'admin' }
|
||
: { id: player.id, displayName: player.displayName, source: 'self' },
|
||
}))
|
||
|
||
const alpha: Team = {
|
||
id: 'team-alpha',
|
||
name: 'Ember Wolves',
|
||
side: 'alpha',
|
||
captainId: 'p2',
|
||
averageRating: 35.6,
|
||
members: [
|
||
{ player: players[1], assignedRole: 'tank' },
|
||
{ player: players[2], assignedRole: 'damage' },
|
||
{ player: players[4], assignedRole: 'damage' },
|
||
{ player: players[3], assignedRole: 'support' },
|
||
{ player: players[5], assignedRole: 'support' },
|
||
],
|
||
}
|
||
|
||
const beta: Team = {
|
||
id: 'team-beta',
|
||
name: 'Azure Phantoms',
|
||
side: 'beta',
|
||
captainId: 'p7',
|
||
averageRating: 34.4,
|
||
members: [
|
||
{ player: players[6], assignedRole: 'tank' },
|
||
{ player: players[7], assignedRole: 'damage' },
|
||
{ player: players[10], assignedRole: 'damage' },
|
||
{ player: players[8], assignedRole: 'support' },
|
||
{ player: players[11], assignedRole: 'support' },
|
||
],
|
||
}
|
||
|
||
export const demoBalanceCandidates: BalanceCandidate[] = [
|
||
{
|
||
id: 'candidate-1',
|
||
score: 4,
|
||
teams: [alpha, beta],
|
||
reserve: [players[0], players[9]],
|
||
explanations: ['9 preferred role assignments', '2 preferred teammate pairs kept together'],
|
||
},
|
||
{
|
||
id: 'candidate-2',
|
||
score: 9,
|
||
teams: [
|
||
{ ...alpha, averageRating: 35.2 },
|
||
{ ...beta, averageRating: 34.6 },
|
||
],
|
||
reserve: [players[0], players[9]],
|
||
explanations: ['8 preferred role assignments', '2 preferred teammate pairs kept together'],
|
||
},
|
||
{
|
||
id: 'candidate-3',
|
||
score: 13,
|
||
teams: [
|
||
{ ...alpha, averageRating: 35.4 },
|
||
{ ...beta, averageRating: 34.5 },
|
||
],
|
||
reserve: [players[0], players[9]],
|
||
explanations: ['10 preferred role assignments', '1 preferred teammate pair kept together'],
|
||
},
|
||
]
|
||
|
||
export const demoSeries: Series = {
|
||
id: 'series-1',
|
||
status: 'hero_draft',
|
||
roundLabel: 'Semifinal 1',
|
||
teamAlpha: alpha,
|
||
teamBeta: beta,
|
||
score: { alpha: 1, beta: 0 },
|
||
version: 12,
|
||
currentStep: {
|
||
title: 'Hero ban 2 of 4',
|
||
instruction: 'Azure Phantoms choose a hero to remove for Map 2.',
|
||
activeTeam: 'beta',
|
||
kind: 'hero_ban',
|
||
version: 12,
|
||
},
|
||
options: [
|
||
{ id: 'hero-1', name: 'Ana', role: 'support' },
|
||
{ id: 'hero-2', name: 'Kiriko', role: 'support' },
|
||
{ id: 'hero-3', name: 'Tracer', role: 'damage' },
|
||
{ id: 'hero-4', name: 'Sojourn', role: 'damage' },
|
||
{ id: 'hero-5', name: 'Winston', role: 'tank', disabled: true, disabledReason: 'Already banned by this team' },
|
||
{ id: 'hero-6', name: 'Sigma', role: 'tank' },
|
||
],
|
||
maps: [
|
||
{
|
||
number: 1,
|
||
name: 'Lijiang Tower',
|
||
mode: 'Control',
|
||
status: 'completed',
|
||
winner: 'alpha',
|
||
heroBans: [
|
||
{ hero: 'Sombra', team: 'alpha', role: 'damage' },
|
||
{ hero: 'Winston', team: 'beta', role: 'tank' },
|
||
{ hero: 'Ana', team: 'alpha', role: 'support' },
|
||
{ hero: 'Tracer', team: 'beta', role: 'damage' },
|
||
],
|
||
},
|
||
{
|
||
number: 2,
|
||
name: 'King’s Row',
|
||
mode: 'Hybrid',
|
||
status: 'drafting',
|
||
heroBans: [{ hero: 'D.Va', team: 'alpha', role: 'tank' }],
|
||
},
|
||
{ number: 3, name: 'TBD', mode: 'Control', status: 'ready', heroBans: [] },
|
||
],
|
||
audit: [
|
||
{ id: 'a1', kind: 'coin_toss', summary: 'Ember Wolves won the toss', actorName: 'System', createdAt: daysFromNow(0, 20) },
|
||
{ id: 'a2', kind: 'map_selected', summary: 'Lijiang Tower selected', actorName: 'Nova', createdAt: daysFromNow(0, 20) },
|
||
{ id: 'a3', kind: 'result', summary: 'Map 1 won by Ember Wolves', actorName: 'Lemintare', createdAt: daysFromNow(0, 21) },
|
||
{ id: 'a4', kind: 'map_selected', summary: 'King’s Row selected', actorName: 'Hex', createdAt: daysFromNow(0, 21) },
|
||
{ id: 'a5', kind: 'hero_ban', summary: 'D.Va banned by Ember Wolves', actorName: 'Nova', createdAt: daysFromNow(0, 21) },
|
||
],
|
||
}
|
||
|
||
export const demoBracket: Bracket = {
|
||
id: 'bracket-1',
|
||
title: 'Summer Clash #4',
|
||
rounds: ['Semifinals', 'Grand Final'],
|
||
matches: [
|
||
{ id: 'm1', round: 0, slot: 0, label: 'Semifinal 1', teamAlpha: 'Ember Wolves', teamBeta: 'Azure Phantoms', scoreAlpha: 1, scoreBeta: 0, status: 'live', seriesId: 'series-1' },
|
||
{ id: 'm2', round: 0, slot: 1, label: 'Semifinal 2', teamAlpha: 'Neon Foxes', teamBeta: 'Void Runners', scoreAlpha: 2, scoreBeta: 1, winner: 'alpha', status: 'completed', seriesId: 'series-2' },
|
||
{ id: 'm3', round: 1, slot: 0, label: 'Grand Final', teamBeta: 'Neon Foxes', status: 'pending' },
|
||
],
|
||
}
|