-
setAuthState(prev => ({ ...prev, password: e.target.value }))}
- placeholder="Enter password"
- className="w-full px-4 py-3.5 bg-white border border-[#d7ccc8] rounded-xl text-[#3e2723] placeholder:text-[#a1887f] focus:outline-none focus:ring-2 focus:ring-[#bcaaa4] focus:border-[#5d4037] transition-all shadow-sm"
- disabled={authState.isLoading}
- />
-
+
+
+
+
+
+
+
+ dk0.dev · admin
+
+
+
- {authState.error && (
-
-
- {authState.error}
-
- )}
+
+ Admin Access
+
+
+ Enter your password to continue
+
-
-
+
+ {authState.attempts > 0 && (
+
+ {5 - authState.attempts} attempt{5 - authState.attempts !== 1 ? 's' : ''} remaining
+
+ )}
+
);
}
@@ -375,4 +402,4 @@ const AdminPage = () => {
);
};
-export default AdminPage;
\ No newline at end of file
+export default AdminPage;
diff --git a/components/ModernAdminDashboard.tsx b/components/ModernAdminDashboard.tsx
index 5081a32..8cf346f 100644
--- a/components/ModernAdminDashboard.tsx
+++ b/components/ModernAdminDashboard.tsx
@@ -1,7 +1,6 @@
'use client';
import React, { useState, useEffect, useCallback } from 'react';
-import { motion, AnimatePresence } from 'framer-motion';
import {
Mail,
Settings,
@@ -21,23 +20,23 @@ import dynamic from 'next/dynamic';
const EmailManager = dynamic(
() => import('./EmailManager').then((m) => m.EmailManager),
- { ssr: false, loading: () =>
Loading emails…
}
+ { ssr: false, loading: () =>
Loading emails…
}
);
const AnalyticsDashboard = dynamic(
() => import('./AnalyticsDashboard').then((m) => m.default),
- { ssr: false, loading: () =>
Loading analytics…
}
+ { ssr: false, loading: () =>
Loading analytics…
}
);
const ImportExport = dynamic(
() => import('./ImportExport').then((m) => m.default),
- { ssr: false, loading: () =>
Loading tools…
}
+ { ssr: false, loading: () =>
Loading tools…
}
);
const ProjectManager = dynamic(
() => import('./ProjectManager').then((m) => m.ProjectManager),
- { ssr: false, loading: () =>
Loading projects…
}
+ { ssr: false, loading: () =>
Loading projects…
}
);
const ContentManager = dynamic(
() => import('./ContentManager').then((m) => m.default),
- { ssr: false, loading: () =>
Loading content…
}
+ { ssr: false, loading: () =>
Loading content…
}
);
interface Project {
@@ -69,8 +68,10 @@ interface ModernAdminDashboardProps {
isAuthenticated?: boolean;
}
+type TabId = 'overview' | 'projects' | 'emails' | 'analytics' | 'content' | 'settings';
+
const ModernAdminDashboard: React.FC
= ({ isAuthenticated = true }) => {
- const [activeTab, setActiveTab] = useState<'overview' | 'projects' | 'emails' | 'analytics' | 'content' | 'settings'>('overview');
+ const [activeTab, setActiveTab] = useState('overview');
const [projects, setProjects] = useState([]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [isLoading, setIsLoading] = useState(false);
@@ -180,7 +181,6 @@ const ModernAdminDashboard: React.FC = ({ isAuthentic
totalViews: ((analytics?.overview as Record)?.totalViews as number) || (analytics?.totalViews as number) || projects.reduce((sum, p) => sum + (p.analytics?.views || 0), 0),
unreadEmails: emails.filter(e => !(e.read as boolean)).length,
avgPerformance: (() => {
- // Only show real performance data, not defaults
const projectsWithPerf = projects.filter(p => {
const perf = p.performance as Record || {};
return (perf.lighthouse as number || 0) > 0;
@@ -198,7 +198,6 @@ const ModernAdminDashboard: React.FC = ({ isAuthentic
};
useEffect(() => {
- // Prioritize the data needed for the initial dashboard render
void (async () => {
await Promise.all([loadProjects(), loadSystemStats()]);
@@ -218,467 +217,424 @@ const ModernAdminDashboard: React.FC = ({ isAuthentic
}, [loadProjects, loadSystemStats, loadAnalytics, loadEmails]);
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: 'content', label: 'Content', icon: Shield, color: 'teal', description: 'Texts, pages & localization' },
- { id: 'settings', label: 'Settings', icon: Settings, color: 'gray', description: 'System Settings' }
+ { id: 'overview' as TabId, label: 'Dashboard', icon: Home, description: 'Overview & Statistics' },
+ { id: 'projects' as TabId, label: 'Projects', icon: Database, description: 'Manage Projects' },
+ { id: 'emails' as TabId, label: 'Emails', icon: Mail, description: 'Email Management' },
+ { id: 'analytics' as TabId, label: 'Analytics', icon: Activity, description: 'Site Analytics' },
+ { id: 'content' as TabId, label: 'Content', icon: Shield, description: 'Texts, pages & localization' },
+ { id: 'settings' as TabId, label: 'Settings', icon: Settings, description: 'System Settings' }
+ ];
+
+ const statCards = [
+ {
+ label: 'Projects',
+ value: stats.totalProjects,
+ sub: `${stats.publishedProjects} published`,
+ icon: Database,
+ tab: 'projects' as TabId,
+ gradient: 'from-emerald-400/20 to-emerald-400/5',
+ border: 'border-emerald-400/20 dark:border-emerald-400/10',
+ iconColor: 'text-emerald-500',
+ tooltip: 'REAL DATA: Total projects in your portfolio from the database. Shows published vs unpublished count.',
+ },
+ {
+ label: 'Page Views',
+ value: stats.totalViews.toLocaleString(),
+ sub: `${stats.totalUsers} users`,
+ icon: Activity,
+ tab: 'analytics' as TabId,
+ gradient: 'from-sky-400/20 to-sky-400/5',
+ border: 'border-sky-400/20 dark:border-sky-400/10',
+ iconColor: 'text-sky-500',
+ tooltip: 'REAL DATA: Total page views from PageView table (last 30 days). Users = unique IP addresses.',
+ },
+ {
+ label: 'Messages',
+ value: emails.length,
+ sub: stats.unreadEmails > 0 ? `${stats.unreadEmails} unread` : 'all read',
+ subColor: stats.unreadEmails > 0 ? 'text-red-500' : 'text-emerald-500',
+ icon: Mail,
+ tab: 'emails' as TabId,
+ gradient: 'from-purple-400/20 to-purple-400/5',
+ border: 'border-purple-400/20 dark:border-purple-400/10',
+ iconColor: 'text-purple-500',
+ },
+ {
+ label: 'Performance',
+ value: stats.avgPerformance || 'N/A',
+ sub: 'Lighthouse score',
+ icon: TrendingUp,
+ tab: 'analytics' as TabId,
+ gradient: 'from-amber-400/20 to-amber-400/5',
+ border: 'border-amber-400/20 dark:border-amber-400/10',
+ iconColor: 'text-amber-500',
+ tooltip: stats.avgPerformance > 0
+ ? 'REAL DATA: Average Lighthouse score from real Web Vitals collected from page visits.'
+ : 'No performance data yet. Scores appear after visitors load pages.',
+ },
+ {
+ label: 'Bounce Rate',
+ value: `${stats.bounceRate}%`,
+ sub: 'Exit rate',
+ icon: Users,
+ tab: 'analytics' as TabId,
+ gradient: 'from-pink-400/20 to-pink-400/5',
+ border: 'border-pink-400/20 dark:border-pink-400/10',
+ iconColor: 'text-pink-500',
+ tooltip: 'REAL DATA: Percentage of sessions with only 1 pageview. Lower is better.',
+ },
+ {
+ label: 'System',
+ value: 'Online',
+ sub: 'Operational',
+ icon: Shield,
+ tab: 'settings' as TabId,
+ gradient: 'from-teal-400/20 to-teal-400/5',
+ border: 'border-teal-400/20 dark:border-teal-400/10',
+ iconColor: 'text-teal-500',
+ pulse: true,
+ },
];
return (
-
- {/* Animated Background - same as main site */}
-
+
- {/* Admin Navbar - Horizontal Navigation */}
-
-
-
-
- {/* Left side - Logo and Admin Panel */}
-
-
-
-
Portfolio
-
-
-
-
- Admin Panel
-
-
+ {/* Navbar */}
+
+ {/* Gradient accent bar */}
+
- {/* Center - Desktop Navigation */}
-
- {navigation.map((item) => (
-
- ))}
-
+
+
- {/* Right side - User info and Logout */}
-
-
- Welcome, Dennis
-
+ {/* Left: branding */}
+
+
+
+
+ dk0.dev
+
+
+
+
+ Admin
+
+
+
+ {/* Center: desktop tabs */}
+
+ {navigation.map((item) => (
+ ))}
+
- {/* Mobile menu button */}
-
-
+ {/* Right: user + logout + mobile toggle */}
+
+
+ Dennis
+
+
+
+
+
- {/* Mobile Navigation Menu */}
-
- {mobileMenuOpen && (
-
-
- {navigation.map((item) => (
+ {/* Mobile menu */}
+ {mobileMenuOpen && (
+
+
+ {navigation.map((item) => (
+
+ ))}
+
+
+ )}
+
+
+ {/* Main content */}
+
+
+ {/* Overview tab */}
+ {activeTab === 'overview' && (
+
+
+
+ Dashboard.
+
+
+ Manage your portfolio and monitor performance
+
+
+
+ {/* Stats grid */}
+
+ {statCards.map((card) => (
+
setActiveTab(card.tab)}
+ >
+
+
+ {card.pulse ? (
+
+
+ {card.value}
+
+ ) : card.value}
+
+
+ {card.sub}
+
+
+ {card.tooltip && (
+
+ )}
+
+ ))}
+
+
+ {/* Recent Activity + Quick Actions */}
+
+
+ {/* Recent Activity */}
+
+
+
+ Recent Activity
+
+
+
+
+
+
+
Projects
+ {projects.slice(0, 3).map((project) => (
+
setActiveTab('projects')}
+ >
+
+
{project.title}
+
{project.analytics?.views || 0} views
+
+
+ {project.published ? 'Live' : 'Draft'}
+
+ {project.featured && (
+ Featured
+ )}
+
+
+
+ ))}
+ {projects.length === 0 && (
+
No projects yet
+ )}
+
+
+
+
Messages
+ {emails.slice(0, 3).map((email, index) => (
+
setActiveTab('emails')}
+ >
+
+
+
+
+
+ {email.name as string}
+
+
{(email.subject as string) || 'No subject'}
+
+ {!(email.read as boolean) && (
+
+ )}
+
+ ))}
+ {emails.length === 0 && (
+
No messages yet
+ )}
+
+
+
+
+ {/* Quick Actions */}
+
+
+ Quick Actions
+
+
+ {[
+ { label: 'Ghost Editor', sub: 'Professional writing tool', icon: Plus, action: () => window.location.href = '/editor', color: 'from-emerald-400/20 to-emerald-400/5 border-emerald-400/20' },
+ { label: 'View Messages', sub: `${stats.unreadEmails} unread`, icon: Mail, action: () => setActiveTab('emails'), color: 'from-purple-400/20 to-purple-400/5 border-purple-400/20' },
+ { label: 'Analytics', sub: 'View detailed stats', icon: TrendingUp, action: () => setActiveTab('analytics'), color: 'from-sky-400/20 to-sky-400/5 border-sky-400/20' },
+ { label: 'Settings', sub: 'System configuration', icon: Settings, action: () => setActiveTab('settings'), color: 'from-stone-400/20 to-stone-400/5 border-stone-400/20' },
+ ].map((item) => (
+
+
+
+ )}
- {/* Main Content - Full Width Horizontal Layout */}
-
- {/* Content */}
-
-
- {activeTab === 'overview' && (
-
-
-
-
Admin Dashboard
-
Manage your portfolio and monitor performance
-
-
+ {activeTab === 'projects' && (
+
+
+
+ Projects.
+
+
Manage your portfolio projects
+
+
+
+ )}
- {/* Stats Grid - Mobile: 2x3, Desktop: 6x1 horizontal */}
-
-
setActiveTab('projects')}
+ {activeTab === 'emails' && (
+
+ )}
+
+ {activeTab === 'analytics' && (
+
+ )}
+
+ {activeTab === 'content' && (
+
+ )}
+
+ {activeTab === 'settings' && (
+
+
+
+ Settings.
+
+
Manage system configuration and preferences
+
+
+
+
+
+ Import / Export
+
+
Backup and restore your portfolio data
+
+
+
+
+
+ System Status
+
+
+ {[
+ { label: 'Database', color: 'bg-emerald-400/20 border-emerald-400/20', dot: 'bg-emerald-500', text: 'text-emerald-600 dark:text-emerald-400' },
+ { label: 'Redis Cache', color: 'bg-emerald-400/20 border-emerald-400/20', dot: 'bg-emerald-500', text: 'text-emerald-600 dark:text-emerald-400' },
+ { label: 'API Services', color: 'bg-emerald-400/20 border-emerald-400/20', dot: 'bg-emerald-500', text: 'text-emerald-600 dark:text-emerald-400' },
+ ].map((item) => (
+
-
-
-
{stats.totalProjects}
-
{stats.publishedProjects} published
-
-
- ✅ REAL DATA: Total projects in your portfolio from the database. Shows published vs unpublished count.
-
+
{item.label}
+
-
-
setActiveTab('analytics')}
- >
-
-
-
{stats.totalViews.toLocaleString()}
-
{stats.totalUsers} users
-
-
- ✅ REAL DATA: Total page views from PageView table (last 30 days). Each visit is tracked with IP, user agent, and timestamp. Users = unique IP addresses.
-
-
-
-
-
setActiveTab('emails')}
- >
-
-
-
{emails.length}
-
{stats.unreadEmails} unread
-
-
-
-
setActiveTab('analytics')}
- >
-
-
-
{stats.avgPerformance || 'N/A'}
-
Lighthouse Score
-
-
- {stats.avgPerformance > 0
- ? "✅ REAL DATA: Average Lighthouse score (0-100) calculated from real Web Vitals (LCP, FCP, CLS, FID, TTFB) collected from actual page visits. Only averages projects with real performance data."
- : "No performance data yet. Scores appear after visitors load pages and Web Vitals are tracked."}
-
-
-
-
-
setActiveTab('analytics')}
- >
-
-
-
{stats.bounceRate}%
-
Exit rate
-
-
- ✅ REAL DATA: Percentage of sessions with only 1 pageview (calculated from PageView records grouped by IP). Lower is better. Shows how many visitors leave after viewing just one page.
-
-
-
-
-
setActiveTab('settings')}
- >
-
-
-
-
- {/* Recent Activity & Quick Actions - Mobile: vertical, Desktop: horizontal */}
-
- {/* Recent Activity */}
-
-
-
Recent Activity
-
-
-
- {/* Mobile: vertical stack, Desktop: horizontal columns */}
-
-
-
Projects
-
- {projects.slice(0, 3).map((project) => (
-
setActiveTab('projects')}>
-
-
{project.title}
-
{project.published ? 'Published' : 'Draft'} • {project.analytics?.views || 0} views
-
-
- {project.published ? 'Live' : 'Draft'}
-
- {project.featured && (
- Featured
- )}
-
-
-
- ))}
-
-
-
-
-
Messages
-
- {emails.slice(0, 3).map((email, index) => (
-
setActiveTab('emails')}>
-
-
-
-
-
From {email.name as string}
-
{(email.subject as string) || 'No subject'}
-
- {!(email.read as boolean) && (
-
- )}
-
- ))}
-
-
-
-
-
- {/* Quick Actions */}
-
-
Quick Actions
-
-
-
-
-
-
-
-
-
-
-
-
-
+ ))}
- )}
+
+
+
+ )}
- {activeTab === 'projects' && (
-
-
-
-
Project Management
-
Manage your portfolio projects
-
-
-
-
-
- )}
-
- {activeTab === 'emails' && (
-
- )}
-
- {activeTab === 'analytics' && (
-
- )}
-
- {activeTab === 'content' && (
-
- )}
-
- {activeTab === 'settings' && (
-
-
-
System Settings
-
Manage system configuration and preferences
-
-
-
-
-
Import / Export
-
Backup and restore your portfolio data
-
-
-
-
-
-
- )}
-
-
-
);
};
-export default ModernAdminDashboard;
\ No newline at end of file
+export default ModernAdminDashboard;