huge update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
BarChart3,
|
||||
@@ -8,16 +8,12 @@ import {
|
||||
Eye,
|
||||
Heart,
|
||||
Zap,
|
||||
Users,
|
||||
Clock,
|
||||
Globe,
|
||||
Activity,
|
||||
Target,
|
||||
Award,
|
||||
RefreshCw,
|
||||
Calendar,
|
||||
MousePointer,
|
||||
Monitor,
|
||||
RotateCcw,
|
||||
Trash2,
|
||||
AlertTriangle
|
||||
@@ -76,7 +72,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
|
||||
const [resetType, setResetType] = useState<'analytics' | 'pageviews' | 'interactions' | 'performance' | 'all'>('analytics');
|
||||
const [resetting, setResetting] = useState(false);
|
||||
|
||||
const fetchAnalyticsData = async () => {
|
||||
const fetchAnalyticsData = useCallback(async () => {
|
||||
if (!isAuthenticated) return;
|
||||
|
||||
try {
|
||||
@@ -132,7 +128,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const resetAnalytics = async () => {
|
||||
if (!isAuthenticated || resetting) return;
|
||||
@@ -167,7 +163,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
|
||||
if (isAuthenticated) {
|
||||
fetchAnalyticsData();
|
||||
}
|
||||
}, [isAuthenticated, timeRange]);
|
||||
}, [isAuthenticated, fetchAnalyticsData]);
|
||||
|
||||
const StatCard = ({ title, value, icon: Icon, color, trend, trendValue, description }: {
|
||||
title: string;
|
||||
@@ -530,7 +526,7 @@ export function AnalyticsDashboard({ isAuthenticated }: AnalyticsDashboardProps)
|
||||
<label className="block text-white/80 text-sm mb-2">Reset Type</label>
|
||||
<select
|
||||
value={resetType}
|
||||
onChange={(e) => setResetType(e.target.value as any)}
|
||||
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"
|
||||
>
|
||||
<option value="analytics">Analytics Only (views, likes, shares)</option>
|
||||
|
||||
@@ -5,11 +5,7 @@ import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
Mail,
|
||||
Search,
|
||||
Filter,
|
||||
Reply,
|
||||
Archive,
|
||||
Trash2,
|
||||
Clock,
|
||||
User,
|
||||
CheckCircle,
|
||||
Circle,
|
||||
@@ -54,7 +50,7 @@ export const EmailManager: React.FC = () => {
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const formattedMessages = data.contacts.map((contact: any) => ({
|
||||
const formattedMessages = data.contacts.map((contact: ContactMessage) => ({
|
||||
id: contact.id.toString(),
|
||||
name: contact.name,
|
||||
email: contact.email,
|
||||
@@ -141,6 +137,7 @@ export const EmailManager: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const getPriorityColor = (priority: string) => {
|
||||
switch (priority) {
|
||||
case 'high': return 'text-red-400';
|
||||
@@ -195,7 +192,7 @@ export const EmailManager: React.FC = () => {
|
||||
{['all', 'unread', 'responded'].map((filterType) => (
|
||||
<button
|
||||
key={filterType}
|
||||
onClick={() => setFilter(filterType as any)}
|
||||
onClick={() => setFilter(filterType as 'all' | 'unread' | 'responded')}
|
||||
className={`px-4 py-2 rounded-lg transition-colors ${
|
||||
filter === filterType
|
||||
? 'bg-blue-500 text-white'
|
||||
|
||||
@@ -6,37 +6,21 @@ import {
|
||||
Save,
|
||||
X,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Settings,
|
||||
Link as LinkIcon,
|
||||
Tag,
|
||||
Calendar,
|
||||
Globe,
|
||||
Github,
|
||||
Image as ImageIcon,
|
||||
Bold,
|
||||
Italic,
|
||||
List,
|
||||
Hash,
|
||||
Quote,
|
||||
Code,
|
||||
Zap,
|
||||
Type,
|
||||
Columns,
|
||||
PanelLeft,
|
||||
PanelRight,
|
||||
Monitor,
|
||||
Smartphone,
|
||||
Tablet,
|
||||
Undo,
|
||||
Redo,
|
||||
AlignLeft,
|
||||
AlignCenter,
|
||||
AlignRight,
|
||||
Link2,
|
||||
ListOrdered,
|
||||
Underline,
|
||||
Strikethrough
|
||||
Strikethrough,
|
||||
Type,
|
||||
Columns
|
||||
} from 'lucide-react';
|
||||
|
||||
interface Project {
|
||||
@@ -60,7 +44,7 @@ interface GhostEditorProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
project?: Project | null;
|
||||
onSave: (projectData: any) => void;
|
||||
onSave: (projectData: Partial<Project>) => void;
|
||||
isCreating: boolean;
|
||||
}
|
||||
|
||||
@@ -251,7 +235,7 @@ export const GhostEditor: React.FC<GhostEditorProps> = ({
|
||||
// Render markdown preview
|
||||
const renderMarkdownPreview = (markdown: string) => {
|
||||
// Simple markdown renderer for preview
|
||||
let html = markdown
|
||||
const html = markdown
|
||||
// Headers
|
||||
.replace(/^### (.*$)/gim, '<h3 class="text-xl font-semibold text-white mb-3 mt-6">$1</h3>')
|
||||
.replace(/^## (.*$)/gim, '<h2 class="text-2xl font-bold text-white mb-4 mt-8">$1</h2>')
|
||||
|
||||
@@ -7,8 +7,6 @@ import {
|
||||
Settings,
|
||||
TrendingUp,
|
||||
Plus,
|
||||
Edit,
|
||||
Trash2,
|
||||
Shield,
|
||||
Users,
|
||||
Activity,
|
||||
@@ -56,6 +54,7 @@ interface ModernAdminDashboardProps {
|
||||
const ModernAdminDashboard: React.FC<ModernAdminDashboardProps> = ({ isAuthenticated = true }) => {
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'projects' | 'emails' | 'analytics' | 'settings'>('overview');
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [analytics, setAnalytics] = useState<Record<string, unknown> | null>(null);
|
||||
@@ -540,7 +539,7 @@ const ModernAdminDashboard: React.FC<ModernAdminDashboardProps> = ({ isAuthentic
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ProjectManager isAuthenticated={isAuthenticated} projects={projects} onProjectsChange={loadProjects} />
|
||||
<ProjectManager projects={projects} onProjectsChange={loadProjects} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
Plus,
|
||||
Edit,
|
||||
Trash2,
|
||||
Eye,
|
||||
Search,
|
||||
Filter,
|
||||
Grid,
|
||||
List,
|
||||
Save,
|
||||
X,
|
||||
Upload,
|
||||
Image as ImageIcon,
|
||||
Link as LinkIcon,
|
||||
Globe,
|
||||
Github,
|
||||
Calendar,
|
||||
Tag,
|
||||
Star,
|
||||
TrendingUp,
|
||||
Settings,
|
||||
MoreVertical,
|
||||
RefreshCw
|
||||
} from 'lucide-react';
|
||||
// Editor is now a separate page at /editor
|
||||
@@ -54,13 +41,11 @@ interface Project {
|
||||
}
|
||||
|
||||
interface ProjectManagerProps {
|
||||
isAuthenticated: boolean;
|
||||
projects: Project[];
|
||||
onProjectsChange: () => void;
|
||||
}
|
||||
|
||||
export const ProjectManager: React.FC<ProjectManagerProps> = ({
|
||||
isAuthenticated,
|
||||
projects,
|
||||
onProjectsChange
|
||||
}) => {
|
||||
@@ -70,7 +55,6 @@ export const ProjectManager: React.FC<ProjectManagerProps> = ({
|
||||
// Editor is now a separate page - no modal state needed
|
||||
|
||||
const categories = ['all', 'Web Development', 'Full-Stack', 'Web Application', 'Mobile App', 'Design'];
|
||||
const difficulties = ['Beginner', 'Intermediate', 'Advanced', 'Expert'];
|
||||
|
||||
// Filter projects
|
||||
const filteredProjects = projects.filter((project) => {
|
||||
|
||||
@@ -8,36 +8,19 @@ import {
|
||||
Eye,
|
||||
EyeOff,
|
||||
Settings,
|
||||
Link as LinkIcon,
|
||||
Tag,
|
||||
Calendar,
|
||||
Globe,
|
||||
Github,
|
||||
Image as ImageIcon,
|
||||
Bold,
|
||||
Italic,
|
||||
List,
|
||||
Hash,
|
||||
Quote,
|
||||
Code,
|
||||
Zap,
|
||||
Type,
|
||||
Columns,
|
||||
PanelLeft,
|
||||
PanelRight,
|
||||
Monitor,
|
||||
Smartphone,
|
||||
Tablet,
|
||||
Undo,
|
||||
Redo,
|
||||
AlignLeft,
|
||||
AlignCenter,
|
||||
AlignRight,
|
||||
Link2,
|
||||
ListOrdered,
|
||||
Underline,
|
||||
Strikethrough,
|
||||
GripVertical
|
||||
GripVertical,
|
||||
Image as ImageIcon
|
||||
} from 'lucide-react';
|
||||
|
||||
interface Project {
|
||||
@@ -59,7 +42,7 @@ interface Project {
|
||||
|
||||
interface ResizableGhostEditorProps {
|
||||
project?: Project | null;
|
||||
onSave: (projectData: any) => void;
|
||||
onSave: (projectData: Partial<Project>) => void;
|
||||
onClose: () => void;
|
||||
isCreating: boolean;
|
||||
}
|
||||
@@ -277,7 +260,7 @@ export const ResizableGhostEditor: React.FC<ResizableGhostEditorProps> = ({
|
||||
|
||||
// Enhanced markdown renderer with proper white text
|
||||
const renderMarkdownPreview = (markdown: string) => {
|
||||
let html = markdown
|
||||
const html = markdown
|
||||
// Headers - WHITE TEXT
|
||||
.replace(/^### (.*$)/gim, '<h3 class="text-xl font-semibold text-white mb-3 mt-6">$1</h3>')
|
||||
.replace(/^## (.*$)/gim, '<h2 class="text-2xl font-bold text-white mb-4 mt-8">$1</h2>')
|
||||
|
||||
Reference in New Issue
Block a user