style: badges as row above flags, centered over each team
Build & Deploy Tippspiel / build (push) Successful in 51s

Badge row uses same flex proportions as match row (flex:1 | spacer | flex:1)
so left badge centers over left flag, right badge over right flag.
Full team names restored (no more truncation from 5-column layout).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ronny
2026-04-12 14:18:22 +02:00
parent 39d9e7d2e2
commit 5a13a91d83
2 changed files with 45 additions and 57 deletions
+12 -12
View File
@@ -21,21 +21,21 @@
inset 0 1px 0 rgba(255,255,255,0.07) !important; inset 0 1px 0 rgba(255,255,255,0.07) !important;
} }
/* Badge columns — vertically centered to flags */ /* Badge row — same flex proportions as matchRow so badges align over flags */
.badgeLeft, .badgeRight { .badgeRow {
display: flex; display: flex;
align-items: center; align-items: center;
align-self: flex-start; margin-bottom: 8px;
height: 56px; /* match flag height */
min-width: 0;
} }
.badgeLeft { .badgeSlot {
justify-content: flex-start; flex: 1;
display: flex;
justify-content: center;
} }
.badgeRight { .badgeSpacer {
justify-content: flex-end; min-width: 60px; /* matches scoreBox width */
} }
.status { .status {
@@ -123,12 +123,12 @@
0 0 16px rgba(254, 174, 50, 0.25); 0 0 16px rgba(254, 174, 50, 0.25);
} }
/* Match block — badges + teams + time/score */ /* Match row — Teams + time/score */
.matchBlock { .matchRow {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
justify-content: center; justify-content: center;
gap: 6px; gap: 10px;
margin-bottom: 16px; margin-bottom: 16px;
} }
+32 -44
View File
@@ -34,16 +34,6 @@ function useCountdown(minutesUntilKickoff: number) {
return remaining; return remaining;
} }
const STATUS_LABELS: Record<string, string> = {
SCHEDULED: 'Geplant',
TIMED: 'Terminiert',
IN_PLAY: 'Live',
PAUSED: 'Pause',
FINISHED: 'Beendet',
POSTPONED: 'Verschoben',
CANCELLED: 'Abgesagt',
};
function formatKickoff(utcDate: string): string { function formatKickoff(utcDate: string): string {
return new Date(utcDate).toLocaleString('de-DE', { return new Date(utcDate).toLocaleString('de-DE', {
hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Berlin', hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Berlin',
@@ -84,24 +74,49 @@ export default function MatchCard({ match, onTip }: Props) {
return ( return (
<div className={`card ${styles.card} ${styles[`card_${state}`]} ${isLive ? styles.live : ''} ${glowClass}`}> <div className={`card ${styles.card} ${styles[`card_${state}`]} ${isLive ? styles.live : ''} ${glowClass}`}>
{/* Main layout: badges + teams + time/score in one block */} {/* Badge row — centered above each flag */}
<div className={styles.matchBlock}> <div className={styles.badgeRow}>
{/* Left badge — group */} <span className={styles.badgeSlot}>
<div className={styles.badgeLeft}>
{match.group && ( {match.group && (
<span className={styles.group}> <span className={styles.group}>
{match.group.replace('GROUP_', 'Gr. ')} {match.group.replace('GROUP_', 'Gruppe ')}
</span> </span>
)} )}
</span>
<span className={styles.badgeSpacer} />
<span className={styles.badgeSlot}>
{isLive && (
<span className={styles.liveBadge}>
<span className={styles.liveDot} />
LIVE
</span>
)}
{isFinished && (
<span className={styles.finishedBadge}>Beendet</span>
)}
{(state === 'open' || state === 'tipped') && match.tippable && (
<span className={`${styles.countdownBadge} ${remainingMins < 60 ? styles.countdownUrgent : ''}`}>
{match.minutesUntilKickoff < 60
? `Noch ${remainingMins} Min!`
: (() => {
const h = Math.floor(match.minutesUntilKickoff / 60);
if (h < 24) return `in ${h}h`;
const d = Math.floor(h / 24);
return `in ${d} Tag${d > 1 ? 'en' : ''}`;
})()
}
</span>
)}
</span>
</div> </div>
{/* Home team */} {/* Teams + Center (time or score) */}
<div className={styles.matchRow}>
<div className={styles.teamHome}> <div className={styles.teamHome}>
<FlagBox crest={match.homeTeam.crest} name={match.homeTeam.name} /> <FlagBox crest={match.homeTeam.crest} name={match.homeTeam.name} />
<span className={styles.teamName}>{match.homeTeam.shortName}</span> <span className={styles.teamName}>{match.homeTeam.shortName}</span>
</div> </div>
{/* Center: LED time or Score */}
<div className={styles.scoreBox}> <div className={styles.scoreBox}>
{isFinished || isLive ? ( {isFinished || isLive ? (
<span className={`${styles.score} ${isLive ? styles.scoreLive : ''}`}> <span className={`${styles.score} ${isLive ? styles.scoreLive : ''}`}>
@@ -112,37 +127,10 @@ export default function MatchCard({ match, onTip }: Props) {
)} )}
</div> </div>
{/* Away team */}
<div className={styles.teamAway}> <div className={styles.teamAway}>
<FlagBox crest={match.awayTeam.crest} name={match.awayTeam.name} /> <FlagBox crest={match.awayTeam.crest} name={match.awayTeam.name} />
<span className={styles.teamName}>{match.awayTeam.shortName}</span> <span className={styles.teamName}>{match.awayTeam.shortName}</span>
</div> </div>
{/* Right badge — status/countdown */}
<div className={styles.badgeRight}>
{isLive && (
<span className={styles.liveBadge}>
<span className={styles.liveDot} />
LIVE
</span>
)}
{isFinished && (
<span className={styles.finishedBadge}>{STATUS_LABELS[match.status] ?? match.status}</span>
)}
{(state === 'open' || state === 'tipped') && match.tippable && (
<span className={`${styles.countdownBadge} ${remainingMins < 60 ? styles.countdownUrgent : ''}`}>
{match.minutesUntilKickoff < 60
? `${remainingMins}m`
: (() => {
const h = Math.floor(match.minutesUntilKickoff / 60);
if (h < 24) return `${h}h`;
const d = Math.floor(h / 24);
return `${d}d`;
})()
}
</span>
)}
</div>
</div> </div>
{/* Tipp area — wird zum farbigen Banner wenn Punkte ausgewertet */} {/* Tipp area — wird zum farbigen Banner wenn Punkte ausgewertet */}