diff --git a/package-lock.json b/package-lock.json index 1bc508ef8..dba23c8af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,9 +18,10 @@ "react-dom": "^18.2.0", "react-icon-cloud": "^1.1.3", "react-icons": "^5.2.1", - "react-router-dom": "^6.23.0", + "react-router-dom": "^6.28.2", "react-spring": "^9.7.3", "react-three-fiber": "^6.0.13", + "react-toastify": "^11.0.3", "simple-icons": "^5.24.0", "stats.js": "^0.17.0", "three": "^0.165.0", @@ -3665,9 +3666,10 @@ } }, "node_modules/@remix-run/router": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", - "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.1.tgz", + "integrity": "sha512-KeBYSwohb8g4/wCcnksvKTYlg69O62sQeLynn2YE+5z7JWEj95if27kclW9QqbrlsQ2DINI8fjbV3zyuKfwjKg==", + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -5261,6 +5263,15 @@ "node": ">=6" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -10110,11 +10121,12 @@ } }, "node_modules/react-router": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz", - "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==", + "version": "6.28.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.2.tgz", + "integrity": "sha512-BgFY7+wEGVjHCiqaj2XiUBQ1kkzfg6UoKYwEe0wv+FF+HNPCxtS/MVPvLAPH++EsuCMReZl9RYVGqcHLk5ms3A==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.16.0" + "@remix-run/router": "1.21.1" }, "engines": { "node": ">=14.0.0" @@ -10124,12 +10136,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz", - "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==", + "version": "6.28.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.2.tgz", + "integrity": "sha512-O81EWqNJWqvlN/a7eTudAdQm0TbI7hw+WIi7OwwMcTn5JMyZ0ibTFNGz+t+Lju0df4LcqowCegcrK22lB1q9Kw==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.16.0", - "react-router": "6.23.0" + "@remix-run/router": "1.21.1", + "react-router": "6.28.2" }, "engines": { "node": ">=14.0.0" @@ -10301,6 +10314,19 @@ } } }, + "node_modules/react-toastify": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.3.tgz", + "integrity": "sha512-cbPtHJPfc0sGqVwozBwaTrTu1ogB9+BLLjd4dDXd863qYLj7DGrQ2sg5RAChjFUB4yc3w8iXOtWcJqPK/6xqRQ==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/react-use-measure": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", diff --git a/package.json b/package.json index d555ef09d..83d796842 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,10 @@ "react-dom": "^18.2.0", "react-icon-cloud": "^1.1.3", "react-icons": "^5.2.1", - "react-router-dom": "^6.23.0", + "react-router-dom": "^6.28.2", "react-spring": "^9.7.3", "react-three-fiber": "^6.0.13", + "react-toastify": "^11.0.3", "simple-icons": "^5.24.0", "stats.js": "^0.17.0", "three": "^0.165.0", diff --git a/src/App.jsx b/src/App.jsx index 99d8d90b1..db9a9dc61 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -14,7 +14,11 @@ import UpcomingProjects from './Components/UpcomingProjects'; import VerticalTimeline from './Components/Timeline'; import Carousel from './Components/Carousel'; import Hero from './Components/Hero'; -import Footer from "./Components/Footer" +import Footer from "./Components/Footer"; +import AdminApproval from './Components/AdminApproval'; +import { AchievementProvider } from './Components/AchievementContext'; +import { ToastContainer } from 'react-toastify'; // Import ToastContainer +import 'react-toastify/dist/ReactToastify.css'; const Homepage = () => { @@ -22,7 +26,7 @@ const Homepage = () => {
{/* Text Section between Hero and Carousel */} -
+

Welcome to Project X

We are an exclusive club at Veermata Jijabai Technological Institute, Mumbai. We provide a collaborative environment for students to learn, grow, and build projects together under mentorship. Explore our achievements, past projects, and upcoming events! @@ -42,6 +46,7 @@ const Homepage = () => { function App() { return (

+ @@ -49,14 +54,19 @@ function App() { } /> } /> } /> + } /> } /> } /> + + {/* } /> */} {/* } /> */}
+ +
diff --git a/src/Components/AchievementContext.jsx b/src/Components/AchievementContext.jsx new file mode 100644 index 000000000..f37dd2ac2 --- /dev/null +++ b/src/Components/AchievementContext.jsx @@ -0,0 +1,89 @@ +import React, { createContext, useState } from 'react'; + +export const AchievementContext = createContext(); + +export const AchievementProvider = ({ children }) => { + const [submittedAchievements, setSubmittedAchievements] = useState([]); + const [approvedAchievements, setApprovedAchievements] = useState([ + { + name: 'Kshitij Shah', + category: 'GSOC', + image: 'https://i.ibb.co/jJyjcvZ/Kshitij-Shah-photoaidcom-cropped.png', + achievementDetails: 'Sugarlabs', + link: 'https://github.com/kshitijdshah99/Pippy_Activity', + }, + { + name: 'Vedant Mehra', + category: 'GSOC', + image: 'https://i.ibb.co/Z6fgX5F/IMG-20231110-003535-01-01-photoaidcom-cropped.png', + achievementDetails: 'CERN', + link: 'https://hepsoftwarefoundation.org/gsoc/blogs/2024/blog_SOFIE_VedantMehra.html', + }, + { + name: 'Mayank Palan', + category: 'GSOC', + image: 'https://i.ibb.co/Sm9FtJ2/1714656497995-photoaidcom-cropped.png', + achievementDetails: 'Red Hen Lab', + link: 'https://medium.com/@mayankpalan066/gsoc24-with-red-hen-lab-modeling-wayfinding-cfb0131b71d1', + }, + { + name: 'Sharan Poojari', + category: 'GSOC', + image: 'https://i.ibb.co/C640S72/Scanned-20240502-1627-1-page-0001-photoaidcom-cropped.png', + achievementDetails: 'NumFOCUS', + link: 'https://github.com/aiidateam/aiida-explorer/blob/gsoc/gsoc/README.md', + }, + { + name: 'Warren Jacinto', + category: 'GSOC', + image: 'https://i.ibb.co/T4D9vMh/IMG-20240502-172654-photoaidcom-cropped.png', + achievementDetails: 'Open Astronomy', + link: 'https://deadspheroid.github.io/my-blog/', + }, + { + name: 'Tvisha Vedant', + category: 'GSOC', + image: 'https://i.ibb.co/t3vy9D9/IMG-20240502-WA0043-2-photoaidcom-cropped.png', + achievementDetails: 'INCF', + link: 'https://tvilight4.github.io/MyBlog/', + }, + { + name: 'Raya Chakravarthy', + category: 'GSOC', + image: 'https://i.ibb.co/9WSz3ss/photo-photoaidcom-cropped.png', + achievementDetails: 'INCF', + link: 'https://raya679.github.io/gsoc/', + }, + ]); + + const submitAchievement = (achievement) => { + setSubmittedAchievements((prev) => [...prev, achievement]); + }; + + const approveAchievement = (achievement) => { + setApprovedAchievements((prev) => [...prev, achievement]); // Add to approvedAchievements + setSubmittedAchievements((prev) => + prev.filter((item) => item !== achievement) // Remove from submittedAchievements + ); + }; + + const rejectAchievement = (achievement) => { + setSubmittedAchievements((prev) => + prev.filter((item) => item !== achievement) // Remove from submittedAchievements + ); + }; + + return ( + + {children} + + ); +}; diff --git a/src/Components/AchievementForm.jsx b/src/Components/AchievementForm.jsx new file mode 100644 index 000000000..50855e641 --- /dev/null +++ b/src/Components/AchievementForm.jsx @@ -0,0 +1,165 @@ +import React, { useState, useContext } from 'react'; +import { AchievementContext } from './AchievementContext'; +import { toast } from 'react-toastify'; // Import toast from react-toastify +import 'react-toastify/dist/ReactToastify.css'; // Import toast styles +import { FaTimes } from 'react-icons/fa'; // Import close icon + +const AchievementForm = ({ onClose }) => { + const { submitAchievement } = useContext(AchievementContext); + const [formData, setFormData] = useState({ + name: '', + category: '', + image: '', + achievementDetails: '', + link: '', + }); + + const categories = ['GSOC', 'Hackathons', 'Competitions', 'Miscellaneous']; + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData({ ...formData, [name]: value }); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + console.log('Submitting achievement:', formData); + submitAchievement(formData); // Submit the achievement + + // Custom dark blue toast + toast.info('Your application will be reviewed.', { + position: 'bottom-right', + autoClose: 5000, // Close the toast after 5 seconds + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + style: { + background: '#1e3a8a', // Dark blue background + color: '#ffffff', // White text + }, + progressStyle: { + background: '#3b82f6', // Blue progress bar + }, + }); + + onClose(); // Close the form + }; + + return ( +
+
+ {/* Close button */} + + +

Add Achievement

+
+ {/* Name Field */} +
+ + +
+ + {/* Category Field */} +
+ + +
+ + {/* Image URL Field */} +
+ + +
+ + {/* Achievement Details Field */} +
+ + +
+ + {/* Achievement Link Field */} +
+ + +
+ + {/* Form Buttons */} +
+ + +
+
+
+
+ ); +}; + +export default AchievementForm; \ No newline at end of file diff --git a/src/Components/Achievements.jsx b/src/Components/Achievements.jsx index 3214455e2..8bce31c1e 100644 --- a/src/Components/Achievements.jsx +++ b/src/Components/Achievements.jsx @@ -1,31 +1,123 @@ -import React from 'react'; +import React, { useState, useContext } from 'react'; +import { AchievementContext } from './AchievementContext'; +import AchievementForm from './AchievementForm'; +import AdminApproval from './AdminApproval'; import CircularCard from './CircularCard'; +import { FaPlus, FaUserCog } from 'react-icons/fa'; const Achievements = () => { + const [isFormOpen, setIsFormOpen] = useState(false); + const [isAdminModalOpen, setIsAdminModalOpen] = useState(false); + const [selectedCategory, setSelectedCategory] = useState('GSOC'); + const { approvedAchievements } = useContext(AchievementContext); // Access approvedAchievements + + const sections = ['GSOC', 'Hackathons', 'Competitions', 'Miscellaneous']; + + const handleSectionClick = (section) => { + setSelectedCategory(section); + }; + + const handleAddAchievementClick = () => { + setIsFormOpen(true); + }; + + const handleFormClose = () => { + setIsFormOpen(false); + }; + + const handleAdminButtonClick = () => { + setIsAdminModalOpen(true); + }; + + const handleAdminModalClose = () => { + setIsAdminModalOpen(false); + }; + + // Filter approved achievements by the selected category + const filteredAchievements = selectedCategory + ? approvedAchievements.filter( + (achievement) => achievement.category === selectedCategory + ) + : []; + return ( -
+
+ {/* Add your achievement button */} +
+ +
+ Add your achievement +
+
+ + {/* Admin button */} +
+ +
+ Admin Approval +
+
+

- GSoC Contributors + Achievements

-
-
- - - - -
+
+ {sections.map((section) => ( +
handleSectionClick(section)} + className={`flex text-center font-mono flex-col items-center p-4 shadow-[0_4px_14px_0_rgba(96,165,250,0.5)] w-72 bg-transparent rounded-lg transform hover:scale-105 transition-transform duration-200 ease-in-out cursor-pointer ${ + selectedCategory === section + ? 'border-2 border-purple-400' + : 'border-2 border-transparent' + }`} + > +

{section}

+
+ ))}
-
-
-
- - - -
-
+ + {/* Display filtered achievements */} + {selectedCategory && ( +
+

+ {selectedCategory} Achievers +

+
+ {filteredAchievements.length > 0 ? ( + filteredAchievements.map((achievement, index) => ( + + )) + ) : ( +

No achievers found for this category.

+ )} +
+ )} + + {/* Achievement form */} + {isFormOpen && } + + {/* Admin approval modal */} + {isAdminModalOpen && }
); -} +}; -export default Achievements; +export default Achievements; \ No newline at end of file diff --git a/src/Components/AdminApproval.jsx b/src/Components/AdminApproval.jsx new file mode 100644 index 000000000..77ebc32dc --- /dev/null +++ b/src/Components/AdminApproval.jsx @@ -0,0 +1,66 @@ +import React, { useContext } from 'react'; +import { AchievementContext } from './AchievementContext'; + +const AdminApproval = ({ onClose }) => { + const { submittedAchievements, approveAchievement, rejectAchievement } = + useContext(AchievementContext); + + return ( +
+
+

Admin Approval

+ {submittedAchievements.length === 0 ? ( +

No achievements to approve.

+ ) : ( +
+ {submittedAchievements.map((achievement, index) => ( +
+

+ {achievement.name} +

+

+ Category: {achievement.category} +

+

+ Details: {achievement.achievementDetails} +

+ {achievement.name} +
+ + +
+
+ ))} +
+ )} +
+ +
+
+
+ ); +}; + +export default AdminApproval; \ No newline at end of file