feat: Fortschritt spürbar machen – Momente, Momentum & Storytelling
- +EP-Float am Button + hochzählendes EP-Badge (EpFloat, useCountUp) - Level-/Streak-/Tagesziel-Overlay (MilestoneOverlay), getriggert aus der saveProgress-Response - Combo-Zähler + variables Lob, ermutigendes Fehler-Feedback statt stillem Verschwinden - Session-Summary mit Story-Zeilen statt End-Sackgasse - Profil führt mit %-bis-Level + Capability-Satz; Kategorie-Stufen, Wochenvergleich, Sound-Toggle - Level-Kurve gespiegelt (utils/leveling.js); Level deploy-unabhängig aus EP abgeleitet Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
35
src/components/MilestoneOverlay.jsx
Normal file
35
src/components/MilestoneOverlay.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useEffect } from 'react'
|
||||
import confetti from 'canvas-confetti'
|
||||
import './Moments.css'
|
||||
|
||||
const COLORS = ['#C4A85A', '#7A5C2E', '#3D7055', '#E8C9A8', '#fff']
|
||||
|
||||
function celebrate() {
|
||||
const reduce = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches
|
||||
if (reduce) return
|
||||
confetti({ particleCount: 140, spread: 90, origin: { y: 0.5 }, colors: COLORS, scalar: 1 })
|
||||
setTimeout(() => confetti({ particleCount: 70, spread: 110, origin: { y: 0.45 }, colors: COLORS, scalar: 0.8 }), 220)
|
||||
}
|
||||
|
||||
// Texte je Milestone-Art. value = Level-Nummer / Streak-Tage / Tagesziel-EP.
|
||||
function content({ kind, value }) {
|
||||
if (kind === 'level') return { cls: '', icon: '🏆', title: `Level ${value} erreicht!`, sub: 'Du wächst spürbar — weiter so.' }
|
||||
if (kind === 'streak') return { cls: 'streak', icon: '🔥', title: `${value} Tage am Stück!`, sub: 'Dranbleiben zahlt sich aus.' }
|
||||
if (kind === 'goal') return { cls: 'goal', icon: '🎯', title: 'Tagesziel erreicht!', sub: 'Stark — heute hast du dein Pensum geschafft.' }
|
||||
return { cls: '', icon: '🎉', title: 'Geschafft!', sub: '' }
|
||||
}
|
||||
|
||||
export default function MilestoneOverlay({ milestone, onClose }) {
|
||||
useEffect(() => { celebrate() }, [milestone])
|
||||
const { cls, icon, title, sub } = content(milestone)
|
||||
return (
|
||||
<div className="milestone-overlay" onClick={onClose} role="dialog" aria-label={title}>
|
||||
<div className="milestone-card" onClick={e => e.stopPropagation()}>
|
||||
<div className={`milestone-badge ${cls}`} aria-hidden="true">{icon}</div>
|
||||
<h2 className="milestone-title">{title}</h2>
|
||||
{sub && <p className="milestone-sub">{sub}</p>}
|
||||
<button className="milestone-btn" onClick={onClose} autoFocus>Weiter</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user