edf33fa932
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>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
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>
|
|
);
|
|
}
|