mejoras de codigo 2

This commit is contained in:
2025-08-29 13:56:26 -06:00
parent 82ab8d7709
commit 52051c4b63
2 changed files with 154 additions and 339 deletions

View File

@@ -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)'
}"
></div>
<div
@@ -61,7 +61,7 @@
borderColor: getEventBorderColor(eventType)
}"
>
<span class="event-icon">{{ eventStyles[eventType]?.icon || '📊' }}</span>
<span class="event-icon">{{ EVENT_STYLES[eventType]?.icon || '📊' }}</span>
<span class="chip-label">{{ friendlyEventName(eventType) }}</span>
<span class="chip-count global">{{ globalValueLabel(eventType) }}</span>
<span v-if="selectedPlayerUuid" class="chip-count player">{{ playerValueLabel(eventType) }}</span>
@@ -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)'
}"
>
<div
@@ -105,7 +105,7 @@
borderColor: getEventBorderColor(action)
}"
>
<span class="ratio-icon">{{ eventStyles[action]?.icon || '📊' }}</span>
<span class="ratio-icon">{{ EVENT_STYLES[action]?.icon || '📊' }}</span>
<span class="ratio-label">{{ group.labels[actionIndex] }}</span>
<span class="ratio-count">{{ group.values[actionIndex] }} ({{ Math.round(group.percentages[actionIndex]) }}%)</span>
</div>
@@ -123,12 +123,14 @@
import { ref, computed } from 'vue';
interface Props {
eventTypes: string[];
eventStyles: Record<string, { icon: string; color: string; gradient: string }>;
globalEventCounts: Record<string, number>;
playerEventCounts: Record<string, number>;
filteredData?: {
events: any[];
players: any[];
metrics: Record<string, number>;
aggregatedCounts: Record<string, number>;
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<Props>(), {
@@ -156,51 +149,174 @@ const props = withDefaults(defineProps<Props>(), {
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<string, { icon: string; color: string; gradient: string }> = {
'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<string, number> = {};
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`;
}