mejoras de UI
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
<div class="panel glass">
|
||||
<h2 class="panel-title">Eventos y comparación</h2>
|
||||
<div v-if="loading" class="placeholder">Cargando datos…</div>
|
||||
<div v-else class="bars big">
|
||||
|
||||
<!-- Regular bars view -->
|
||||
<div v-else-if="viewMode !== 'ratio'" class="bars big">
|
||||
<div
|
||||
v-for="eventType in eventTypes"
|
||||
:key="eventType"
|
||||
@@ -43,6 +45,47 @@
|
||||
</div>
|
||||
<div class="hint small">Basado en mensajes disponibles por sala. Click jugador para comparar.</div>
|
||||
</div>
|
||||
|
||||
<!-- Ratio bars view -->
|
||||
<div v-else class="ratio-bars">
|
||||
<div
|
||||
v-for="(group, index) in ratioData"
|
||||
:key="group.name"
|
||||
class="ratio-group"
|
||||
:class="{ highlight: highlighted === group.name }"
|
||||
@mouseenter="highlighted = group.name"
|
||||
@mouseleave="highlighted = ''"
|
||||
>
|
||||
<div class="ratio-bar">
|
||||
<div
|
||||
v-for="(action, actionIndex) in group.actions"
|
||||
:key="action"
|
||||
class="ratio-segment"
|
||||
:style="{
|
||||
width: group.percentages[actionIndex] + '%',
|
||||
background: eventStyles[action]?.gradient || 'linear-gradient(90deg, #94a3b8, #64748b)'
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="ratio-event-chip"
|
||||
v-if="group.percentages[actionIndex] > 5"
|
||||
:style="{
|
||||
background: getEventChipBg(action),
|
||||
borderColor: getEventBorderColor(action)
|
||||
}"
|
||||
>
|
||||
<span class="ratio-icon">{{ eventStyles[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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint small">
|
||||
{{ selectedPlayerUuid ? 'Ratios del jugador seleccionado' : 'Ratios globales' }}.
|
||||
Los segmentos muestran la proporción relativa dentro de cada categoría.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -56,7 +99,7 @@ interface Props {
|
||||
playerEventCounts: Record<string, number>;
|
||||
selectedPlayerUuid?: string;
|
||||
playerBarGradient: string;
|
||||
showPercent: boolean;
|
||||
viewMode: 'count' | 'percent' | 'ratio';
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
@@ -67,6 +110,54 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const highlighted = ref('');
|
||||
|
||||
// Define ratio groups for superposed view
|
||||
const ratioGroups = [
|
||||
{
|
||||
name: 'Ofertas',
|
||||
actions: ['p1_propose', 'p1_no_offer'],
|
||||
labels: ['Ofrecer', 'No Ofrecer']
|
||||
},
|
||||
{
|
||||
name: 'Respuestas',
|
||||
actions: ['p2_accept', 'p2_reject', 'p2_snatch'],
|
||||
labels: ['Aceptar', 'Rechazar', 'Robar']
|
||||
},
|
||||
{
|
||||
name: 'Fuerzas',
|
||||
actions: ['p2_force', 'p2_no_force'],
|
||||
labels: ['Forzar', 'No Forzar']
|
||||
},
|
||||
{
|
||||
name: 'Vergüenzas',
|
||||
actions: ['p1_shame', 'p1_no_shame'],
|
||||
labels: ['Asignar', 'No Asignar']
|
||||
},
|
||||
{
|
||||
name: 'Denuncias',
|
||||
actions: ['p1_report', 'p1_no_report'],
|
||||
labels: ['Denunciar', 'No Denunciar']
|
||||
}
|
||||
];
|
||||
|
||||
// Compute ratio data for each group
|
||||
const ratioData = computed(() => {
|
||||
return ratioGroups.map(group => {
|
||||
const counts = props.selectedPlayerUuid
|
||||
? props.playerEventCounts
|
||||
: props.globalEventCounts;
|
||||
|
||||
const values = group.actions.map(action => counts[action] || 0);
|
||||
const total = values.reduce((sum, val) => sum + val, 0);
|
||||
|
||||
return {
|
||||
...group,
|
||||
values,
|
||||
total,
|
||||
percentages: total > 0 ? values.map(val => (val / total) * 100) : values.map(() => 0)
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// Global calculations
|
||||
const globalMax = computed(() => {
|
||||
const vals = props.eventTypes.map(k => props.globalEventCounts[k] || 0);
|
||||
@@ -80,12 +171,12 @@ const globalTotal = computed(() =>
|
||||
|
||||
function globalBarWidth(eventType: string) {
|
||||
const v = props.globalEventCounts[eventType] || 0;
|
||||
return Math.round((v / (props.showPercent ? globalTotal.value : globalMax.value)) * 100);
|
||||
return Math.round((v / (props.viewMode === 'percent' ? globalTotal.value : globalMax.value)) * 100);
|
||||
}
|
||||
|
||||
function globalValueLabel(eventType: string) {
|
||||
const v = props.globalEventCounts[eventType] || 0;
|
||||
return props.showPercent ? `${Math.round((v / globalTotal.value) * 100)}%` : String(v);
|
||||
return props.viewMode === 'percent' ? `${Math.round((v / globalTotal.value) * 100)}%` : String(v);
|
||||
}
|
||||
|
||||
// Player calculations
|
||||
@@ -101,12 +192,12 @@ const playerTotal = computed(() =>
|
||||
|
||||
function playerBarWidth(eventType: string) {
|
||||
const v = props.playerEventCounts[eventType] || 0;
|
||||
return Math.round((v / (props.showPercent ? playerTotal.value : playerMax.value)) * 100);
|
||||
return Math.round((v / (props.viewMode === 'percent' ? playerTotal.value : playerMax.value)) * 100);
|
||||
}
|
||||
|
||||
function playerValueLabel(eventType: string) {
|
||||
const v = props.playerEventCounts[eventType] || 0;
|
||||
return props.showPercent ? `${Math.round((v / playerTotal.value) * 100)}%` : String(v);
|
||||
return props.viewMode === 'percent' ? `${Math.round((v / playerTotal.value) * 100)}%` : String(v);
|
||||
}
|
||||
|
||||
// Styling helpers
|
||||
@@ -310,6 +401,101 @@ function friendlyEventName(eventType: string): string {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* Ratio bars styles */
|
||||
.ratio-bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.ratio-group {
|
||||
flex: 1 1 0;
|
||||
min-height: 60px;
|
||||
transition: transform .18s ease;
|
||||
}
|
||||
|
||||
.ratio-group.highlight {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.ratio-bar {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(238,242,255,0.4) 0%, rgba(199,210,254,0.2) 100%);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(199,210,254,0.3);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ratio-segment {
|
||||
height: 100%;
|
||||
transition: all 0.6s cubic-bezier(.2,.7,.1,1);
|
||||
backdrop-filter: blur(4px);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.ratio-segment:first-child {
|
||||
border-top-left-radius: 12px;
|
||||
border-bottom-left-radius: 12px;
|
||||
}
|
||||
|
||||
.ratio-segment:last-child {
|
||||
border-top-right-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
.ratio-event-chip {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.4);
|
||||
backdrop-filter: blur(8px) saturate(120%);
|
||||
-webkit-backdrop-filter: blur(8px) saturate(120%);
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
.ratio-segment:hover .ratio-event-chip {
|
||||
transform: translate(-50%, -50%) scale(1.05);
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
.ratio-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ratio-label {
|
||||
color: #0f172a;
|
||||
font-weight: 800;
|
||||
font-size: 13px;
|
||||
letter-spacing: .1px;
|
||||
}
|
||||
|
||||
.ratio-count {
|
||||
color: #1f2937;
|
||||
font-weight: 800;
|
||||
background: rgba(255,255,255,0.7);
|
||||
border: 1px solid rgba(229,231,235,0.5);
|
||||
border-radius: 999px;
|
||||
padding: 2px 6px;
|
||||
font-size: 11px;
|
||||
margin-left: 2px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
/* Responsive chip sizing */
|
||||
@media (min-width: 1200px) {
|
||||
.bar-chip {
|
||||
@@ -395,5 +581,83 @@ function friendlyEventName(eventType: string): string {
|
||||
font-size: 8px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
/* Ratio responsive styles */
|
||||
.ratio-event-chip {
|
||||
padding: 3px 6px;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.ratio-icon {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.ratio-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.ratio-count {
|
||||
font-size: 8px;
|
||||
padding: 1px 3px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.ratio-event-chip {
|
||||
padding: 3px 7px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.ratio-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ratio-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.ratio-count {
|
||||
font-size: 9px;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 1199px) {
|
||||
.ratio-event-chip {
|
||||
padding: 4px 8px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.ratio-icon {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ratio-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ratio-count {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.ratio-event-chip {
|
||||
padding: 6px 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ratio-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ratio-label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ratio-count {
|
||||
font-size: 12px;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user