diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f7b9c59 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,95 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to this project! We welcome contributions from the community. Please read the following guidelines to help us maintain a high standard and streamline the contribution process. + +## How to Contribute + +1. **Fork the Repository** + + - Click the 'Fork' button at the top right of this repository page to create your own copy. + +2. **Clone Your Fork** + + - Clone your forked repository to your local machine: + ```bash + git clone https://github.com//ElixirV3.git + cd ElixirV3 + ``` + +3. **Create a New Branch** + + - Create a branch for your feature or bugfix: + ```bash + git checkout -b + ``` + +4. **Make Your Changes** + + - Make your changes or additions. Please ensure your code follows the existing style and conventions. + +5. **Test Your Changes** + + - Run the application and ensure your changes work as expected and do not break existing functionality. + +6. **Commit Your Changes** + + - Use clear and descriptive commit messages. Follow the commit message format below. + +7. **Push to Your Fork** + + - Push your branch to your forked repository: + ```bash + git push origin + ``` + +8. **Open a Pull Request** + - Go to the original repository and open a pull request. Provide a clear description of your changes and reference any related issues. + +## Commit Message Format + +``` + | (): +``` + +### Examples + +``` +GFG | feat(event): new card designs +GFG | fix(event): api request improve +GFG | chore(event): title copy change +GFG | refactor(event): mentor's search logic + +ELX | feat(event): new card designs +GDG | fix(event): api request improve +CC | chore(event): title copy change +``` + +- ``: Team code. If you are a member of one of the Elixir clubs, use your club's code: + + - `GFG` for GeeksforGeeks ABESEC + - `GDG` for Google Developer Group ABESEC + - `CC` for CodeChef ABESEC + - `ELX` for external contributers + +- ``: Type of change (feat, fix, chore, refactor, etc.) +- ``: Area affected (e.g., event) +- ``: Short summary of the change + +## Code Style & Standards + +- Follow the existing code style and formatting. +- Write clear, concise, and well-documented code. +- Add comments where necessary, especially for complex logic. +- Write tests for new features or bug fixes if applicable. + +## Community Standards + +- Be respectful and inclusive in all interactions. +- Provide constructive feedback in code reviews. +- Report issues or bugs using the issue tracker. + +## Need Help? + +If you have any questions or need guidance, feel free to open an issue or reach out to the maintainers. + +Thank you for contributing! 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/MentorCard.tsx b/components/MentorCard.tsx index cb4f6c7..ff2ca4a 100644 --- a/components/MentorCard.tsx +++ b/components/MentorCard.tsx @@ -1,42 +1,86 @@ import Image from "next/image"; -import React, { memo } from "react"; -import { FaLinkedin } from "react-icons/fa6"; +import { FaLinkedin, FaGithub } from "react-icons/fa"; +import { motion } from "framer-motion"; const MentorCard = ({ data }: { data: any }) => { return ( -
-
+ +
{data.name} + {/* gradient overlay */} +
-
- {data.name} - {data.techStack} + {/* Content overlay */} +
+ +

+ {data.name} +

+

+ {data.techStack} +

+
- + + LinkedIn + + + {/* Hidden for now */} + {/* {data.discord && ( + + + Discord + + )} */} + +
-
+ + {/* hover effect */} +
+ ); }; -export default memo(MentorCard); +export default MentorCard; 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/components/ui/SearchBar.tsx b/components/ui/SearchBar.tsx index bb1f841..8980a61 100644 --- a/components/ui/SearchBar.tsx +++ b/components/ui/SearchBar.tsx @@ -1,22 +1,23 @@ "use client"; -import { useState } from "react"; -import { PlaceholdersAndVanishInput } from "../ui/placeholders-and-vanish-input"; +import { motion } from "framer-motion"; import { MentorData } from "@/constants/constants"; +import { cn } from "@/lib/utils"; -export function SearchBar({ mentors = [{}], setMentorData }: { mentors: any[]; setMentorData: (data: any) => void }) { - const placeholders = [ - "Search for React", - "Search for Frontend", - "Search for Backend", - "Search for Go", - "Search for Devops", - ]; +export function SearchBar({ + mentors = [{}], + setMentorData, +}: { + mentors: any[]; + setMentorData: (data: any) => void; +}) { const getFilteredMentor = (searchTerm: string) => { const filteredMentors = MentorData.filter((mentor) => { const values = Object.values(mentor); return values.some( - (value) => typeof value === "string" && value.toLowerCase().includes(searchTerm.toLowerCase()), + (value) => + typeof value === "string" && + value.toLowerCase().includes(searchTerm.toLowerCase()) ); }); return filteredMentors; @@ -24,32 +25,69 @@ export function SearchBar({ mentors = [{}], setMentorData }: { mentors: any[]; s const handleChange = (e: React.ChangeEvent) => { const searchTerm = e.target.value; - const filteredMentors = getFilteredMentor(searchTerm); if (searchTerm.length === 0) { setMentorData(MentorData); - } else if (filteredMentors.length === 0) { - setMentorData([ - { - name: "No Mentors Found", - image: "/jsm-logo.png", - description: "Please try a different search term", - skills: "No skills found", - }, - ]); } else { setMentorData(filteredMentors); } }; const onSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (mentors[0].name === "No Mentors Found") { - setMentorData(MentorData); - } }; return ( -
- +
+
+ + + +
); } diff --git a/layouts/MentorPageLayout.tsx b/layouts/MentorPageLayout.tsx index a234ce1..ef3980d 100644 --- a/layouts/MentorPageLayout.tsx +++ b/layouts/MentorPageLayout.tsx @@ -9,19 +9,23 @@ const MentorPageLayout = () => { const [mentorData, setMentorData] = useState(MentorData); return ( -
+
- {mentorData.length > 0 && ( - - )} +
- {mentorData.map((mentor, idx) => ( - - ))} + {mentorData?.length === 0 ? ( +
+

No mentors found

+
+ ) : ( + mentorData?.map((mentor, idx) => ( + + )) + )}
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} -
);