From 6f2f7a4e12617317840e04415d3aa04793ed64f2 Mon Sep 17 00:00:00 2001 From: josedario87 Date: Thu, 28 Aug 2025 18:31:11 -0600 Subject: [PATCH] ux mejoras --- client/src/views/Lobby.vue | 11 ++- client/src/views/games/GameEndModal.vue | 117 +++++++++++------------- 2 files changed, 63 insertions(+), 65 deletions(-) diff --git a/client/src/views/Lobby.vue b/client/src/views/Lobby.vue index bc22dfb..ffd44ce 100644 --- a/client/src/views/Lobby.vue +++ b/client/src/views/Lobby.vue @@ -41,6 +41,12 @@ placeholder="Ingresa tu nombre" class="name-input" maxlength="20" + autocomplete="off" + autocapitalize="off" + autocorrect="off" + spellcheck="false" + name="sg_player_name" + inputmode="text" /> @@ -182,7 +188,7 @@ onMounted(async () => { const room = await colyseusService.joinLobby(); colorInput.value = colyseusService.playerColor.value || '#667eea'; - inputName.value = colyseusService.playerName.value || ''; + // Mantener el input vacío por defecto; no precargar nombres previos let resumed = false; const guardResume = () => { if (resumed) return true; resumed = true; return false; }; @@ -461,7 +467,8 @@ function dismissBanner() { function startEditName() { editingName.value = true; - inputName.value = colyseusService.playerName.value || ''; + // Siempre iniciar la edición con el campo vacío (sin sugerencias) + inputName.value = ''; nextTick(() => { nameInputRef.value?.focus(); }); diff --git a/client/src/views/games/GameEndModal.vue b/client/src/views/games/GameEndModal.vue index 34f5f63..3631ee7 100644 --- a/client/src/views/games/GameEndModal.vue +++ b/client/src/views/games/GameEndModal.vue @@ -27,26 +27,19 @@
Ronda {{ round }} de {{ totalRounds }} — aún quedan rondas por jugar. La siguiente comenzará en breve.
-
Se cerrará en {{ remainingSeconds }}s
+
Se cerrará en {{ remainingSeconds }}s
@@ -100,10 +93,7 @@ function stopTimer() { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } } -watch(() => props.visible, (v) => { - if (v) startTimer(); - else stopTimer(); -}, { immediate: true }); +// (moved below isFinal definition) onBeforeUnmount(() => { stopTimer(); }); @@ -111,28 +101,46 @@ const totalRounds = computed(() => Math.max(1, Number(props.totalRounds || 3))); const round = computed(() => Math.max(1, Number(props.round || 1))); const isFinal = computed(() => round.value >= totalRounds.value); -// Hidden admin unlock via 5 rapid clicks on the round info text +// Auto-cierre solo cuando no es final +watch([() => props.visible, () => isFinal.value], ([vis, final]) => { + if (vis && !final) startTimer(); + else stopTimer(); +}, { immediate: true }); + +// Hidden admin unlock via 5 rapid clicks on the control buttons const adminUnlocked = ref(false); const clickCount = ref(0); +const showHint = ref(false); let clickResetTimer: any = null; -function onRoundInfoClick() { +function onControlAttempt(kind: 'prev'|'next'|'restart') { + if (adminUnlocked.value) { + if (kind === 'prev') onPrev(); + else if (kind === 'next') onNext(); + else onRestart(); + return; + } + // Locked: count clicks and show hint + showHint.value = true; if (clickResetTimer) { clearTimeout(clickResetTimer); clickResetTimer = null; } clickCount.value += 1; if (clickCount.value >= 5) { adminUnlocked.value = true; + // Perform the action that was attempted on the unlocking click + if (kind === 'prev') onPrev(); + else if (kind === 'next') onNext(); + else onRestart(); } else { - // Small window to keep clicks "seguido" - clickResetTimer = setTimeout(() => { clickCount.value = 0; }, 1200); + // Small window to keep clicks consecutivos + clickResetTimer = setTimeout(() => { clickCount.value = 0; showHint.value = false; }, 1200); } } -const roundInfoTitle = computed(() => adminUnlocked.value ? 'Controles de variante desbloqueados' : `Clicks: ${clickCount.value}/5 para desbloquear`); - watch(() => props.visible, (v) => { if (v) { adminUnlocked.value = false; clickCount.value = 0; + showHint.value = false; if (clickResetTimer) { clearTimeout(clickResetTimer); clickResetTimer = null; } } }); @@ -199,46 +207,29 @@ function onRestart() { emit('restart-variant'); } .end-modal .score-row .role { font-size:12px; padding:2px 8px; border-radius:10px; background:#f0f0f0; color:#555; } .end-modal .score-row .role.P1, .end-modal .score-row .role.P2 { background: color-mix(in srgb, var(--primary) 15%, white); color: var(--primary); } -.end-modal .modal-actions { - margin: 16px 0; - display: flex; - gap: 12px; - justify-content: center; - align-items: center; -} -.end-modal .btn-next-variant, .end-modal .btn-prev-variant, .end-modal .btn-restart-variant { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - padding: 12px 16px; - border-radius: 8px; - font-size: 14px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); - min-width: 85px; -} -.end-modal .btn-prev-variant { - background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); - box-shadow: 0 4px 12px rgba(240, 147, 251, 0.3); -} -.end-modal .btn-restart-variant { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - box-shadow: 0 4px 12px rgba(79, 172, 254, 0.3); -} -.end-modal .btn-next-variant:hover { - transform: translateY(-2px); - box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4); -} -.end-modal .btn-prev-variant:hover { - transform: translateY(-2px); - box-shadow: 0 6px 16px rgba(240, 147, 251, 0.4); -} -.end-modal .btn-restart-variant:hover { - transform: translateY(-2px); - box-shadow: 0 6px 16px rgba(79, 172, 254, 0.4); +.end-modal .modal-actions { margin: 12px 0 8px; display: flex; gap: 6px; justify-content: center; align-items: center; flex-wrap: wrap; } + +/* Glassy compact buttons, aligned with home style */ +.end-modal .btn { + appearance: none; + background: linear-gradient(135deg, rgba(102,126,234,0.24) 0%, rgba(118,75,162,0.24) 100%); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255,255,255,0.55); + color: #243147; + padding: 6px 10px; + border-radius: 10px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease; + box-shadow: 0 6px 18px rgba(102,126,234,0.12); + min-width: 72px; + -webkit-tap-highlight-color: transparent; } +.end-modal .btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(118,75,162,0.16); } +.end-modal .btn:active { transform: translateY(0); box-shadow: 0 2px 8px rgba(0,0,0,0.12); } +.end-modal .btn:focus, .end-modal .btn:focus-visible { outline: none; } .end-modal .hint { font-size: 12px; color:#6b7280; text-align:right; } .round-info { margin: 10px 2px 2px; font-size: 13px; font-weight:600; color:#334155; text-align:center; } .round-info.clickable { cursor: pointer; user-select: none; }