feat: Ergebnis-Banner, Dev-Simulations-Panel & Spiele-Reset
- MatchCard: farbiger Ergebnis-Banner (Exakt/Tendenz/Falsch) ersetzt tipRow, passender Card-Glow je Ergebnis; Lucide-Icons (lucide-react) - MatchCard: Ändern-Button links, vertikal mittig zur Tipp-Box ausgerichtet - DevPanel: Simulationsmodus mit User-Switcher, Zeit- & Status-Presets - DevPanel: Reset-Section mit "Spiel zurücksetzen", "Alle Spiele" und "Tipps löschen" (3 Buttons, farblich differenziert) - Backend: dev.ts mit set-time, set-status, reset-tips, reset-match - reset-match stellt Original-Datum wieder her (original_utc_date Spalte) - set-time sichert Original-Datum per COALESCE beim ersten Aufruf - Supabase Migration: original_utc_date TIMESTAMPTZ zu matches hinzugefügt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { Check, TrendingUp, X } from 'lucide-react';
|
||||
import { Match } from '../api/client';
|
||||
import styles from './MatchCard.module.css';
|
||||
|
||||
@@ -47,9 +48,19 @@ export default function MatchCard({ match, onTip }: Props) {
|
||||
const isFinished = match.status === 'FINISHED';
|
||||
const isLive = match.status === 'IN_PLAY' || match.status === 'PAUSED';
|
||||
const hasTip = !!match.userTip;
|
||||
const points = match.userTip?.points ?? null;
|
||||
const resultClass =
|
||||
points === 3 ? styles.exact :
|
||||
points === 1 ? styles.tendency :
|
||||
(points === 0 && isFinished) ? styles.wrong : '';
|
||||
|
||||
const glowClass =
|
||||
isFinished && points === 3 ? styles.glowExact :
|
||||
isFinished && points === 1 ? styles.glowTendency :
|
||||
isFinished && points === 0 ? styles.glowWrong : '';
|
||||
|
||||
return (
|
||||
<div className={`card ${styles.card} ${isLive ? styles.live : ''}`}>
|
||||
<div className={`card ${styles.card} ${isLive ? styles.live : ''} ${glowClass}`}>
|
||||
|
||||
{/* Top row: Status / Kickoff / Badges */}
|
||||
<div className={styles.topRow}>
|
||||
@@ -92,27 +103,56 @@ export default function MatchCard({ match, onTip }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tipp area */}
|
||||
<div className={styles.tipRow}>
|
||||
{/* Tipp area — wird zum farbigen Banner wenn Punkte ausgewertet */}
|
||||
<div className={`${styles.tipRow} ${hasTip && points !== null ? `${styles.resultBanner} ${resultClass}` : ''}`}>
|
||||
{hasTip ? (
|
||||
<div className={styles.tipDisplay}>
|
||||
<span className={styles.tipLabel}>Dein Tipp</span>
|
||||
<span className={styles.tipScore}>
|
||||
{match.userTip!.home} : {match.userTip!.away}
|
||||
</span>
|
||||
{match.userTip!.points !== null && (
|
||||
<span className={`${styles.points} ${
|
||||
match.userTip!.points === 3 ? styles.exact :
|
||||
match.userTip!.points === 1 ? styles.tendency : styles.wrong
|
||||
}`}>
|
||||
{match.userTip!.points === 3 ? '🎯 3 Punkte' :
|
||||
match.userTip!.points === 1 ? '✓ 1 Punkt' : '✗ 0 Punkte'}
|
||||
</span>
|
||||
)}
|
||||
{match.tippable && (
|
||||
<button className={styles.editBtn} onClick={onTip}>Ändern</button>
|
||||
)}
|
||||
</div>
|
||||
points !== null ? (
|
||||
/* ── Auswertungs-Banner ── */
|
||||
<div className={styles.tipDisplay}>
|
||||
{/* Links: Icon + Ergebnis-Label nebeneinander, zentriert zur Tippbox */}
|
||||
<div className={`${styles.tipLeft} ${styles.bannerLeft}`}>
|
||||
<span className={styles.resultIcon}>
|
||||
{points === 3 ? <Check size={14} strokeWidth={3} /> :
|
||||
points === 1 ? <TrendingUp size={14} strokeWidth={2.5} /> :
|
||||
<X size={14} strokeWidth={3} />}
|
||||
</span>
|
||||
<span className={styles.resultLabel}>
|
||||
{points === 3 ? 'Exakt' : points === 1 ? 'Tendenz' : 'Falsch'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Mitte: nur Score, kein Label */}
|
||||
<div className={styles.tipCenter}>
|
||||
<span className={styles.tipScoreBanner}>
|
||||
{match.userTip!.home} : {match.userTip!.away}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Rechts: Punkte */}
|
||||
<div className={styles.tipRight}>
|
||||
<span className={styles.resultPoints}>
|
||||
{points === 0 ? '0 Pkt.' : `+${points} Pkt.`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* ── Tipp vorhanden, noch nicht ausgewertet ── */
|
||||
<div className={styles.tipDisplay}>
|
||||
<div className={styles.tipLeft}>
|
||||
{match.tippable && (
|
||||
<button className={styles.editBtn} onClick={onTip}>Ändern</button>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.tipCenter}>
|
||||
{/* Label nur zeigen wenn kein Ändern-Button da ist, sonst fluchtet der Button nicht */}
|
||||
{!match.tippable && <span className={styles.tipLabel}>DEIN TIPP</span>}
|
||||
<span className={styles.tipScore}>
|
||||
{match.userTip!.home} : {match.userTip!.away}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.tipRight} />
|
||||
</div>
|
||||
)
|
||||
) : match.tippable ? (
|
||||
<button className={`btn-primary ${styles.tipBtn}`} onClick={onTip}>
|
||||
Tipp abgeben
|
||||
|
||||
Reference in New Issue
Block a user