✅ Updated Middleware Logic: - Enhanced admin route protection with Basic Auth for legacy routes and session-based auth for `/manage` and `/editor`. ✅ Improved Admin Panel Styles: - Added glassmorphism styles for admin components to enhance UI aesthetics. ✅ Refined Rate Limiting: - Adjusted rate limits for admin dashboard requests to allow more generous access. ✅ Introduced Analytics Reset API: - Added a new endpoint for resetting analytics data with rate limiting and admin authentication. 🎯 Overall Improvements: - Strengthened security and user experience for admin functionalities. - Enhanced visual design for better usability. - Streamlined analytics management processes.
606 lines
29 KiB
TypeScript
606 lines
29 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import {
|
|
Mail,
|
|
Settings,
|
|
TrendingUp,
|
|
Plus,
|
|
Edit,
|
|
Trash2,
|
|
Shield,
|
|
Users,
|
|
Activity,
|
|
Database,
|
|
Home,
|
|
LogOut,
|
|
Menu,
|
|
X
|
|
} from 'lucide-react';
|
|
import Link from 'next/link';
|
|
import { EmailManager } from './EmailManager';
|
|
import { AnalyticsDashboard } from './AnalyticsDashboard';
|
|
import ImportExport from './ImportExport';
|
|
import { ProjectManager } from './ProjectManager';
|
|
|
|
interface Project {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
content?: string;
|
|
category: string;
|
|
difficulty?: string;
|
|
tags?: string[];
|
|
featured: boolean;
|
|
published: boolean;
|
|
github?: string;
|
|
live?: string;
|
|
image?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
analytics?: {
|
|
views: number;
|
|
likes: number;
|
|
shares: number;
|
|
};
|
|
performance?: {
|
|
lighthouse: number;
|
|
};
|
|
}
|
|
|
|
interface ModernAdminDashboardProps {
|
|
isAuthenticated?: boolean;
|
|
}
|
|
|
|
const ModernAdminDashboard: React.FC<ModernAdminDashboardProps> = ({ isAuthenticated = true }) => {
|
|
const [activeTab, setActiveTab] = useState<'overview' | 'projects' | 'emails' | 'analytics' | 'settings'>('overview');
|
|
const [projects, setProjects] = useState<Project[]>([]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
const [analytics, setAnalytics] = useState<Record<string, unknown> | null>(null);
|
|
const [emails, setEmails] = useState<Record<string, unknown>[]>([]);
|
|
const [systemStats, setSystemStats] = useState<Record<string, unknown> | null>(null);
|
|
|
|
const loadProjects = useCallback(async () => {
|
|
if (!isAuthenticated) return;
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
const response = await fetch('/api/projects', {
|
|
headers: {
|
|
'x-admin-request': 'true'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.warn('Failed to load projects:', response.status);
|
|
setProjects([]);
|
|
return;
|
|
}
|
|
|
|
const data = await response.json();
|
|
setProjects(data.projects || []);
|
|
} catch {
|
|
setProjects([]);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [isAuthenticated]);
|
|
|
|
const loadAnalytics = useCallback(async () => {
|
|
if (!isAuthenticated) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/analytics/dashboard', {
|
|
headers: {
|
|
'x-admin-request': 'true'
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setAnalytics(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading analytics:', error);
|
|
}
|
|
}, [isAuthenticated]);
|
|
|
|
const loadEmails = useCallback(async () => {
|
|
if (!isAuthenticated) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/contacts', {
|
|
headers: {
|
|
'x-admin-request': 'true'
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setEmails(data.contacts || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading emails:', error);
|
|
}
|
|
}, [isAuthenticated]);
|
|
|
|
const loadSystemStats = useCallback(async () => {
|
|
if (!isAuthenticated) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/health', {
|
|
headers: {
|
|
'x-admin-request': 'true'
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setSystemStats(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading system stats:', error);
|
|
}
|
|
}, [isAuthenticated]);
|
|
|
|
const loadAllData = useCallback(async () => {
|
|
await Promise.all([
|
|
loadProjects(),
|
|
loadAnalytics(),
|
|
loadEmails(),
|
|
loadSystemStats()
|
|
]);
|
|
}, [loadProjects, loadAnalytics, loadEmails, loadSystemStats]);
|
|
|
|
// Real stats from API data
|
|
const stats = {
|
|
totalProjects: projects.length,
|
|
publishedProjects: projects.filter(p => p.published).length,
|
|
totalViews: (analytics?.totalViews as number) || projects.reduce((sum, p) => sum + (p.analytics?.views || 0), 0),
|
|
unreadEmails: emails.filter(e => !(e.read as boolean)).length,
|
|
avgPerformance: (analytics?.avgPerformance as number) || (projects.length > 0 ?
|
|
Math.round(projects.reduce((sum, p) => sum + (p.performance?.lighthouse || 90), 0) / projects.length) : 90),
|
|
systemHealth: (systemStats?.status as string) || 'unknown',
|
|
totalUsers: (analytics?.totalUsers as number) || 0,
|
|
bounceRate: (analytics?.bounceRate as number) || 0,
|
|
avgSessionDuration: (analytics?.avgSessionDuration as number) || 0
|
|
};
|
|
|
|
useEffect(() => {
|
|
// Load all data if authenticated
|
|
if (isAuthenticated) {
|
|
loadAllData();
|
|
}
|
|
}, [isAuthenticated, loadAllData]);
|
|
|
|
const navigation = [
|
|
{ id: 'overview', label: 'Dashboard', icon: Home, color: 'blue', description: 'Overview & Statistics' },
|
|
{ id: 'projects', label: 'Projects', icon: Database, color: 'green', description: 'Manage Projects' },
|
|
{ id: 'emails', label: 'Emails', icon: Mail, color: 'purple', description: 'Email Management' },
|
|
{ id: 'analytics', label: 'Analytics', icon: Activity, color: 'orange', description: 'Site Analytics' },
|
|
{ id: 'settings', label: 'Settings', icon: Settings, color: 'gray', description: 'System Settings' }
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen">
|
|
{/* Animated Background - same as main site */}
|
|
<div className="fixed inset-0 animated-bg"></div>
|
|
|
|
{/* Admin Navbar - Horizontal Navigation */}
|
|
<div className="relative z-10">
|
|
<div className="admin-glass border-b border-white/20 sticky top-0">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex items-center justify-between h-16">
|
|
{/* Left side - Logo and Admin Panel */}
|
|
<div className="flex items-center space-x-4">
|
|
<Link
|
|
href="/"
|
|
className="flex items-center space-x-2 text-white/90 hover:text-white transition-colors"
|
|
>
|
|
<Home size={20} className="text-blue-400" />
|
|
<span className="font-medium text-white">Portfolio</span>
|
|
</Link>
|
|
<div className="h-6 w-px bg-white/30" />
|
|
<div className="flex items-center space-x-2">
|
|
<Shield size={20} className="text-purple-400" />
|
|
<span className="text-white font-semibold">Admin Panel</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Center - Desktop Navigation */}
|
|
<div className="hidden md:flex items-center space-x-1">
|
|
{navigation.map((item) => (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => setActiveTab(item.id as 'overview' | 'projects' | 'emails' | 'analytics' | 'settings')}
|
|
className={`flex items-center space-x-2 px-4 py-2 rounded-lg transition-all duration-200 ${
|
|
activeTab === item.id
|
|
? 'admin-glass-light border border-blue-500/40 text-blue-300 shadow-lg'
|
|
: 'text-white/80 hover:text-white hover:admin-glass-light'
|
|
}`}
|
|
>
|
|
<item.icon size={16} className={activeTab === item.id ? 'text-blue-400' : 'text-white/70'} />
|
|
<span className="font-medium text-sm">{item.label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Right side - User info and Logout */}
|
|
<div className="flex items-center space-x-4">
|
|
<div className="hidden sm:block text-sm text-white/80">
|
|
Welcome, <span className="text-white font-semibold">Dennis</span>
|
|
</div>
|
|
<button
|
|
onClick={() => window.location.href = '/api/auth/logout'}
|
|
className="flex items-center space-x-2 px-3 py-2 rounded-lg admin-glass-light hover:bg-red-500/20 text-red-300 hover:text-red-200 transition-all duration-200"
|
|
>
|
|
<LogOut size={16} />
|
|
<span className="hidden sm:inline text-sm font-medium">Logout</span>
|
|
</button>
|
|
|
|
{/* Mobile menu button */}
|
|
<button
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
className="md:hidden flex items-center justify-center p-2 rounded-lg admin-glass-light text-white hover:text-blue-300 transition-colors"
|
|
>
|
|
{mobileMenuOpen ? <X size={20} /> : <Menu size={20} />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Navigation Menu */}
|
|
<AnimatePresence>
|
|
{mobileMenuOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0, height: 0 }}
|
|
animate={{ opacity: 1, height: 'auto' }}
|
|
exit={{ opacity: 0, height: 0 }}
|
|
className="md:hidden border-t border-white/20 admin-glass-light"
|
|
>
|
|
<div className="px-4 py-4 space-y-2">
|
|
{navigation.map((item) => (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => {
|
|
setActiveTab(item.id as 'overview' | 'projects' | 'emails' | 'analytics' | 'settings');
|
|
setMobileMenuOpen(false);
|
|
}}
|
|
className={`w-full flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200 ${
|
|
activeTab === item.id
|
|
? 'admin-glass-light border border-blue-500/40 text-blue-300 shadow-lg'
|
|
: 'text-white/80 hover:text-white hover:admin-glass-light'
|
|
}`}
|
|
>
|
|
<item.icon size={18} className={activeTab === item.id ? 'text-blue-400' : 'text-white/70'} />
|
|
<div className="text-left">
|
|
<div className="font-medium text-sm">{item.label}</div>
|
|
<div className="text-xs opacity-70">{item.description}</div>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
|
|
{/* Main Content - Full Width Horizontal Layout */}
|
|
<div className="px-4 sm:px-6 lg:px-8 xl:px-12 2xl:px-16 py-6 lg:py-8">
|
|
{/* Content */}
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={activeTab}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -20 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
{activeTab === 'overview' && (
|
|
<div className="space-y-8">
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-white">Admin Dashboard</h1>
|
|
<p className="text-white/80 text-lg">Manage your portfolio and monitor performance</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats Grid - Mobile: 2x3, Desktop: 6x1 horizontal */}
|
|
<div className="grid grid-cols-2 md:grid-cols-6 gap-3 md:gap-6">
|
|
<div
|
|
className="admin-glass-light p-4 rounded-xl hover:scale-105 transition-all duration-200 cursor-pointer"
|
|
onClick={() => setActiveTab('projects')}
|
|
>
|
|
<div className="flex flex-col space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-white/80 text-xs md:text-sm font-medium">Projects</p>
|
|
<Database size={20} className="text-blue-400" />
|
|
</div>
|
|
<p className="text-xl md:text-2xl font-bold text-white">{stats.totalProjects}</p>
|
|
<p className="text-green-400 text-xs font-medium">{stats.publishedProjects} published</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="admin-glass-light p-4 rounded-xl hover:scale-105 transition-all duration-200 cursor-pointer"
|
|
onClick={() => setActiveTab('analytics')}
|
|
>
|
|
<div className="flex flex-col space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-white/80 text-xs md:text-sm font-medium">Page Views</p>
|
|
<Activity size={20} className="text-purple-400" />
|
|
</div>
|
|
<p className="text-xl md:text-2xl font-bold text-white">{stats.totalViews.toLocaleString()}</p>
|
|
<p className="text-blue-400 text-xs font-medium">{stats.totalUsers} users</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="admin-glass-light p-4 rounded-xl hover:scale-105 transition-all duration-200 cursor-pointer"
|
|
onClick={() => setActiveTab('emails')}
|
|
>
|
|
<div className="flex flex-col space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-white/80 text-xs md:text-sm font-medium">Messages</p>
|
|
<Mail size={20} className="text-green-400" />
|
|
</div>
|
|
<p className="text-xl md:text-2xl font-bold text-white">{emails.length}</p>
|
|
<p className="text-red-400 text-xs font-medium">{stats.unreadEmails} unread</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="admin-glass-light p-4 rounded-xl hover:scale-105 transition-all duration-200 cursor-pointer"
|
|
onClick={() => setActiveTab('analytics')}
|
|
>
|
|
<div className="flex flex-col space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-white/80 text-xs md:text-sm font-medium">Performance</p>
|
|
<TrendingUp size={20} className="text-orange-400" />
|
|
</div>
|
|
<p className="text-xl md:text-2xl font-bold text-white">{stats.avgPerformance}</p>
|
|
<p className="text-orange-400 text-xs font-medium">Lighthouse Score</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="admin-glass-light p-4 rounded-xl hover:scale-105 transition-all duration-200 cursor-pointer"
|
|
onClick={() => setActiveTab('analytics')}
|
|
>
|
|
<div className="flex flex-col space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-white/80 text-xs md:text-sm font-medium">Bounce Rate</p>
|
|
<Users size={20} className="text-red-400" />
|
|
</div>
|
|
<p className="text-xl md:text-2xl font-bold text-white">{stats.bounceRate}%</p>
|
|
<p className="text-red-400 text-xs font-medium">Exit rate</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="admin-glass-light p-4 rounded-xl hover:scale-105 transition-all duration-200 cursor-pointer"
|
|
onClick={() => setActiveTab('settings')}
|
|
>
|
|
<div className="flex flex-col space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-white/80 text-xs md:text-sm font-medium">System</p>
|
|
<Shield size={20} className="text-green-400" />
|
|
</div>
|
|
<p className="text-xl md:text-2xl font-bold text-white">Online</p>
|
|
<div className="flex items-center space-x-1">
|
|
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
|
<p className="text-green-400 text-xs font-medium">All systems operational</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Recent Activity & Quick Actions - Mobile: vertical, Desktop: horizontal */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{/* Recent Activity */}
|
|
<div className="admin-glass-card p-6 rounded-xl md:col-span-2">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h2 className="text-xl font-bold text-white">Recent Activity</h2>
|
|
<button
|
|
onClick={() => loadAllData()}
|
|
className="text-blue-400 hover:text-blue-300 text-sm font-medium px-3 py-1 admin-glass-light rounded-lg transition-colors"
|
|
>
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
|
|
{/* Mobile: vertical stack, Desktop: horizontal columns */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-6">
|
|
<h3 className="text-sm font-medium text-white/60 uppercase tracking-wider">Projects</h3>
|
|
<div className="space-y-4">
|
|
{projects.slice(0, 3).map((project) => (
|
|
<div key={project.id} className="flex items-start space-x-3 p-4 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 cursor-pointer" onClick={() => setActiveTab('projects')}>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-white font-medium text-sm truncate">{project.title}</p>
|
|
<p className="text-white/60 text-xs">{project.published ? 'Published' : 'Draft'} • {project.analytics?.views || 0} views</p>
|
|
<div className="flex items-center space-x-2 mt-2">
|
|
<span className={`px-2 py-1 rounded-full text-xs ${project.published ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'}`}>
|
|
{project.published ? 'Live' : 'Draft'}
|
|
</span>
|
|
{project.featured && (
|
|
<span className="px-2 py-1 bg-purple-500/20 text-purple-400 rounded-full text-xs">Featured</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<h3 className="text-sm font-medium text-white/60 uppercase tracking-wider">Messages</h3>
|
|
<div className="space-y-3">
|
|
{emails.slice(0, 3).map((email, index) => (
|
|
<div key={index} className="flex items-center space-x-3 p-3 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 cursor-pointer" onClick={() => setActiveTab('emails')}>
|
|
<div className="w-8 h-8 bg-green-500/30 rounded-lg flex items-center justify-center flex-shrink-0">
|
|
<Mail size={14} className="text-green-400" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-white font-medium text-sm truncate">From {email.name as string}</p>
|
|
<p className="text-white/60 text-xs truncate">{(email.subject as string) || 'No subject'}</p>
|
|
</div>
|
|
{!(email.read as boolean) && (
|
|
<div className="w-2 h-2 bg-red-400 rounded-full flex-shrink-0"></div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Quick Actions */}
|
|
<div className="admin-glass-card p-6 rounded-xl">
|
|
<h2 className="text-xl font-bold text-white mb-6">Quick Actions</h2>
|
|
<div className="space-y-4">
|
|
<button
|
|
onClick={() => window.location.href = '/editor'}
|
|
className="w-full flex items-center space-x-3 p-3 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 text-left group"
|
|
>
|
|
<div className="w-10 h-10 bg-green-500/30 rounded-lg flex items-center justify-center group-hover:bg-green-500/40 transition-colors">
|
|
<Plus size={18} className="text-green-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-white font-medium text-sm">Ghost Editor</p>
|
|
<p className="text-white/60 text-xs">Professional writing tool</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => setActiveTab('analytics')}
|
|
className="w-full flex items-center space-x-3 p-3 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 text-left group"
|
|
>
|
|
<div className="w-10 h-10 bg-red-500/30 rounded-lg flex items-center justify-center group-hover:bg-red-500/40 transition-colors">
|
|
<Activity size={18} className="text-red-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-white font-medium text-sm">Reset Analytics</p>
|
|
<p className="text-white/60 text-xs">Clear analytics data</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => setActiveTab('emails')}
|
|
className="w-full flex items-center space-x-3 p-3 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 text-left group"
|
|
>
|
|
<div className="w-10 h-10 bg-green-500/30 rounded-lg flex items-center justify-center group-hover:bg-green-500/40 transition-colors">
|
|
<Mail size={18} className="text-green-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-white font-medium text-sm">View Messages</p>
|
|
<p className="text-white/60 text-xs">{stats.unreadEmails} unread messages</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => setActiveTab('analytics')}
|
|
className="w-full flex items-center space-x-3 p-3 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 text-left group"
|
|
>
|
|
<div className="w-10 h-10 bg-purple-500/30 rounded-lg flex items-center justify-center group-hover:bg-purple-500/40 transition-colors">
|
|
<TrendingUp size={18} className="text-purple-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-white font-medium text-sm">Analytics</p>
|
|
<p className="text-white/60 text-xs">View detailed statistics</p>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => setActiveTab('settings')}
|
|
className="w-full flex items-center space-x-3 p-3 admin-glass-light rounded-lg hover:scale-[1.02] transition-all duration-200 text-left group"
|
|
>
|
|
<div className="w-10 h-10 bg-gray-500/30 rounded-lg flex items-center justify-center group-hover:bg-gray-500/40 transition-colors">
|
|
<Settings size={18} className="text-gray-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-white font-medium text-sm">Settings</p>
|
|
<p className="text-white/60 text-xs">System configuration</p>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'projects' && (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h2 className="text-2xl font-bold text-white">Project Management</h2>
|
|
<p className="text-white/70 mt-1">Manage your portfolio projects</p>
|
|
</div>
|
|
</div>
|
|
|
|
<ProjectManager isAuthenticated={isAuthenticated} projects={projects} onProjectsChange={loadProjects} />
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'emails' && (
|
|
<EmailManager />
|
|
)}
|
|
|
|
{activeTab === 'analytics' && (
|
|
<AnalyticsDashboard isAuthenticated={isAuthenticated} />
|
|
)}
|
|
|
|
{activeTab === 'settings' && (
|
|
<div className="space-y-8">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-white">System Settings</h1>
|
|
<p className="text-white/60">Manage system configuration and preferences</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<div className="admin-glass-card p-6 rounded-xl">
|
|
<h2 className="text-xl font-bold text-white mb-4">Import / Export</h2>
|
|
<p className="text-white/70 mb-4">Backup and restore your portfolio data</p>
|
|
<ImportExport />
|
|
</div>
|
|
|
|
<div className="admin-glass-card p-6 rounded-xl">
|
|
<h2 className="text-xl font-bold text-white mb-4">System Status</h2>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
|
<span className="text-white/80">Database</span>
|
|
<div className="flex items-center space-x-3">
|
|
<div className="w-4 h-4 bg-green-400 rounded-full animate-pulse"></div>
|
|
<span className="text-green-400 font-medium">Online</span>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
|
<span className="text-white/80">Redis Cache</span>
|
|
<div className="flex items-center space-x-3">
|
|
<div className="w-4 h-4 bg-green-400 rounded-full animate-pulse"></div>
|
|
<span className="text-green-400 font-medium">Online</span>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
|
<span className="text-white/80">API Services</span>
|
|
<div className="flex items-center space-x-3">
|
|
<div className="w-4 h-4 bg-green-400 rounded-full animate-pulse"></div>
|
|
<span className="text-green-400 font-medium">Online</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ModernAdminDashboard; |