diff --git a/app/admin/dsoc/page.tsx b/app/admin/dsoc/page.tsx new file mode 100644 index 0000000..c1e03a3 --- /dev/null +++ b/app/admin/dsoc/page.tsx @@ -0,0 +1,423 @@ +'use client'; + +import Link from "next/link"; +import { useState, useEffect } from "react"; +import { + ArrowLeft, + Plus, + Code2, + Users, + FileText, + Settings, + CheckCircle, + XCircle, + Clock, + Search, + Filter, + Trash2, + Edit, + Eye +} from "lucide-react"; +import "../../styles.css"; + +interface Project { + _id: string; + title: string; + organization: string; + status: string; + difficulty: string; + technologies: string[]; + mentors: { _id: string; name: string }[]; + selectedMentees: { _id: string; name: string }[]; + maxMentees: number; + createdAt: string; +} + +interface Application { + _id: string; + status: string; + createdAt: string; + mentee: { _id: string; name: string; email: string }; + project: { _id: string; title: string }; +} + +interface Stats { + projects: { total: number; open: number; inProgress: number; completed: number }; + mentors: number; + mentees: number; + applications: number; +} + +export default function AdminDSOCPage() { + const [activeTab, setActiveTab] = useState('overview'); + const [projects, setProjects] = useState([]); + const [applications, setApplications] = useState([]); + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(''); + + useEffect(() => { + fetchData(); + }, []); + + const fetchData = async () => { + try { + const [statsRes, projectsRes, appsRes] = await Promise.all([ + fetch('/api/dsoc/stats'), + fetch('/api/dsoc/projects?limit=100'), + fetch('/api/dsoc/applications') + ]); + + const [statsData, projectsData, appsData] = await Promise.all([ + statsRes.json(), + projectsRes.json(), + appsRes.json() + ]); + + if (statsData.success) setStats(statsData.data); + if (projectsData.success) setProjects(projectsData.data); + if (appsData.success) setApplications(appsData.data); + } catch (error) { + console.error('Error fetching data:', error); + } finally { + setLoading(false); + } + }; + + const handleDeleteProject = async (id: string) => { + if (!confirm('Are you sure you want to delete this project?')) return; + + try { + const res = await fetch(`/api/dsoc/projects/${id}`, { method: 'DELETE' }); + if (res.ok) { + setProjects(projects.filter(p => p._id !== id)); + } + } catch (error) { + console.error('Error deleting project:', error); + } + }; + + const handleApplicationAction = async (id: string, status: 'accepted' | 'rejected') => { + try { + const res = await fetch(`/api/dsoc/applications/${id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ status }) + }); + + if (res.ok) { + fetchData(); + } + } catch (error) { + console.error('Error updating application:', error); + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case 'open': case 'accepted': return 'bg-[var(--dsoc-success)]'; + case 'in-progress': case 'under-review': return 'bg-[var(--dsoc-accent)]'; + case 'completed': return 'bg-[var(--dsoc-purple)]'; + case 'pending': return 'bg-[var(--dsoc-secondary)]'; + case 'rejected': return 'bg-[var(--dsoc-pink)]'; + default: return 'bg-gray-400'; + } + }; + + if (loading) { + return ( +
+
+
+

Loading admin panel...

+
+
+ ); + } + + return ( +
+ {/* Header */} +
+
+
+
+ + + +

DSOC Admin

+
+ + + View Public Page + +
+
+
+ +
+ {/* Stats Overview */} +
+
+
+ {stats?.projects.total || 0} +
+
Projects
+
+
+
+ {stats?.projects.open || 0} +
+
Open
+
+
+
+ {stats?.mentors || 0} +
+
Mentors
+
+
+
+ {stats?.mentees || 0} +
+
Mentees
+
+
+
+ {stats?.applications || 0} +
+
Applications
+
+
+ + {/* Tabs */} +
+ {['overview', 'projects', 'applications', 'mentors', 'mentees'].map((tab) => ( + + ))} +
+ + {/* Projects Tab */} + {activeTab === 'projects' && ( +
+
+
+ + setSearch(e.target.value)} + className="neo-brutal-input pl-12" + /> +
+ + + Add Project + +
+ +
+ + + + + + + + + + + + {projects + .filter(p => + p.title.toLowerCase().includes(search.toLowerCase()) || + p.organization.toLowerCase().includes(search.toLowerCase()) + ) + .map((project) => ( + + + + + + + + ))} + +
ProjectStatusDifficultyMenteesActions
+
{project.title}
+
{project.organization}
+
+ + {project.status} + + + + {project.difficulty} + + + {project.selectedMentees?.length || 0} / {project.maxMentees} + +
+ + + + + + + +
+
+
+
+ )} + + {/* Applications Tab */} + {activeTab === 'applications' && ( +
+ {applications.map((app) => ( +
+
+
+
+ + {app.status} + + + {new Date(app.createdAt).toLocaleDateString()} + +
+
{app.mentee.name}
+
+ {app.mentee.email} → {app.project.title} +
+
+ {app.status === 'pending' && ( +
+ + +
+ )} +
+
+ ))} +
+ )} + + {/* Overview Tab */} + {activeTab === 'overview' && ( +
+
+

+ + Recent Applications +

+
+ {applications.slice(0, 5).map((app) => ( +
+
+
{app.mentee.name}
+
{app.project.title}
+
+ + {app.status} + +
+ ))} +
+ +
+ +
+

+ + Recent Projects +

+
+ {projects.slice(0, 5).map((project) => ( +
+
+
{project.title}
+
{project.organization}
+
+ + {project.status} + +
+ ))} +
+ +
+
+ )} + + {/* Mentors Tab */} + {activeTab === 'mentors' && ( +
+ +

Mentor Management

+

+ Coming soon - manage mentor profiles, verification, and project assignments. +

+
+ )} + + {/* Mentees Tab */} + {activeTab === 'mentees' && ( +
+ +

Mentee Management

+

+ Coming soon - manage mentee profiles and project participation. +

+
+ )} +
+
+ ); +} diff --git a/app/admin/dsoc/projects/new/page.tsx b/app/admin/dsoc/projects/new/page.tsx new file mode 100644 index 0000000..1b27032 --- /dev/null +++ b/app/admin/dsoc/projects/new/page.tsx @@ -0,0 +1,410 @@ +'use client'; + +import Link from "next/link"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { + ArrowLeft, + Save, + Plus, + Trash2 +} from "lucide-react"; +import "../../../styles.css"; + +export default function NewProjectPage() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const [formData, setFormData] = useState({ + title: '', + description: '', + longDescription: '', + organization: '', + repositoryUrl: '', + websiteUrl: '', + difficulty: 'intermediate', + duration: '3 months', + technologies: '', + tags: '', + maxMentees: 3, + applicationDeadline: '', + startDate: '', + endDate: '', + requirements: [''], + learningOutcomes: [''], + season: '2025' + }); + + const handleChange = (e: React.ChangeEvent) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; + + const handleArrayChange = (field: 'requirements' | 'learningOutcomes', index: number, value: string) => { + const updated = [...formData[field]]; + updated[index] = value; + setFormData({ ...formData, [field]: updated }); + }; + + const addArrayItem = (field: 'requirements' | 'learningOutcomes') => { + setFormData({ ...formData, [field]: [...formData[field], ''] }); + }; + + const removeArrayItem = (field: 'requirements' | 'learningOutcomes', index: number) => { + const updated = formData[field].filter((_, i) => i !== index); + setFormData({ ...formData, [field]: updated }); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + setLoading(true); + + try { + const res = await fetch('/api/dsoc/projects', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + ...formData, + technologies: formData.technologies.split(',').map(s => s.trim()).filter(Boolean), + tags: formData.tags.split(',').map(s => s.trim()).filter(Boolean), + requirements: formData.requirements.filter(Boolean), + learningOutcomes: formData.learningOutcomes.filter(Boolean), + status: 'draft' + }) + }); + + const data = await res.json(); + + if (data.success) { + router.push('/admin/dsoc'); + } else { + setError(data.error || 'Failed to create project'); + } + } catch (err) { + console.error('Error creating project:', err); + setError('Something went wrong. Please try again.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+ + + Back to DSOC Admin + + +
+

Create New Project

+ +
+ {error && ( +
+ {error} +
+ )} + + {/* Basic Info */} +
+

Basic Information

+ +
+ + +
+ +
+ + +
+ +
+ +