feat: premium achievement badges with Material Symbols icons
Backend: - New /api/achievements endpoint calculating 6 badges: Scharfschütze, Serien-Tipper, Tabellenführer, Frühtipper, Globetrotter, Diamant - Each with progress tracking (current/target) Frontend: - AchievementBadge component with Stitch-inspired design - Material Symbols Outlined font (filled icons) - Unlocked: colored icon with glow + drop-shadow, rank label - Locked: grayscale, lock overlay, progress bar - ProfilePage: real badges replacing emoji placeholders - Progress bar showing X/6 collected - Mobile: 2-col grid, Desktop: 6-col grid Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { Achievement } from '../api/client';
|
||||
import styles from './AchievementBadge.module.css';
|
||||
|
||||
interface Props {
|
||||
achievement: Achievement;
|
||||
}
|
||||
|
||||
export default function AchievementBadge({ achievement }: Props) {
|
||||
const { name, description, icon, color, rankLabel, unlocked, current, target } = achievement;
|
||||
|
||||
return (
|
||||
<div className={`${styles.badge} ${unlocked ? styles.unlocked : styles.locked}`}>
|
||||
{/* Glow background for unlocked */}
|
||||
{unlocked && (
|
||||
<div className={styles.glow} style={{ background: `${color}20` }} />
|
||||
)}
|
||||
|
||||
{/* Lock overlay for locked */}
|
||||
{!unlocked && (
|
||||
<div className={styles.lockOverlay}>
|
||||
<div className={styles.lockCircle}>
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 18 }}>lock</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Icon */}
|
||||
<div className={styles.iconWrap}>
|
||||
{unlocked && (
|
||||
<div className={styles.iconGlow} style={{ background: `${color}30` }} />
|
||||
)}
|
||||
<span
|
||||
className={`material-symbols-outlined ${styles.icon}`}
|
||||
style={{
|
||||
color: unlocked ? color : 'var(--text-muted)',
|
||||
fontVariationSettings: "'FILL' 1",
|
||||
filter: unlocked ? `drop-shadow(0 0 10px ${color}cc)` : 'none',
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Name + Description */}
|
||||
<h3 className={styles.name}>{name}</h3>
|
||||
<p className={styles.desc}>{description}</p>
|
||||
|
||||
{/* Progress or Rank label */}
|
||||
{unlocked ? (
|
||||
<div className={styles.rankBadge} style={{
|
||||
background: `${color}15`,
|
||||
borderColor: `${color}40`,
|
||||
color: color,
|
||||
}}>
|
||||
{rankLabel}
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.progress}>
|
||||
<div className={styles.progressBar}>
|
||||
<div
|
||||
className={styles.progressFill}
|
||||
style={{ width: `${achievement.progress}%`, background: color }}
|
||||
/>
|
||||
</div>
|
||||
<span className={styles.progressText}>{current}/{target}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user