Update result & scoreboard UI

This commit is contained in:
Mathias Wagner 2025-03-01 15:14:45 +01:00
parent c31b7e35b6
commit e633a5e004
3 changed files with 305 additions and 41 deletions

View File

@ -84,7 +84,17 @@ export const Game = () => {
}, },
"round-results": (results) => { "round-results": (results) => {
setPhase("results"); setPhase("results");
setScores(results.scores); const scoresWithNames = {};
Object.entries(results.scores).forEach(([userId, score]) => {
const user = connectedUsers.find(u => u.id === userId);
scoresWithNames[userId] = {
score: score,
name: user?.name || results.playerNames?.[userId] || "Player"
};
});
setScores(scoresWithNames);
if (!currentSong) { if (!currentSong) {
setCurrentSong(results.selectedSong); setCurrentSong(results.selectedSong);
} }
@ -149,7 +159,7 @@ export const Game = () => {
} }
return () => cleanupFunctions.forEach(cleanup => cleanup()); return () => cleanupFunctions.forEach(cleanup => cleanup());
}, [socket, on, send, role, currentSong, phase, connected]); }, [socket, on, send, role, currentSong, phase, connected, connectedUsers]);
useEffect(() => { useEffect(() => {
if (timerIntervalRef.current) clearInterval(timerIntervalRef.current); if (timerIntervalRef.current) clearInterval(timerIntervalRef.current);
@ -435,18 +445,24 @@ export const Game = () => {
<div className="scoreboard"> <div className="scoreboard">
<h4>Punktestand</h4> <h4>Punktestand</h4>
<div className="scores"> <div className="scores">
{Object.entries(scores).map(([userId, score]) => { {Object.entries(scores).map(([userId, scoreData]) => {
const user = connectedUsers.find(u => u.id === userId) || { name: 'Unbekannt' }; const isCurrentUser = userId === socket?.id;
const isHost = connectedUsers.find(u => u.id === "user1")?.id === userId;
const playerName = typeof scoreData === 'object' ?
scoreData.name :
(connectedUsers.find(u => u.id === userId)?.name || "Player");
const playerScore = typeof scoreData === 'object' ? scoreData.score : scoreData;
return ( return (
<div key={userId} className="score-entry"> <div key={userId} className={`score-entry ${isCurrentUser ? 'current-user' : ''}`}>
<span className="player-name"> <span className="player-name">
{user.id === (socket?.id || "you") && "👉 "} {isCurrentUser && "👉 "}
{user.name} {playerName}
{userId === connectedUsers.find(u => u.id === "user1")?.id && ( {isHost && (
<FontAwesomeIcon icon={faCrown} className="host-icon" /> <FontAwesomeIcon icon={faCrown} className="host-icon" />
)} )}
</span> </span>
<span className="player-score">{score}</span> <span className="player-score">{playerScore}</span>
</div> </div>
); );
})} })}

View File

@ -1,6 +1,6 @@
@import "@/common/styles/colors" @import "@/common/styles/colors"
// Main layout improvements
.game-page .game-page
display: flex display: flex
flex-direction: column flex-direction: column
@ -40,7 +40,6 @@
min-width: 0 min-width: 0
margin-right: 20px margin-right: 20px
// Redesigned chat panel
.chat-panel .chat-panel
width: 280px width: 280px
height: 100% height: 100%
@ -162,7 +161,6 @@
&:active &:active
transform: translateY(0) transform: translateY(0)
// Game header improvements
.game-header .game-header
display: flex display: flex
justify-content: space-between justify-content: space-between
@ -197,7 +195,6 @@
margin-right: 8px margin-right: 8px
color: $yellow color: $yellow
// Improved button styles
.submit-guess-button, .next-round-button, button:not(.send-button) .submit-guess-button, .next-round-button, button:not(.send-button)
background: linear-gradient(135deg, $purple, $blue) background: linear-gradient(135deg, $purple, $blue)
color: #fff color: #fff
@ -244,7 +241,6 @@
cursor: not-allowed cursor: not-allowed
background: linear-gradient(135deg, #777, #555) background: linear-gradient(135deg, #777, #555)
// Content component improvements
.waiting-phase .waiting-phase
text-align: center text-align: center
padding: 100px 0 padding: 100px 0
@ -274,45 +270,29 @@
display: flex display: flex
flex-direction: row flex-direction: row
align-items: center align-items: center
background: rgba(255, 255, 255, 0.08) background: rgba(40, 40, 60, 0.85)
padding: 25px padding: 20px
border-radius: 20px border-radius: 15px
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3) box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4)
backdrop-filter: blur(10px) backdrop-filter: blur(10px)
background: rgba(255, 255, 255, 0.08) border: 1px solid rgba(255, 255, 255, 0.15)
padding: 25px
border-radius: 20px
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3)
backdrop-filter: blur(10px)
border: 1px solid rgba(255, 255, 255, 0.2)
max-width: 500px max-width: 500px
margin: 20px auto margin: 20px auto
transition: all 0.3s ease transition: all 0.3s ease
&:hover &:hover
transform: translateY(-5px) transform: translateY(-5px)
border-color: rgba(255, 255, 255, 0.4) border-color: rgba(255, 255, 255, 0.3)
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4), 0 0 30px rgba(255, 255, 255, 0.1) box-shadow: 0 15px 40px rgba(0, 0, 0, 0.5), 0 0 30px rgba(255, 255, 255, 0.1)
img img
width: 120px width: 120px
height: 120px height: 120px
object-fit: cover object-fit: cover
border-radius: 15px border-radius: 8px
margin-right: 20px margin-right: 20px
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5) box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5)
border: 2px solid rgba(255, 255, 255, 0.2)
.song-info
text-align: left
.song-names
font-size: 24px
color: $white
margin-bottom: 10px
.song-description
font-size: 16px
color: rgba(255, 255, 255, 0.7)
.music-controls .music-controls
margin-top: 30px margin-top: 30px
@ -800,3 +780,265 @@
width: 100% width: 100%
height: auto height: auto
aspect-ratio: 16 / 9 aspect-ratio: 16 / 9
.results-phase
display: flex
flex-direction: column
align-items: center
width: 100%
h3
margin-bottom: 30px
font-size: 28px
color: $white
text-shadow: 0 2px 15px rgba(255, 255, 255, 0.3)
.round-results
width: 100%
max-width: 600px
display: flex
flex-direction: column
align-items: center
.composer-results, .guesser-results
width: 100%
margin-bottom: 20px
text-align: center
p
font-size: 18px
margin-bottom: 15px
color: $white
.correct-song
margin-top: 10px
position: relative
overflow: visible
z-index: 5
&:before
content: ""
position: absolute
top: -30px
left: -30px
right: -30px
bottom: -30px
background: rgba(20, 20, 40, 0.2)
border-radius: 40px
filter: blur(40px)
z-index: -1
.song-card.highlight
position: relative
transform: scale(1.05)
background: linear-gradient(135deg, rgba(40, 40, 60, 0.9), rgba(30, 30, 50, 0.95))
border: 2px solid $yellow
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4), 0 0 50px rgba(255, 255, 0, 0.2)
backdrop-filter: blur(30px)
padding: 25px
transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)
-webkit-backdrop-filter: blur(30px)
background: rgba(35, 35, 50, 0.5)
border-radius: 18px
&:hover
transform: translateY(-8px) scale(1.08)
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.5), 0 0 60px rgba(255, 255, 0, 0.3), inset 0 0 50px rgba(255, 255, 255, 0.08)
&:after
content: ""
position: absolute
top: -15px
right: -15px
width: 40px
height: 40px
background: linear-gradient(135deg, $yellow, darken($yellow, 15%))
color: #000
border-radius: 50%
border: 3px solid rgba(0, 0, 0, 0.5)
display: flex
align-items: center
justify-content: center
font-size: 20px
font-weight: bold
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4), 0 0 20px rgba(255, 255, 0, 0.5)
animation: pulse-highlight 2s infinite alternate ease-in-out
&:before
content: ""
position: absolute
inset: 0
background: linear-gradient(135deg, rgba(80, 80, 120, 0.3), rgba(30, 30, 60, 0.8))
border-radius: 18px
z-index: -1
opacity: 0.8
img
width: 130px
height: 130px
border-radius: 14px
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.6)
border: 3px solid rgba(255, 255, 255, 0.3)
transform: rotate(-2deg)
transition: all 0.5s ease, border-radius 0.3s ease
&:hover
transform: rotate(0deg) scale(1.05)
border-color: rgba(255, 255, 0, 0.6)
border-radius: 12px
.song-info
margin-left: 25px
.song-names
font-size: 26px
font-weight: bold
color: $white
text-shadow: 0 2px 10px rgba(255, 255, 255, 0.5)
margin-bottom: 10px
background: linear-gradient(90deg, $white, rgba(255,255,255,0.7))
-webkit-background-clip: text
background-clip: text
-webkit-text-fill-color: transparent
.song-description
font-size: 18px
color: rgba(255, 255, 255, 0.8)
text-shadow: 0 1px 5px rgba(0, 0, 0, 0.7)
.guess-result
margin: 20px 0
padding: 15px 20px
border-radius: 16px
font-size: 18px
font-weight: bold
animation: bounce-in 0.5s ease-out
&.correct
background: linear-gradient(135deg, rgba(50, 200, 100, 0.8), rgba(40, 160, 80, 0.8))
color: white
box-shadow: 0 4px 20px rgba(50, 200, 100, 0.4)
&.incorrect
background: linear-gradient(135deg, rgba(220, 60, 60, 0.8), rgba(180, 40, 40, 0.8))
color: white
box-shadow: 0 4px 20px rgba(220, 60, 60, 0.4)
.scoreboard
width: 100%
background: rgba(40, 40, 60, 0.6)
backdrop-filter: blur(10px)
border-radius: 20px
padding: 20px
margin-bottom: 20px
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3)
border: 1px solid rgba(255, 255, 255, 0.1)
margin-top: 5px
h4
text-align: center
font-size: 22px
color: $white
margin-top: 10px
margin-bottom: 10px
padding-bottom: 8px
.scores
display: flex
flex-direction: column
gap: 8px
.score-entry
display: flex
justify-content: space-between
align-items: center
padding: 12px 15px
border-radius: 14px
background: rgba(255, 255, 255, 0.1)
transition: all 0.2s ease
&:hover
background: rgba(255, 255, 255, 0.15)
transform: translateY(-2px)
&.current-user
background: rgba(102, 204, 255, 0.2)
border-left: 3px solid $blue
.player-name
display: flex
align-items: center
font-weight: 500
color: $white
font-size: 16px
.host-icon
color: $yellow
margin-left: 8px
filter: drop-shadow(0 0 5px rgba(255, 255, 0, 0.5))
.player-score
background: rgba(255, 255, 255, 0.2)
padding: 5px 12px
border-radius: 20px
font-weight: bold
font-size: 18px
color: $yellow
min-width: 40px
text-align: center
position: relative
overflow: hidden
&:after
content: ""
position: absolute
top: -50%
left: -50%
width: 200%
height: 200%
background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%)
opacity: 0
transition: opacity 0.5s ease
&.highlighted
animation: score-highlight 1s ease
.next-round-button
margin-top: 10px
padding: 15px 30px
font-size: 18px
.waiting-message
margin-top: 20px
font-style: italic
color: rgba(255, 255, 255, 0.7)
@keyframes score-highlight
0%
transform: scale(1)
background: rgba(255, 255, 0, 0.8)
color: #000
50%
transform: scale(1.2)
100%
transform: scale(1)
background: rgba(255, 255, 255, 0.2)
color: $yellow
@keyframes bounce-in
0%
opacity: 0
transform: scale(0.8)
50%
transform: scale(1.05)
100%
opacity: 1
transform: scale(1)
@keyframes pulse-highlight
0%, 100%
transform: scale(1)
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4), 0 0 20px rgba(255, 255, 0, 0.3)
50%
transform: scale(1.1)
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5), 0 0 30px rgba(255, 255, 0, 0.6)

View File

@ -187,11 +187,17 @@ const getRoundResults = (roomId) => {
const gameState = gameStates[roomId]; const gameState = gameStates[roomId];
if (!gameState) return {round: 0, scores: {}, guessResults: {}, selectedSong: null}; if (!gameState) return {round: 0, scores: {}, guessResults: {}, selectedSong: null};
const playerNames = {};
Object.keys(gameState.scores).forEach(userId => {
playerNames[userId] = roomController.getUserName(userId) || "Player";
});
return { return {
round: gameState.round, round: gameState.round,
selectedSong: gameState.selectedSong, selectedSong: gameState.selectedSong,
guessResults: gameState.guessResults, guessResults: gameState.guessResults,
scores: gameState.scores scores: gameState.scores,
playerNames: playerNames
}; };
}; };