feat: Erfolge im Feed-Overlay + Profil-Sektion
- getAchievements() im API-Client - unlocked_achievements aus saveProgress als Overlay-Typ 'achievement' - ERFOLGE-Sektion im Profil (freigeschaltet/gesperrt), degradiert lautlos ohne Endpoint Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useMemo } from 'react'
|
||||
import './Profil.css'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
import { getProfilData, getStats, getLanguageOptions, langById } from '../api/directus'
|
||||
import { getProfilData, getStats, getLanguageOptions, langById, getAchievements } from '../api/directus'
|
||||
import ProgressRing from '../components/ProgressRing'
|
||||
import { levelInfo } from '../utils/leveling'
|
||||
import { categoryTier, capabilitySentence } from '../utils/praise'
|
||||
@@ -146,6 +146,7 @@ export default function Profil() {
|
||||
const [profil, setProfil] = useState(null)
|
||||
const [stats, setStats] = useState(null)
|
||||
const [langs, setLangs] = useState([])
|
||||
const [achievements, setAchievements] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setRadarReady(true), 120)
|
||||
@@ -160,6 +161,8 @@ export default function Profil() {
|
||||
} catch { /* Fallback unten */ }
|
||||
// Stats getrennt – degradiert lautlos, falls /auth/stats noch nicht deployed ist
|
||||
try { setStats(await getStats(token)) } catch { /* kein Tracking verfügbar */ }
|
||||
// Erfolge – degradiert lautlos, falls /auth/achievements noch nicht deployed ist
|
||||
try { setAchievements(await getAchievements(token)) } catch { /* keine Erfolge verfügbar */ }
|
||||
}
|
||||
load()
|
||||
}, [token])
|
||||
@@ -315,6 +318,21 @@ export default function Profil() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Erfolge ── */}
|
||||
{achievements.length > 0 && (
|
||||
<div className="card">
|
||||
<p className="card-title">ERFOLGE · {achievements.filter(a => a.unlocked).length}/{achievements.length}</p>
|
||||
<div className="ach-grid">
|
||||
{achievements.map(a => (
|
||||
<div key={a.key} className={`ach-tile ${a.unlocked ? 'on' : 'off'}`} title={a.label}>
|
||||
<span className="ach-icon">{a.unlocked ? a.icon : '🔒'}</span>
|
||||
<span className="ach-label">{a.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Streak-Kalender ── */}
|
||||
{stats && (
|
||||
<div className="card">
|
||||
|
||||
Reference in New Issue
Block a user