Files
botfights/frontend/src/components/BetHistory.vue
T
DorianandClaude Opus 4.6 5e598e3987 feat: betting history with P&L tracking on bot profile
BetHistory component shows personal bet history with fight links,
amounts, outcomes, and payouts. P&L summary shows total wagered, total
won, and net sats. Added to BotProfilePage for the bot owner's view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:58:53 +00:00

125 lines
4.4 KiB
Vue

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps<{ pubkey: string }>()
const router = useRouter()
interface Bet {
id: string
fightId: string
botId: string
amountSats: number
oddsAtPlacement: number
status: string
payoutSats: number | null
createdAt: string
settledAt: string | null
}
const bets = ref<Bet[]>([])
const isLoading = ref(true)
const totalWagered = computed(() => bets.value.reduce((sum, b) => sum + b.amountSats, 0))
const totalWon = computed(() => bets.value.reduce((sum, b) => sum + (b.payoutSats || 0), 0))
const netSats = computed(() => totalWon.value - totalWagered.value)
function statusColor(status: string): string {
if (status === 'won') return 'text-neon-green'
if (status === 'lost') return 'text-ko'
if (status === 'refunded') return 'text-neon-yellow'
return 'text-text-muted'
}
function statusLabel(status: string): string {
if (status === 'won') return 'WON'
if (status === 'lost') return 'LOST'
if (status === 'refunded') return 'REFUND'
if (status === 'locked') return 'ACTIVE'
return status.toUpperCase()
}
function timeAgo(dateStr: string): string {
const diffSec = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000)
if (diffSec < 60) return `${diffSec}s`
const diffMin = Math.floor(diffSec / 60)
if (diffMin < 60) return `${diffMin}m`
const diffHr = Math.floor(diffMin / 60)
if (diffHr < 24) return `${diffHr}h`
return `${Math.floor(diffHr / 24)}d`
}
onMounted(async () => {
try {
const res = await fetch(`/api/bets/history/${props.pubkey}`)
if (res.ok) {
const data = await res.json()
bets.value = data.bets || []
}
} catch { /* ignore */ }
isLoading.value = false
})
</script>
<template>
<div>
<h3 class="font-display font-black text-xs tracking-widest text-neon-purple mb-2">BET HISTORY</h3>
<div v-if="isLoading" class="flex justify-center py-4">
<div class="w-6 h-6 border-2 border-neon-purple/30 border-t-neon-purple rounded-full animate-spin" />
</div>
<div v-else-if="bets.length === 0" class="py-4">
<p class="font-mono text-text-muted text-xs text-center">No bets placed yet.</p>
</div>
<template v-else>
<!-- P&L summary -->
<div class="flex gap-2 mb-3">
<div class="flex-1 px-2 py-1.5 bg-surface border border-border rounded-md text-center">
<p class="font-pixel text-[8px] text-text-muted tracking-wider">WAGERED</p>
<p class="font-mono text-xs text-text-primary">{{ totalWagered }}</p>
</div>
<div class="flex-1 px-2 py-1.5 bg-surface border border-border rounded-md text-center">
<p class="font-pixel text-[8px] text-text-muted tracking-wider">WON</p>
<p class="font-mono text-xs text-neon-green">{{ totalWon }}</p>
</div>
<div class="flex-1 px-2 py-1.5 bg-surface border border-border rounded-md text-center">
<p class="font-pixel text-[8px] text-text-muted tracking-wider">NET</p>
<p class="font-mono text-xs" :class="netSats >= 0 ? 'text-neon-green' : 'text-ko'">
{{ netSats >= 0 ? '+' : '' }}{{ netSats }}
</p>
</div>
</div>
<!-- Bet list -->
<div class="space-y-1.5 max-h-48 overflow-y-auto">
<button
v-for="bet in bets"
:key="bet.id"
class="w-full text-left px-2.5 py-1.5 bg-surface/50 border border-border rounded-md
hover:border-neon-cyan/20 transition-all text-xs"
@click="router.push(`/arena/${bet.fightId}`)"
>
<div class="flex items-center justify-between">
<span class="font-mono text-text-primary">{{ bet.amountSats }} sats</span>
<span class="font-display font-bold text-[10px] tracking-wider" :class="statusColor(bet.status)">
{{ statusLabel(bet.status) }}
</span>
</div>
<div class="flex items-center justify-between mt-0.5">
<span v-if="bet.payoutSats" class="font-mono text-[10px] text-neon-green">
+{{ bet.payoutSats }} sats
</span>
<span v-else class="font-mono text-[10px] text-text-muted">
{{ (bet.oddsAtPlacement * 0.95).toFixed(2) }}x odds
</span>
<span class="font-pixel text-[8px] text-text-muted">{{ timeAgo(bet.createdAt) }}</span>
</div>
</button>
</div>
</template>
</div>
</template>