feat: stronger visual scoring differentiation + streak fix

- Exakt cards: gold glow border, gold banner, trophy emoji, larger animated badge
- Tendency: green accent (was blue), clearer differentiation from Exakt
- Falsch: muted gray, reduced opacity — clearly "lost"
- Profile tip history: solid gold/green/gray badges with distinct borders
- Streak: remove utc_date <= NOW() filter so dev-finished matches count; handle PostgreSQL boolean serialization

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ronny
2026-04-12 09:31:15 +02:00
parent 1eaec75901
commit 8b7b31826a
4 changed files with 48 additions and 29 deletions
+2 -2
View File
@@ -53,13 +53,13 @@ router.get('/', async (req: Request, res: Response): Promise<void> => {
`SELECT CASE WHEN t.id IS NOT NULL THEN true ELSE false END AS has_tip `SELECT CASE WHEN t.id IS NOT NULL THEN true ELSE false END AS has_tip
FROM matches m FROM matches m
LEFT JOIN tips t ON t.match_id = m.id AND t.user_id = $1 LEFT JOIN tips t ON t.match_id = m.id AND t.user_id = $1
WHERE m.utc_date <= NOW() AND m.status IN ('FINISHED', 'IN_PLAY') WHERE m.status IN ('FINISHED', 'IN_PLAY')
ORDER BY m.utc_date DESC`, ORDER BY m.utc_date DESC`,
[userId] [userId]
); );
let streak = 0; let streak = 0;
for (const m of pastMatches) { for (const m of pastMatches) {
if (m.has_tip) streak++; if (m.has_tip === true || (m.has_tip as unknown) === 't' || (m.has_tip as unknown) === '1') streak++;
else break; else break;
} }
+30 -23
View File
@@ -341,43 +341,46 @@
/* Farb-Varianten Banner */ /* Farb-Varianten Banner */
.exact { .exact {
background: linear-gradient(90deg, rgba(52,211,153,0.18) 0%, rgba(52,211,153,0.08) 100%); background: linear-gradient(90deg, rgba(254,174,50,0.22) 0%, rgba(254,174,50,0.09) 100%);
color: #4ade80; color: #FECC4C;
border-top: 1px solid rgba(52,211,153,0.20); border-top: 1px solid rgba(254,174,50,0.30);
} }
.tendency { .tendency {
background: linear-gradient(90deg, rgba(75,183,248,0.18) 0%, rgba(75,183,248,0.08) 100%); background: linear-gradient(90deg, rgba(52,211,153,0.18) 0%, rgba(52,211,153,0.08) 100%);
color: var(--primary); color: #4ade80;
border-top: 1px solid rgba(75,183,248,0.20); border-top: 1px solid rgba(52,211,153,0.22);
} }
.wrong { .wrong {
background: linear-gradient(90deg, rgba(248,113,113,0.15) 0%, rgba(248,113,113,0.06) 100%); background: linear-gradient(90deg, rgba(148,163,184,0.12) 0%, rgba(148,163,184,0.05) 100%);
color: var(--error); color: var(--text-muted);
border-top: 1px solid rgba(248,113,113,0.18); border-top: 1px solid rgba(148,163,184,0.15);
} }
/* Card-Glow je Ergebnis */ /* Card-Glow je Ergebnis */
.glowExact { .glowExact {
box-shadow: box-shadow:
0 0 0 1px rgba(52,211,153,0.18), 0 0 0 1.5px rgba(254,174,50,0.40),
0 10px 30px rgba(52,211,153,0.07), 0 0 20px rgba(254,174,50,0.18),
inset 0 1px 0 rgba(255,255,255,0.07) !important; 0 10px 30px rgba(254,174,50,0.10),
inset 0 1px 0 rgba(255,255,255,0.09) !important;
border-color: rgba(254,174,50,0.35) !important;
} }
.glowTendency { .glowTendency {
box-shadow: box-shadow:
0 0 0 1px rgba(75,183,248,0.18), 0 0 0 1px rgba(52,211,153,0.28),
0 10px 30px rgba(75,183,248,0.07), 0 10px 30px rgba(52,211,153,0.10),
inset 0 1px 0 rgba(255,255,255,0.07) !important; inset 0 1px 0 rgba(255,255,255,0.07) !important;
} }
.glowWrong { .glowWrong {
box-shadow: box-shadow:
0 0 0 1px rgba(248,113,113,0.15), 0 0 0 1px rgba(148,163,184,0.15),
0 10px 30px rgba(248,113,113,0.05), 0 10px 20px rgba(0,0,0,0.08),
inset 0 1px 0 rgba(255,255,255,0.07) !important; inset 0 1px 0 rgba(255,255,255,0.05) !important;
opacity: 0.85;
} }
.editBtn { .editBtn {
@@ -493,19 +496,23 @@
} }
.pointsBadge_exact { .pointsBadge_exact {
background: linear-gradient(135deg, var(--gold), #FFD700); background: linear-gradient(135deg, #FEAE32, #FFD700, #FECC4C);
color: #1a1a1a; color: #1a1a1a;
animation: shimmer 2s ease-in-out; font-size: 0.9rem;
padding: 5px 12px;
box-shadow: 0 0 12px rgba(254,174,50,0.45);
animation: shimmer 2.5s ease-in-out infinite;
} }
.pointsBadge_tendency { .pointsBadge_tendency {
background: var(--success); background: linear-gradient(135deg, var(--success), #22c55e);
color: #1a1a1a; color: #0a2a1a;
} }
.pointsBadge_wrong { .pointsBadge_wrong {
background: var(--text-muted); background: var(--surface-high);
color: var(--bg-deep); color: var(--text-muted);
border: 1px solid var(--border-subtle);
} }
/* Missed label */ /* Missed label */
+1 -1
View File
@@ -182,7 +182,7 @@ export default function MatchCard({ match, onTip }: Props) {
<X size={14} strokeWidth={3} />} <X size={14} strokeWidth={3} />}
</span> </span>
<span className={styles.resultLabel}> <span className={styles.resultLabel}>
{points === 3 ? 'Exakt' : points === 1 ? 'Tendenz' : 'Falsch'} {points === 3 ? '🏆 Exakt' : points === 1 ? 'Tendenz' : 'Falsch'}
</span> </span>
</div> </div>
+15 -3
View File
@@ -186,9 +186,21 @@
color: var(--text-muted); color: var(--text-muted);
} }
.badgeExact { background: rgba(255,196,0,0.15); color: var(--gold); } .badgeExact {
.badgeTendency { background: rgba(34,197,94,0.15); color: var(--success); } background: linear-gradient(135deg, #FEAE32, #FFD700);
.badgeWrong { background: rgba(239,68,68,0.12); color: var(--error); } color: #1a1a1a;
box-shadow: 0 0 8px rgba(254,174,50,0.35);
}
.badgeTendency {
background: rgba(34,197,94,0.22);
color: #4ade80;
border: 1px solid rgba(34,197,94,0.30);
}
.badgeWrong {
background: var(--surface-high);
color: var(--text-muted);
border: 1px solid var(--border-subtle);
}
/* ── Fun stats ── */ /* ── Fun stats ── */
.funStats { display: flex; flex-direction: column; gap: 12px; } .funStats { display: flex; flex-direction: column; gap: 12px; }