Files
botfights/frontend/src/pages/SchedulePage.vue
T
DorianandClaude Opus 4.6 c98007e95c fix: replace silent error swallowing with console.warn and user-facing error messages
All empty catch blocks across 8 Vue pages now log warnings. User-initiated
actions (fight, matchmake) also surface errors in the UI via fightError/loadError refs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 23:49:45 +00:00

130 lines
4.4 KiB
Vue

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { RouterLink } from 'vue-router'
interface Bot {
id: string
name: string
eloRating: number
tier: number
wins: number
losses: number
}
const bots = ref<Bot[]>([])
const isLoading = ref(true)
onMounted(async () => {
try {
const res = await fetch('/api/bots')
if (res.ok) {
const data = await res.json()
bots.value = data.sort((a: Bot, b: Bot) => b.eloRating - a.eloRating)
}
} catch (err) {
console.warn('[Schedule] load bots failed:', err)
}
isLoading.value = false
})
const tierClass = (t: number) => `tier-${t}`
const tierName = (t: number) => ['UNRANKED', 'ROOKIE', 'RISING', 'CONTENDER', 'CHAMPION', 'LEGEND'][t] || '???'
// Generate potential matchups from top bots
const matchups = ref<{ botA: Bot; botB: Bot; hype: string }[]>([])
onMounted(() => {
setTimeout(() => {
if (bots.value.length >= 2) {
const top = bots.value.slice(0, 6)
const hypes = [
'MAIN EVENT',
'CO-MAIN EVENT',
'TITLE ELIMINATOR',
'GRUDGE MATCH',
'UNDERCARD',
'DEBUT',
]
for (let i = 0; i < Math.min(3, Math.floor(top.length / 2)); i++) {
matchups.value.push({
botA: top[i * 2],
botB: top[i * 2 + 1],
hype: hypes[i],
})
}
}
}, 500)
})
</script>
<template>
<div class="h-[calc(100vh-4rem)] flex flex-col px-6 py-6 overflow-hidden">
<div class="max-w-4xl mx-auto w-full flex flex-col flex-1 min-h-0">
<!-- Header -->
<div class="mb-6 text-center">
<p class="font-display text-[10px] font-bold text-neon-purple tracking-[0.2em] mb-2 glow-purple">
UPCOMING
</p>
<h2 class="font-display font-black text-3xl sm:text-5xl tracking-wider">
<span class="gradient-text">FIGHT CARD</span>
</h2>
</div>
<!-- Matchups -->
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
<div
v-for="(matchup, i) in matchups"
:key="i"
class="border border-border rounded-lg bg-surface-raised/50 p-5
hover:border-neon-pink/30 transition-all"
:class="i === 0 ? 'neon-border-pink' : ''"
>
<p class="text-center font-display text-[10px] font-bold tracking-[0.2em] mb-4"
:class="i === 0 ? 'text-neon-yellow' : i === 1 ? 'text-neon-pink' : 'text-text-muted'">
{{ matchup.hype }}
</p>
<div class="flex items-center justify-between">
<div class="flex-1 text-right pr-6">
<RouterLink :to="`/bot/${matchup.botA.name}`"
class="font-display font-black text-xl sm:text-2xl tracking-wider text-text-primary
hover:text-neon-cyan transition-colors">
{{ matchup.botA.name }}
</RouterLink>
<p class="font-mono text-xs mt-1">
<span :class="tierClass(matchup.botA.tier)">{{ tierName(matchup.botA.tier) }}</span>
<span class="text-text-muted"> &middot; {{ Math.round(matchup.botA.eloRating) }}</span>
<span class="text-text-muted"> &middot; {{ matchup.botA.wins }}W-{{ matchup.botA.losses }}L</span>
</p>
</div>
<div class="flex-shrink-0">
<span class="font-display font-black text-2xl text-neon-purple glow-purple">VS</span>
</div>
<div class="flex-1 pl-6">
<RouterLink :to="`/bot/${matchup.botB.name}`"
class="font-display font-black text-xl sm:text-2xl tracking-wider text-text-primary
hover:text-neon-cyan transition-colors">
{{ matchup.botB.name }}
</RouterLink>
<p class="font-mono text-xs mt-1">
<span :class="tierClass(matchup.botB.tier)">{{ tierName(matchup.botB.tier) }}</span>
<span class="text-text-muted"> &middot; {{ Math.round(matchup.botB.eloRating) }}</span>
<span class="text-text-muted"> &middot; {{ matchup.botB.wins }}W-{{ matchup.botB.losses }}L</span>
</p>
</div>
</div>
</div>
<div v-if="matchups.length === 0 && !isLoading" class="flex-1 flex items-center justify-center">
<p class="font-display text-text-muted text-sm">
Card loading... check back soon.
</p>
</div>
</div>
</div>
</div>
</template>