diff --git a/client/components/admin/AdminFoundationManager.tsx b/client/components/admin/AdminFoundationManager.tsx new file mode 100644 index 00000000..8f97f5ce --- /dev/null +++ b/client/components/admin/AdminFoundationManager.tsx @@ -0,0 +1,588 @@ +import React, { useState, useEffect } from "react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Users, + BookOpen, + Award, + CheckCircle, + XCircle, + Clock, + Trash2, + Search, +} from "lucide-react"; +import { aethexToast } from "@/lib/aethex-toast"; + +interface Mentor { + user_id: string; + bio: string; + expertise: string[]; + available: boolean; + max_mentees: number; + current_mentees: number; + approval_status: "pending" | "approved" | "rejected"; + user_name?: string; + user_email?: string; +} + +interface Course { + id: string; + title: string; + description?: string; + category: string; + difficulty: string; + instructor_id: string; + is_published: boolean; + estimated_hours?: number; + instructor_name?: string; +} + +interface Achievement { + id: string; + name: string; + description?: string; + requirement_type: string; + tier: number; +} + +export default function AdminFoundationManager() { + const [mentors, setMentors] = useState([]); + const [courses, setCourses] = useState([]); + const [achievements, setAchievements] = useState([]); + const [loadingMentors, setLoadingMentors] = useState(true); + const [loadingCourses, setLoadingCourses] = useState(true); + const [loadingAchievements, setLoadingAchievements] = useState(true); + const [searchMentor, setSearchMentor] = useState(""); + const [searchCourse, setSearchCourse] = useState(""); + const [selectedMentor, setSelectedMentor] = useState(null); + const [approvalDialogOpen, setApprovalDialogOpen] = useState(false); + const [approvalAction, setApprovalAction] = useState<"approve" | "reject">( + "approve" + ); + + useEffect(() => { + fetchMentors(); + fetchCourses(); + fetchAchievements(); + }, []); + + const fetchMentors = async () => { + try { + setLoadingMentors(true); + const response = await fetch("/api/admin/foundation/mentors"); + if (!response.ok) throw new Error("Failed to fetch mentors"); + const data = await response.json(); + setMentors(data || []); + } catch (error) { + aethexToast.error("Failed to load mentors"); + console.error(error); + } finally { + setLoadingMentors(false); + } + }; + + const fetchCourses = async () => { + try { + setLoadingCourses(true); + const response = await fetch("/api/admin/foundation/courses"); + if (!response.ok) throw new Error("Failed to fetch courses"); + const data = await response.json(); + setCourses(data || []); + } catch (error) { + aethexToast.error("Failed to load courses"); + console.error(error); + } finally { + setLoadingCourses(false); + } + }; + + const fetchAchievements = async () => { + try { + setLoadingAchievements(true); + const response = await fetch("/api/admin/foundation/achievements"); + if (!response.ok) throw new Error("Failed to fetch achievements"); + const data = await response.json(); + setAchievements(data || []); + } catch (error) { + aethexToast.error("Failed to load achievements"); + console.error(error); + } finally { + setLoadingAchievements(false); + } + }; + + const handleMentorApproval = async () => { + if (!selectedMentor) return; + + try { + const response = await fetch( + `/api/admin/foundation/mentors/${selectedMentor.user_id}`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ approval_status: approvalAction }), + } + ); + + if (!response.ok) throw new Error("Failed to update mentor"); + aethexToast.success( + `Mentor ${approvalAction === "approve" ? "approved" : "rejected"}` + ); + setApprovalDialogOpen(false); + setSelectedMentor(null); + fetchMentors(); + } catch (error) { + aethexToast.error("Failed to update mentor"); + console.error(error); + } + }; + + const handlePublishCourse = async (courseId: string, publish: boolean) => { + try { + const response = await fetch(`/api/admin/foundation/courses/${courseId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ is_published: publish }), + }); + + if (!response.ok) throw new Error("Failed to update course"); + aethexToast.success( + `Course ${publish ? "published" : "unpublished"}` + ); + fetchCourses(); + } catch (error) { + aethexToast.error("Failed to update course"); + console.error(error); + } + }; + + const handleDeleteCourse = async (courseId: string) => { + try { + const response = await fetch(`/api/admin/foundation/courses/${courseId}`, { + method: "DELETE", + }); + + if (!response.ok) throw new Error("Failed to delete course"); + aethexToast.success("Course deleted"); + fetchCourses(); + } catch (error) { + aethexToast.error("Failed to delete course"); + console.error(error); + } + }; + + const filteredMentors = mentors.filter((m) => + (m.user_name || "") + .toLowerCase() + .includes(searchMentor.toLowerCase()) + ); + + const filteredCourses = courses.filter((c) => + c.title.toLowerCase().includes(searchCourse.toLowerCase()) + ); + + const pendingMentors = filteredMentors.filter( + (m) => m.approval_status === "pending" + ); + const approvedMentors = filteredMentors.filter( + (m) => m.approval_status === "approved" + ); + + const publishedCourses = courses.filter((c) => c.is_published).length; + const draftCourses = courses.filter((c) => !c.is_published).length; + + return ( +
+ {/* Overview Cards */} +
+ + + + Total Mentors + + + +
{mentors.length}
+

+ {approvedMentors.length} approved +

+
+
+ + + + + Pending Approval + + + +
+ {pendingMentors.length} +
+

+ Mentors awaiting review +

+
+
+ + + + + Total Courses + + + +
{courses.length}
+

+ {publishedCourses} published +

+
+
+ + + + + Achievements + + + +
{achievements.length}
+

+ Total badge types +

+
+
+
+ + {/* Tabs */} + + + + + Mentors + + + + Courses + + + + Achievements + + + + {/* MENTORS TAB */} + +
+
+ + setSearchMentor(e.target.value)} + className="pl-10" + /> +
+
+ + {loadingMentors ? ( + + +

Loading mentors...

+
+
+ ) : ( + <> + {/* Pending Approvals */} + {pendingMentors.length > 0 && ( + + + + Pending Mentor Approvals + + + {pendingMentors.length} mentors awaiting review + + + +
+ {pendingMentors.map((mentor) => ( +
+
+

{mentor.user_name}

+

+ {mentor.user_email} +

+
+ {mentor.expertise.map((skill) => ( + + {skill} + + ))} +
+ {mentor.bio && ( +

{mentor.bio}

+ )} +
+ +
+ ))} +
+
+
+ )} + + {/* Approved Mentors */} + + + + Approved Mentors ({approvedMentors.length}) + + + + {approvedMentors.length === 0 ? ( +

+ No approved mentors yet +

+ ) : ( +
+ {approvedMentors.map((mentor) => ( +
+
+

{mentor.user_name}

+

+ {mentor.current_mentees}/{mentor.max_mentees}{" "} + mentees +

+
+ + + Approved + +
+ ))} +
+ )} +
+
+ + )} +
+ + {/* COURSES TAB */} + +
+
+ + setSearchCourse(e.target.value)} + className="pl-10" + /> +
+
+ + {loadingCourses ? ( + + +

Loading courses...

+
+
+ ) : ( +
+ {filteredCourses.length === 0 ? ( + + +

No courses found

+
+
+ ) : ( + filteredCourses.map((course) => ( + + +
+
+
+

+ {course.title} +

+ {course.is_published ? ( + + Published + + ) : ( + + Draft + + )} +
+

+ {course.description} +

+
+ Category: {course.category} + + Difficulty: {course.difficulty} + {course.estimated_hours && ( + <> + + {course.estimated_hours}h + + )} +
+
+
+ + +
+
+
+
+ )) + )} +
+ )} +
+ + {/* ACHIEVEMENTS TAB */} + + {loadingAchievements ? ( + + +

Loading achievements...

+
+
+ ) : achievements.length === 0 ? ( + + +

No achievements found

+
+
+ ) : ( +
+ {achievements.map((achievement) => ( + + +
+ +
+

{achievement.name}

+

+ {achievement.description} +

+
+ + {achievement.requirement_type} + + + Tier {achievement.tier} + +
+
+
+
+
+ ))} +
+ )} +
+
+ + {/* Approval Dialog */} + + + + {approvalAction === "approve" ? "Approve Mentor?" : "Reject Mentor?"} + + + {approvalAction === "approve" ? ( + <> + Are you sure you want to approve{" "} + {selectedMentor?.user_name} as a mentor? They + will be visible to mentees and can start receiving requests. + + ) : ( + <> + Are you sure you want to reject{" "} + {selectedMentor?.user_name}? They can reapply + later. + + )} + +
+ Cancel + + {approvalAction === "approve" ? "Approve" : "Reject"} + +
+
+
+
+ ); +}