'use client'
/**
* Breakpilot Drive - Game Dashboard
*
* Admin-Interface fuer das Lernspiel:
* - Uebersicht: Statistiken und KPIs
* - Spielen: Embedded WebGL Game
* - Statistiken: Detaillierte Auswertungen
*/
import AdminLayout from '@/components/admin/AdminLayout'
import SystemInfoSection, { SYSTEM_INFO_CONFIGS } from '@/components/admin/SystemInfoSection'
import Link from 'next/link'
import { useState, useEffect, useCallback } from 'react'
// Types
interface GameStats {
activePlayers: number
totalSessions: number
questionsAnswered: number
averageAccuracy: number
totalPlayTimeHours: number
}
interface LeaderboardEntry {
rank: number
displayName: string
score: number
accuracy: number
}
type TabType = 'overview' | 'play' | 'stats'
// Mock data - in production this would come from the API
const mockStats: GameStats = {
activePlayers: 42,
totalSessions: 1234,
questionsAnswered: 8567,
averageAccuracy: 0.78,
totalPlayTimeHours: 156.5,
}
const mockLeaderboard: LeaderboardEntry[] = [
{ rank: 1, displayName: 'Max M.', score: 25000, accuracy: 0.92 },
{ rank: 2, displayName: 'Lisa K.', score: 23500, accuracy: 0.88 },
{ rank: 3, displayName: 'Tim S.', score: 21000, accuracy: 0.85 },
{ rank: 4, displayName: 'Anna B.', score: 19500, accuracy: 0.82 },
{ rank: 5, displayName: 'Paul R.', score: 18000, accuracy: 0.79 },
]
// Stat Card Component
function StatCard({
title,
value,
suffix = '',
icon,
trend,
}: {
title: string
value: string | number
suffix?: string
icon: React.ReactNode
trend?: { value: number; positive: boolean }
}) {
return (
{title}
{value}{suffix}
{trend && (
{trend.positive ? '+' : ''}{trend.value}% vs. letzte Woche
)}
{icon}
)
}
// Leaderboard Component
function Leaderboard({ entries }: { entries: LeaderboardEntry[] }) {
return (
Top 5 Spieler
{entries.map((entry) => (
{entry.rank}
{entry.displayName}
{entry.score.toLocaleString()} Punkte
{Math.round(entry.accuracy * 100)}%
))}
)
}
// Main Page Component
export default function GameDashboardPage() {
const [activeTab, setActiveTab] = useState('overview')
const [stats, setStats] = useState(mockStats)
const [leaderboard, setLeaderboard] = useState(mockLeaderboard)
const [loading, setLoading] = useState(false)
const [gameLoaded, setGameLoaded] = useState(false)
// Game URL - in production this would be from environment
const GAME_URL = process.env.NEXT_PUBLIC_GAME_URL || 'http://localhost:3001'
// Fetch stats from API
const fetchStats = useCallback(async () => {
setLoading(true)
try {
// In production: fetch from /api/game/stats
// const response = await fetch('/api/admin/game/stats')
// const data = await response.json()
// setStats(data)
// For now, use mock data
await new Promise(resolve => setTimeout(resolve, 500))
setStats(mockStats)
} catch (error) {
console.error('Failed to fetch stats:', error)
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
fetchStats()
}, [fetchStats])
// Tab buttons
const tabs: { id: TabType; label: string; icon: React.ReactNode }[] = [
{
id: 'overview',
label: 'Uebersicht',
icon: (
),
},
{
id: 'play',
label: 'Spielen',
icon: (
),
},
{
id: 'stats',
label: 'Statistiken',
icon: (
),
},
]
return (
{/* Tab Navigation */}
{tabs.map((tab) => (
setActiveTab(tab.id)}
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-colors ${
activeTab === tab.id
? 'bg-primary-600 text-white'
: 'bg-white text-slate-600 border border-slate-200 hover:bg-slate-50'
}`}
>
{tab.icon}
{tab.label}
))}
{/* Wizard Link */}
Lern-Wizard
{/* Multiplayer Wizard Link */}
Multiplayer
{/* Build Pipeline Wizard Link */}
Builds
{/* Refresh Button */}
{loading ? 'Laden...' : 'Aktualisieren'}
{/* Overview Tab */}
{activeTab === 'overview' && (
<>
{/* Stats Grid */}
}
trend={{ value: 12, positive: true }}
/>
}
/>
}
/>
}
trend={{ value: 3, positive: true }}
/>
}
/>
{/* Leaderboard & Info */}
{/* Quick Actions */}
Schnellzugriff
setActiveTab('play')}
className="w-full flex items-center gap-3 px-4 py-3 bg-primary-50 text-primary-700 rounded-lg hover:bg-primary-100 transition-colors"
>
Spiel starten
Im neuen Tab oeffnen
setActiveTab('stats')}
className="w-full flex items-center gap-3 px-4 py-3 bg-slate-50 text-slate-700 rounded-lg hover:bg-slate-100 transition-colors"
>
Detaillierte Statistiken
>
)}
{/* Play Tab - Embedded Game */}
{activeTab === 'play' && (
{!gameLoaded && (
Spiel wird geladen...
Unity WebGL wird initialisiert
)}
)}
{/* Stats Tab */}
{activeTab === 'stats' && (
{/* Coming Soon Notice */}
Statistiken in Entwicklung
Detaillierte Statistiken werden nach dem ersten WebGL-Build und echten Spielsessions verfuegbar sein.
Aktuell werden Mock-Daten angezeigt.
{/* Placeholder Charts */}
{/* Sessions over Time */}
Sessions pro Tag
Chart kommt nach WebGL Build
{/* Accuracy by Subject */}
Genauigkeit nach Fach
{[
{ subject: 'Mathematik', accuracy: 82, color: 'bg-blue-500' },
{ subject: 'Deutsch', accuracy: 75, color: 'bg-green-500' },
{ subject: 'Englisch', accuracy: 78, color: 'bg-purple-500' },
].map((item) => (
{item.subject}
{item.accuracy}%
))}
{/* Learning Level Distribution */}
Lernniveau-Verteilung
Chart kommt nach WebGL Build
{/* Recent Activity */}
Letzte Aktivitaet
{[
{ time: 'Vor 5 Min', action: 'Max M. hat Level 4 erreicht', type: 'achievement' },
{ time: 'Vor 12 Min', action: 'Lisa K. hat 10 Fragen richtig beantwortet', type: 'quiz' },
{ time: 'Vor 18 Min', action: 'Tim S. hat eine neue Session gestartet', type: 'session' },
{ time: 'Vor 25 Min', action: 'Anna B. hat den Highscore geknackt', type: 'highscore' },
].map((item, index) => (
{item.time}
{item.action}
))}
)}
{/* Info Footer */}
Breakpilot Drive
Endless Runner Lernspiel fuer Klasse 2-6. Das Spiel laeuft auf Port 3001.
Fuer die volle Funktionalitaet muss der Unity WebGL Build erstellt und der Game-Container gestartet werden.
docker-compose --profile game up -d
{/* System Info Section - For Internal/External Audits */}
)
}