mejoras de UI
This commit is contained in:
@@ -1,7 +1,16 @@
|
||||
<template>
|
||||
<div class="panel glass">
|
||||
<div class="panel-header">
|
||||
<h2 class="panel-title">Eventos y comparación</h2>
|
||||
<!-- Loading state as a single card -->
|
||||
<div v-if="loading" class="card glass">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Eventos y comparación</h2>
|
||||
</div>
|
||||
<div class="placeholder">Cargando datos…</div>
|
||||
</div>
|
||||
|
||||
<!-- Count/percent view in a single card -->
|
||||
<div v-else-if="viewMode !== 'ratio'" class="card glass">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Eventos y comparación</h2>
|
||||
<div v-if="filtersCollapsed && (activeFilters?.hasFilters || selectedPlayerUuid)" class="active-filters-summary">
|
||||
<span class="filter-tag" v-if="activeFilters?.dataSource !== 'aggregated'">
|
||||
{{ activeFilters?.dataSource === 'active-rooms' ? '🔴 Tiempo Real' : '📁 Agregado' }}
|
||||
@@ -20,10 +29,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loading" class="placeholder">Cargando datos…</div>
|
||||
|
||||
<!-- Regular bars view -->
|
||||
<div v-else-if="viewMode !== 'ratio'" class="bars big">
|
||||
<div class="bars big">
|
||||
<div
|
||||
v-for="eventType in eventTypes"
|
||||
:key="eventType"
|
||||
@@ -64,54 +70,51 @@
|
||||
</div>
|
||||
<div class="hint small">Basado en mensajes disponibles por sala. Click jugador para comparar.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ratio bars view -->
|
||||
<div v-else class="ratio-bars">
|
||||
<div
|
||||
v-for="group in ratioData"
|
||||
:key="group.name"
|
||||
v-show="group.total > 0"
|
||||
class="ratio-group"
|
||||
:class="{ highlight: highlighted === group.name }"
|
||||
@mouseenter="highlighted = group.name"
|
||||
@mouseleave="highlighted = ''"
|
||||
>
|
||||
<!-- Group Header -->
|
||||
<div class="ratio-group-header">
|
||||
<h3 class="group-title">{{ group.name }}</h3>
|
||||
<span class="group-total">{{ group.total }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Ratio Bar -->
|
||||
<div class="ratio-bar">
|
||||
<!-- Ratio view: one card per group -->
|
||||
<div v-else class="ratio-cards">
|
||||
<div
|
||||
v-for="group in ratioData"
|
||||
:key="group.name"
|
||||
v-show="group.total > 0"
|
||||
class="card glass ratio-card"
|
||||
:class="{ highlight: highlighted === group.name }"
|
||||
@mouseenter="highlighted = group.name"
|
||||
@mouseleave="highlighted = ''"
|
||||
>
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ group.name }}</h3>
|
||||
<span class="group-total">{{ group.total }}</span>
|
||||
</div>
|
||||
<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
|
||||
v-for="(action, actionIndex) in group.actions"
|
||||
:key="action"
|
||||
class="ratio-segment"
|
||||
class="ratio-event-chip"
|
||||
v-if="group.percentages[actionIndex] > 5"
|
||||
:style="{
|
||||
width: group.percentages[actionIndex] + '%',
|
||||
background: eventStyles[action]?.gradient || 'linear-gradient(90deg, #94a3b8, #64748b)'
|
||||
background: getEventChipBg(action),
|
||||
borderColor: getEventBorderColor(action)
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
<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 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 class="hint small">
|
||||
{{ selectedPlayerUuid ? 'Ratios del jugador seleccionado' : 'Ratios globales' }}.
|
||||
Los segmentos muestran la proporción relativa dentro de cada categoría.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -303,7 +306,7 @@ function friendlyEventName(eventType: string): string {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.panel {
|
||||
.card {
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -312,15 +315,15 @@ function friendlyEventName(eventType: string): string {
|
||||
}
|
||||
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
border: 1px solid rgba(229, 231, 235, 0.9);
|
||||
box-shadow: 0 18px 50px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.6);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid rgba(229, 231, 235, 0.95);
|
||||
box-shadow: 0 18px 50px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.75);
|
||||
backdrop-filter: blur(18px) saturate(120%);
|
||||
-webkit-backdrop-filter: blur(18px) saturate(120%);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -328,12 +331,17 @@ function friendlyEventName(eventType: string): string {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
.card-title {
|
||||
margin: 0;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.ratio-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.active-filters-summary {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
@@ -677,6 +685,12 @@ function friendlyEventName(eventType: string): string {
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.card { padding: 10px 12px; }
|
||||
.card-header { margin-bottom: 8px; }
|
||||
.card-title { font-size: 16px; }
|
||||
.bars.big .bar-row { min-height: 28px; }
|
||||
.ratio-group { min-height: 90px; }
|
||||
.ratio-bar { height: 42px; }
|
||||
.bar-chip {
|
||||
min-width: 120px;
|
||||
padding: 4px 8px;
|
||||
@@ -699,6 +713,42 @@ function friendlyEventName(eventType: string): string {
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.ratio-cards {
|
||||
gap: 8px;
|
||||
}
|
||||
.card {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
.bars.big .bar-row { min-height: 24px; }
|
||||
|
||||
.group-total {
|
||||
font-size: 12px;
|
||||
padding: 3px 8px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.ratio-bar {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.ratio-group {
|
||||
min-height: 74px;
|
||||
}
|
||||
|
||||
.ratio-group-header {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.bar-chip {
|
||||
min-width: 100px;
|
||||
padding: 3px 6px;
|
||||
@@ -737,6 +787,15 @@ function friendlyEventName(eventType: string): string {
|
||||
font-size: 8px;
|
||||
padding: 1px 3px;
|
||||
}
|
||||
|
||||
.filter-tag {
|
||||
font-size: 10px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
.hint.small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
@@ -3,18 +3,17 @@
|
||||
<div class="header glass light">
|
||||
<div class="header-left">
|
||||
<button class="btn-back" @click="goHome" title="Volver al inicio">
|
||||
← Inicio
|
||||
← <span class="label">Inicio</span>
|
||||
</button>
|
||||
<h1>📈 Leaderboard</h1>
|
||||
<h1><span class="emoji">📈</span> Leaderboard</h1>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn-collapse" @click="filtersCollapsed = !filtersCollapsed" :title="filtersCollapsed ? 'Mostrar filtros' : 'Ocultar filtros'">
|
||||
<span class="collapse-icon" :class="{ rotated: filtersCollapsed }">▼</span>
|
||||
<span class="collapse-text">{{ filtersCollapsed ? 'Mostrar filtros' : 'Ocultar filtros' }}</span>
|
||||
</button>
|
||||
<button class="btn" @click="refreshAll" :disabled="loading">{{ loading ? 'Actualizando…' : 'Actualizar' }}</button>
|
||||
<button class="btn" @click="downloadJSON" :disabled="loading" title="Descargar datos actuales como JSON">
|
||||
📊 JSON
|
||||
📊 <span class="label">JSON</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -924,15 +923,15 @@ function downloadJSON() {
|
||||
/* Light theme aligned with other pages (UUID selector, lobby, game) */
|
||||
.leaderboard.light { min-height: 100vh; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color:#0f172a; display:flex; flex-direction:column; }
|
||||
|
||||
.glass.light { background: rgba(255, 255, 255, 0.78); border: 1px solid rgba(229, 231, 235, 0.9); box-shadow: 0 18px 50px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.6); backdrop-filter: blur(18px) saturate(120%); -webkit-backdrop-filter: blur(18px) saturate(120%); border-radius: 16px; }
|
||||
.glass.light { background: rgba(255, 255, 255, 0.92); border: 1px solid rgba(229, 231, 235, 0.95); box-shadow: 0 18px 50px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.75); backdrop-filter: blur(18px) saturate(120%); -webkit-backdrop-filter: blur(18px) saturate(120%); border-radius: 16px; }
|
||||
|
||||
.header { display:flex; align-items:center; justify-content:space-between; padding: 12px 14px; margin-bottom: 14px; }
|
||||
.header h1 { margin: 0; }
|
||||
.header-left { display:flex; align-items:center; gap: 16px; }
|
||||
.btn-back { background:#667eea; color:#fff; border:none; border-radius:8px; padding:8px 14px; font-weight:600; cursor:pointer; transition: all 0.3s ease; font-size: 14px; }
|
||||
.header { display:flex; align-items:center; justify-content:space-between; gap: 8px; flex-wrap: wrap; padding: 8px 10px; margin-bottom: 10px; }
|
||||
.header h1 { margin: 0; font-size: 18px; line-height: 1.2; flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.header-left { display:flex; align-items:center; gap: 10px; flex: 1 1 auto; min-width: 200px; }
|
||||
.btn-back { background:#667eea; color:#fff; border:none; border-radius:6px; padding:6px 10px; font-weight:600; cursor:pointer; transition: all 0.3s ease; font-size: 12px; }
|
||||
.btn-back:hover { background:#5b6bda; transform: translateY(-1px); box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); }
|
||||
.actions { display:flex; gap: 8px; }
|
||||
.actions .btn { background:#667eea; color:#fff; border:none; border-radius:10px; padding:8px 12px; font-weight:800; cursor:pointer; }
|
||||
.actions { display:flex; gap: 6px; flex-wrap: wrap; align-items: center; justify-content: flex-end; flex: 1 1 280px; }
|
||||
.actions .btn { background:#667eea; color:#fff; border:none; border-radius:6px; padding:4px 8px; font-weight:800; font-size: 11px; cursor:pointer; }
|
||||
.actions .btn.toggle { background:#eef2ff; color:#3949ab; border:1px solid #c7d2fe; }
|
||||
.actions .btn.toggle.active { background:#3949ab; color:#fff; border-color:#2e3f9a; }
|
||||
|
||||
@@ -1082,14 +1081,14 @@ function downloadJSON() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
padding: 5px 8px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
border-radius: 6px;
|
||||
background: #eef2ff;
|
||||
color: #3949ab;
|
||||
border: 1px solid #c7d2fe;
|
||||
font-weight: 800;
|
||||
font-size: 13px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
@@ -1101,10 +1100,7 @@ function downloadJSON() {
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
transition: transform 0.3s ease;
|
||||
font-size: 12px;
|
||||
}
|
||||
.collapse-icon { transition: transform 0.3s ease; font-size: 11px; }
|
||||
|
||||
.collapse-icon.rotated {
|
||||
transform: rotate(-90deg);
|
||||
@@ -1138,6 +1134,11 @@ function downloadJSON() {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header { padding: 6px 8px; margin-bottom: 8px; }
|
||||
.header h1 { font-size: 16px; }
|
||||
.btn-back { padding: 5px 8px; font-size: 11px; border-radius: 6px; }
|
||||
.actions { gap: 6px; }
|
||||
.actions .btn { padding: 4px 6px; font-size: 10px; border-radius: 5px; }
|
||||
.search-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
@@ -1155,18 +1156,35 @@ function downloadJSON() {
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.header { gap: 6px; }
|
||||
.header-left { min-width: 140px; }
|
||||
.actions { justify-content: flex-start; }
|
||||
.btn-collapse .collapse-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-collapse {
|
||||
padding: 8px;
|
||||
min-width: 40px;
|
||||
padding: 4px 6px;
|
||||
min-width: 34px;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.header h1 { font-size: 14px; }
|
||||
.btn-back { padding: 4px 6px; font-size: 10px; }
|
||||
.actions { gap: 4px; }
|
||||
.actions .btn { padding: 3px 5px; font-size: 9px; border-radius: 4px; }
|
||||
.btn-collapse { padding: 3px 5px; min-width: 30px; font-size: 9px; border-radius: 4px; }
|
||||
/* Ultra-compact: single-line header on very small screens */
|
||||
.header { flex-wrap: nowrap; padding: 4px 6px; }
|
||||
.header-left { flex: 0 1 auto; }
|
||||
.btn-back .label { display: none; }
|
||||
.actions .btn .label { display: none; }
|
||||
.header h1 { font-size: 13px; }
|
||||
.header h1 .emoji { display: none; }
|
||||
.player-chips {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user