diff --git a/app/(default)/achievements/page.tsx b/app/(default)/achievements/page.tsx index 55551c7..3eceb8e 100644 --- a/app/(default)/achievements/page.tsx +++ b/app/(default)/achievements/page.tsx @@ -73,4 +73,4 @@ export default function AchievementsPage() { ); -} +} \ No newline at end of file diff --git a/app/(default)/api/membersData/route.ts b/app/(default)/api/membersData/route.ts new file mode 100644 index 0000000..cec22bd --- /dev/null +++ b/app/(default)/api/membersData/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/Firebase'; +import { collection, addDoc } from 'firebase/firestore'; + +export async function POST(request: Request) { + try { + const data = await request.json(); + + if (!Array.isArray(data)) { + return NextResponse.json({ message: 'Expected an array of JSON objects', error: true }); + } + + const savedData = []; + for (const item of data) { + // Save each item to Firebase Firestore + const docRef = await addDoc(collection(db, "members"), item); + console.log("Document written with ID: ", docRef.id); + + // Collect saved data for response + savedData.push({ id: docRef.id, ...item }); + } + + // Return a success response + return NextResponse.json({ message: 'Registration successful', data: savedData }); + } catch (error) { + console.error('An error occurred:', error); + return NextResponse.json({ message: 'An error occurred', error }); + } +} \ No newline at end of file diff --git a/app/(default)/api/registration/sih/route.ts b/app/(default)/api/registration/sih/route.ts index 524d7c7..f3e98fc 100644 --- a/app/(default)/api/registration/sih/route.ts +++ b/app/(default)/api/registration/sih/route.ts @@ -1,19 +1,19 @@ import { db } from '@/Firebase'; import { sihValidate } from '@/lib/utils'; import { addDoc, collection, getDocs } from 'firebase/firestore'; -import { NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; // Add a new registration -export async function POST(request: Request) { - const data = await request.json(); +export async function POST(request: Request) { + const data = await request.json(); // Validate the data const val = sihValidate(data); - if(val.error){ + if (val.error) { return NextResponse.json({ message: 'Validation error', error: val.error }); } - try{ + try { // Save to Firebase const docRef = await addDoc(collection(db, "sih2024"), data); console.log("Document written with ID: ", docRef.id); @@ -22,12 +22,12 @@ export async function POST(request: Request) { return NextResponse.json({ message: 'An error occurred', error }); } // Return a response - return NextResponse.json({ message: 'Registration successful', data }); -} + return NextResponse.json({ message: 'Registration successful', data }); +} //get all registrations -export async function GET() { - try{ +export async function GET() { + try { // Get all registrations in sih2024 collection const querySnapshot = await getDocs(collection(db, "sih2024")); // Map the data to get only the data diff --git a/app/(default)/events/page.tsx b/app/(default)/events/page.tsx index db8be18..ef5bc9b 100644 --- a/app/(default)/events/page.tsx +++ b/app/(default)/events/page.tsx @@ -2,7 +2,7 @@ export const metadata = { title: 'Events', description: 'Events page', } - export default function Events() { +export default function Events() { return ( <> diff --git a/app/(default)/layout.tsx b/app/(default)/layout.tsx index d0a2788..83a7959 100644 --- a/app/(default)/layout.tsx +++ b/app/(default)/layout.tsx @@ -11,7 +11,7 @@ export default function DefaultLayout({ children, }: { children: React.ReactNode -}) { +}) { useEffect(() => { AOS.init({ diff --git a/app/(default)/leads/page.tsx b/app/(default)/leads/page.tsx index e8468ea..7ab37b0 100644 --- a/app/(default)/leads/page.tsx +++ b/app/(default)/leads/page.tsx @@ -3,7 +3,8 @@ export const metadata = { description: 'Leads page', } - export default function Leads() { +export default function Leads() { + return ( <> diff --git a/app/(default)/members/page.tsx b/app/(default)/members/page.tsx new file mode 100644 index 0000000..d2f1fca --- /dev/null +++ b/app/(default)/members/page.tsx @@ -0,0 +1,13 @@ +import Members from "@/components/Members" + +export const metadata = { + title: 'Members', + description: 'Members page', +} +export default function Events() { + return ( + <> + + + ) +} \ No newline at end of file diff --git a/components/Members.tsx b/components/Members.tsx new file mode 100644 index 0000000..8e3a215 --- /dev/null +++ b/components/Members.tsx @@ -0,0 +1,99 @@ +"use client"; + +import React, { useState, useEffect } from 'react'; +import Card from '@/components/ui/Card'; +import CollapsibleSection from '@/components/ui/CollapsibleSection'; +import { db } from "@/Firebase"; +import { collection, query, where, getDocs } from "firebase/firestore"; + +interface Member { + id: string; + name: string; + domain: string; + company?: string; + year: string; +} + +const headings = ["Alumni", "Fourth Year", "Third Year", "Second Year", "First Year"]; + +export default function Members() { + // Set default open index to the index of "Alumni" + const [openIndex, setOpenIndex] = useState(headings.indexOf("Alumni")); + const [data, setData] = useState<{ [key: string]: Member[] }>({}); + + const handleToggle = (index: number) => { + setOpenIndex(openIndex === index ? -1 : index); + }; + + useEffect(() => { + const fetchData = async () => { + try { + const fetchedData: { [key: string]: Member[] } = {}; + + for (const heading of headings) { + const membersCollection = collection(db, "members"); + const q = query(membersCollection, where("year", "==", heading)); + const querySnapshot = await getDocs(q); + + // Collect all documents + const allMembers = querySnapshot.docs.map(doc => ({ + id: doc.id, + ...(doc.data() as Omit) + })) as Member[]; + + // Remove duplicates based on name, domain, and year + const uniqueMembers = Array.from( + new Map( + allMembers.map(member => [ + `${member.name}-${member.domain}-${member.year}`, + member + ]) + ).values() + ); + + // Sort members alphabetically by name + const sortedMembers = uniqueMembers.sort((a, b) => a.name.localeCompare(b.name)); + + fetchedData[heading] = sortedMembers; + } + + setData(fetchedData); + } catch (error) { + console.error("Error fetching data: ", error); + } + }; + fetchData(); + }, []); + + return ( +
+

+
+
+ {headings.map((heading, index) => ( + +
+ {data[heading]?.map((profile, cardIndex) => ( + + ))} +
+
+ } + isOpen={openIndex === index} + onToggle={() => handleToggle(index)} + /> + ))} +
+
+ + ); +} diff --git a/components/landing.tsx b/components/landing.tsx index 2cb7af2..572a157 100644 --- a/components/landing.tsx +++ b/components/landing.tsx @@ -4,102 +4,101 @@ import '../app/css/additional-styles/landing.css'; import ImageOne from '../public/images/demo.jpg'; import Image from "next/image"; -export default function Landing() -{ - return( - <> +export default function Landing() { + return ( + <>

- Activities + Activities

- We organise lots of student centric activities + We organise lots of student centric activities
- -
-
-
-

+ +
+
+
+

COMPETITIVE PROGRAMMING -

-

+

+

PB Hustle -

-

- Point Blank has organized over 40+ editions of its PB Hustle coding contest, - where participants solve 5-7 increasingly difficult problems in their preferred +

+

+ Point Blank has organized over 40+ editions of its PB Hustle coding contest, + where participants solve 5-7 increasingly difficult problems in their preferred programming language. The contest aims to enhance the programming culture in colleges - and help teams qualify for the ACM ICPC. Impressively, DSCE's leading programmers have - risen through this platform, with participation in the CodeChef Long Challenge expanding - from just 3 to over 70+ participants. -

-
-
+ and help teams qualify for the ACM ICPC. Impressively, DSCE's leading programmers have + risen through this platform, with participation in the CodeChef Long Challenge expanding + from just 3 to over 70+ participants. +

+
+
- +
- + -
+
- +
-

+

DEVELOPMENT -

-

+

+

PB Chronicals -

-

- Point Blank has hosted over 100+ workshops, covering a wide range - of topics including open source, DevOps, machine learning, placement - preparation, data structures and algorithms, and mobile and web development, - among others. These workshops are typically conducted in an informal and unscripted - manner, though we do document some of our most significant sessions. - One notable example is our primer on F/OSS development. -

-
-
+ +

+ Point Blank has hosted over 100+ workshops, covering a wide range + of topics including open source, DevOps, machine learning, placement + preparation, data structures and algorithms, and mobile and web development, + among others. These workshops are typically conducted in an informal and unscripted + manner, though we do document some of our most significant sessions. + One notable example is our primer on F/OSS development. +

+
+ -
-
-
-

- HACKATHONS -

-

+
+
+
+

+ HACKATHONS +

+

Smart India Hackathon -

-

- Each year, we organize the internal - round of the Smart India Hackathon. In the 2020 edition, over 300+ - individuals from DSCE participated. Two of our teams advanced to the +

+

+ Each year, we organize the internal + round of the Smart India Hackathon. In the 2020 edition, over 300+ + individuals from DSCE participated. Two of our teams advanced to the finals, with one emerging as the winner of the software edition. Along with this, teams from Point Blank have won hackathons all across - the city and country. -

-
-
+ the city and country. +

+
+
- +
- + + +
-
-
- +
-
+

CYBER SECURITY

@@ -107,26 +106,26 @@ export default function Landing() PB CTF

- We organize workshops and sessions on various topics in cybersecurity, - including hands-on practice on different platforms. In 2023, we launched - the first in-house Capture The Flag event, PBCTF, which attracted over 70+ participants. -

-
-
-
+ We organize workshops and sessions on various topics in cybersecurity, + including hands-on practice on different platforms. In 2023, we launched + the first in-house Capture The Flag event, PBCTF, which attracted over 70+ participants. +

+
+
+
-

- Events -

-
- We organise lots of student centric activities -
+

+ Events +

+
+ We organise lots of student centric activities +
- +
@@ -137,18 +136,18 @@ export default function Landing()
- + - +
- +
- - ) + + ) } @@ -160,10 +159,10 @@ const leads = [ ]; const eventCard = [ - { id:4 ,textt:"Back of Card", textb:"Additional info on the back of the card"}, - { id:5 ,textt:"Back of Card", textb:"Additional info on the back of the card"}, - { id:6 ,textt:"Back of Card", textb:"Additional info on the back of the card"}, - + { id: 4, textt: "Back of Card", textb: "Additional info on the back of the card" }, + { id: 5, textt: "Back of Card", textb: "Additional info on the back of the card" }, + { id: 6, textt: "Back of Card", textb: "Additional info on the back of the card" }, + ]; const CardComponents = () => { @@ -188,20 +187,20 @@ const CardComponents = () => { const EventComponent = () => { return ( <> - {eventCard.map((ec) => ( -
-
-
-
+ {eventCard.map((ec) => ( +
+
+
+
-

{ec.textt}

-

{ec.textb}

+

{ec.textt}

+

{ec.textb}

-
+
+
-
- ))} + ))} ) } \ No newline at end of file diff --git a/components/magicui/animated-grid-pattern.tsx b/components/magicui/animated-grid-pattern.tsx new file mode 100644 index 0000000..ce9201d --- /dev/null +++ b/components/magicui/animated-grid-pattern.tsx @@ -0,0 +1,150 @@ +"use client"; + +import { useEffect, useId, useRef, useState } from "react"; +import { motion } from "framer-motion"; + +import { cn } from "@/lib/utils"; + +interface GridPatternProps { + width?: number; + height?: number; + x?: number; + y?: number; + strokeDasharray?: any; + numSquares?: number; + className?: string; + maxOpacity?: number; + duration?: number; + repeatDelay?: number; +} + +export function GridPattern({ + width = 40, + height = 40, + x = -1, + y = -1, + strokeDasharray = 0, + numSquares = 50, + className, + maxOpacity = 0.5, + duration = 4, + repeatDelay = 0.5, + ...props +}: GridPatternProps) { + const id = useId(); + const containerRef = useRef(null); + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + const [squares, setSquares] = useState(() => generateSquares(numSquares)); + + function getPos() { + return [ + Math.floor((Math.random() * dimensions.width) / width), + Math.floor((Math.random() * dimensions.height) / height), + ]; + } + + // Adjust the generateSquares function to return objects with an id, x, and y + function generateSquares(count: number) { + return Array.from({ length: count }, (_, i) => ({ + id: i, + pos: getPos(), + })); + } + + // Function to update a single square's position + const updateSquarePosition = (id: number) => { + setSquares((currentSquares) => + currentSquares.map((sq) => + sq.id === id + ? { + ...sq, + pos: getPos(), + } + : sq, + ), + ); + }; + + // Update squares to animate in + useEffect(() => { + if (dimensions.width && dimensions.height) { + setSquares(generateSquares(numSquares)); + } + }, [dimensions, numSquares]); + + // Resize observer to update container dimensions + useEffect(() => { + const resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + setDimensions({ + width: entry.contentRect.width, + height: entry.contentRect.height, + }); + } + }); + + if (containerRef.current) { + resizeObserver.observe(containerRef.current); + } + + return () => { + if (containerRef.current) { + resizeObserver.unobserve(containerRef.current); + } + }; + }, [containerRef]); + + return ( + + ); +} + +export default GridPattern; diff --git a/components/ui/Card.tsx b/components/ui/Card.tsx new file mode 100644 index 0000000..c107426 --- /dev/null +++ b/components/ui/Card.tsx @@ -0,0 +1,26 @@ +interface CardProps { + name: string; + domain: string; + company: string; +} + +const Card: React.FC = ({ name, domain, company }) => { + return ( + +
+
+
+
+

{name}

+

{domain}

+

+ {company} +

+
+
+
+ + ); +} + +export default Card; \ No newline at end of file diff --git a/components/ui/CollapsibleSection.tsx b/components/ui/CollapsibleSection.tsx new file mode 100644 index 0000000..e5f0c8e --- /dev/null +++ b/components/ui/CollapsibleSection.tsx @@ -0,0 +1,56 @@ +import React, { forwardRef, useImperativeHandle, useRef, useEffect } from 'react'; +import { FaChevronDown, FaChevronUp } from 'react-icons/fa'; + +interface CollapsibleSectionProps { + heading: string; + content: JSX.Element; + isOpen: boolean; + onToggle: () => void; +} + +const CollapsibleSection = forwardRef( + ({ heading, content, isOpen, onToggle }, ref) => { + const localRef = useRef(null); + + useImperativeHandle(ref, () => localRef.current!); + + useEffect(() => { + if (isOpen && localRef.current) { + localRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }, [isOpen]); + + return ( +
+
{ + e.preventDefault(); + onToggle(); + }} + tabIndex={-1} + className="flex items-center justify-between p-4 cursor-pointer bg-black text-white" + > +

+ {heading} +

+ + {isOpen ? ( + + ) : ( + + )} +
+ {isOpen && ( +
+ {content} +
+ )} +
+ ); + } +); + +CollapsibleSection.displayName = 'CollapsibleSection'; + +export default CollapsibleSection; \ No newline at end of file diff --git a/components/ui/footer.tsx b/components/ui/footer.tsx index 6bb15ad..960499a 100644 --- a/components/ui/footer.tsx +++ b/components/ui/footer.tsx @@ -13,6 +13,7 @@ export default function Footer() {
  • Home
  • Events
  • Leads
  • +
  • Members
  • Achievements
  • Made with ❤️ by Point Blank. All Rights Reserved. diff --git a/components/ui/header.tsx b/components/ui/header.tsx index 5990975..9bbf24c 100644 --- a/components/ui/header.tsx +++ b/components/ui/header.tsx @@ -40,6 +40,11 @@ export default function Header() {

    Leads

    +
  • + +

    Members

    + +
  • Achievements

    diff --git a/components/ui/mobile-menu.tsx b/components/ui/mobile-menu.tsx index d7d65dc..01978e8 100644 --- a/components/ui/mobile-menu.tsx +++ b/components/ui/mobile-menu.tsx @@ -72,11 +72,14 @@ export default function MobileMenu() {
  • setMobileNavOpen(false)}>Leads
  • +
  • + setMobileNavOpen(false)}>Members +
  • setMobileNavOpen(false)}>Achievements
  • - setMobileNavOpen(false)}> + setMobileNavOpen(false)}> Contact Us
  • diff --git a/package-lock.json b/package-lock.json index f242dd1..729a1f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "next-themes": "^0.3.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-icons": "^5.3.0", "react-spring": "^9.7.4", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", @@ -8254,6 +8255,14 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 80f910e..6b3b7c4 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "next-themes": "^0.3.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-icons": "^5.3.0", "react-spring": "^9.7.4", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", diff --git a/tsconfig.json b/tsconfig.json index e7ff90f..52494fa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -18,9 +22,18 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file