import { useState, useEffect } from 'react'
import { Plus, Trash2, Pencil, Check, X, Shield, Key, Settings } from 'lucide-react'
import { Link } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import { api } from '../utils/api'
const COLORS = ['#1D9E75','#378ADD','#D85A30','#EF9F27','#7F77DD','#D4537E','#639922','#888780']
function Avatar({ profile, size=36 }) {
const initials = profile.name.split(' ').map(n=>n[0]).join('').toUpperCase().slice(0,2)
return (
Neues Profil
Name
set('name',e.target.value)} autoFocus/>
Farbe
{COLORS.map(c=>(
set('avatar_color',c)}
style={{width:24,height:24,borderRadius:'50%',background:c,cursor:'pointer',
border:`3px solid ${form.avatar_color===c?'white':'transparent'}`,
boxShadow:form.avatar_color===c?`0 0 0 2px ${c}`:'none'}}/>
))}
Geschlecht
set('sex',e.target.value)}>
Männlich
Weiblich
Größe
set('height',e.target.value)}/>
cm
E-Mail
set('email',e.target.value)}/>
Login
set('auth_type',e.target.value)}>
PIN
Passwort
PIN/Passwort
set('pin',e.target.value)}/>
Session
set('session_days',parseInt(e.target.value))}>
7 Tage
30 Tage
Immer
{error &&
{error}
}
Erstellen
Abbrechen
)
}
function EmailEditor({ profileId, currentEmail, onSaved }) {
const [email, setEmail] = useState(currentEmail||'')
const [msg, setMsg] = useState(null)
const save = async () => {
const token = localStorage.getItem('bodytrack_token')||''
await fetch(`/api/admin/profiles/${profileId}/email`, {
method:'PUT', headers:{'Content-Type':'application/json','X-Auth-Token':token},
body: JSON.stringify({email})
})
setMsg('✓ Gespeichert'); onSaved()
setTimeout(()=>setMsg(null),2000)
}
return (
setEmail(e.target.value)} style={{flex:1}}/>
Setzen
{msg && {msg} }
)
}
function ProfileCard({ profile, currentId, onRefresh }) {
const [expanded, setExpanded] = useState(false)
const [perms, setPerms] = useState({
ai_enabled: profile.ai_enabled ?? 1,
ai_limit_day: profile.ai_limit_day || '',
export_enabled: profile.export_enabled ?? 1,
role: profile.role || 'user',
})
const [saving, setSaving] = useState(false)
const [newPin, setNewPin] = useState('')
const [pinMsg, setPinMsg] = useState(null)
const isSelf = profile.id === currentId
const savePerms = async () => {
setSaving(true)
try {
await api.adminSetPermissions(profile.id, {
ai_enabled: perms.ai_enabled,
ai_limit_day: perms.ai_limit_day ? parseInt(perms.ai_limit_day) : null,
export_enabled: perms.export_enabled,
role: perms.role,
})
await onRefresh()
} finally { setSaving(false) }
}
const savePin = async () => {
if (newPin.length < 4) return setPinMsg('Mind. 4 Zeichen')
try {
await fetch(`/api/admin/profiles/${profile.id}/pin`, {
method:'PUT', headers:{'Content-Type':'application/json',
'X-Auth-Token': localStorage.getItem('bodytrack_token')||''},
body: JSON.stringify({pin: newPin})
})
setNewPin(''); setPinMsg('✓ PIN geändert')
setTimeout(()=>setPinMsg(null),2000)
} catch(e) { setPinMsg('Fehler: '+e.message) }
}
const deleteProfile = async () => {
if (!confirm(`Profil "${profile.name}" und ALLE Daten löschen?`)) return
await api.adminDeleteProfile(profile.id)
await onRefresh()
}
return (
{profile.name}
{profile.role==='admin' && 👑 Admin }
{isSelf && Du }
KI: {profile.ai_enabled?`✓${profile.ai_limit_day?` (max ${profile.ai_limit_day}/Tag)`:''}` : '✗'} ·
Export: {profile.export_enabled?'✓':'✗'} ·
Calls heute: {profile.ai_calls_today||0}
setExpanded(e=>!e)}>
{!isSelf && (
)}
{expanded && (
{/* Permissions */}
BERECHTIGUNGEN
Rolle
{['user','admin'].map(r=>(
setPerms(p=>({...p,role:r}))}
style={{flex:1,padding:'6px',borderRadius:8,border:`1.5px solid ${perms.role===r?'var(--accent)':'var(--border2)'}`,
background:perms.role===r?'var(--accent-light)':'var(--surface)',
color:perms.role===r?'var(--accent-dark)':'var(--text2)',
fontFamily:'var(--font)',fontSize:13,cursor:'pointer'}}>
{r==='admin'?'👑 Admin':'👤 Nutzer'}
))}
setPerms(p=>({...p,ai_enabled:v?1:0}))} label="KI-Analysen erlaubt"/>
{!!perms.ai_enabled && (
Max. KI-Calls/Tag
setPerms(p=>({...p,ai_limit_day:e.target.value}))}/>
/Tag
)}
setPerms(p=>({...p,export_enabled:v?1:0}))} label="Daten-Export erlaubt"/>
{saving?'Speichern…':'Berechtigungen speichern'}
{/* Email */}
E-MAIL (für Recovery & Zusammenfassungen)
{/* PIN change */}
PIN / PASSWORT ÄNDERN
setNewPin(e.target.value)} style={{flex:1}}/>
Setzen
{pinMsg &&
{pinMsg}
}
)}
)
}
function EmailSettings() {
const [status, setStatus] = useState(null)
const [testTo, setTestTo] = useState('')
const [testing, setTesting] = useState(false)
const [testMsg, setTestMsg] = useState(null)
useEffect(()=>{
const token = localStorage.getItem('bodytrack_token')||''
fetch('/api/admin/email/status',{headers:{'X-Auth-Token':token}})
.then(r=>r.json()).then(setStatus)
},[])
const sendTest = async () => {
if (!testTo) return
setTesting(true); setTestMsg(null)
try {
const token = localStorage.getItem('bodytrack_token')||''
const r = await fetch('/api/admin/email/test',{
method:'POST',headers:{'Content-Type':'application/json','X-Auth-Token':token},
body:JSON.stringify({to:testTo})
})
if(!r.ok) throw new Error((await r.json()).detail)
setTestMsg('✓ Test-E-Mail gesendet!')
} catch(e){ setTestMsg('✗ Fehler: '+e.message) }
finally{ setTesting(false) }
}
return (
📧 E-Mail Konfiguration
{!status ?
: (
<>
{status.configured
? <>✓ Konfiguriert: {status.smtp_user} via {status.smtp_host}>
: <>⚠️ Nicht konfiguriert. SMTP-Einstellungen in der .env Datei setzen.>}
{status.configured && (
<>
App-URL: {status.app_url}
Für korrekte Links in E-Mails (z.B. Recovery-Links). In .env als APP_URL setzen.
setTestTo(e.target.value)} style={{flex:1}}/>
{testing?'…':'Test'}
{testMsg &&
{testMsg}
}
>
)}
{!status.configured && (
Füge folgende Zeilen zur .env Datei hinzu:
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=deine@gmail.com
SMTP_PASS=dein_app_passwort
APP_URL=http://192.168.2.49:3002
)}
>
)}
)
}
export default function AdminPanel() {
const { session } = useAuth()
const [profiles, setProfiles] = useState([])
const [creating, setCreating] = useState(false)
const [loading, setLoading] = useState(true)
const load = () => api.adminListProfiles().then(data=>{ setProfiles(data); setLoading(false) })
useEffect(()=>{ load() },[])
const handleCreate = async (form) => {
await api.adminCreateProfile(form)
setCreating(false)
await load()
}
if (loading) return
return (
Benutzerverwaltung
👑 Du bist Admin. Hier kannst du Profile verwalten, Berechtigungen setzen und KI-Limits konfigurieren.
{creating && (
setCreating(false)}/>
)}
{profiles.map(p=>(
))}
{!creating && (
setCreating(true)}>
Neues Profil anlegen
)}
{/* Email Settings */}
{/* v9c Subscription Management */}
Subscription-System (v9c)
Verwalte Tiers, Features und Limits für das neue Freemium-System.
🎯 Tiers verwalten
🔧 Feature-Registry verwalten
📊 Tier Limits Matrix bearbeiten
🎟️ Coupons verwalten
)
}