"use client"; import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Save, Eye, Plus, Edit, Trash2, Upload, Bold, Italic, List, Link as LinkIcon, Image as ImageIcon, Code, Quote, ArrowLeft, ChevronDown, ChevronRight, Palette, Smile, FileText, Download, Upload as UploadIcon, Settings, Smartphone } from 'lucide-react'; import Link from 'next/link'; import ReactMarkdown from 'react-markdown'; interface Project { id: number; title: string; description: string; content: string; tags: string[]; featured: boolean; category: string; date: string; github?: string; live?: string; published: boolean; imageUrl?: string; metaDescription?: string; keywords?: string; ogImage?: string; schema?: any; } const AdminPage = () => { const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); const [projects, setProjects] = useState([]); // Load projects from localStorage on mount useEffect(() => { const savedProjects = localStorage.getItem('portfolio-projects'); if (savedProjects) { setProjects(JSON.parse(savedProjects)); } else { // Default projects if none exist const defaultProjects: Project[] = [ { id: 1, title: "Portfolio Website", description: "A modern, responsive portfolio website built with Next.js, TypeScript, and Tailwind CSS.", content: "# Portfolio Website\n\nThis is my personal portfolio website built with modern web technologies. The site features a dark theme with glassmorphism effects and smooth animations.\n\n## Features\n\n- **Responsive Design**: Works perfectly on all devices\n- **Dark Theme**: Modern dark mode with glassmorphism effects\n- **Animations**: Smooth animations powered by Framer Motion\n- **Markdown Support**: Projects are written in Markdown for easy editing\n- **Performance**: Optimized for speed and SEO\n\n## Technologies Used\n\n- Next.js 15\n- TypeScript\n- Tailwind CSS\n- Framer Motion\n- React Markdown\n\n## Development Process\n\nThe website was designed with a focus on user experience and performance. I used modern CSS techniques like CSS Grid, Flexbox, and custom properties to create a responsive layout.\n\n## Future Improvements\n\n- Add blog functionality\n- Implement project filtering\n- Add more interactive elements\n- Optimize for Core Web Vitals\n\n## Links\n\n- [Live Demo](https://dki.one)\n- [GitHub Repository](https://github.com/Denshooter/portfolio)", tags: ["Next.js", "TypeScript", "Tailwind CSS", "Framer Motion"], featured: true, category: "Web Development", date: "2024" } ]; setProjects(defaultProjects); localStorage.setItem('portfolio-projects', JSON.stringify(defaultProjects)); } }, []); const [selectedProject, setSelectedProject] = useState(null); const [isPreview, setIsPreview] = useState(false); const [isProjectsCollapsed, setIsProjectsCollapsed] = useState(false); const [formData, setFormData] = useState({ title: '', description: '', content: '', tags: '', category: '', featured: false, github: '', live: '', published: true, imageUrl: '' }); const [markdownContent, setMarkdownContent] = useState(''); const categories = [ "Web Development", "Full-Stack", "Web Application", "Mobile App", "Desktop App", "API Development", "Database Design", "DevOps", "UI/UX Design", "Game Development", "Machine Learning", "Data Science", "Blockchain", "IoT", "Cybersecurity" ]; if (!mounted) { return null; } const handleSave = () => { if (!formData.title || !formData.description || !markdownContent || !formData.category) { alert('Please fill in all required fields!'); return; } try { if (selectedProject) { // Update existing project const updatedProjects = projects.map(p => p.id === selectedProject.id ? { ...p, title: formData.title, description: formData.description, content: markdownContent, tags: formData.tags ? formData.tags.split(',').map(tag => tag.trim()).filter(tag => tag) : [], category: formData.category, featured: formData.featured, github: formData.github || undefined, live: formData.live || undefined, published: formData.published, imageUrl: formData.imageUrl || undefined } : p ); setProjects(updatedProjects); localStorage.setItem('portfolio-projects', JSON.stringify(updatedProjects)); console.log('Project updated successfully:', selectedProject.id); } else { // Create new project const newProject: Project = { id: Math.floor(Math.random() * 1000000), title: formData.title, description: formData.description, content: markdownContent, tags: formData.tags ? formData.tags.split(',').map(tag => tag.trim()).filter(tag => tag) : [], category: formData.category, featured: formData.featured, github: formData.github || undefined, live: formData.live || undefined, published: formData.published, imageUrl: formData.imageUrl || undefined, date: new Date().getFullYear().toString() }; const updatedProjects = [...projects, newProject]; setProjects(updatedProjects); localStorage.setItem('portfolio-projects', JSON.stringify(updatedProjects)); console.log('New project created successfully:', newProject.id); } resetForm(); alert('Project saved successfully!'); } catch (error) { console.error('Error saving project:', error); alert('Error saving project. Please try again.'); } }; const handleEdit = (project: Project) => { console.log('Editing project:', project); setSelectedProject(project); setFormData({ title: project.title, description: project.description, content: project.content, tags: project.tags.join(', '), category: project.category, featured: project.featured, github: project.github || '', live: project.live || '', published: project.published !== undefined ? project.published : true, imageUrl: project.imageUrl || '' }); setMarkdownContent(project.content); setIsPreview(false); }; const handleDelete = (projectId: number) => { if (confirm('Are you sure you want to delete this project?')) { const updatedProjects = projects.filter(p => p.id !== projectId); setProjects(updatedProjects); localStorage.setItem('portfolio-projects', JSON.stringify(updatedProjects)); } }; const resetForm = () => { console.log('Resetting form'); setSelectedProject(null); setFormData({ title: '', description: '', content: '', tags: '', category: '', featured: false, github: '', live: '', published: true, imageUrl: '' }); setMarkdownContent(''); setIsPreview(false); }; const insertMarkdown = (type: string) => { const textarea = document.getElementById('markdown-editor') as HTMLTextAreaElement; if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; let insertion = ''; let cursorOffset = 0; switch (type) { case 'h1': insertion = `# ${text.substring(start, end) || 'Heading'}`; cursorOffset = 2; break; case 'h2': insertion = `## ${text.substring(start, end) || 'Heading'}`; cursorOffset = 3; break; case 'bold': insertion = `**${text.substring(start, end) || 'bold text'}**`; cursorOffset = 2; break; case 'italic': insertion = `*${text.substring(start, end) || 'italic text'}*`; cursorOffset = 1; break; case 'list': insertion = `- ${text.substring(start, end) || 'list item'}`; cursorOffset = 2; break; case 'link': insertion = `[${text.substring(start, end) || 'link text'}](url)`; cursorOffset = 3; break; case 'image': insertion = `![alt text](image-url)`; cursorOffset = 9; break; case 'code': insertion = `\`${text.substring(start, end) || 'code'}\``; cursorOffset = 1; break; case 'quote': insertion = `> ${text.substring(start, end) || 'quote text'}`; cursorOffset = 2; break; case 'table': insertion = `| Header 1 | Header 2 | Header 3 |\n|----------|----------|----------|\n| Cell 1 | Cell 2 | Cell 3 |\n| Cell 4 | Cell 5 | Cell 6 |`; cursorOffset = 0; break; } const newText = text.substring(0, start) + insertion + text.substring(end); setMarkdownContent(newText); // Set cursor position and select the placeholder text for easy editing setTimeout(() => { textarea.focus(); if (type === 'h1' || type === 'h2') { // For headings, select the placeholder text so user can type directly const placeholderStart = start + (type === 'h1' ? 2 : 3); const placeholderEnd = start + insertion.length; textarea.setSelectionRange(placeholderStart, placeholderEnd); } else { // For other elements, position cursor appropriately textarea.setSelectionRange(start + insertion.length - cursorOffset, start + insertion.length - cursorOffset); } }, 0); }; const handleProjectImageUpload = (e: React.ChangeEvent) => { const files = e.target.files; if (!files || files.length === 0) return; const file = files[0]; if (file) { // Simulate image upload - in production you'd upload to a real service const imageUrl = URL.createObjectURL(file); setFormData(prev => ({ ...prev, imageUrl })); } }; const handleImageUpload = (e: React.ChangeEvent) => { const files = e.target.files; if (!files || files.length === 0) return; const file = files[0]; if (file) { // Create a more descriptive image URL for better organization const imageName = file.name.replace(/\.[^/.]+$/, ""); // Remove file extension const imageUrl = URL.createObjectURL(file); const textarea = document.getElementById('markdown-editor') as HTMLTextAreaElement; if (!textarea) return; const start = textarea.selectionStart; const text = textarea.value; // Insert image with better alt text and a newline for spacing const insertion = `\n![${imageName}](${imageUrl})\n`; const newText = text.substring(0, start) + insertion + text.substring(start); setMarkdownContent(newText); // Focus back to textarea and position cursor after the image setTimeout(() => { textarea.focus(); const newCursorPos = start + insertion.length; textarea.setSelectionRange(newCursorPos, newCursorPos); }, 0); } }; return (
{/* Header */} Back to Home

Admin Dashboard

Manage your projects with the built-in Markdown editor. Create, edit, and preview your content easily.

{/* Projects Toggle Button - Always Visible */}
setIsProjectsCollapsed(!isProjectsCollapsed)} className="flex items-center space-x-2 px-6 py-3 bg-gradient-to-r from-gray-700 to-gray-800 hover:from-gray-600 hover:to-gray-700 rounded-xl text-white transition-all duration-200 hover:scale-105 border border-gray-600/50 shadow-lg" title={isProjectsCollapsed ? "Show Projects" : "Hide Projects"} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {isProjectsCollapsed ? ( <> Show Projects ) : ( <> Hide Projects )}
{/* Projects List */}

Projects

{projects.map((project) => (
handleEdit(project)} >

{project.title}

{project.description}

{project.category}
))}
{/* Editor */}

{selectedProject ? 'Edit Project' : 'New Project'}

{selectedProject && ( )}
{!isPreview ? (
{/* Basic Info */}
setFormData({...formData, title: e.target.value})} className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Project title" />
{/* Links */}
setFormData({...formData, github: e.target.value})} className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="https://github.com/username/repo" />
setFormData({...formData, live: e.target.value})} className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="https://demo.example.com" />
{/* Project Image */}
{formData.imageUrl ? ( Project preview ) : (
{formData.title ? formData.title.split(' ').map(word => word[0]).join('').toUpperCase() : 'P'}
)}

Upload a project image or use auto-generated initials