diff --git a/app/dashboard/admin/page.tsx b/app/dashboard/admin/page.tsx new file mode 100644 index 0000000..93d8ea4 --- /dev/null +++ b/app/dashboard/admin/page.tsx @@ -0,0 +1,254 @@ +"use client"; +import { useState } from "react"; +import { AnimatePresence } from "framer-motion"; +import WelcomeSection from "../components/WelcomeSection"; +import ManagementSection from "../components/ManagementSection"; +import Modal from "../components/Modal"; +import StatsCards from "../components/StatsCards"; +import { ColorScheme } from "../types"; + +// Mock Data +const mockStudents = [ + { + id: 1, + name: "Vaibhav Tripathi", + email: "vxtr@abes.ac.in", + role: "Student", + joinDate: "2024-01-15", + }, + { + id: 2, + name: "Akash Chaudhary", + email: "akash@abes.ac.in", + role: "Student", + joinDate: "2024-02-01", + }, + { + id: 3, + name: "Shreyash Singh", + email: "shreyash@abes.ac.in", + role: "Club Head", + joinDate: "2024-01-20", + }, +]; + +const mockMentors = [ + { + id: 1, + name: "Dewank Rastogi", + expertise: "Frontend", + achievements: "3+ years experience", + status: "Active", + }, + { + id: 2, + name: "Rudraksh Tyagi", + expertise: "Open Source", + achievements: "LFX", + status: "Active", + }, + { + id: 3, + name: "Vandit Singh", + expertise: "DevOps", + achievements: "Google Summer of Code", + status: "Active", + }, +]; + +const mockEvents = [ + { + id: 1, + name: "Binary Vault", + club: "GFG_ABESEC", + date: "2024-03-15", + participants: 450, + }, + { + id: 2, + name: "Hackhaven", + club: "GDG_ABESEC", + date: "2024-03-20", + participants: 320, + }, + { + id: 3, + name: "Some Event", + club: "CodeChef_ABESEC", + date: "2024-03-25", + participants: 280, + }, +]; + +const mockBlogs = [ + { + id: 1, + title: "Future of AI", + author: "Priya Kumari", + status: "Pending", + date: "2024-03-10", + }, + { + id: 2, + title: "Web Development Trends", + author: "Radhika Chauhan", + status: "Approved", + date: "2024-03-08", + }, + { + id: 3, + title: "Cloud Computing Basics", + author: "Granth Agarwal", + status: "Pending", + date: "2024-03-09", + }, +]; + +const AdminDashboard = () => { + const [activeModal, setActiveModal] = useState(null); + const [selectedItem, setSelectedItem] = useState(null); + + const openModal = (modalType: string, item?: any) => { + setActiveModal(modalType); + if (item) setSelectedItem(item); + }; + + const closeModal = () => { + setActiveModal(null); + setSelectedItem(null); + }; + + const adminStats = [ + { + heading: "1.2k", + subheading: "Total Students", + description: "+120 this month", + colorScheme: "purple" as ColorScheme, + }, + { + heading: "45", + subheading: "Upcoming Events", + description: "Next: 3 days", + colorScheme: "blue" as ColorScheme, + }, + { + heading: "8", + subheading: "Blog Submissions", + description: "3 pending approval", + colorScheme: "green" as ColorScheme, + }, + { + heading: "24", + subheading: "Mentors", + description: "5 new additions", + colorScheme: "orange" as ColorScheme, + }, + ]; + + const renderModalContent = () => { + if (!selectedItem) return null; + return ( +
+ + + {Object.entries(selectedItem).map(([key, value]) => ( + + + + + ))} + +
+ {key} + {String(value)}
+
+ ); + }; + + return ( + <> +
+ + +
+ {adminStats.map((stat, idx) => ( + + ))} +
+ +
+ row.name }, + { header: "Role", accessor: (row) => row.role }, + { header: "Join Date", accessor: (row) => row.joinDate }, + ]} + onViewAll={() => openModal("students")} + onRowClick={(item) => openModal("Student Details", item)} + /> + + row.name }, + { header: "Expertise", accessor: (row) => row.expertise }, + { header: "Status", accessor: (row) => row.status }, + ]} + onViewAll={() => openModal("mentors")} + onRowClick={(item) => openModal("Mentor Details", item)} + /> + + row.name }, + { header: "Club", accessor: (row) => row.club }, + { header: "Date", accessor: (row) => row.date }, + ]} + onViewAll={() => openModal("events")} + onRowClick={(item) => openModal("Event Details", item)} + /> + + row.title }, + { header: "Author", accessor: (row) => row.author }, + { header: "Status", accessor: (row) => row.status }, + ]} + onViewAll={() => openModal("blogs")} + onRowClick={(item) => openModal("Blog Details", item)} + /> +
+
+ + + {activeModal && ( + + {renderModalContent()} + + )} + + + ); +}; + +export default AdminDashboard; diff --git a/app/dashboard/components/ManagementSection.tsx b/app/dashboard/components/ManagementSection.tsx new file mode 100644 index 0000000..0d92b04 --- /dev/null +++ b/app/dashboard/components/ManagementSection.tsx @@ -0,0 +1,58 @@ +import { ManagementSectionProps, Column } from "../types"; +import { managementStyles } from "../styles"; + +interface ExtendedManagementSectionProps extends ManagementSectionProps { + onRowClick?: (row: any) => void; +} + +const ManagementSection: React.FC = ({ + title, + columns, + data = [], + onViewAll, + className = "", + onRowClick, +}) => { + return ( +
+
+

{title}

+ {onViewAll && ( + + )} +
+
+ + + + {columns.map((column: Column, index: number) => ( + + ))} + + + + {data.map((row, rowIndex) => ( + onRowClick(row) : undefined} + > + {columns.map((column: Column, colIndex: number) => ( + + ))} + + ))} + +
+ {column.header} +
+ {column.accessor(row)} +
+
+
+ ); +}; + +export default ManagementSection; diff --git a/app/dashboard/components/Modal.tsx b/app/dashboard/components/Modal.tsx new file mode 100644 index 0000000..f07cdc6 --- /dev/null +++ b/app/dashboard/components/Modal.tsx @@ -0,0 +1,52 @@ +import { useEffect } from "react"; +import { ModalProps } from "../types"; +import { modalStyles } from "../styles"; +import { motion, AnimatePresence } from "framer-motion"; + +const Modal: React.FC = ({ + isOpen, + onClose, + title, + children, + className = "", +}) => { + useEffect(() => { + const handleEscapeKey = (event: KeyboardEvent) => { + if (event.key === "Escape" && isOpen) { + onClose(); + } + }; + + document.addEventListener("keydown", handleEscapeKey); + return () => { + document.removeEventListener("keydown", handleEscapeKey); + }; + }, [isOpen, onClose]); + + return ( + + {isOpen && ( +
+ e.stopPropagation()} + > +
+

{title}

+ +
+
{children}
+
+
+ )} +
+ ); +}; + +export default Modal; diff --git a/app/dashboard/components/QuickStats.tsx b/app/dashboard/components/QuickStats.tsx new file mode 100644 index 0000000..fb4d649 --- /dev/null +++ b/app/dashboard/components/QuickStats.tsx @@ -0,0 +1,65 @@ +type StatColorScheme = "purple" | "blue" | "green" | "orange"; + +interface StatItem { + value: string; + label: string; + subtext: string; + colorScheme: StatColorScheme; +} + +interface QuickStatsProps { + stats: StatItem[]; +} + +const colorSchemes = { + purple: { + gradient: "from-purple to-blue-500", + hover: "hover:border-purple/50", + subtext: "text-purple", + }, + blue: { + gradient: "from-blue-400 to-cyan-400", + hover: "hover:border-blue-400/50", + subtext: "text-blue-400", + }, + green: { + gradient: "from-green-400 to-emerald-400", + hover: "hover:border-green-400/50", + subtext: "text-green-400", + }, + orange: { + gradient: "from-orange-400 to-red-400", + hover: "hover:border-orange-400/50", + subtext: "text-orange-400", + }, +}; + +const QuickStats: React.FC = ({ stats }) => { + return ( +
+ {stats.map((stat, index) => { + const colors = colorSchemes[stat.colorScheme]; + return ( +
+
+ + {stat.value} + + {stat.label} + + {stat.subtext} + +
+
+ ); + })} +
+ ); +}; + +export default QuickStats; diff --git a/app/dashboard/components/RecentActivity.tsx b/app/dashboard/components/RecentActivity.tsx new file mode 100644 index 0000000..4ab0f6b --- /dev/null +++ b/app/dashboard/components/RecentActivity.tsx @@ -0,0 +1,39 @@ +import { ActivityItem } from "../types"; +import { baseCardStyles } from "../styles"; + +interface RecentActivityProps { + heading: string; + activities: ActivityItem[]; + className?: string; +} + +const RecentActivity: React.FC = ({ + heading, + activities, + className = "", +}) => { + return ( +
+

{heading}

+
+ {activities.map((activity, index) => ( +
+
+ {activity.icon} +
+
+

{activity.title}

+

{activity.description}

+

{activity.time}

+
+
+ ))} +
+
+ ); +}; + +export default RecentActivity; diff --git a/app/dashboard/components/StatsCards.tsx b/app/dashboard/components/StatsCards.tsx new file mode 100644 index 0000000..ce6a00f --- /dev/null +++ b/app/dashboard/components/StatsCards.tsx @@ -0,0 +1,32 @@ +import { StatCardProps } from "../types"; +import { colorSchemes, baseCardStyles } from "../styles"; + +const StatsCards: React.FC = ({ + heading, + subheading, + description, + colorScheme, + className = "", +}) => { + const colors = colorSchemes[colorScheme]; + + return ( +
+
+ + {heading} + + {subheading} + + {description} + +
+
+ ); +}; + +export default StatsCards; diff --git a/app/dashboard/components/WelcomeCard.tsx b/app/dashboard/components/WelcomeCard.tsx new file mode 100644 index 0000000..49f0c20 --- /dev/null +++ b/app/dashboard/components/WelcomeCard.tsx @@ -0,0 +1,20 @@ +const WelcomeCard = ({ name }: { name: string }) => { + return ( + <> +
+
+
+

+ Welcome back, {name}! +

+

+ Here's what's happening today. +

+
+
+
+ + ); +}; + +export default WelcomeCard; diff --git a/app/dashboard/components/WelcomeSection.tsx b/app/dashboard/components/WelcomeSection.tsx new file mode 100644 index 0000000..a648aec --- /dev/null +++ b/app/dashboard/components/WelcomeSection.tsx @@ -0,0 +1,22 @@ +interface WelcomeSectionProps { + title: string; + description: string; +} + +const WelcomeSection: React.FC = ({ + title, + description, +}) => { + return ( +
+
+
+

{title}

+

{description}

+
+
+
+ ); +}; + +export default WelcomeSection; diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..aabe112 --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,62 @@ +import StatsCards from "./components/StatsCards"; +import WelcomeCard from "./components/WelcomeCard"; +import RecentActivity from "./components/RecentActivity"; + +const Dashboard = () => { + const recentActivities = [ + { + icon: 📝, + iconBg: "bg-green-400/20", + title: "Added a new note", + description: "Next.js Tips", + time: "2 hours ago", + }, + { + icon: 🎉, + iconBg: "bg-blue-400/20", + title: "Attended Event", + description: "Elixir Community Meetup", + time: "Yesterday", + }, + { + icon: 📝, + iconBg: "bg-purple/20", + title: "Created a new project", + description: "", + time: "2 days ago", + }, + ]; + + return ( + <> +
+ +
+ + + +
+ + + +
+ + ); +}; + +export default Dashboard; diff --git a/app/dashboard/styles/index.ts b/app/dashboard/styles/index.ts new file mode 100644 index 0000000..453e6d5 --- /dev/null +++ b/app/dashboard/styles/index.ts @@ -0,0 +1,60 @@ +export const colorSchemes = { + purple: { + gradient: "from-purple to-blue-500", + hover: "hover:border-purple/50", + text: "text-purple", + bg: "bg-purple/20", + }, + blue: { + gradient: "from-blue-400 to-cyan-400", + hover: "hover:border-blue-400/50", + text: "text-blue-400", + bg: "bg-blue-400/20", + }, + green: { + gradient: "from-green-400 to-emerald-400", + hover: "hover:border-green-400/50", + text: "text-green-400", + bg: "bg-green-400/20", + }, + orange: { + gradient: "from-orange-400 to-red-400", + hover: "hover:border-orange-400/50", + text: "text-orange-400", + bg: "bg-orange-400/20", + }, +}; + +export const baseCardStyles = { + container: + "rounded-xl p-6 bg-[rgba(17,25,40,0.75)] backdrop-blur-md border border-white/10 shadow transition-all duration-300", + heading: "text-4xl font-bold bg-clip-text text-transparent", + subheading: "text-white/80 mt-2 block", + description: "text-sm mt-1 block", +}; + +export const managementStyles = { + container: + "rounded-2xl p-6 bg-[rgba(17,25,40,0.80)] backdrop-blur-lg border border-white/10 shadow", + header: "flex justify-between items-center mb-6", + title: "text-xl font-semibold text-white", + viewAll: "text-sm text-blue-400 hover:text-blue-300 transition-colors", + table: { + container: "overflow-x-auto", + table: "w-full", + header: "text-left text-white/60 text-sm", + headerCell: "pb-4", + row: "cursor-pointer hover:bg-white/5 transition-colors", + cell: "py-3 text-white", + }, +}; + +export const modalStyles = { + overlay: + "fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center", + container: + "bg-[rgba(17,25,40,0.95)] border border-white/10 rounded-2xl p-6 w-full max-w-2xl mx-4", + header: "flex justify-between items-center mb-4", + title: "text-xl font-semibold text-white", + closeButton: "text-white/60 text-3xl hover:text-white", +}; diff --git a/app/dashboard/types/index.ts b/app/dashboard/types/index.ts new file mode 100644 index 0000000..fd7891e --- /dev/null +++ b/app/dashboard/types/index.ts @@ -0,0 +1,54 @@ +export type ColorScheme = "purple" | "blue" | "green" | "orange"; + +export interface BaseCardProps { + className?: string; + children: React.ReactNode; +} + +export interface StatCardProps { + heading: string; + subheading: string; + description: string; + colorScheme: ColorScheme; + className?: string; +} + +export interface ActivityItem { + icon: React.ReactNode; + iconBg: string; + title: string; + description: string; + time: string; +} + +export interface ManagementItem { + id: number; + [key: string]: any; +} + +export interface Column { + header: string; + accessor: (row: any) => React.ReactNode; +} + +export interface ManagementSectionProps { + title: string; + columns: Column[]; + data: any[]; + onViewAll?: () => void; + className?: string; +} + +export interface ModalProps { + isOpen: boolean; + onClose: () => void; + title: string; + children: React.ReactNode; + className?: string; +} + +export interface WelcomeSectionProps { + title: string; + description: string; + className?: string; +} diff --git a/components/Footer.tsx b/components/Footer.tsx index 7131125..06a79e0 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -2,6 +2,7 @@ import { FaLocationArrow } from "react-icons/fa6"; import { socialMedia } from "@/constants/constants"; import MagicButton from "./MagicButton"; import Image from "next/image"; +import MinFooter from "./MinFooter"; const Footer = () => { return ( @@ -23,29 +24,7 @@ const Footer = () => { />
-
-

- Copyright © 2024 Elixir Tech Community -

-
- {socialMedia.map((info) => ( - -
- icons -
-
- ))} -
-
+ ); }; diff --git a/components/MinFooter.tsx b/components/MinFooter.tsx new file mode 100644 index 0000000..db63e2d --- /dev/null +++ b/components/MinFooter.tsx @@ -0,0 +1,38 @@ +import { socialMedia } from "@/constants/constants"; +import Image from "next/image"; +const MinFooter = () => { + return ( +
+
+

+ Copyright © 2024 Elixir Tech Community +

+
+ {socialMedia.map((info) => ( + +
+ icons +
+
+ ))} +
+
+
+ ); +}; + +export default MinFooter; diff --git a/layouts/WrapperLayout/Server/ServerWrapperLayout.tsx b/layouts/WrapperLayout/Server/ServerWrapperLayout.tsx index f0991ef..7bbd0b9 100644 --- a/layouts/WrapperLayout/Server/ServerWrapperLayout.tsx +++ b/layouts/WrapperLayout/Server/ServerWrapperLayout.tsx @@ -1,8 +1,11 @@ +"use client"; import dynamic from "next/dynamic"; import BackDrop from "@/components/BackDrop/BackDrop"; import ClientWrapperLayout from "../Client/ClientWrapperLayout"; import FloatingNavbar from "@/components/ui/FloatingNavbar"; import { navItems } from "@/constants/constants"; +import { usePathname } from "next/navigation"; +import MinFooter from "@/components/MinFooter"; const Footer = dynamic(() => import("@/components/Footer")); @@ -13,13 +16,16 @@ type ServerWrapperLayoutProps = { export default function ServerWrapperLayout({ children, }: ServerWrapperLayoutProps) { + const pathname = usePathname(); + const isDashboard = pathname.startsWith("/dashboard"); + return (
{children} -
);