diff --git a/client/src/components/EventChart.vue b/client/src/components/EventChart.vue index 53a24ed..567896e 100644 --- a/client/src/components/EventChart.vue +++ b/client/src/components/EventChart.vue @@ -43,7 +43,7 @@ class="bar-fill global shimmer" :style="{ width: globalBarWidth(eventType) + '%', - background: eventStyles[eventType]?.gradient || 'linear-gradient(90deg, #94a3b8, #64748b)' + background: EVENT_STYLES[eventType]?.gradient || 'linear-gradient(90deg, #94a3b8, #64748b)' }" >
- {{ eventStyles[eventType]?.icon || 'πŸ“Š' }} + {{ EVENT_STYLES[eventType]?.icon || 'πŸ“Š' }} {{ friendlyEventName(eventType) }} {{ globalValueLabel(eventType) }} {{ playerValueLabel(eventType) }} @@ -94,7 +94,7 @@ class="ratio-segment" :style="{ width: group.percentages[actionIndex] + '%', - background: eventStyles[action]?.gradient || 'linear-gradient(90deg, #94a3b8, #64748b)' + background: EVENT_STYLES[action]?.gradient || 'linear-gradient(90deg, #94a3b8, #64748b)' }" >
- {{ eventStyles[action]?.icon || 'πŸ“Š' }} + {{ EVENT_STYLES[action]?.icon || 'πŸ“Š' }} {{ group.labels[actionIndex] }} {{ group.values[actionIndex] }} ({{ Math.round(group.percentages[actionIndex]) }}%)
@@ -123,12 +123,14 @@ import { ref, computed } from 'vue'; interface Props { - eventTypes: string[]; - eventStyles: Record; - globalEventCounts: Record; - playerEventCounts: Record; + filteredData?: { + events: any[]; + players: any[]; + metrics: Record; + aggregatedCounts: Record; + sourceData: string; + }; selectedPlayerUuid?: string; - playerBarGradient: string; viewMode: 'count' | 'percent' | 'ratio'; loading?: boolean; filtersCollapsed?: boolean; @@ -140,15 +142,6 @@ interface Props { selectedPlayer?: string; selectedRoom?: string; }; - groupTotals?: { - offers: number; - responses: number; - force: number; - shame: number; - report: number; - averageScore: number; - totalPlayers: number; - }; } const props = withDefaults(defineProps(), { @@ -156,51 +149,174 @@ const props = withDefaults(defineProps(), { loading: false }); +// Event types and styles +const EVENTS = [ + 'p1_propose', 'p1_no_offer', + 'p2_snatch', 'p2_accept', 'p2_force', 'p2_no_force', 'p2_reject', + 'p1_shame', 'p1_no_shame', 'p1_report', 'p1_no_report' +]; + +const METRICS = [ + 'players_seated', 'score_p1', 'score_p2', 'players_with_shame', 'players_without_shame' +]; + +const ALL_CHART_TYPES = [...EVENTS, ...METRICS]; + +const EVENT_STYLES: Record = { + 'p1_propose': { icon: '✨', color: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, + 'p1_no_offer': { icon: '❌', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, + 'p2_accept': { icon: 'βœ“', color: '#10b981', gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)' }, + 'p2_reject': { icon: 'βœ•', color: '#f59e0b', gradient: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)' }, + 'p2_snatch': { icon: 'πŸ‘Ή', color: '#ef4444', gradient: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)' }, + 'p2_force': { icon: '⚑', color: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, + 'p2_no_force': { icon: '🚫', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, + 'p1_shame': { icon: '😢', color: '#fbbf24', gradient: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)' }, + 'p1_no_shame': { icon: 'πŸ™‚', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, + 'p1_report': { icon: 'βš–οΈ', color: '#8b5cf6', gradient: 'linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)' }, + 'p1_no_report': { icon: '🀝', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, + 'players_seated': { icon: 'πŸ‘₯', color: '#06b6d4', gradient: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)' }, + 'score_p1': { icon: 'πŸ¦ƒ', color: '#16a34a', gradient: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)' }, + 'score_p2': { icon: '🌽', color: '#d97706', gradient: 'linear-gradient(135deg, #d97706 0%, #b45309 100%)' }, + 'players_with_shame': { icon: '😢', color: '#dc2626', gradient: 'linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)' }, + 'players_without_shame': { icon: 'πŸ‘₯', color: '#06b6d4', gradient: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)' } +}; + const highlighted = ref(''); +// Main data computations +const eventTypes = computed(() => ALL_CHART_TYPES); + +const globalEventCounts = computed(() => { + if (!props.filteredData) return {}; + return { + ...props.filteredData.aggregatedCounts, + ...props.filteredData.metrics + }; +}); + +const playerEventCounts = computed(() => { + if (!props.selectedPlayerUuid || !props.filteredData) return {}; + + // Calculate player-specific counts from filtered events + const playerCounts: Record = {}; + EVENTS.forEach(eventType => { + playerCounts[eventType] = props.filteredData!.events.filter( + (e: any) => e.kind === eventType && e.playerUuid === props.selectedPlayerUuid + ).length; + }); + + // Calculate player-specific metrics + const selectedPlayer = props.filteredData.players.find((p: any) => p.uuid === props.selectedPlayerUuid); + if (selectedPlayer) { + let totalP1Scores = 0; + let totalP2Scores = 0; + let p1Count = 0; + let p2Count = 0; + let playersWithShame = selectedPlayer.shameTokens > 0 ? 1 : 0; + + if (selectedPlayer.roomScoreHistory) { + selectedPlayer.roomScoreHistory.forEach((roomScore: any) => { + roomScore.scores.forEach((score: any) => { + if (score.role === 'P1') { + totalP1Scores += score.score; + p1Count++; + } else if (score.role === 'P2') { + totalP2Scores += score.score; + p2Count++; + } + }); + }); + } + + playerCounts.players_seated = 1; + playerCounts.score_p1 = p1Count > 0 ? Math.round((totalP1Scores / p1Count) * 10) / 10 : 0; + playerCounts.score_p2 = p2Count > 0 ? Math.round((totalP2Scores / p2Count) * 10) / 10 : 0; + playerCounts.players_with_shame = playersWithShame; + playerCounts.players_without_shame = 1 - playersWithShame; + } + + return playerCounts; +}); + +const playerBarGradient = computed(() => '#8b5cf6'); + +// Group totals computation +const groupTotals = computed(() => { + const counts = globalEventCounts.value; + return { + offers: (counts.p1_propose || 0) + (counts.p1_no_offer || 0), + responses: (counts.p2_accept || 0) + (counts.p2_reject || 0) + (counts.p2_snatch || 0), + force: (counts.p2_force || 0) + (counts.p2_no_force || 0), + shame: (counts.p1_shame || 0) + (counts.p1_no_shame || 0), + report: (counts.p1_report || 0) + (counts.p1_no_report || 0), + averageScore: calculateAverageScore(), + totalPlayers: counts.players_seated || 0 + }; +}); + +function calculateAverageScore(): number { + if (!props.filteredData?.players.length) return 0; + + let totalScores = 0; + let totalScoreCount = 0; + + props.filteredData.players.forEach((player: any) => { + if (player.roomScoreHistory) { + player.roomScoreHistory.forEach((roomScore: any) => { + roomScore.scores.forEach((score: any) => { + totalScores += score.score; + totalScoreCount++; + }); + }); + } + }); + + return totalScoreCount > 0 ? Math.round((totalScores / totalScoreCount) * 10) / 10 : 0; +} + // Define ratio groups for superposed view const ratioGroups = computed(() => [ { name: 'Ofertas', actions: ['p1_propose', 'p1_no_offer'], labels: ['Ofrecer', 'No Ofrecer'], - total: props.groupTotals?.offers || 0 + total: groupTotals.value.offers }, { name: 'Respuestas', actions: ['p2_accept', 'p2_reject', 'p2_snatch'], labels: ['Aceptar', 'Rechazar', 'Robar'], - total: props.groupTotals?.responses || 0 + total: groupTotals.value.responses }, { name: 'Forzar', actions: ['p2_force', 'p2_no_force'], labels: ['Forzar', 'No Forzar'], - total: props.groupTotals?.force || 0 + total: groupTotals.value.force }, { name: 'Avergonzar', actions: ['p1_shame', 'p1_no_shame'], labels: ['Asignar', 'No Asignar'], - total: props.groupTotals?.shame || 0 + total: groupTotals.value.shame }, { name: 'Denunciar', actions: ['p1_report', 'p1_no_report'], labels: ['Denunciar', 'No Denunciar'], - total: props.groupTotals?.report || 0 + total: groupTotals.value.report }, { name: 'Puntaje Promedio', actions: ['score_p1', 'score_p2'], labels: ['P1', 'P2'], - total: props.groupTotals?.averageScore ? props.groupTotals.averageScore.toFixed(1) : '0.0' + total: groupTotals.value.averageScore.toFixed(1) }, { name: 'Total Jugadores', actions: ['players_with_shame', 'players_without_shame'], labels: ['Con vergΓΌenza', 'Sin vergΓΌenza'], - total: props.groupTotals?.totalPlayers || 0, + total: groupTotals.value.totalPlayers, isCustomRatio: true // Special handling needed } ]); @@ -209,8 +325,8 @@ const ratioGroups = computed(() => [ const ratioData = computed(() => { return ratioGroups.value.map(group => { const counts = props.selectedPlayerUuid - ? props.playerEventCounts - : props.globalEventCounts; + ? playerEventCounts.value + : globalEventCounts.value; let values = group.actions.map(action => counts[action] || 0); @@ -234,55 +350,55 @@ const ratioData = computed(() => { // Global calculations const globalMax = computed(() => { - const vals = props.eventTypes.map(k => props.globalEventCounts[k] || 0); + const vals = eventTypes.value.map(k => globalEventCounts.value[k] || 0); const m = Math.max(0, ...vals); return m || 1; }); const globalTotal = computed(() => - props.eventTypes.reduce((acc, k) => acc + (props.globalEventCounts[k] || 0), 0) || 1 + eventTypes.value.reduce((acc, k) => acc + (globalEventCounts.value[k] || 0), 0) || 1 ); function globalBarWidth(eventType: string) { - const v = props.globalEventCounts[eventType] || 0; + const v = globalEventCounts.value[eventType] || 0; return Math.round((v / (props.viewMode === 'percent' ? globalTotal.value : globalMax.value)) * 100); } function globalValueLabel(eventType: string) { - const v = props.globalEventCounts[eventType] || 0; + const v = globalEventCounts.value[eventType] || 0; return props.viewMode === 'percent' ? `${Math.round((v / globalTotal.value) * 100)}%` : String(v); } // Player calculations const playerMax = computed(() => { - const vals = props.eventTypes.map(k => props.playerEventCounts[k] || 0); + const vals = eventTypes.value.map(k => playerEventCounts.value[k] || 0); const m = Math.max(0, ...vals); return m || 1; }); const playerTotal = computed(() => - props.eventTypes.reduce((acc, k) => acc + (props.playerEventCounts[k] || 0), 0) || 1 + eventTypes.value.reduce((acc, k) => acc + (playerEventCounts.value[k] || 0), 0) || 1 ); function playerBarWidth(eventType: string) { - const v = props.playerEventCounts[eventType] || 0; + const v = playerEventCounts.value[eventType] || 0; return Math.round((v / (props.viewMode === 'percent' ? playerTotal.value : playerMax.value)) * 100); } function playerValueLabel(eventType: string) { - const v = props.playerEventCounts[eventType] || 0; + const v = playerEventCounts.value[eventType] || 0; return props.viewMode === 'percent' ? `${Math.round((v / playerTotal.value) * 100)}%` : String(v); } // Styling helpers function getEventChipBg(eventType: string): string { - const style = props.eventStyles[eventType]; + const style = EVENT_STYLES[eventType]; if (!style) return 'rgba(255,255,255,0.82)'; return `linear-gradient(135deg, ${style.color}15 0%, rgba(255,255,255,0.9) 100%)`; } function getEventBorderColor(eventType: string): string { - const style = props.eventStyles[eventType]; + const style = EVENT_STYLES[eventType]; if (!style) return 'rgba(229,231,235,0.9)'; return `${style.color}40`; } diff --git a/client/src/views/Leaderboard.vue b/client/src/views/Leaderboard.vue index ac0d923..ecbae27 100644 --- a/client/src/views/Leaderboard.vue +++ b/client/src/views/Leaderboard.vue @@ -64,25 +64,12 @@ @@ -104,12 +91,10 @@ import EventFilters from '../components/EventFilters.vue'; import FilterDataViewer from '../components/FilterDataViewer.vue'; import GameLogo from '../components/GameLogo.vue'; import AppCredits from '../components/AppCredits.vue'; -import { useEventFilters } from '../composables/useEventFilters'; const router = useRouter(); const loading = ref(false); -const eventFilters = useEventFilters(); const filtersCollapsed = ref(false); // Filter state using new EventFilters approach @@ -135,263 +120,15 @@ function clearTimeFilter() { // Handle filtered data from EventFilters component function onFiltered(data: any) { filteredData.value = data; - if (data) { - // Update global counts from filtered data - eventFilters.globalEventCounts.value = data.aggregatedCounts || {}; - // Update metrics - additionalMetrics.value = data.metrics || {}; - // Recompute selected player metrics - computeSelectedPlayersMetrics(); - } } -const EVENTS = [ - 'p1_propose', 'p1_no_offer', - 'p2_snatch', 'p2_accept', 'p2_force', 'p2_no_force', 'p2_reject', - 'p1_shame', 'p1_no_shame', 'p1_report', 'p1_no_report' -]; -// New metric types for additional charts -const METRICS = [ - 'players_seated', 'score_p1', 'score_p2', 'players_with_shame', 'players_without_shame' -]; - -const ALL_CHART_TYPES = [...EVENTS, ...METRICS]; - -// Event and metric styles matching OfferActions and OfferControls components -const EVENT_STYLES: Record = { - // Event types - 'p1_propose': { icon: '✨', color: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, - 'p1_no_offer': { icon: '❌', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, - 'p2_accept': { icon: 'βœ“', color: '#10b981', gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)' }, - 'p2_reject': { icon: 'βœ•', color: '#f59e0b', gradient: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)' }, - 'p2_snatch': { icon: 'πŸ‘Ή', color: '#ef4444', gradient: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)' }, - 'p2_force': { icon: '⚑', color: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, - 'p2_no_force': { icon: '🚫', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, - 'p1_shame': { icon: '😢', color: '#fbbf24', gradient: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)' }, - 'p1_no_shame': { icon: 'πŸ™‚', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, - 'p1_report': { icon: 'βš–οΈ', color: '#8b5cf6', gradient: 'linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)' }, - 'p1_no_report': { icon: '🀝', color: '#6b7280', gradient: 'linear-gradient(135deg, #94a3b8 0%, #64748b 100%)' }, - - // Metric types - 'players_seated': { icon: 'πŸ‘₯', color: '#06b6d4', gradient: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)' }, - 'score_p1': { icon: 'πŸ¦ƒ', color: '#16a34a', gradient: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)' }, - 'score_p2': { icon: '🌽', color: '#d97706', gradient: 'linear-gradient(135deg, #d97706 0%, #b45309 100%)' }, - 'players_with_shame': { icon: '😢', color: '#dc2626', gradient: 'linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)' }, - 'players_without_shame': { icon: 'πŸ‘₯', color: '#06b6d4', gradient: 'linear-gradient(135deg, #06b6d4 0%, #0891b2 100%)' } -}; - - -// Additional metrics computation -const additionalMetrics = ref>({ - players_seated: 0, - score_p1: 0, - score_p2: 0, - players_with_shame: 0, - players_without_shame: 0 -}); - -const selectedPlayerMetrics = ref>({ - players_seated: 0, - score_p1: 0, - score_p2: 0, - players_with_shame: 0, - players_without_shame: 0 -}); // Store room score history from players const allPlayersWithScores = ref([]); -// Function to check if a score passes the current filters -function scorePassesFilters(score: any, roomId: string) { - // Room filter (array): empty means all - const rf = filterState.value.rooms; - if (rf.length && !rf.includes(String(roomId))) return false; - // Round filter (array) - const rds = filterState.value.rounds; - if (rds.length && !rds.includes(Number(score.round))) return false; - - // Game variant filter (array) - const gfs = filterState.value.games; - if (gfs.length && !gfs.includes(String(score.variant))) return false; - - return true; -} - -// Function to compute additional metrics from players' score history -function computeMetricsFromScores() { - if (!allPlayersWithScores.value.length) return; - - let totalP1Scores = 0; - let totalP2Scores = 0; - let p1Count = 0; - let p2Count = 0; - let playersWithShame = 0; - let totalPlayersWithNames = 0; - - // Get score data from players with room score history - allPlayersWithScores.value.forEach((player: any) => { - if (player.name) { - totalPlayersWithNames++; - - // Count players with shame tokens - if (player.shameTokens && player.shameTokens > 0) { - playersWithShame++; - } - - // Extract scores from roomScoreHistory if available - if (player.roomScoreHistory) { - player.roomScoreHistory.forEach((roomScore: any) => { - roomScore.scores.forEach((score: any) => { - // Apply filters to scores - if (!scorePassesFilters(score, roomScore.roomId)) { - return; - } - - if (score.role === 'P1') { - totalP1Scores += score.score; - p1Count++; - } else if (score.role === 'P2') { - totalP2Scores += score.score; - p2Count++; - } - }); - }); - } - } - }); - - const avgP1Score = p1Count > 0 ? totalP1Scores / p1Count : 0; - const avgP2Score = p2Count > 0 ? totalP2Scores / p2Count : 0; - - additionalMetrics.value = { - players_seated: totalPlayersWithNames, - score_p1: Math.round(avgP1Score * 10) / 10, // Round to 1 decimal - score_p2: Math.round(avgP2Score * 10) / 10, - players_with_shame: playersWithShame, - players_without_shame: totalPlayersWithNames - playersWithShame - }; -} - -// Function to compute metrics for selected players (multi-select) -function computeSelectedPlayersMetrics() { - const uuids = filterState.value.playerUuids; - if (!uuids.length) { - selectedPlayerMetrics.value = { - players_seated: 0, - score_p1: 0, - score_p2: 0, - players_with_shame: 0, - players_without_shame: 0 - }; - return; - } - - let totalP1Scores = 0; - let totalP2Scores = 0; - let p1Count = 0; - let p2Count = 0; - let playersWithShame = 0; - - uuids.forEach(uuid => { - const playerData = allPlayersWithScores.value.find(p => p.uuid === uuid); - if (!playerData) return; - if (playerData.shameTokens && playerData.shameTokens > 0) playersWithShame++; - if (playerData.roomScoreHistory) { - playerData.roomScoreHistory.forEach((roomScore: any) => { - roomScore.scores.forEach((score: any) => { - if (!scorePassesFilters(score, roomScore.roomId)) return; - if (score.role === 'P1') { totalP1Scores += score.score; p1Count++; } - else if (score.role === 'P2') { totalP2Scores += score.score; p2Count++; } - }); - }); - } - }); - - selectedPlayerMetrics.value = { - players_seated: uuids.length, - score_p1: p1Count > 0 ? Math.round((totalP1Scores / p1Count) * 10) / 10 : 0, - score_p2: p2Count > 0 ? Math.round((totalP2Scores / p2Count) * 10) / 10 : 0, - players_with_shame: playersWithShame, - players_without_shame: uuids.length - playersWithShame - }; -} - -// Combined counts for charts (events + metrics) -const combinedGlobalCounts = computed(() => ({ - ...eventFilters.globalEventCounts.value, - ...additionalMetrics.value -})); - -const combinedPlayerCounts = computed(() => ({ - ...playerEventCounts.value, - ...selectedPlayerMetrics.value -})); - - -// Chart group totals -const offersTotal = computed(() => { - const propose = eventFilters.globalEventCounts.value.p1_propose || 0; - const noOffer = eventFilters.globalEventCounts.value.p1_no_offer || 0; - return propose + noOffer; -}); - -const responsesTotal = computed(() => { - const accept = eventFilters.globalEventCounts.value.p2_accept || 0; - const reject = eventFilters.globalEventCounts.value.p2_reject || 0; - const snatch = eventFilters.globalEventCounts.value.p2_snatch || 0; - return accept + reject + snatch; -}); - -const forceTotal = computed(() => { - const force = eventFilters.globalEventCounts.value.p2_force || 0; - const noForce = eventFilters.globalEventCounts.value.p2_no_force || 0; - return force + noForce; -}); - -const shameTotal = computed(() => { - const shame = eventFilters.globalEventCounts.value.p1_shame || 0; - const noShame = eventFilters.globalEventCounts.value.p1_no_shame || 0; - return shame + noShame; -}); - -const reportTotal = computed(() => { - const report = eventFilters.globalEventCounts.value.p1_report || 0; - const noReport = eventFilters.globalEventCounts.value.p1_no_report || 0; - return report + noReport; -}); - -const averageScoreTotal = computed(() => { - if (!allPlayersWithScores.value.length) return 0; - - let totalScores = 0; - let totalScoreCount = 0; - - // Sum all individual scores from all players regardless of role, applying filters - allPlayersWithScores.value.forEach((player: any) => { - if (player.roomScoreHistory) { - player.roomScoreHistory.forEach((roomScore: any) => { - roomScore.scores.forEach((score: any) => { - // Apply filters to scores - if (!scorePassesFilters(score, roomScore.roomId)) { - return; - } - - totalScores += score.score; - totalScoreCount++; - }); - }); - } - }); - - return totalScoreCount > 0 ? Math.round((totalScores / totalScoreCount) * 10) / 10 : 0; -}); - -const totalPlayersCount = computed(() => { - return additionalMetrics.value.players_seated || 0; -}); // Active filters object const activeFilters = computed(() => ({ @@ -403,15 +140,8 @@ const activeFilters = computed(() => ({ selectedRoom: filterState.value.rooms.length ? `${filterState.value.rooms.length} salas` : undefined })); -// Watch for changes in filter state to update selected player metrics -watch(filterState, () => { - computeSelectedPlayersMetrics(); -}, { deep: true }); const availableRooms = ref<{ roomId: string; name: string; playerCount?: number }[]>([]); -const playerLoading = ref(false); -const playerEventCounts = ref>((Object.fromEntries(EVENTS.map(k => [k, 0])) as Record)); -const playersActionsByUuid = ref>>({}); @@ -421,8 +151,6 @@ function goHome() { } -// Dynamic per-player overlay bar gradient and label color -const playerBarGradient = computed(() => '#8b5cf6'); @@ -472,17 +200,6 @@ function setupStreams() { }); - // Mapear counts por jugador - const byUuid: Record> = {}; - list.forEach((p: any) => { - const uuid = String(p?.uuid || ''); - if (!uuid) return; - const src = p?.counts || {}; - const normalized: Record = Object.fromEntries(EVENTS.map(k => [k, 0])) as any; - EVENTS.forEach(k => { normalized[k] = Number(src[k] || 0); }); - byUuid[uuid] = normalized; - }); - playersActionsByUuid.value = byUuid; // Store aggregated events for the EventFilters component const aggEvents = Array.isArray(data?.aggregated?.detailedEvents) ? data.aggregated.detailedEvents : []; @@ -518,11 +235,6 @@ function setupStreams() { })).sort((a: any, b: any) => (a._lastSeen - b._lastSeen)).map(({ _lastSeen, ...rest }: any) => rest); } - // Recalculate metrics from score history - computeMetricsFromScores(); - - // Actualizar conteos del/los jugador(es) seleccionado(s) - updateSelectedPlayersCounts(); } finally { loading.value = false; @@ -535,23 +247,10 @@ function setupStreams() { -function updateSelectedPlayersCounts() { - playerLoading.value = true; - const next: Record = Object.fromEntries(EVENTS.map(k => [k, 0])) as any; - const byUuid = playersActionsByUuid.value; - filterState.value.playerUuids.forEach(uuid => { - const counts = byUuid[uuid] || {}; - EVENTS.forEach(k => { next[k] = (next[k] || 0) + Number(counts[k] || 0); }); - }); - playerEventCounts.value = next as any; - playerLoading.value = false; -} onMounted(() => { setupStreams(); - // Initialize with aggregated data as default - eventFilters.globalEventCounts.value = { ...eventFilters.globalEventCountsAggregated.value }; // Initialize default time range for filter state const now = new Date();