- Add AdminLayout component for consistent admin UI - Create AdminDashboardPage with statistics overview - Create AdminUsersPage with user management (CRUD, role changes) - Create AdminReportsPage with user progress reports and CSV export - Create AdminStoryGeneratorPage for AI-powered story generation - Create AdminUserDetailPage for detailed user progress tracking - Add comprehensive admin API client with all endpoints - Add TypeScript types for admin DTOs (users, reports, stories) - Add extensive CSS styles for all admin pages - Update App.tsx with proper admin sub-routes - Update admin-module.md feature documentation with completion status Admin features: - Dashboard with stats (users, lessons, quizzes, stories, activity) - User management (list, view details, change roles, delete) - Progress reports (view all, detailed user reports, CSV export) - Story generator (AI story creation for levels with audio) - Role-based authentication (AdminRoute protects all admin pages) - Responsive design for all admin pages Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
157 lines
4.9 KiB
TypeScript
157 lines
4.9 KiB
TypeScript
/**
|
|
* Admin Dashboard page
|
|
* Shows overview statistics and quick actions
|
|
*/
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useAuth } from '@/stores/authStore';
|
|
import { getDashboardStats } from '@/lib/api/admin';
|
|
import type { DashboardStats } from '@/types/api/admin';
|
|
import { AdminLayout } from '@/components/features/admin/AdminLayout';
|
|
|
|
export function AdminDashboardPage() {
|
|
const { token } = useAuth();
|
|
const [stats, setStats] = useState<DashboardStats | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchStats = async () => {
|
|
if (!token) return;
|
|
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
const data = await getDashboardStats(token);
|
|
setStats(data);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to load dashboard stats');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchStats();
|
|
}, [token]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<AdminLayout>
|
|
<div className="loading-overlay">
|
|
<div className="spinner"></div>
|
|
<p>Loading dashboard...</p>
|
|
</div>
|
|
</AdminLayout>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<AdminLayout>
|
|
<div className="error-message">{error}</div>
|
|
</AdminLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<AdminLayout>
|
|
<div className="admin-dashboard">
|
|
<header className="dashboard-header">
|
|
<h1>Dashboard</h1>
|
|
<p className="dashboard-subtitle">Overview of your DeutschLernen application</p>
|
|
</header>
|
|
|
|
<section className="dashboard-stats-grid">
|
|
<div className="stat-card">
|
|
<div className="stat-icon users">👥</div>
|
|
<div className="stat-content">
|
|
<h3>Total Users</h3>
|
|
<p className="stat-value">{stats?.totalUsers || 0}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="stat-card">
|
|
<div className="stat-icon lessons">📚</div>
|
|
<div className="stat-content">
|
|
<h3>Total Lessons</h3>
|
|
<p className="stat-value">{stats?.totalLessons || 0}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="stat-card">
|
|
<div className="stat-icon quizzes">🎯</div>
|
|
<div className="stat-content">
|
|
<h3>Total Quizzes</h3>
|
|
<p className="stat-value">{stats?.totalQuizzes || 0}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="stat-card">
|
|
<div className="stat-icon stories">📖</div>
|
|
<div className="stat-content">
|
|
<h3>Story Segments</h3>
|
|
<p className="stat-value">{stats?.totalStorySegments || 0}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="stat-card">
|
|
<div className="stat-icon active">✅</div>
|
|
<div className="stat-content">
|
|
<h3>Active Users (Week)</h3>
|
|
<p className="stat-value">{stats?.activeUsersThisWeek || 0}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="stat-card">
|
|
<div className="stat-icon progress">📈</div>
|
|
<div className="stat-content">
|
|
<h3>Avg. Progress</h3>
|
|
<p className="stat-value">{stats ? stats.averageUserProgress.toFixed(1) + '%' : '0%'}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="dashboard-quick-actions">
|
|
<h2>Quick Actions</h2>
|
|
<div className="quick-action-cards">
|
|
<a href="/admin/users" className="quick-action-card">
|
|
<div className="action-icon">👥</div>
|
|
<div className="action-content">
|
|
<h3>Manage Users</h3>
|
|
<p>View, edit, and manage all users</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="/admin/reports" className="quick-action-card">
|
|
<div className="action-icon">📊</div>
|
|
<div className="action-content">
|
|
<h3>View Reports</h3>
|
|
<p>Generate and export user progress reports</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="/admin/stories" className="quick-action-card">
|
|
<div className="action-icon">📖</div>
|
|
<div className="action-content">
|
|
<h3>Generate Stories</h3>
|
|
<p>Create new story segments for levels</p>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="dashboard-info">
|
|
<h2>About</h2>
|
|
<p>
|
|
This is the DeutschLernen admin panel. Here you can manage users,
|
|
view progress reports, and generate story content for different CEFR levels.
|
|
</p>
|
|
<p>
|
|
<strong>Note:</strong> All actions in this panel require administrator privileges.
|
|
Please ensure you are logged in as an admin user.
|
|
</p>
|
|
</section>
|
|
</div>
|
|
</AdminLayout>
|
|
);
|
|
}
|