Spaces:
Running
Running
| <!-- src/views/LiveView.vue --> | |
| <script setup> | |
| import { ref, computed, onMounted } from 'vue' | |
| import CompareChartE from '../components/CompareChartE.vue' | |
| import AssetTabs from '../components/AssetTabs.vue' | |
| import { dataService } from '../lib/dataService.js' | |
| const ASSETS = ['BTC','ETH','SOL','BNB','DOGE','XRP'] | |
| const asset = ref(ASSETS[0]) | |
| const agents = ref([]) | |
| function score(row){ return typeof row.balance === 'number' ? row.balance : -Infinity } | |
| const winners = computed(() => { | |
| const rows = (agents.value || []).filter(r => r.asset === asset.value) | |
| const byAgent = new Map() | |
| for (const r of rows) { | |
| const k = r.agent_name | |
| const cur = byAgent.get(k) | |
| if (!cur || score(r) > score(cur)) byAgent.set(k, r) | |
| } | |
| return [...byAgent.values()] | |
| }) | |
| const winnersForChart = computed(() => | |
| winners.value.map(w => ({ | |
| agent_name: w.agent_name, | |
| asset: w.asset, | |
| model: w.model, | |
| strategy: w.strategy, | |
| decision_ids: Array.isArray(w.decision_ids) ? w.decision_ids : undefined | |
| })) | |
| ) | |
| const winnersSorted = computed(() => [...winners.value].sort((a,b) => score(b) - score(a))) | |
| const fmtUSD = n => (n ?? 0).toLocaleString(undefined,{ style:'currency', currency:'USD', maximumFractionDigits:2 }) | |
| onMounted(async () => { | |
| try { | |
| // run the service load if needed (this populates tableRows + caches decisions) | |
| if (!dataService.loaded) await dataService.load(false) | |
| } catch (e) { | |
| console.error('LiveView: dataService.load failed', e) | |
| } | |
| // copy rows to local reactive state | |
| agents.value = Array.isArray(dataService.tableRows) ? dataService.tableRows : [] | |
| console.log('LiveView: loaded rows =', agents.value.length) | |
| }) | |
| </script> | |
| <template> | |
| <div class="p-4 space-y-4"> | |
| <AssetTabs v-model="asset" /> | |
| <CompareChartE v-if="winnersForChart.length" :selected="winnersForChart" :visible="true" /> | |
| <div v-else class="p-4 text-sm text-gray-500 border rounded-lg"> | |
| No data for {{ asset }} yet. Make sure Supabase has decisions/runs for this asset and that | |
| <code>dataService.load()</code> completes. Check console for “LiveView: loaded rows” count. | |
| </div> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> | |
| <div v-for="row in winnersSorted" :key="row.agent_name+'|'+row.asset+'|'+row.model" | |
| class="p-4 rounded-2xl border shadow-sm flex justify-between items-center"> | |
| <div class="min-w-0"> | |
| <div class="font-semibold truncate">{{ row.agent_name }}</div> | |
| <div class="text-xs opacity-70 truncate">{{ row.model }}</div> | |
| </div> | |
| <div class="text-right"> | |
| <div class="font-bold text-xl leading-6">{{ fmtUSD(row.balance) }}</div> | |
| <div class="text-xs opacity-70">EOD {{ (row.end_date || row.last_nav_ts) ? new Date(row.end_date || row.last_nav_ts).toLocaleDateString() : '-' }}</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |