shinkan-jinkendo/frontend/src/pages/Dashboard.jsx
Lars efc2a11a76
All checks were successful
Deploy Development / deploy (push) Successful in 39s
feat: Add Auth system with Login UI
Backend:
- Auth router (login, register, logout)
- Profiles router (get current profile)
- Registered in main.py

Frontend:
- LoginPage with login/register tabs
- Dashboard with welcome screen
- Simplified AuthContext for Shinkan
- Protected routes in App.jsx
- Public routes redirect when logged in

Ready for testing!

version: 0.1.0
2026-04-21 14:56:16 +02:00

160 lines
4.6 KiB
JavaScript

import React, { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import api from '../utils/api'
function Dashboard() {
const [version, setVersion] = useState(null)
const [profile, setProfile] = useState(null)
const [loading, setLoading] = useState(true)
const { logout } = useAuth()
const navigate = useNavigate()
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
try {
const [versionData, profileData] = await Promise.all([
api.getVersion(),
api.getCurrentProfile()
])
setVersion(versionData)
setProfile(profileData)
} catch (err) {
console.error('Failed to load data:', err)
} finally {
setLoading(false)
}
}
const handleLogout = async () => {
try {
await api.logout()
} catch (err) {
console.error('Logout error:', err)
} finally {
localStorage.removeItem('authToken')
logout()
navigate('/login')
}
}
if (loading) {
return (
<div style={{ padding: '2rem', textAlign: 'center' }}>
<div className="spinner"></div>
<p>Laden...</p>
</div>
)
}
return (
<div style={{ minHeight: '100vh', background: 'var(--bg)', padding: '1rem' }}>
{/* Header */}
<div style={{
maxWidth: '1200px',
margin: '0 auto',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '2rem'
}}>
<div>
<h1 style={{ margin: 0 }}>🥋 Shinkan Jinkendo</h1>
<p style={{ color: 'var(--text2)', margin: '0.25rem 0 0 0' }}>
Willkommen, {profile?.name || profile?.email}
</p>
</div>
<button className="btn btn-secondary" onClick={handleLogout}>
Abmelden
</button>
</div>
{/* Main Content */}
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
{/* Welcome Card */}
<div className="card" style={{ marginBottom: '1.5rem' }}>
<h2>Dashboard</h2>
<p style={{ color: 'var(--text2)' }}>
Trainer- und Vereinsplattform für Kampfsport-Trainingsplanung
</p>
</div>
{/* Status Grid */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '1rem',
marginBottom: '1.5rem'
}}>
<div className="card">
<h3>✅ Fertig</h3>
<ul style={{ marginTop: '1rem', paddingLeft: '1.5rem' }}>
<li>Backend-Basis</li>
<li>Datenbank-Schema</li>
<li>Auth-System</li>
<li>Login & Registrierung</li>
</ul>
</div>
<div className="card">
<h3>🚧 In Arbeit</h3>
<ul style={{ marginTop: '1rem', paddingLeft: '1.5rem' }}>
<li>Übungsverwaltung</li>
<li>Trainingsplanung</li>
<li>Kataloge (Skills, Methods)</li>
</ul>
</div>
<div className="card">
<h3>📋 Geplant</h3>
<ul style={{ marginTop: '1rem', paddingLeft: '1.5rem' }}>
<li>MediaWiki-Import</li>
<li>Trainingsprogramme</li>
<li>Admin-Panel</li>
</ul>
</div>
</div>
{/* System Info */}
{version && (
<div className="card">
<h3>System-Information</h3>
<div style={{ display: 'grid', gridTemplateColumns: '150px 1fr', gap: '0.5rem', marginTop: '1rem' }}>
<strong>Version:</strong>
<span>{version.app_version}</span>
<strong>Build:</strong>
<span>{version.build_date}</span>
<strong>Umgebung:</strong>
<span>{version.environment}</span>
<strong>DB Schema:</strong>
<span>{version.db_schema_version}</span>
<strong>Dein Tier:</strong>
<span style={{
padding: '0.25rem 0.5rem',
background: profile?.tier === 'premium' ? 'var(--accent)' : 'var(--surface2)',
color: profile?.tier === 'premium' ? 'white' : 'var(--text1)',
borderRadius: '4px',
display: 'inline-block'
}}>
{profile?.tier || 'free'}
</span>
<strong>Rolle:</strong>
<span>{profile?.role || 'user'}</span>
</div>
</div>
)}
</div>
</div>
)
}
export default Dashboard