deckhearth/src/components/admin/AdminPanel.tsx
Randall Stillwell dc95d0d76d Implement complete user authentication and admin panel system
 Features Added:
- User registration and login with JWT authentication
- Role-based access control (user/admin)
- Comprehensive admin panel with user management
- Password hashing with bcrypt
- Session management and token storage
- Protected routes and public routes
- Modern login/register forms with validation

🗄️ Database Schema:
- Users table with profile information
- Roles and permissions system
- User-role junction tables
- Session management tables
- Database triggers and indexes

🎨 UI/UX Improvements:
- Updated navigation with user menu
- Admin badge and access controls
- Responsive authentication forms
- Loading states and error handling
- Role-based UI elements

🔧 API Endpoints:
- /api/auth/login - User authentication
- /api/auth/register - User registration
- /api/admin/users - User management (admin only)
- /api/setup-auth - Database schema setup

🚀 Admin Panel Features:
- User listing with search and pagination
- Role assignment (user/admin)
- User activation/deactivation
- System dashboard with stats
- Card management placeholder
- Real-time user management
2025-07-21 21:06:38 -05:00

125 lines
No EOL
4.8 KiB
TypeScript

import React, { useState } from 'react';
import { useAuth } from '../../contexts/AuthContext';
import { Navigate } from 'react-router-dom';
import UserManagement from './UserManagement';
import CardManagement from './CardManagement';
import SystemStats from './SystemStats';
type AdminTab = 'dashboard' | 'users' | 'cards' | 'decks' | 'settings';
const AdminPanel: React.FC = () => {
const { user, isAdmin } = useAuth();
const [activeTab, setActiveTab] = useState<AdminTab>('dashboard');
// Redirect if not admin
if (!isAdmin()) {
return <Navigate to="/dashboard" replace />;
}
const tabs = [
{ id: 'dashboard' as AdminTab, name: 'Dashboard', icon: '📊' },
{ id: 'users' as AdminTab, name: 'Users', icon: '👥' },
{ id: 'cards' as AdminTab, name: 'Cards', icon: '🃏' },
{ id: 'decks' as AdminTab, name: 'Decks', icon: '📚' },
{ id: 'settings' as AdminTab, name: 'Settings', icon: '⚙️' },
];
const renderContent = () => {
switch (activeTab) {
case 'dashboard':
return <SystemStats />;
case 'users':
return <UserManagement />;
case 'cards':
return <CardManagement />;
case 'decks':
return <div className="p-6">Deck Management - Coming Soon</div>;
case 'settings':
return <div className="p-6">System Settings - Coming Soon</div>;
default:
return <SystemStats />;
}
};
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<div className="bg-white shadow-sm border-b border-gray-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center py-4">
<div>
<h1 className="text-2xl font-bold text-gray-900">Admin Panel</h1>
<p className="text-sm text-gray-600">
Welcome back, {user?.firstName || user?.username}
</p>
</div>
<div className="flex items-center space-x-4">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">
Admin
</span>
</div>
</div>
</div>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex flex-col lg:flex-row gap-8">
{/* Sidebar Navigation */}
<div className="lg:w-64 flex-shrink-0">
<nav className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
<div className="p-4 bg-gray-50 border-b border-gray-200">
<h2 className="text-sm font-semibold text-gray-900 uppercase tracking-wide">
Administration
</h2>
</div>
<div className="p-2">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`w-full flex items-center px-3 py-2 text-sm font-medium rounded-md mb-1 transition-colors duration-200 ${
activeTab === tab.id
? 'bg-indigo-100 text-indigo-700 border-r-2 border-indigo-500'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
}`}
>
<span className="mr-3 text-lg">{tab.icon}</span>
{tab.name}
</button>
))}
</div>
</nav>
{/* Quick Stats */}
<div className="mt-6 bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<h3 className="text-sm font-semibold text-gray-900 mb-3">Quick Stats</h3>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-gray-600">Total Users</span>
<span className="font-medium text-gray-900">-</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-600">Total Cards</span>
<span className="font-medium text-gray-900">-</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-600">Active Sessions</span>
<span className="font-medium text-gray-900">-</span>
</div>
</div>
</div>
</div>
{/* Main Content */}
<div className="flex-1">
<div className="bg-white rounded-lg shadow-sm border border-gray-200 min-h-[600px]">
{renderContent()}
</div>
</div>
</div>
</div>
</div>
);
};
export default AdminPanel;