feat: persönlichere Profilseite + iOS-App-Setup
Profil: Begrüßung in Zielsprache, Kategorie-Punkte-Übersicht, ruhigerer Header (kein rotierender Avatar/Online-Dot), Notch-Fix und kompaktere Aktivitäts-Heatmap. Außerdem Capacitor-iOS-Projekt und diverse Auth/Feed/Audio-Verbesserungen aus dem Premium-Redesign. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,9 @@ import { useAuth } from '../context/AuthContext'
|
||||
import { getProfilData, getStats, getLanguageOptions, langById } from '../api/directus'
|
||||
import ProgressRing from '../components/ProgressRing'
|
||||
|
||||
// Erdige Palette für die Kategorie-Dots/Balken (harmoniert mit dem Profil-Theme)
|
||||
const CAT_COLORS = ['#C4A85A', '#7A5C3A', '#3D7055', '#B5732E', '#5B7DB1', '#9C5A8A']
|
||||
|
||||
function LogoutButton() {
|
||||
const { logout } = useAuth()
|
||||
return (
|
||||
@@ -142,6 +145,7 @@ export default function Profil() {
|
||||
|
||||
const displayName = profil?.username || user?.username || '…'
|
||||
const initials = displayName.slice(0, 2).toUpperCase()
|
||||
const greeting = profil?.language_target_greeting || 'Hallo'
|
||||
const points = profil?.total_ep ?? user?.total_ep ?? 0
|
||||
const level = profil?.level ?? Math.floor(points / 500)
|
||||
const epIntoLevel = points - level * 500
|
||||
@@ -160,6 +164,8 @@ export default function Profil() {
|
||||
const skills = stats?.skills || []
|
||||
const hasSkillData = skills.some(s => s.seen > 0)
|
||||
const accuracyPct = totals ? Math.round((totals.accuracy || 0) * 100) : null
|
||||
const categories = stats?.categories || []
|
||||
const maxCatPoints = Math.max(1, ...categories.map(c => c.points))
|
||||
|
||||
return (
|
||||
<div className="profil page-enter">
|
||||
@@ -171,7 +177,6 @@ export default function Profil() {
|
||||
<div className="avatar-ring">
|
||||
<div className="avatar-inner"><div className="avatar">{initials}</div></div>
|
||||
</div>
|
||||
<span className="online-dot" />
|
||||
<div className="avatar-level-badge">
|
||||
<svg viewBox="0 0 48 54" width="28" height="32">
|
||||
<defs>
|
||||
@@ -185,8 +190,8 @@ export default function Profil() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="profil-info">
|
||||
<h2 className="profil-name">{displayName}</h2>
|
||||
<p className="profil-handle">@{displayName.toLowerCase()}</p>
|
||||
<h2 className="profil-name">{greeting}, {displayName}</h2>
|
||||
<p className="profil-learning">lernt {langLabel}</p>
|
||||
{streak > 0 && (
|
||||
<p className="profil-streak">🔥 {streak} Tag{streak !== 1 ? 'e' : ''} Streak</p>
|
||||
)}
|
||||
@@ -230,6 +235,35 @@ export default function Profil() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Kategorien (Punkte je Thema) ── */}
|
||||
{stats && (
|
||||
<div className="card">
|
||||
<p className="card-title">KATEGORIEN</p>
|
||||
{categories.length ? (
|
||||
<div className="cat-list">
|
||||
{categories.map((c, i) => {
|
||||
const color = CAT_COLORS[i % CAT_COLORS.length]
|
||||
return (
|
||||
<div key={c.id} className="cat-row">
|
||||
<div className="cat-head">
|
||||
<span className="cat-dot" style={{ background: color }} />
|
||||
<span className="cat-label">{c.label || 'Allgemein'}</span>
|
||||
<span className="cat-points">{c.points} P</span>
|
||||
</div>
|
||||
<div className="cat-bar">
|
||||
<div className="cat-bar-fill"
|
||||
style={{ width: `${Math.round((c.points / maxCatPoints) * 100)}%`, background: color }} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<p className="skills-empty">Sammle Punkte — deine Themen erscheinen hier.</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Streak-Kalender ── */}
|
||||
{stats && (
|
||||
<div className="card">
|
||||
|
||||
Reference in New Issue
Block a user