style: refine admin dashboard and project management UI with cohesive color palette and improved readability

- Update background colors and text styles for better contrast and legibility.
- Enhance button styles and hover effects for a more modern look.
- Remove unnecessary scaling effects and adjust border styles for consistency.
- Introduce a cohesive design language across components to improve user experience.
This commit is contained in:
2026-01-10 02:40:50 +01:00
parent 7d84d35f09
commit b051d9d2ef
14 changed files with 387 additions and 314 deletions

View File

@@ -177,24 +177,24 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="admin-glass-card p-6 rounded-xl hover:scale-105 transition-all duration-200"
className="bg-white border border-stone-200 p-6 rounded-xl hover:shadow-md transition-all duration-200"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center space-x-3 mb-4">
<div className={`p-3 rounded-xl ${color}`}>
<Icon className="w-6 h-6 text-white" size={24} />
<Icon className="w-6 h-6" size={24} />
</div>
<div>
<p className="text-white/60 text-sm font-medium">{title}</p>
{description && <p className="text-white/40 text-xs">{description}</p>}
<p className="text-stone-500 text-sm font-medium">{title}</p>
{description && <p className="text-stone-400 text-xs">{description}</p>}
</div>
</div>
<p className="text-3xl font-bold text-white mb-2">{value}</p>
<p className="text-3xl font-bold text-stone-900 mb-2">{value}</p>
{trend && trendValue && (
<div className={`flex items-center space-x-1 text-sm ${
trend === 'up' ? 'text-green-400' :
trend === 'down' ? 'text-red-400' : 'text-yellow-400'
trend === 'up' ? 'text-green-600' :
trend === 'down' ? 'text-red-600' : 'text-yellow-600'
}`}>
<TrendingUp className={`w-4 h-4 ${trend === 'down' ? 'rotate-180' : ''}`} />
<span>{trendValue}</span>
@@ -207,21 +207,21 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
const getDifficultyColor = (difficulty: string) => {
switch (difficulty) {
case 'Beginner': return 'bg-green-500/30 text-green-400 border-green-500/40';
case 'Intermediate': return 'bg-yellow-500/30 text-yellow-400 border-yellow-500/40';
case 'Advanced': return 'bg-orange-500/30 text-orange-400 border-orange-500/40';
case 'Expert': return 'bg-red-500/30 text-red-400 border-red-500/40';
default: return 'bg-gray-500/30 text-gray-400 border-gray-500/40';
case 'Beginner': return 'bg-green-50 text-green-700 border-green-200';
case 'Intermediate': return 'bg-yellow-50 text-yellow-700 border-yellow-200';
case 'Advanced': return 'bg-orange-50 text-orange-700 border-orange-200';
case 'Expert': return 'bg-red-50 text-red-700 border-red-200';
default: return 'bg-stone-50 text-stone-600 border-stone-200';
}
};
const getCategoryColor = (index: number) => {
const colors = [
'bg-blue-500/30 text-blue-400',
'bg-purple-500/30 text-purple-400',
'bg-green-500/30 text-green-400',
'bg-pink-500/30 text-pink-400',
'bg-indigo-500/30 text-indigo-400'
'bg-stone-100 text-stone-700',
'bg-stone-200 text-stone-800',
'bg-stone-300 text-stone-900',
'bg-stone-100 text-stone-700',
'bg-stone-200 text-stone-800'
];
return colors[index % colors.length];
};
@@ -233,23 +233,23 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-white flex items-center">
<BarChart3 className="w-8 h-8 mr-3 text-blue-400" />
<h1 className="text-3xl font-bold text-stone-900 flex items-center">
<BarChart3 className="w-8 h-8 mr-3 text-stone-600" />
Analytics Dashboard
</h1>
<p className="text-white/80 mt-2">Portfolio performance and user engagement metrics</p>
<p className="text-stone-500 mt-2">Portfolio performance and user engagement metrics</p>
</div>
<div className="flex items-center space-x-3">
{/* Time Range Selector */}
<div className="flex items-center space-x-1 admin-glass-light rounded-xl p-1">
<div className="flex items-center space-x-1 bg-white border border-stone-200 rounded-xl p-1">
{(['7d', '30d', '90d', '1y'] as const).map((range) => (
<button
key={range}
onClick={() => setTimeRange(range)}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${
timeRange === range
? 'bg-blue-500/40 text-blue-300 shadow-lg'
: 'text-white/70 hover:text-white hover:bg-white/10'
? 'bg-stone-100 text-stone-900 shadow-sm'
: 'text-stone-500 hover:text-stone-800 hover:bg-stone-50'
}`}
>
{range === '7d' ? '7 Days' : range === '30d' ? '30 Days' : range === '90d' ? '90 Days' : '1 Year'}
@@ -259,15 +259,15 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
<button
onClick={fetchAnalyticsData}
disabled={loading}
className="flex items-center space-x-2 px-4 py-2 admin-glass-light rounded-xl hover:scale-105 transition-all duration-200 disabled:opacity-50"
className="flex items-center space-x-2 px-4 py-2 bg-white border border-stone-200 rounded-xl hover:bg-stone-50 transition-all duration-200 disabled:opacity-50 text-stone-600"
>
<RefreshCw className={`w-4 h-4 text-blue-400 ${loading ? 'animate-spin' : ''}`} />
<span className="text-white font-medium">Refresh</span>
<RefreshCw className={`w-4 h-4 text-stone-600 ${loading ? 'animate-spin' : ''}`} />
<span className="font-medium">Refresh</span>
</button>
<button
onClick={() => setShowResetModal(true)}
className="flex items-center space-x-2 px-4 py-2 bg-red-600/20 text-red-400 border border-red-500/30 rounded-xl hover:bg-red-600/30 hover:scale-105 transition-all"
className="flex items-center space-x-2 px-4 py-2 bg-red-50 text-red-600 border border-red-100 rounded-xl hover:bg-red-100 transition-all"
>
<RotateCcw className="w-4 h-4" />
<span>Reset</span>
@@ -276,17 +276,17 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
</div>
{loading && (
<div className="admin-glass-card p-8 rounded-xl">
<div className="bg-white border border-stone-200 p-8 rounded-xl shadow-sm">
<div className="flex items-center justify-center space-x-3">
<RefreshCw className="w-6 h-6 text-blue-400 animate-spin" />
<span className="text-white/80 text-lg">Loading analytics data...</span>
<RefreshCw className="w-6 h-6 text-stone-600 animate-spin" />
<span className="text-stone-500 text-lg">Loading analytics data...</span>
</div>
</div>
)}
{error && (
<div className="admin-glass-card p-6 rounded-xl border border-red-500/40">
<div className="flex items-center space-x-3 text-red-300">
<div className="bg-white border border-red-200 p-6 rounded-xl">
<div className="flex items-center space-x-3 text-red-600">
<Activity className="w-5 h-5" />
<span>Error: {error}</span>
</div>
@@ -297,8 +297,8 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
<>
{/* Overview Stats */}
<div>
<h2 className="text-xl font-bold text-white mb-6 flex items-center">
<Target className="w-5 h-5 mr-2 text-purple-400" />
<h2 className="text-xl font-bold text-stone-900 mb-6 flex items-center">
<Target className="w-5 h-5 mr-2 text-stone-600" />
Overview
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
@@ -306,7 +306,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
title="Total Views"
value={data.overview.totalViews.toLocaleString()}
icon={Eye}
color="bg-blue-500/30"
color="bg-stone-100 text-stone-600"
trend="up"
trendValue="+12.5%"
description="All-time page views"
@@ -315,7 +315,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
title="Projects"
value={data.overview.totalProjects}
icon={Globe}
color="bg-green-500/30"
color="bg-green-100 text-green-600"
trend="up"
trendValue="+2"
description={`${data.overview.publishedProjects} published`}
@@ -324,7 +324,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
title="Engagement"
value={data.overview.totalLikes}
icon={Heart}
color="bg-pink-500/30"
color="bg-pink-100 text-pink-600"
trend="up"
trendValue="+8.2%"
description="Total likes & shares"
@@ -333,7 +333,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
title="Performance"
value={data.overview.avgLighthouse}
icon={Zap}
color="bg-orange-500/30"
color="bg-orange-100 text-orange-600"
trend="up"
trendValue="+5%"
description="Avg Lighthouse score"
@@ -342,7 +342,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
title="Bounce Rate"
value={`${data.metrics.bounceRate}%`}
icon={MousePointer}
color="bg-purple-500/30"
color="bg-stone-100 text-stone-600"
trend="down"
trendValue="-2.1%"
description="User retention"
@@ -353,9 +353,9 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
{/* Project Performance */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Top Projects */}
<div className="admin-glass-card p-6 rounded-xl">
<h3 className="text-xl font-bold text-white mb-6 flex items-center">
<Award className="w-5 h-5 mr-2 text-yellow-400" />
<div className="bg-white border border-stone-200 p-6 rounded-xl shadow-sm">
<h3 className="text-xl font-bold text-stone-900 mb-6 flex items-center">
<Award className="w-5 h-5 mr-2 text-yellow-500" />
Top Performing Projects
</h3>
<div className="space-y-4">
@@ -368,20 +368,20 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-center justify-between p-4 admin-glass-light rounded-xl"
className="flex items-center justify-between p-4 bg-stone-50 rounded-xl border border-stone-100"
>
<div className="flex items-center space-x-4">
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-500 rounded-lg flex items-center justify-center text-white font-bold">
<div className="w-8 h-8 bg-stone-600 rounded-lg flex items-center justify-center text-white font-bold shadow-sm">
#{index + 1}
</div>
<div>
<p className="text-white font-medium">{project.title}</p>
<p className="text-white/60 text-sm">{project.category}</p>
<p className="text-stone-900 font-medium">{project.title}</p>
<p className="text-stone-500 text-sm">{project.category}</p>
</div>
</div>
<div className="text-right">
<p className="text-white font-bold">{project.views.toLocaleString()}</p>
<p className="text-white/60 text-sm">views</p>
<p className="text-stone-900 font-bold">{project.views.toLocaleString()}</p>
<p className="text-stone-500 text-sm">views</p>
</div>
</motion.div>
))}
@@ -389,9 +389,9 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
</div>
{/* Categories Distribution */}
<div className="admin-glass-card p-6 rounded-xl">
<h3 className="text-xl font-bold text-white mb-6 flex items-center">
<BarChart3 className="w-5 h-5 mr-2 text-green-400" />
<div className="bg-white border border-stone-200 p-6 rounded-xl shadow-sm">
<h3 className="text-xl font-bold text-stone-900 mb-6 flex items-center">
<BarChart3 className="w-5 h-5 mr-2 text-green-600" />
Categories
</h3>
<div className="space-y-4">
@@ -405,16 +405,16 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
>
<div className="flex items-center space-x-3">
<div className={`w-4 h-4 rounded-full ${getCategoryColor(index)}`}></div>
<span className="text-white font-medium">{category}</span>
<span className="text-stone-700 font-medium">{category}</span>
</div>
<div className="flex items-center space-x-3">
<div className="w-32 h-2 bg-white/10 rounded-full overflow-hidden">
<div className="w-32 h-2 bg-stone-100 rounded-full overflow-hidden">
<div
className={`h-full ${getCategoryColor(index)} transition-all duration-500`}
style={{ width: `${(count / Math.max(...Object.values(data.categories))) * 100}%` }}
></div>
</div>
<span className="text-white/80 font-medium w-8 text-right">{count}</span>
<span className="text-stone-500 font-medium w-8 text-right">{count}</span>
</div>
</motion.div>
))}
@@ -425,9 +425,9 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
{/* Difficulty & Engagement */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Difficulty Distribution */}
<div className="admin-glass-card p-6 rounded-xl">
<h3 className="text-xl font-bold text-white mb-6 flex items-center">
<Target className="w-5 h-5 mr-2 text-red-400" />
<div className="bg-white border border-stone-200 p-6 rounded-xl shadow-sm">
<h3 className="text-xl font-bold text-stone-900 mb-6 flex items-center">
<Target className="w-5 h-5 mr-2 text-red-500" />
Difficulty Levels
</h3>
<div className="grid grid-cols-2 gap-4">
@@ -448,9 +448,9 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
</div>
{/* Recent Activity */}
<div className="admin-glass-card p-6 rounded-xl">
<h3 className="text-xl font-bold text-white mb-6 flex items-center">
<Activity className="w-5 h-5 mr-2 text-blue-400" />
<div className="bg-white border border-stone-200 p-6 rounded-xl shadow-sm">
<h3 className="text-xl font-bold text-stone-900 mb-6 flex items-center">
<Activity className="w-5 h-5 mr-2 text-blue-600" />
Recent Activity
</h3>
<div className="space-y-4">
@@ -463,25 +463,25 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-center space-x-4 p-3 admin-glass-light rounded-xl"
className="flex items-center space-x-4 p-3 bg-stone-50 rounded-xl border border-stone-100"
>
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<div className="flex-1">
<p className="text-white font-medium text-sm">{project.title}</p>
<p className="text-white/60 text-xs">
<p className="text-stone-900 font-medium text-sm">{project.title}</p>
<p className="text-stone-500 text-xs">
Updated {new Date(project.updatedAt).toLocaleDateString()}
</p>
</div>
<div className="flex items-center space-x-2">
{project.featured && (
<span className="px-2 py-1 bg-purple-500/20 text-purple-400 rounded-full text-xs">
<span className="px-2 py-1 bg-stone-100 text-stone-700 rounded-full text-xs font-medium">
Featured
</span>
)}
<span className={`px-2 py-1 rounded-full text-xs ${
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
project.published
? 'bg-green-500/20 text-green-400'
: 'bg-yellow-500/20 text-yellow-400'
? 'bg-green-100 text-green-700'
: 'bg-yellow-100 text-yellow-700'
}`}>
{project.published ? 'Live' : 'Draft'}
</span>
@@ -496,30 +496,30 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
{/* Reset Modal */}
{showResetModal && (
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div className="fixed inset-0 bg-stone-900/20 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
className="admin-glass-card rounded-2xl p-6 w-full max-w-md"
className="bg-white border border-stone-200 rounded-2xl p-6 w-full max-w-md shadow-xl"
>
<div className="flex items-center space-x-3 mb-4">
<div className="w-10 h-10 bg-red-500/20 rounded-lg flex items-center justify-center">
<AlertTriangle className="w-5 h-5 text-red-400" />
<div className="w-10 h-10 bg-red-100 rounded-lg flex items-center justify-center">
<AlertTriangle className="w-5 h-5 text-red-600" />
</div>
<div>
<h3 className="text-lg font-bold text-white">Reset Analytics Data</h3>
<p className="text-white/60 text-sm">This action cannot be undone</p>
<h3 className="text-lg font-bold text-stone-900">Reset Analytics Data</h3>
<p className="text-stone-500 text-sm">This action cannot be undone</p>
</div>
</div>
<div className="space-y-4 mb-6">
<div>
<label className="block text-white/80 text-sm mb-2">Reset Type</label>
<label className="block text-stone-600 text-sm mb-2">Reset Type</label>
<select
value={resetType}
onChange={(e) => setResetType(e.target.value as 'all' | 'performance' | 'analytics')}
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-red-500"
className="w-full px-3 py-2 bg-stone-50 border border-stone-200 rounded-lg text-stone-900 focus:outline-none focus:ring-2 focus:ring-red-500"
>
<option value="analytics">Analytics Only (views, likes, shares)</option>
<option value="pageviews">Page Views Only</option>
@@ -529,10 +529,10 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
</select>
</div>
<div className="bg-red-500/10 border border-red-500/20 rounded-lg p-3">
<div className="bg-red-50 border border-red-100 rounded-lg p-3">
<div className="flex items-start space-x-2">
<AlertTriangle className="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
<div className="text-sm text-red-300">
<AlertTriangle className="w-4 h-4 text-red-500 mt-0.5 flex-shrink-0" />
<div className="text-sm text-red-700">
<p className="font-medium mb-1">Warning:</p>
<p>This will permanently delete the selected analytics data. This action cannot be reversed.</p>
</div>
@@ -544,14 +544,14 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
<button
onClick={() => setShowResetModal(false)}
disabled={resetting}
className="flex-1 px-4 py-2 admin-glass-light text-white rounded-lg hover:scale-105 transition-all disabled:opacity-50"
className="flex-1 px-4 py-2 bg-white border border-stone-200 text-stone-700 rounded-lg hover:bg-stone-50 transition-all disabled:opacity-50"
>
Cancel
</button>
<button
onClick={resetAnalytics}
disabled={resetting}
className="flex-1 flex items-center justify-center space-x-2 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg hover:scale-105 transition-all disabled:opacity-50"
className="flex-1 flex items-center justify-center space-x-2 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-all disabled:opacity-50"
>
{resetting ? (
<>

View File

@@ -143,7 +143,7 @@ export const EmailManager: React.FC = () => {
case 'high': return 'text-red-400';
case 'medium': return 'text-yellow-400';
case 'low': return 'text-green-400';
default: return 'text-blue-400';
default: return 'text-stone-400';
}
};
@@ -153,7 +153,7 @@ export const EmailManager: React.FC = () => {
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
className="w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full"
className="w-8 h-8 border-2 border-stone-500 border-t-transparent rounded-full"
/>
</div>
);
@@ -164,12 +164,12 @@ export const EmailManager: React.FC = () => {
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-white">Email Manager</h2>
<p className="text-white/70 mt-1">Manage your contact messages</p>
<h2 className="text-2xl font-bold text-stone-900">Email Manager</h2>
<p className="text-stone-500 mt-1">Manage your contact messages</p>
</div>
<button
onClick={loadMessages}
className="flex items-center space-x-2 px-4 py-2 bg-blue-500/20 text-blue-400 rounded-lg hover:bg-blue-500/30 transition-colors"
className="flex items-center space-x-2 px-4 py-2 bg-stone-100 text-stone-700 rounded-lg hover:bg-stone-200 transition-colors"
>
<RefreshCw className="w-4 h-4" />
<span>Refresh</span>
@@ -179,13 +179,13 @@ export const EmailManager: React.FC = () => {
{/* Filters and Search */}
<div className="flex flex-col sm:flex-row gap-4">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-white/50 w-4 h-4" />
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-stone-400 w-4 h-4" />
<input
type="text"
placeholder="Search messages..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full pl-10 pr-4 py-2 bg-white border border-stone-200 rounded-lg text-stone-900 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-400"
/>
</div>
<div className="flex space-x-2">
@@ -195,8 +195,8 @@ export const EmailManager: React.FC = () => {
onClick={() => setFilter(filterType as 'all' | 'unread' | 'responded')}
className={`px-4 py-2 rounded-lg transition-colors ${
filter === filterType
? 'bg-blue-500 text-white'
: 'bg-white/10 text-white/70 hover:bg-white/20'
? 'bg-stone-900 text-stone-50'
: 'bg-white border border-stone-200 text-stone-600 hover:bg-stone-50'
}`}
>
{filterType.charAt(0).toUpperCase() + filterType.slice(1)}
@@ -209,7 +209,7 @@ export const EmailManager: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-1 space-y-3">
{filteredMessages.length === 0 ? (
<div className="text-center py-12 text-white/50">
<div className="text-center py-12 text-stone-400">
<Mail className="w-12 h-12 mx-auto mb-4 opacity-50" />
<p>No messages found</p>
</div>
@@ -219,36 +219,36 @@ export const EmailManager: React.FC = () => {
key={message.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className={`p-4 rounded-lg cursor-pointer transition-all ${
className={`p-4 rounded-lg cursor-pointer transition-all border ${
selectedMessage?.id === message.id
? 'bg-blue-500/20 border border-blue-500/50'
: 'bg-white/5 border border-white/10 hover:bg-white/10'
? 'bg-stone-100 border-stone-300 shadow-sm'
: 'bg-white border-stone-200 hover:bg-stone-50'
}`}
onClick={() => handleMessageClick(message)}
>
<div className="flex items-start justify-between mb-2">
<h3 className="font-semibold text-white truncate">{message.subject}</h3>
<h3 className="font-semibold text-stone-900 truncate">{message.subject}</h3>
<div className="flex items-center space-x-2">
{!message.read && <Circle className="w-3 h-3 text-blue-400" />}
{message.responded && <CheckCircle className="w-3 h-3 text-green-400" />}
{!message.read && <Circle className="w-3 h-3 text-stone-600" />}
{message.responded && <CheckCircle className="w-3 h-3 text-green-500" />}
</div>
</div>
<p className="text-white/70 text-sm mb-2">{message.name}</p>
<p className="text-white/50 text-xs">{formatDate(message.createdAt)}</p>
<p className="text-stone-600 text-sm mb-2">{message.name}</p>
<p className="text-stone-400 text-xs">{formatDate(message.createdAt)}</p>
</motion.div>
))
)}
</div>
{/* Message Detail */}
<div className="lg:col-span-2 admin-glass-card p-6 rounded-xl">
<div className="lg:col-span-2 admin-glass-card p-6 rounded-xl bg-white border border-stone-200">
{selectedMessage ? (
<div className="space-y-6">
{/* Message Header */}
<div className="flex items-start justify-between">
<div className="space-y-2">
<h3 className="text-xl font-bold text-white">{selectedMessage.subject}</h3>
<div className="flex items-center space-x-4 text-sm text-white/70">
<h3 className="text-xl font-bold text-stone-900">{selectedMessage.subject}</h3>
<div className="flex items-center space-x-4 text-sm text-stone-500">
<div className="flex items-center space-x-2">
<User className="w-4 h-4" />
<span>{selectedMessage.name}</span>
@@ -264,15 +264,15 @@ export const EmailManager: React.FC = () => {
</div>
</div>
<div className="flex items-center space-x-2">
{!selectedMessage.read && <Circle className="w-4 h-4 text-blue-400" />}
{selectedMessage.responded && <CheckCircle className="w-4 h-4 text-green-400" />}
{!selectedMessage.read && <Circle className="w-4 h-4 text-stone-600" />}
{selectedMessage.responded && <CheckCircle className="w-4 h-4 text-green-500" />}
</div>
</div>
{/* Message Body */}
<div className="p-4 bg-white/5 rounded-lg border border-white/10">
<h4 className="text-white font-medium mb-3">Message:</h4>
<div className="text-white/80 whitespace-pre-wrap leading-relaxed">
<div className="p-4 bg-stone-50 rounded-lg border border-stone-200">
<h4 className="text-stone-700 font-medium mb-3">Message:</h4>
<div className="text-stone-600 whitespace-pre-wrap leading-relaxed">
{selectedMessage.message}
</div>
</div>
@@ -281,21 +281,21 @@ export const EmailManager: React.FC = () => {
<div className="flex space-x-3">
<button
onClick={() => setShowReplyModal(true)}
className="flex items-center space-x-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
className="flex items-center space-x-2 px-4 py-2 bg-stone-900 text-stone-50 rounded-lg hover:bg-stone-800 transition-colors"
>
<Reply className="w-4 h-4" />
<span>Reply</span>
</button>
<button
onClick={() => setSelectedMessage(null)}
className="px-4 py-2 bg-white/10 text-white rounded-lg hover:bg-white/20 transition-colors"
className="px-4 py-2 bg-white border border-stone-200 text-stone-600 rounded-lg hover:bg-stone-50 transition-colors"
>
Close
</button>
</div>
</div>
) : (
<div className="text-center py-12 text-white/50">
<div className="text-center py-12 text-stone-400">
<Eye className="w-12 h-12 mx-auto mb-4 opacity-50" />
<p>Select a message to view details</p>
</div>
@@ -311,23 +311,23 @@ export const EmailManager: React.FC = () => {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
className="fixed inset-0 bg-stone-900/20 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={() => setShowReplyModal(false)}
>
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
className="bg-gray-900/95 backdrop-blur-xl border border-white/20 rounded-2xl p-6 max-w-2xl w-full"
className="bg-white border border-stone-200 rounded-2xl p-6 max-w-2xl w-full shadow-xl"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold text-white">Reply to {selectedMessage.name}</h2>
<h2 className="text-xl font-bold text-stone-900">Reply to {selectedMessage.name}</h2>
<button
onClick={() => setShowReplyModal(false)}
className="p-2 hover:bg-white/10 rounded-lg transition-colors"
className="p-2 hover:bg-stone-100 rounded-lg transition-colors"
>
<X className="w-5 h-5 text-white/70" />
<X className="w-5 h-5 text-stone-500" />
</button>
</div>
@@ -336,20 +336,20 @@ export const EmailManager: React.FC = () => {
value={replyContent}
onChange={(e) => setReplyContent(e.target.value)}
placeholder="Type your reply..."
className="w-full h-32 p-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
className="w-full h-32 p-3 bg-stone-50 border border-stone-200 rounded-lg text-stone-900 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-400 resize-none"
/>
<div className="flex space-x-3">
<button
onClick={handleReply}
className="flex items-center space-x-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
className="flex items-center space-x-2 px-4 py-2 bg-stone-900 text-stone-50 rounded-lg hover:bg-stone-800 transition-colors"
>
<Send className="w-4 h-4" />
<span>Send Reply</span>
</button>
<button
onClick={() => setShowReplyModal(false)}
className="px-4 py-2 bg-white/10 text-white rounded-lg hover:bg-white/20 transition-colors"
className="px-4 py-2 bg-white border border-stone-200 text-stone-600 rounded-lg hover:bg-stone-50 transition-colors"
>
Cancel
</button>

View File

@@ -85,19 +85,19 @@ export const EmailResponder: React.FC<EmailResponderProps> = ({
return (
<>
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="fixed inset-0 bg-stone-900/20 backdrop-blur-sm flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto border border-stone-200">
{/* Header */}
<div className="bg-gradient-to-r from-blue-600 to-purple-600 text-white p-6 rounded-t-2xl">
<div className="bg-stone-50 border-b border-stone-200 text-stone-900 p-6 rounded-t-2xl">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold">📧 E-Mail Antwort senden</h2>
<p className="text-blue-100 mt-1">Wähle ein schönes Template für deine Antwort</p>
<p className="text-stone-500 mt-1">Wähle ein schönes Template für deine Antwort</p>
</div>
<button
onClick={onClose}
className="text-white hover:text-gray-200 transition-colors"
className="text-stone-400 hover:text-stone-600 transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -110,54 +110,54 @@ export const EmailResponder: React.FC<EmailResponderProps> = ({
<div className="p-6">
{/* Contact Info */}
<div className="bg-gray-50 rounded-xl p-4 mb-6">
<h3 className="font-semibold text-gray-800 mb-2">📬 Kontakt-Informationen</h3>
<div className="bg-stone-50 border border-stone-200 rounded-xl p-4 mb-6">
<h3 className="font-semibold text-stone-800 mb-2">📬 Kontakt-Informationen</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<span className="text-sm text-gray-600">Name:</span>
<p className="font-medium text-gray-900">{contactName}</p>
<span className="text-sm text-stone-500">Name:</span>
<p className="font-medium text-stone-900">{contactName}</p>
</div>
<div>
<span className="text-sm text-gray-600">E-Mail:</span>
<p className="font-medium text-gray-900">{contactEmail}</p>
<span className="text-sm text-stone-500">E-Mail:</span>
<p className="font-medium text-stone-900">{contactEmail}</p>
</div>
</div>
</div>
{/* Original Message Preview */}
<div className="bg-blue-50 rounded-xl p-4 mb-6">
<h3 className="font-semibold text-blue-800 mb-2">💬 Ursprüngliche Nachricht</h3>
<div className="bg-white rounded-lg p-3 border-l-4 border-blue-500">
<p className="text-gray-700 text-sm whitespace-pre-wrap">{originalMessage}</p>
<div className="bg-stone-50 border border-stone-200 rounded-xl p-4 mb-6">
<h3 className="font-semibold text-stone-800 mb-2">💬 Ursprüngliche Nachricht</h3>
<div className="bg-white rounded-lg p-3 border-l-4 border-blue-500 shadow-sm">
<p className="text-stone-700 text-sm whitespace-pre-wrap">{originalMessage}</p>
</div>
</div>
{/* Template Selection */}
<div className="mb-6">
<h3 className="font-semibold text-gray-800 mb-4">🎨 Template auswählen</h3>
<h3 className="font-semibold text-stone-800 mb-4">🎨 Template auswählen</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{Object.entries(templates).map(([key, template]) => (
<div
key={key}
className={`relative cursor-pointer rounded-xl border-2 transition-all duration-200 ${
selectedTemplate === key
? 'border-blue-500 bg-blue-50 shadow-lg scale-105'
: 'border-gray-200 hover:border-gray-300 hover:shadow-md'
? 'border-stone-500 bg-stone-50 shadow-md'
: 'border-stone-200 hover:border-stone-300 hover:shadow-sm'
}`}
onClick={() => setSelectedTemplate(key as keyof typeof templates)}
>
<div className={`bg-gradient-to-r ${template.color} text-white p-4 rounded-t-xl`}>
<div className={`p-4 rounded-t-xl bg-white border-b border-stone-100`}>
<div className="text-center">
<div className="text-3xl mb-2">{template.icon}</div>
<h4 className="font-bold text-lg">{template.name}</h4>
<h4 className="font-bold text-lg text-stone-900">{template.name}</h4>
</div>
</div>
<div className="p-4">
<p className="text-sm text-gray-600 text-center">{template.description}</p>
<p className="text-sm text-stone-600 text-center">{template.description}</p>
</div>
{selectedTemplate === key && (
<div className="absolute top-2 right-2">
<div className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
<div className="w-6 h-6 bg-stone-600 rounded-full flex items-center justify-center">
<svg className="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
@@ -171,15 +171,15 @@ export const EmailResponder: React.FC<EmailResponderProps> = ({
{/* Preview */}
<div className="mb-6">
<h3 className="font-semibold text-gray-800 mb-4">👀 Vorschau</h3>
<div className="bg-gray-100 rounded-xl p-4">
<div className="bg-white rounded-lg shadow-sm border">
<div className={`bg-gradient-to-r ${templates[selectedTemplate].color} text-white p-4 rounded-t-lg`}>
<h4 className="font-bold text-lg">{templates[selectedTemplate].icon} {templates[selectedTemplate].name}</h4>
<p className="text-sm opacity-90">An: {contactName}</p>
<h3 className="font-semibold text-stone-800 mb-4">👀 Vorschau</h3>
<div className="bg-stone-100 rounded-xl p-4 border border-stone-200">
<div className="bg-white rounded-lg shadow-sm border border-stone-200">
<div className="p-4 rounded-t-lg bg-stone-50 border-b border-stone-100">
<h4 className="font-bold text-lg text-stone-900">{templates[selectedTemplate].icon} {templates[selectedTemplate].name}</h4>
<p className="text-sm text-stone-500">An: {contactName}</p>
</div>
<div className="p-4">
<p className="text-sm text-gray-600">
<p className="text-sm text-stone-600">
{selectedTemplate === 'welcome' && 'Freundliche Begrüßung mit Portfolio-Links und nächsten Schritten'}
{selectedTemplate === 'project' && 'Professionelle Projekt-Antwort mit Arbeitsprozess und CTA'}
{selectedTemplate === 'quick' && 'Schnelle, kurze Bestätigung der Nachricht'}
@@ -193,14 +193,14 @@ export const EmailResponder: React.FC<EmailResponderProps> = ({
<div className="flex gap-4">
<button
onClick={onClose}
className="flex-1 px-6 py-3 border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 transition-colors font-medium"
className="flex-1 px-6 py-3 border border-stone-300 text-stone-700 rounded-xl hover:bg-stone-50 transition-colors font-medium"
>
Abbrechen
</button>
<button
onClick={handleSendEmail}
disabled={isLoading}
className="flex-1 px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-xl hover:from-blue-700 hover:to-purple-700 transition-all font-medium disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
className="flex-1 px-6 py-3 bg-stone-900 text-white rounded-xl hover:bg-stone-800 transition-all font-medium disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
>
{isLoading ? (
<>

View File

@@ -99,23 +99,23 @@ export default function ImportExport() {
};
return (
<div className="admin-glass-card rounded-lg p-6">
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
<FileText className="w-5 h-5 mr-2 text-blue-400" />
<div className="bg-white border border-stone-200 rounded-xl p-6">
<h3 className="text-lg font-semibold text-stone-900 mb-4 flex items-center">
<FileText className="w-5 h-5 mr-2 text-stone-600" />
Import & Export
</h3>
<div className="space-y-4">
{/* Export Section */}
<div className="admin-glass-light rounded-lg p-4">
<h4 className="font-medium text-white mb-2">Export Projekte</h4>
<p className="text-sm text-white/70 mb-3">
<div className="bg-stone-50 border border-stone-200 rounded-xl p-4">
<h4 className="font-medium text-stone-900 mb-2">Export Projekte</h4>
<p className="text-sm text-stone-600 mb-3">
Alle Projekte als JSON-Datei herunterladen
</p>
<button
onClick={handleExport}
disabled={isExporting}
className="flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 hover:scale-105 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
className="flex items-center px-4 py-2 bg-stone-900 text-white rounded-lg hover:bg-stone-800 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<Download className="w-4 h-4 mr-2" />
{isExporting ? 'Exportiere...' : 'Exportieren'}
@@ -123,12 +123,12 @@ export default function ImportExport() {
</div>
{/* Import Section */}
<div className="admin-glass-light rounded-lg p-4">
<h4 className="font-medium text-white mb-2">Import Projekte</h4>
<p className="text-sm text-white/70 mb-3">
<div className="bg-stone-50 border border-stone-200 rounded-xl p-4">
<h4 className="font-medium text-stone-900 mb-2">Import Projekte</h4>
<p className="text-sm text-stone-600 mb-3">
JSON-Datei mit Projekten hochladen
</p>
<label className="flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 hover:scale-105 transition-all cursor-pointer">
<label className="flex items-center px-4 py-2 bg-stone-900 text-white rounded-lg hover:bg-stone-800 transition-colors cursor-pointer w-fit">
<Upload className="w-4 h-4 mr-2" />
{isImporting ? 'Importiere...' : 'Datei auswählen'}
<input
@@ -143,16 +143,16 @@ export default function ImportExport() {
{/* Import Results */}
{importResult && (
<div className="admin-glass-light rounded-lg p-4">
<h4 className="font-medium text-white mb-2 flex items-center">
<div className="bg-stone-50 border border-stone-200 rounded-xl p-4">
<h4 className="font-medium text-stone-900 mb-2 flex items-center">
{importResult.success ? (
<CheckCircle className="w-5 h-5 mr-2 text-green-400" />
<CheckCircle className="w-5 h-5 mr-2 text-green-600" />
) : (
<AlertCircle className="w-5 h-5 mr-2 text-red-400" />
<AlertCircle className="w-5 h-5 mr-2 text-red-600" />
)}
Import Ergebnis
</h4>
<div className="text-sm text-white/70 space-y-1">
<div className="text-sm text-stone-600 space-y-1">
<p><strong>Importiert:</strong> {importResult.results.imported}</p>
<p><strong>Übersprungen:</strong> {importResult.results.skipped}</p>
{importResult.results.errors.length > 0 && (
@@ -160,7 +160,7 @@ export default function ImportExport() {
<p><strong>Fehler:</strong></p>
<ul className="list-disc list-inside ml-4">
{importResult.results.errors.map((error, index) => (
<li key={index} className="text-red-400">{error}</li>
<li key={index} className="text-red-600">{error}</li>
))}
</ul>
</div>

View File

@@ -343,7 +343,7 @@ const ModernAdminDashboard: React.FC<ModernAdminDashboardProps> = ({ isAuthentic
<Activity size={20} className="text-stone-400" />
</div>
<p className="text-xl md:text-2xl font-bold text-stone-900">{stats.totalViews.toLocaleString()}</p>
<p className="text-blue-600 text-xs font-medium">{stats.totalUsers} users</p>
<p className="text-stone-600 text-xs font-medium">{stats.totalUsers} users</p>
</div>
</div>

View File

@@ -75,7 +75,7 @@ export const PerformanceDashboard: React.FC = () => {
setIsVisible(true);
trackEvent('dashboard-toggle', { action: 'show' });
}}
className="fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow-lg hover:bg-blue-700 transition-colors z-50"
className="fixed bottom-4 right-4 bg-white text-stone-700 border border-stone-200 px-4 py-2 rounded-lg shadow-md hover:bg-stone-50 transition-colors z-50"
>
📊 Performance
</button>

View File

@@ -52,7 +52,6 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState<string>('all');
// Editor is now a separate page - no modal state needed
const categories = ['all', 'Web Development', 'Full-Stack', 'Web Application', 'Mobile App', 'Design'];
@@ -77,10 +76,6 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
}
};
// closeEditor removed - editor is now separate page
// saveProject removed - editor is now separate page
const deleteProject = async (projectId: string) => {
if (!confirm('Are you sure you want to delete this project?')) return;
@@ -100,9 +95,9 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
const getStatusColor = (project: Project) => {
if (project.published) {
return project.featured ? 'text-purple-400 bg-purple-500/20' : 'text-green-400 bg-green-500/20';
return project.featured ? 'text-stone-700 bg-stone-200' : 'text-green-700 bg-green-100';
}
return 'text-yellow-400 bg-yellow-500/20';
return 'text-yellow-700 bg-yellow-100';
};
const getStatusText = (project: Project) => {
@@ -117,20 +112,20 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-white">Project Management</h1>
<p className="text-white/80">{projects.length} projects {projects.filter(p => p.published).length} published</p>
<h1 className="text-3xl font-bold text-stone-900">Project Management</h1>
<p className="text-stone-500">{projects.length} projects {projects.filter(p => p.published).length} published</p>
</div>
<div className="flex items-center space-x-3">
<button
onClick={onProjectsChange}
className="flex items-center space-x-2 px-4 py-2 admin-glass-light rounded-xl hover:scale-105 transition-all duration-200"
className="flex items-center space-x-2 px-4 py-2 bg-stone-100 border border-stone-200 rounded-xl hover:bg-stone-200 transition-all duration-200"
>
<RefreshCw className="w-4 h-4 text-blue-400" />
<span className="text-white font-medium">Refresh</span>
<RefreshCw className="w-4 h-4 text-stone-600" />
<span className="text-stone-700 font-medium">Refresh</span>
</button>
<button
onClick={() => openEditor()}
className="flex items-center space-x-2 px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-xl hover:scale-105 transition-all duration-200 shadow-lg"
className="flex items-center space-x-2 px-6 py-2 bg-stone-900 text-white rounded-xl hover:bg-stone-800 transition-all duration-200 shadow-md"
>
<Plus size={18} />
<span className="font-medium">New Project</span>
@@ -142,13 +137,13 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
<div className="flex flex-col sm:flex-row gap-4">
{/* Search */}
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-white/60" />
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-stone-400" />
<input
type="text"
placeholder="Search projects..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-3 admin-glass-light border border-white/30 rounded-xl text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full pl-10 pr-4 py-3 bg-white border border-stone-200 rounded-xl text-stone-900 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-400"
/>
</div>
@@ -156,23 +151,23 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
<select
value={selectedCategory}
onChange={(e) => setSelectedCategory(e.target.value)}
className="px-4 py-3 admin-glass-light border border-white/30 rounded-xl text-white focus:outline-none focus:ring-2 focus:ring-blue-500 bg-transparent"
className="px-4 py-3 bg-white border border-stone-200 rounded-xl text-stone-900 focus:outline-none focus:ring-2 focus:ring-stone-400"
>
{categories.map(category => (
<option key={category} value={category} className="bg-gray-800">
<option key={category} value={category} className="bg-white text-stone-900">
{category === 'all' ? 'All Categories' : category}
</option>
))}
</select>
{/* View Toggle */}
<div className="flex items-center space-x-1 admin-glass-light rounded-xl p-1">
<div className="flex items-center space-x-1 bg-white border border-stone-200 rounded-xl p-1">
<button
onClick={() => setViewMode('grid')}
className={`p-2 rounded-lg transition-all duration-200 ${
viewMode === 'grid'
? 'bg-blue-500/40 text-blue-300'
: 'text-white/70 hover:text-white hover:bg-white/10'
? 'bg-stone-100 text-stone-900'
: 'text-stone-400 hover:text-stone-900 hover:bg-stone-50'
}`}
>
<Grid className="w-4 h-4" />
@@ -181,8 +176,8 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
onClick={() => setViewMode('list')}
className={`p-2 rounded-lg transition-all duration-200 ${
viewMode === 'list'
? 'bg-blue-500/40 text-blue-300'
: 'text-white/70 hover:text-white hover:bg-white/10'
? 'bg-stone-100 text-stone-900'
: 'text-stone-400 hover:text-stone-900 hover:bg-stone-50'
}`}
>
<List className="w-4 h-4" />
@@ -198,24 +193,24 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
key={project.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="admin-glass-card p-6 rounded-xl hover:scale-105 transition-all duration-300 group"
className="admin-glass-card p-6 rounded-xl hover:shadow-lg transition-all duration-300 group bg-white border border-stone-200"
>
{/* Project Header */}
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<h3 className="text-xl font-bold text-white mb-1">{project.title}</h3>
<p className="text-white/70 text-sm">{project.category}</p>
<h3 className="text-xl font-bold text-stone-900 mb-1">{project.title}</h3>
<p className="text-stone-500 text-sm">{project.category}</p>
</div>
<div className="flex items-center space-x-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => openEditor(project)}
className="p-2 text-white/70 hover:text-blue-400 hover:bg-white/10 rounded-lg transition-colors"
className="p-2 text-stone-500 hover:text-stone-700 hover:bg-stone-100 rounded-lg transition-colors"
>
<Edit size={16} />
</button>
<button
onClick={() => deleteProject(project.id)}
className="p-2 text-white/70 hover:text-red-400 hover:bg-white/10 rounded-lg transition-colors"
className="p-2 text-stone-500 hover:text-red-600 hover:bg-stone-100 rounded-lg transition-colors"
>
<Trash2 size={16} />
</button>
@@ -225,7 +220,7 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
{/* Project Content */}
<div className="space-y-4">
<div>
<p className="text-white/70 text-sm line-clamp-2 leading-relaxed">{project.description}</p>
<p className="text-stone-600 text-sm line-clamp-2 leading-relaxed">{project.description}</p>
</div>
{/* Tags */}
@@ -234,13 +229,13 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
{project.tags.slice(0, 3).map((tag) => (
<span
key={tag}
className="px-2 py-1 bg-white/10 text-white/70 rounded-full text-xs"
className="px-2 py-1 bg-stone-100 text-stone-600 border border-stone-200 rounded-full text-xs"
>
{tag}
</span>
))}
{project.tags.length > 3 && (
<span className="px-2 py-1 bg-white/10 text-white/70 rounded-full text-xs">
<span className="px-2 py-1 bg-stone-100 text-stone-600 border border-stone-200 rounded-full text-xs">
+{project.tags.length - 3}
</span>
)}
@@ -258,7 +253,7 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
href={project.github}
target="_blank"
rel="noopener noreferrer"
className="p-1 text-white/60 hover:text-white transition-colors"
className="p-1 text-stone-400 hover:text-stone-900 transition-colors"
>
<Github size={14} />
</a>
@@ -268,7 +263,7 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
href={project.live}
target="_blank"
rel="noopener noreferrer"
className="p-1 text-white/60 hover:text-white transition-colors"
className="p-1 text-stone-400 hover:text-stone-900 transition-colors"
>
<Globe size={14} />
</a>
@@ -277,18 +272,18 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
</div>
{/* Analytics */}
<div className="grid grid-cols-3 gap-2 pt-3 border-t border-white/10">
<div className="grid grid-cols-3 gap-2 pt-3 border-t border-stone-100">
<div className="text-center">
<p className="text-white font-bold text-sm">{project.analytics?.views || 0}</p>
<p className="text-white/60 text-xs">Views</p>
<p className="text-stone-900 font-bold text-sm">{project.analytics?.views || 0}</p>
<p className="text-stone-500 text-xs">Views</p>
</div>
<div className="text-center">
<p className="text-white font-bold text-sm">{project.analytics?.likes || 0}</p>
<p className="text-white/60 text-xs">Likes</p>
<p className="text-stone-900 font-bold text-sm">{project.analytics?.likes || 0}</p>
<p className="text-stone-500 text-xs">Likes</p>
</div>
<div className="text-center">
<p className="text-white font-bold text-sm">{project.performance?.lighthouse || 90}</p>
<p className="text-white/60 text-xs">Score</p>
<p className="text-stone-900 font-bold text-sm">{project.performance?.lighthouse || 90}</p>
<p className="text-stone-500 text-xs">Score</p>
</div>
</div>
</div>
@@ -302,13 +297,13 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
key={project.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="admin-glass-card p-6 rounded-xl hover:scale-[1.01] transition-all duration-300 group"
className="admin-glass-card p-6 rounded-xl hover:shadow-md transition-all duration-300 group bg-white border border-stone-200"
>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="flex-1">
<h3 className="text-white font-bold text-lg">{project.title}</h3>
<p className="text-white/70 text-sm">{project.category}</p>
<h3 className="text-stone-900 font-bold text-lg">{project.title}</h3>
<p className="text-stone-500 text-sm">{project.category}</p>
</div>
</div>
@@ -316,7 +311,7 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(project)}`}>
{getStatusText(project)}
</span>
<div className="flex items-center space-x-3 text-white/60 text-sm">
<div className="flex items-center space-x-3 text-stone-500 text-sm">
<span>{project.analytics?.views || 0} views</span>
<span></span>
<span>{new Date(project.updatedAt).toLocaleDateString()}</span>
@@ -324,13 +319,13 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
<div className="flex items-center space-x-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => openEditor(project)}
className="p-2 text-white/70 hover:text-blue-400 hover:bg-white/10 rounded-lg transition-colors"
className="p-2 text-stone-500 hover:text-stone-700 hover:bg-stone-100 rounded-lg transition-colors"
>
<Edit size={16} />
</button>
<button
onClick={() => deleteProject(project.id)}
className="p-2 text-white/70 hover:text-red-400 hover:bg-white/10 rounded-lg transition-colors"
className="p-2 text-stone-500 hover:text-red-600 hover:bg-stone-100 rounded-lg transition-colors"
>
<Trash2 size={16} />
</button>
@@ -341,8 +336,6 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
))}
</div>
)}
{/* Editor is now a separate page at /editor */}
</div>
);
};

View File

@@ -50,9 +50,9 @@ const ToastItem = ({ toast, onRemove }: ToastProps) => {
case 'warning':
return <AlertTriangle className="w-5 h-5 text-yellow-400" />;
case 'info':
return <Info className="w-5 h-5 text-blue-400" />;
return <Info className="w-5 h-5 text-stone-400" />;
default:
return <Info className="w-5 h-5 text-blue-400" />;
return <Info className="w-5 h-5 text-stone-400" />;
}
};
@@ -112,7 +112,7 @@ const ToastItem = ({ toast, onRemove }: ToastProps) => {
initial={{ width: '100%' }}
animate={{ width: '0%' }}
transition={{ duration: (toast.duration || 5000) / 1000, ease: "linear" }}
className="absolute bottom-0 left-0 h-1 bg-gradient-to-r from-blue-400 to-green-400 rounded-b-xl"
className="absolute bottom-0 left-0 h-1 bg-gradient-to-r from-stone-400 to-stone-600 rounded-b-xl"
/>
)}
</motion.div>