{"version":3,"file":"index-BT6r2QxI.js","sources":["../../src/Components/Navbar.jsx","../../src/utilis/auth.js","../../src/Components/Login.jsx","../../src/Components/Home.jsx","../../src/Components/About.jsx","../../src/Components/Activity.jsx","../../src/Components/Member_ship.jsx","../../src/Components/Photogallery.jsx","../../src/Components/Journal.jsx","../../src/Components/JournalDetail.jsx","../../src/Components/Contactus.jsx","../../src/Components/Footer.jsx","../../src/Components/ExecutiveCouncil.jsx","../../src/Components/ExternalLinks.jsx","../../src/Components/admin/Home.jsx","../../src/Components/admin/Notifications.jsx","../../src/Components/admin/Navbar.jsx","../../src/Components/admin/Aboutus.jsx","../../src/Components/admin/Activity.jsx","../../src/Components/admin/Executive.jsx","../../src/Components/admin/Photogallery.jsx","../../src/Components/admin/Journal.jsx","../../src/Components/admin/Adminlogin.jsx","../../src/Components/admin/MemberTable.jsx","../../src/Components/admin/ExternalLinks.jsx","../../src/Components/admin/Login.jsx","../../src/Components/MemberModification.jsx","../../src/Components/admin/MembershipHandle.jsx","../../src/App.jsx","../../src/main.jsx"],"sourcesContent":["import React, { useState } from 'react';\n\nconst Navbar = () => {\n const [isOpen, setIsOpen] = useState(false);\n const [clicked, setClicked] = useState('Home');\n \n const scrollToSection = (sectionId) => {\n const element = document.getElementById(sectionId);\n if (element) {\n element.scrollIntoView({ behavior: 'smooth' });\n }\n };\n\n const toggleMenu = () => {\n setIsOpen(!isOpen);\n };\n\n const click = (e) => {\n e.preventDefault();\n console.log(e.target.value);\n };\n\n return (\n
\n \n
\n \"Logo\"\n
\n

\n THE AERONAUTICAL SOCIETY OF INDIA
KOCHI BRANCH\n

\n

\n Promoting Excellence in Aeronautical and Aerospace profession\n

\n
\n
\n\n \n\n
\n \n
\n\n {/* Mobile Menu */}\n
\n Home\n About Us\n Activities\n Photo gallery\n Membership\n Journal\n Contact Us\n
\n
\n );\n};\n\nexport default Navbar;","import Cookies from 'js-cookie';\n\n/**\n * Gets the JWT token from cookies.\n * @returns {string|null} The JWT token.\n */\nconst getToken = () => Cookies.get('jwtToken');\n\n/**\n * Sets authentication-related cookies.\n * @param {string} jwtToken - JWT token.\n * @param {string} membershipId - User's membership ID.\n */\nconst setAuthData = (jwtToken, membershipId) => {\n Cookies.set('jwtToken', jwtToken, { expires: 7, sameSite: 'Lax' });\n Cookies.set('membershipId', membershipId, { expires: 7, sameSite: 'Lax' });\n};\n\n/**\n * Clears all authentication-related cookies.\n */\nconst clearAuthData = () => {\n Cookies.remove('jwtToken');\n Cookies.remove('membershipId');\n};\n\n/**\n * Checks if the user is authenticated.\n * @returns {boolean} True if authenticated, otherwise false.\n */\nconst isAuthenticated = () => !!getToken();\n\n/**\n * Stores the JWT token for admin login in cookies.\n * @param {string} adminJwtToken - Admin's JWT token.\n */\nconst setAdminAuthToken = (adminJwtToken) => {\n Cookies.set('adminJwtToken', adminJwtToken, { expires: 7, sameSite: 'Lax' });\n};\n\n/**\n * Gets the JWT token for admin login from cookies.\n * @returns {string|null} The admin JWT token.\n */\nconst getAdminAuthToken = () => Cookies.get('adminJwtToken');\n\n/**\n * Clears the admin JWT token from cookies.\n */\nconst clearAdminAuthToken = () => {\n Cookies.remove('adminJwtToken');\n};\n\nexport const isUserLoggedIn = () => {\n const token = Cookies.get('AuthToken');\n return !!token;\n};\n\nexport {\n getToken,\n setAuthData,\n clearAuthData,\n isAuthenticated,\n setAdminAuthToken,\n getAdminAuthToken,\n clearAdminAuthToken,\n};\n","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport { X } from 'lucide-react';\nimport { setAuthData, isUserLoggedIn } from '../utilis/auth'; // Import your setAuthData utility function and isUserLoggedIn function\nimport Cookies from 'js-cookie'; // Import the js-cookie library\nimport { ToastContainer, toast } from 'react-toastify';\nimport 'react-toastify/dist/ReactToastify.css';\nimport { FaEye, FaEyeSlash } from 'react-icons/fa';\nimport { useNavigate } from 'react-router-dom';\n\nconst LoginModal = ({ isOpen, onClose }) => {\n const navigate = useNavigate();\n const [emailOrMemId, setEmailOrMemId] = useState('');\n const [password, setPassword] = useState('');\n const [errorMessage, setErrorMessage] = useState('');\n const [reveal, setReveal] = useState(false);\n const [isLoggedIn, setIsLoggedIn] = useState(false);\n\n useEffect(() => {\n setIsLoggedIn(isUserLoggedIn());\n }, []);\n\n const handleReveal = () => {\n setReveal(prev => !prev);\n };\n\n const handleLogout = () => {\n Cookies.remove('AuthToken');\n Cookies.remove('Userid');\n setIsLoggedIn(false);\n window.location.reload();\n };\n\n if (isLoggedIn) {\n return (\n \n Logout\n \n );\n }\n\n const handleSubmit = async (e) => {\n e.preventDefault();\n try {\n let requestBody = { password };\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (emailRegex.test(emailOrMemId)) {\n requestBody.email = emailOrMemId;\n } else {\n requestBody.membershipId = emailOrMemId;\n }\n\n const response = await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/login`,\n requestBody\n );\n\n const { token, membershipId, Userid } = response.data;\n setAuthData(token, membershipId);\n Cookies.set('AuthToken', token, { expires: 7 });\n Cookies.set('Userid', Userid, { expires: 7 });\n setIsLoggedIn(true);\n alert('Welcome back!');\n setErrorMessage('');\n onClose();\n window.location.reload(); // Refresh to update login state\n } catch (error) {\n console.error(error);\n setErrorMessage(error.response?.data?.message || 'Login failed. Please try again.');\n }\n };\n\n if (!isOpen) return null;\n\n const HandleForget = () => {\n toast.info(`Contact Admin for the login details !`);\n }\n\n return (\n
\n {/* Backdrop */}\n \n\n {/* Modal */}\n
\n {/* Close button */}\n \n \n \n\n {/* Header */}\n
\n

Welcome Back

\n

Please enter your details to sign in

\n
\n\n {/* Error message */}\n {errorMessage && (\n

{errorMessage}

\n )}\n\n {/* Form */}\n
\n
\n \n Membership ID\n \n setEmailOrMemId(e.target.value)}\n className=\"w-full px-4 py-3 rounded-xl border border-gray-300 focus:outline-none focus:ring-2 focus:ring-[#0098FF] focus:border-transparent transition-all\"\n placeholder=\"AB-12345\"\n required\n />\n
\n\n
\n \n Password\n \n
\n setPassword(e.target.value)}\n className=\"w-full px-4 py-3 rounded-xl border border-gray-300 focus:outline-none focus:ring-2 focus:ring-[#0098FF] focus:border-transparent transition-all\"\n placeholder=\"Enter your password\"\n required\n />\n
\n
\n {reveal ? : }\n
\n
\n
\n
\n\n {/* Forgot password link */}\n
\n \n Forgot password?\n \n
\n\n {/* Submit button */}\n \n Sign in\n \n \n
\n \n\n
\n );\n};\n\nexport default LoginModal;","import React, { useEffect, useState, useCallback } from 'react';\nimport { GoArrowDown, GoArrowRight } from 'react-icons/go';\nimport axios from 'axios';\nimport LoginModal from './Login';\nimport { isUserLoggedIn } from '../utilis/auth';\nimport Cookies from 'js-cookie';\n\nconst Home = () => {\n // State management with default values\n const [images, setImages] = useState(['/home-left-bottom-main.webp']);\n const [currentImageIndex, setCurrentImageIndex] = useState(0);\n const [marqueeText, setMarqueeText] = useState(\n 'Welcome to The Aeronautical Society of India'\n );\n const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n const [isLoggedIn, setIsLoggedIn] = useState(false);\n\n // Pre-define image dimensions to prevent layout shift\n const imageDimensions = {\n backgroundLeft: { width: 500, height: 500 },\n topRight: { width: 300, height: 300 },\n };\n\n useEffect(() => {\n const HandleMarqueeText = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page/`\n );\n setMarqueeText(response.data.marqueeText);\n } catch (error) {\n console.log('Error updating link:', error);\n }\n };\n HandleMarqueeText();\n }, []);\n\n useEffect(() => {\n const fetchLandingPageData = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page/`\n );\n if (response.data && Array.isArray(response.data.images)) {\n setImages(response.data.images);\n } else if (response.data && response.data.bottomImage) {\n setImages([response.data.bottomImage]);\n }\n } catch (error) {\n console.error('Error fetching landing page data:', error);\n }\n };\n\n fetchLandingPageData();\n }, []);\n\n useEffect(() => {\n setIsLoggedIn(isUserLoggedIn());\n }, []);\n\n // Memoized image change callback\n const changeImage = useCallback(() => {\n setCurrentImageIndex((prevIndex) =>\n prevIndex === images.length - 1 ? 0 : prevIndex + 1\n );\n }, [images.length]);\n\n useEffect(() => {\n if (images.length > 1) {\n const interval = setInterval(() => {\n setCurrentImageIndex((prevIndex) =>\n prevIndex === images.length - 1 ? 0 : prevIndex + 1\n );\n }, 5000); // Change image every 5 seconds\n\n return () => clearInterval(interval);\n }\n }, [images]);\n\n const handleLoginClick = () => {\n if (isLoggedIn) {\n // Handle logout\n Cookies.remove('AuthToken');\n Cookies.remove('Userid');\n setIsLoggedIn(false);\n window.location.reload();\n } else {\n setIsLoginModalOpen(true);\n }\n };\n\n return (\n \n
\n \n
\n\n {/* Top Section */}\n
\n \n setIsImageLoaded(true)}\n style={{\n opacity: isImageLoaded ? 1 : 0,\n transition: 'opacity 0.3s',\n }}\n />\n
\n\n {/* Left Content */}\n \n

\n \n The Aeronautical Society of India was founded in 1948 with the\n encouragement from the late Prime Minister Pandit Jawaharlal Nehru\n as a professional body devoted to the advancement of Aeronautical\n Sciences and Engineering in India.\n {' '}\n   The objectives of the Society are to promote the advancement\n and dissemination of knowledge of the Aeronautical Sciences and\n Aircraft Engineering and the elevation of the Aeronautical\n Profession.\n

\n\n

\n Some notable activities of the Aeronautical Society of India include\n yearly forums to exchange ideas and expertise in the field of\n Aeronautics as part of Annual General Meetings, periodic meetings of\n various professionals\n \n   engaged in Aeronautics and related fields from\n organizations like HAL, ISRO, DRDO, Academic institutions,\n Regulatory agencies, and Civil airline operators, conduct of\n prestigious seminars and exhibitions like Aero India, formulation\n of National Aeronautical Policy, Aeronautical awareness programmes\n etc.\n \n

\n \n\n {/* Right Side Image */}\n
\n \n
\n \n\n {/* Marquee */}\n
\n
\n {/* Duplicate the text content for a seamless loop */}\n
\n {[...Array(5)].map((_, index) => (\n \n

\n {marqueeText}\n

\n \n
\n ))}\n
\n
\n \n\n {/* Bottom Section */}\n
\n
\n
\n {!isImageLoaded && (\n
\n
\n
\n )}\n setIsImageLoaded(true)}\n onError={(e) => {\n e.target.src = '/home-left-bottom-main.webp';\n setIsImageLoaded(true);\n }}\n />\n
\n {images.map((_, index) => (\n \n ))}\n
\n
\n
\n\n {/* Scroll Indicator */}\n \n\n
\n \n {isLoggedIn ? 'Logout' : 'Login'} \n \n
\n
\n\n {!isLoggedIn && (\n setIsLoginModalOpen(false)}\n />\n )}\n \n );\n};\n\nexport default Home;\n","import React, { useEffect, useState } from 'react';\nimport axios from 'axios';\nimport { Link } from 'react-router-dom';\n\nconst AboutUs = () => {\n const [images, setImages] = useState({\n topImage: '',\n middleImage: '',\n bottomImage: '',\n });\n\n const [imageStates, setImageStates] = useState({\n topImage: { loading: true },\n middleImage: { loading: true },\n bottomImage: { loading: true }\n });\n\n // Pre-define image dimensions\n const imagePlaceholders = {\n width: '100%',\n height: '300px',\n background: '#f0f0f0'\n };\n\n useEffect(() => {\n const fetchImages = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/aboutus`);\n\n if (response.data) {\n setImages({\n topImage: response.data.topImage || '',\n middleImage: response.data.middleImage || '',\n bottomImage: response.data.bottomImage || '',\n });\n }\n } catch (error) {\n console.error('Error fetching images:', error);\n }\n };\n\n fetchImages();\n\n document.title = 'THE AERONAUTICAL SOCIETY OF INDIA - Kochi Branch';\n \n const metaTags = [\n { name: 'description', content: 'Learn about the Kochi Chapter of the Aeronautical Society of India, its mission, vision, and various outreach programs for the youth in the field of aeronautics.' },\n { name: 'keywords', content: 'Aerospace, Aeronautics, Kochi, Engineering, Aeronautical Society of India, Workshops, Outreach, Seminars' },\n { property: 'og:title', content: 'About Us - Kochi Chapter Aeronautical Society' },\n { property: 'og:description', content: 'Learn about the Kochi Chapter of the Aeronautical Society of India, its mission, vision, and various outreach programs for the youth in the field of aeronautics.' },\n { property: 'og:image', content: images.topImage || 'default-image.jpg' },\n { property: 'og:url', content: window.location.href },\n { name: 'twitter:card', content: 'summary_large_image' },\n { name: 'twitter:title', content: 'About Us - Kochi Chapter Aeronautical Society' },\n { name: 'twitter:description', content: 'Learn about the Kochi Chapter of the Aeronautical Society of India, its mission, vision, and various outreach programs for the youth in the field of aeronautics.' },\n { name: 'twitter:image', content: images.topImage || 'default-image.jpg' }\n ];\n\n metaTags.forEach(tag => {\n let metaTag = document.querySelector(`meta[${tag.property ? 'property' : 'name'}=\"${tag.property || tag.name}\"]`);\n if (!metaTag) {\n metaTag = document.createElement('meta');\n if (tag.property) {\n metaTag.setAttribute('property', tag.property);\n } else {\n metaTag.setAttribute('name', tag.name);\n }\n document.head.appendChild(metaTag);\n }\n metaTag.setAttribute('content', tag.content);\n });\n\n return () => {\n document.title = 'THE AERONAUTICAL SOCIETY OF INDIA - Kochi Branch';\n };\n }, [images.topImage]);\n\n const handleImageLoad = (imageKey) => {\n setImageStates(prev => ({\n ...prev,\n [imageKey]: { loading: false }\n }));\n };\n\n return (\n
\n \n
\n

\n ABOUT US\n

\n\n
\n {/* Left Column: Text content */}\n
\n

\n The Kochi chapter of the Aeronautical Society of India was conceptualized in late 1996 and formally inaugurated on 29 Jun 1997. This vibrant chapter conducts Annual National seminars, School/College outreach programs to increase awareness and generate interest in aeronautics among the young minds, Aero quiz competitions, monthly lecture series by eminent personalities in the field of aviation, and various workshops to promote Aerospace and Aeronautical Engineering.\n

\n \n \n \n
\n\n {/* Right Column: Image content */}\n
\n {/* First Row: Top and Middle images */}\n
\n {/* Top Image */}\n
\n {imageStates.topImage.loading && (\n
\n
\n
\n )}\n handleImageLoad('topImage')}\n loading=\"lazy\"\n decoding=\"async\"\n />\n
\n\n {/* Middle Image */}\n
\n {imageStates.middleImage.loading && (\n
\n
\n
\n )}\n handleImageLoad('middleImage')}\n />\n
\n
\n\n {/* Second Row: Bottom image */}\n
\n {imageStates.bottomImage.loading && (\n
\n
\n
\n )}\n handleImageLoad('bottomImage')}\n />\n
\n
\n
\n
\n
\n );\n};\n\nexport default AboutUs;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport { ChevronDown } from 'lucide-react';\n\nconst ActivitiesPage = () => {\n const [activitiesAhead, setActivitiesAhead] = useState([]);\n const [activitiesCompleted, setActivitiesCompleted] = useState([]);\n\n useEffect(() => {\n fetchActivities();\n }, []);\n\n const fetchActivities = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/activities`);\n const activities = Array.isArray(response.data) ? response.data : response.data.activities || [];\n const ahead = activities.filter(activity => !activity.completed);\n const completed = activities.filter(activity => activity.completed);\n\n setActivitiesAhead(ahead);\n setActivitiesCompleted(completed);\n } catch (error) {\n console.error(\"Error fetching activities:\", error);\n }\n };\n\n const ActivityCard = ({ title, endDate, isCompleted }) => {\n const [isOpen, setIsOpen] = useState(false);\n\n const formatDate = (dateString) => {\n return new Date(dateString).toLocaleDateString();\n };\n\n return (\n
\n \n
isCompleted && setIsOpen(!isOpen)}\n >\n

{title}

\n {isCompleted && (\n \n )}\n
\n {isCompleted && (\n \n
\n
\n Completed On :\n {formatDate(endDate)}\n
\n
\n
\n )}\n \n \n );\n };\n\n return (\n
\n
\n

\n ACTIVITIES\n

\n
\n\n
\n
\n
\n \"Fighter\n
\n
\n\n
\n
\n

Activities Ahead

\n {activitiesAhead.length > 0 ? (\n activitiesAhead.map((activity, index) => (\n \n ))\n ) : (\n
No activities are present at this moment
\n )}\n
\n\n
\n

Activities Completed

\n {activitiesCompleted.map((activity, index) => (\n \n ))}\n
\n
\n
\n
\n );\n};\n\nexport default ActivitiesPage;\n","import React, { useEffect, useState } from 'react';\nimport axios from 'axios';\nimport { useNavigate } from 'react-router-dom';\nimport { ToastContainer, toast } from 'react-toastify';\nimport 'react-toastify/dist/ReactToastify.css';\nimport Cookies from 'js-cookie'; // Import the js-cookie library\n\nconst Member_ship = () => {\n const [applyOnlineLink, setApplyOnlineLink] = useState(\n 'https://www.aerosocietyindia.co.in/Home/MembershipForms'\n );\n const [isUserLoggedIn, setIsUserLoggedIn] = useState(false);\n const navigate = useNavigate();\n const [chart, setChart] = useState([]);\n\n // Check if Userid exists in cookies when component mounts\n useEffect(() => {\n const storedUserId = Cookies.get('Userid');\n setIsUserLoggedIn(!!storedUserId);\n }, []);\n\n const checkUpdates = () => {\n // Retrieve the Userid from the cookie\n const storedUserId = Cookies.get('Userid');\n\n if (storedUserId) {\n // Navigate to the member modification page\n navigate('/MemberModification');\n } else {\n // Show alert if user is not logged in\n toast.info('Please login to edit your information');\n }\n };\n\n const handleDownloadPDF = () => {\n const pdfUrl = '/42.pdf';\n\n const link = document.createElement('a');\n link.href = pdfUrl;\n link.download = 'AeSI-membership-form.pdf';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n };\n\n const handleApplyOnline = () => {\n window.open(applyOnlineLink, '_blank');\n };\n\n // Fetch the current membership link\n useEffect(() => {\n const fetchMembershipLink = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page/membership-link`\n );\n\n setApplyOnlineLink(response.data.membershipLink);\n } catch (error) {\n console.error('Error fetching membership link:', error);\n }\n };\n\n fetchMembershipLink();\n }, []);\n\n useEffect(() => {\n const fetchCategories = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/membership/categories`\n );\n const categoriesData = response.data.map((cat) => ({\n name: cat.category,\n designation: cat.abbreviation,\n }));\n setChart(categoriesData);\n } catch (error) {\n console.error('Error fetching categories:', error);\n }\n };\n\n fetchCategories();\n }, []);\n\n return (\n
\n
\n

\n MEMBERSHIP\n

\n
\n\n
\n
\n \n
\n\n
\n The Society provides a platform for members to meet and discuss issues\n of mutual interest with professionals in the field of Aeronautics and\n Aerospace Sciences. The Society provides free participation in\n seminars, workshops, and conferences organized by the Society to\n promote aeronautics and aerospace sciences. Every member is entitled\n to a copy of the quarterly journal, devoted to research and technical\n papers on various facets of aeronautical sciences.\n
\n
\n\n
\n
\n

\n Members have the privileges to use the following abbreviation after\n their names on election:\n

\n
\n
\n
\n
\n

\n CATEGORY\n

\n
\n
\n

\n ABBREVIATION\n

\n
\n
\n
\n {chart.map((items, id) => (\n
\n
\n

\n {items.name}\n

\n
\n
\n

\n {items.designation}\n

\n
\n
\n ))}\n
\n
\n
\n\n
\n \n APPLY ONLINE\n \n \n DOWNLOAD MEMBERSHIP FORM\n \n \n EDIT INFORMATION\n \n
\n \n
\n );\n};\n\nexport default Member_ship;","import React, { useState, useEffect } from 'react';\n\nconst ImageModal = ({ imageUrl, isOpen, onClose }) => {\n if (!isOpen) return null;\n\n const handleModalClick = (e) => {\n if (e.target === e.currentTarget) {\n onClose();\n }\n };\n\n return (\n \n
\n \n ✕\n \n \n
\n \n );\n};\n\nconst PhotoGallery = () => {\n const [years, setYears] = useState([]);\n const [photos, setPhotos] = useState({});\n const [year, setYear] = useState(null);\n const [availableEvents, setAvailableEvents] = useState([]);\n const [selectedEvent, setSelectedEvent] = useState(null);\n const [availablePrograms, setAvailablePrograms] = useState([]);\n const [selectedProgram, setSelectedProgram] = useState(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n const [imageLoadingStates, setImageLoadingStates] = useState({});\n const [expandedSchools, setExpandedSchools] = useState({});\n const [modalImage, setModalImage] = useState(null);\n\n const handleImageClick = (imageUrl) => {\n setModalImage(imageUrl);\n };\n\n const handleCloseModal = () => {\n setModalImage(null);\n };\n\n // Reset all filters\n const resetFilters = () => {\n setSelectedEvent(null);\n setSelectedProgram(null);\n setAvailableEvents([]);\n setAvailablePrograms([]);\n };\n\n // Initial fetch to get available years and select the first year\n useEffect(() => {\n const fetchPhotos = async () => {\n try {\n setLoading(true);\n setError(null);\n resetFilters(); // Reset filters when fetching new data\n\n const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/photos`);\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (data?.availableYears?.length) {\n const yearsData = data.availableYears.map(year => ({\n year,\n id: year,\n }));\n setYears(yearsData);\n // Automatically select the first year\n handleYearChange(yearsData[0].year);\n } else {\n setError('No available years found');\n }\n } catch (err) {\n setError(`Failed to load photos: ${err.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n fetchPhotos();\n }, []);\n\n // Fetch photos for selected year\n useEffect(() => {\n if (year) {\n const fetchYearPhotos = async () => {\n try {\n setLoading(true);\n setError(null);\n resetFilters(); // Reset filters when changing year\n\n const response = await fetch(\n `${import.meta.env.VITE_API_BASE_URL}/api/photos?year=${year}`\n );\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (data?.photos && Array.isArray(data.photos)) {\n processPhotosData(data.photos);\n } else {\n setError('No photos found for selected year');\n }\n } catch (err) {\n setError(`Failed to load photos for selected year: ${err.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n fetchYearPhotos();\n }\n }, [year]);\n\n const processPhotosData = (photosData) => {\n if (!Array.isArray(photosData) || photosData.length === 0) {\n setPhotos({});\n setAvailableEvents([]);\n setAvailablePrograms([]);\n return;\n }\n\n // Group photos by event and program\n const photosByEventAndProgram = {};\n const eventsSet = new Set();\n const programsSet = new Set();\n\n photosData.forEach(photo => {\n if (!photo.eventName) return; // Skip photos without event\n\n const eventKey = photo.eventName;\n const programKey = photo.programName || 'No Program'; // Handle missing program name\n\n eventsSet.add(eventKey);\n programsSet.add(programKey);\n\n if (!photosByEventAndProgram[eventKey]) {\n photosByEventAndProgram[eventKey] = {};\n }\n if (!photosByEventAndProgram[eventKey][programKey]) {\n photosByEventAndProgram[eventKey][programKey] = [];\n }\n photosByEventAndProgram[eventKey][programKey].push(photo);\n });\n\n const availableEventsArray = Array.from(eventsSet);\n setPhotos(photosByEventAndProgram);\n setAvailableEvents(availableEventsArray);\n\n // Automatically select the first event\n if (availableEventsArray.length > 0) {\n const firstEvent = availableEventsArray[0];\n setSelectedEvent(firstEvent);\n\n // Check if 'No Program' is the only available program for the first event\n const programsForEvent = Object.keys(photosByEventAndProgram[firstEvent] || {});\n if (programsForEvent.length === 1 && programsForEvent[0] === 'No Program') {\n // If 'No Program' is the only program, skip the program filter\n setSelectedProgram('No Program');\n } else {\n setAvailablePrograms(programsForEvent);\n setSelectedProgram(programsForEvent[0]);\n }\n }\n\n // Initialize loading states for images\n const newLoadingStates = {};\n photosData.forEach(photo => {\n if (photo.imageUrl) {\n newLoadingStates[photo.imageUrl] = true;\n }\n if (Array.isArray(photo.additionalImages)) {\n photo.additionalImages.forEach(img => {\n newLoadingStates[img] = true;\n });\n }\n });\n setImageLoadingStates(newLoadingStates);\n };\n\n const handleYearChange = (selectedYear) => {\n if (selectedYear !== year) {\n setYear(selectedYear);\n resetFilters(); // Reset filters when changing year\n }\n };\n\n const handleEventChange = (event) => {\n if (event === selectedEvent) return; // Prevent re-clicking the selected event\n setSelectedEvent(event);\n setSelectedProgram(null); // Reset program selection\n\n // Find available programs for this event\n const programsForEvent = Object.keys(photos[event] || {});\n setAvailablePrograms(programsForEvent);\n\n // Select the first available program\n if (programsForEvent.length > 0) {\n setSelectedProgram(programsForEvent[0]);\n }\n };\n\n const handleProgramChange = (program) => {\n setSelectedProgram(program);\n };\n\n const handleImageLoad = (imageUrl) => {\n setImageLoadingStates(prev => ({\n ...prev,\n [imageUrl]: false\n }));\n };\n\n const toggleSchoolExpansion = (schoolNo) => {\n setExpandedSchools(prev => ({\n ...prev,\n [schoolNo]: !prev[schoolNo]\n }));\n };\n\n if (error) {\n return (\n
\n
\n

Error

\n

{error}

\n
\n
\n );\n }\n\n return (\n
\n \n\n
\n

\n PHOTO GALLERY\n

\n
\n\n {loading && (\n
\n
\n
\n )}\n\n {!loading && (\n
\n {/* Year Selection */}\n {years.length > 0 && (\n
\n {years.map((item) => (\n handleYearChange(item.year)}\n className={`transition-all font-semibold duration-300 ease-in-out hover:bg-blue-300 hover:text-black px-6\n rounded-full text-lg sm:text-xl py-2 \n ${year === item.year ? 'bg-blue-500 text-white' : 'bg-blue-100 text-zinc-900'}`}\n >\n {item.year}\n \n ))}\n
\n )}\n\n {/* Event Selection */}\n {availableEvents.length > 0 && (\n
\n {availableEvents.map((event, id) => (\n handleEventChange(event)}\n className={`transition-all duration-300 ease-in-out hover:bg-blue-300 hover:text-black px-6\n rounded-full text-base sm:text-lg py-2\n ${selectedEvent === event ? 'bg-blue-500 text-white' : 'bg-blue-100 text-zinc-900'}`}\n >\n {event}\n \n ))}\n
\n )}\n\n {/* Program Selection - Only show if available programs exist and if program is not 'No Program' */}\n {selectedEvent && availablePrograms.length > 0 && selectedProgram !== 'No Program' && (\n
\n {availablePrograms.map((program, id) => (\n handleProgramChange(program)}\n className={`transition-all duration-300 ease-in-out hover:bg-blue-300 hover:text-black px-6\n rounded-full text-base sm:text-lg py-2\n ${selectedProgram === program ? 'bg-blue-500 text-white' : 'bg-blue-100 text-zinc-900'}`}\n >\n {program}\n \n ))}\n
\n )}\n\n {/* Photo Display */}\n {selectedEvent && (selectedProgram ? photos[selectedEvent]?.[selectedProgram] : photos[selectedEvent]) && (\n
\n
\n {(selectedProgram ? photos[selectedEvent][selectedProgram] : photos[selectedEvent]).map((photo, index) => (\n \n
\n
\n handleImageClick(photo.imageUrl)}\n >\n {imageLoadingStates[photo.imageUrl] && (\n
\n
\n
\n )}\n handleImageLoad(photo.imageUrl)}\n onError={() => handleImageLoad(photo.imageUrl)}\n />\n
\n
\n\n
\n

\n {photo.name}\n

\n

\n {photo.description}\n

\n
\n
\n\n {Array.isArray(photo.additionalImages) && photo.additionalImages.length > 0 && (\n
\n toggleSchoolExpansion(index)}\n className=\"w-full flex items-center justify-center p-3 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors mb-4\"\n >\n \n {expandedSchools[index] ? 'Hide Additional Images' : 'View More Images'}\n \n \n ▼\n \n \n\n {expandedSchools[index] && (
\n {photo.additionalImages.map((image, imgIndex) => (\n handleImageClick(image)}\n >\n {imageLoadingStates[image] && (\n
\n
\n
\n )}\n handleImageLoad(image)}\n onError={() => handleImageLoad(image)}\n />\n
\n ))}\n\n
\n )}\n
\n )}\n
\n ))}\n\n
\n \n )}\n \n )}\n \n );\n};\n\nexport default PhotoGallery;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport Cookies from 'js-cookie'; // Import the js-cookie library\nimport { useNavigate } from 'react-router-dom'; // Import useNavigate\nimport { ToastContainer, toast } from 'react-toastify';\nimport 'react-toastify/dist/ReactToastify.css';\n\nconst JournalCard = ({ journal }) => {\n const [imageLoading, setImageLoading] = useState(true);\n\n return (\n
\n
\n {imageLoading && (\n
\n
\n
\n )}\n setImageLoading(false)}\n onError={() => setImageLoading(false)}\n />\n
\n
\n );\n};\n\nconst Journal = () => {\n const [journals, setJournals] = useState({ latest: [], journals: {} });\n const [selectedYear, setSelectedYear] = useState(null);\n const [selectedMonth, setSelectedMonth] = useState(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n const [imageLoadingStates, setImageLoadingStates] = useState({});\n\n const storedUserId = Cookies.get('Userid');\n const navigate = useNavigate(); // Initialize useNavigate\n\n useEffect(() => {\n const fetchJournals = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/journal`\n );\n if (response.data && response.data.journals) {\n setJournals(response.data);\n \n // Get the first year from the journals\n const years = Object.values(response.data.journals).map(item => item.year);\n const firstYear = Math.max(...years); // Get the most recent year\n \n if (firstYear) {\n setSelectedYear(firstYear);\n \n // Get the first month for this year\n const firstMonthData = Object.values(response.data.journals).find(\n item => item.year === firstYear\n );\n if (firstMonthData) {\n setSelectedMonth(firstMonthData.month);\n }\n }\n\n // Initialize loading states for all images\n const newLoadingStates = {};\n \n // For latest journals\n if (response.data.latest) {\n response.data.latest.forEach(journal => {\n if (journal.image) {\n newLoadingStates[journal.image] = true;\n const img = new Image();\n img.src = journal.image;\n img.onload = () => handleImageLoad(journal.image);\n img.onerror = () => handleImageLoad(journal.image);\n }\n });\n }\n \n // For all other journals\n Object.values(response.data.journals).forEach(yearData => {\n if (yearData.journal) {\n yearData.journal.forEach(journal => {\n if (journal.image) {\n newLoadingStates[journal.image] = true;\n const img = new Image();\n img.src = journal.image;\n img.onload = () => handleImageLoad(journal.image);\n img.onerror = () => handleImageLoad(journal.image);\n }\n });\n }\n });\n \n setImageLoadingStates(newLoadingStates);\n }\n setLoading(false);\n } catch (err) {\n setError('Failed to fetch journals');\n setLoading(false);\n }\n };\n\n fetchJournals();\n }, []);\n\n const handleYearClick = (year) => {\n setSelectedYear(year);\n const firstMonthOfYear = Object.values(journals.journals).find(\n (item) => item.year === year\n )?.month;\n setSelectedMonth(firstMonthOfYear);\n };\n\n const handleMonthClick = (month) => {\n setSelectedMonth(month);\n };\n\n const handleReadMore = (pdfUrl) => {\n // Check if user is logged in\n const storedUserId = Cookies.get('Userid');\n\n if (storedUserId) {\n // If logged in, open the PDF\n window.open(pdfUrl, '_blank');\n } else {\n // If not logged in, show alert\n toast.info('Please login to access the journal');\n }\n };\n\n const truncateContent = (content) => {\n if (!content) return '';\n const words = content.split(' ');\n if (words.length > 100) {\n return words.slice(0, 30).join(' ') + '...';\n }\n return content;\n };\n\n // Get unique years\n const uniqueYears = journals.journals\n ? [\n ...new Set(Object.values(journals.journals).map((item) => item.year)),\n ].sort((a, b) => b - a)\n : [];\n\n // Get months for selected year\n const filteredMonths = journals.journals\n ? Object.values(journals.journals)\n .filter((item) => item.year === selectedYear)\n .sort((a, b) => {\n const months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n ];\n return months.indexOf(a.month) - months.indexOf(b.month);\n })\n : [];\n\n // Get journals for selected month\n const journalsToDisplay =\n selectedYear && selectedMonth\n ? Object.values(journals.journals).find(\n (item) => item.year === selectedYear && item.month === selectedMonth\n )?.journal || []\n : [];\n\n // Add image loading handler\n const handleImageLoad = (imageId) => {\n setImageLoadingStates((prev) => ({\n ...prev,\n [imageId]: false,\n }));\n };\n\n if (loading) {\n return (\n
\n Loading...\n
\n );\n }\n\n if (error) {\n return (\n
\n {error}\n
\n );\n }\n\n return (\n
\n
\n

\n JOURNAL\n

\n
\n\n
\n
\n

LATEST

\n
\n {journals.latest.map((journal) => (\n \n {/*
\n {imageLoadingStates[journal.image] && (\n
\n
\n
\n )}\n \n handleImageLoad(journal.image)}\n onError={() => handleImageLoad(journal.image)}\n />\n
\n
*/}\n
\n

\n

\n {truncateContent(journal.description)}{' '}\n handleReadMore(journal.pdfUrl)}\n className=\"text-red-500 cursor-pointer\"\n >\n Read More\n \n

\n

\n
\n
\n ))}\n\n
\n {uniqueYears.map((year) => (\n handleYearClick(year)}\n className={`transition-all duration-300 ease-in-out hover:bg-blue-300 hover:text-black tex w-[35vw] md:w-[10vw] \n h-[6vh] md:h-[5vh] ${\n selectedYear === year\n ? 'bg-[#008CFF] text-white'\n : 'bg-[#D6E6FF] text-zinc-900'\n } rounded-full text-[4vw] md:text-[1vw] items-center h-[3vh] md:h-[5vh]`}\n >\n {year}\n \n ))}\n
\n\n {selectedYear && (\n
\n {filteredMonths.map((item) => (\n handleMonthClick(item.month)}\n className={`transition-all duration-300 ease-in-out hover:bg-blue-300 hover:text-black \n w-[35vw] md:w-[10vw] \n h-[6vh] md:h-[5vh]\n ${selectedMonth === item.month ? 'bg-[#008CFF] text-white' : 'bg-[#D6E6FF] text-zinc-900'} \n rounded-full \n text-[4vw] md:text-[1vw] \n items-center`}\n >\n {item.month}\n \n ))}\n
\n )}\n\n {journalsToDisplay.length > 0 && (\n
\n {journalsToDisplay.map((journal) => (\n \n
\n {imageLoadingStates[journal.image] && (\n
\n
\n
\n )}\n \n handleImageLoad(journal.image)}\n onError={() => handleImageLoad(journal.image)}\n />\n
\n
\n
\n

\n

\n {truncateContent(journal.description)}{' '}\n handleReadMore(journal.pdfUrl)}\n className=\"text-red-500 cursor-pointer\"\n >\n Read More\n \n

\n

\n
\n \n ))}\n \n )}\n \n \n \n );\n};\n\nexport default Journal;","import React, { useState, useEffect } from 'react';\nimport { useParams, Link } from 'react-router-dom';\nimport axios from 'axios';\n\nconst JournalDetail = () => {\n const { id } = useParams();\n const [journal, setJournal] = useState(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n\n useEffect(() => {\n const fetchJournal = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/journal/${id}`);\n setJournal(response.data);\n setLoading(false);\n } catch (err) {\n setError('Failed to fetch journal details');\n setLoading(false);\n }\n };\n\n fetchJournal();\n }, [id]);\n\n if (loading) {\n return (\n
\n
\n
\n );\n }\n\n if (error || !journal) {\n return (\n
\n
{error || 'Journal not found'}
\n Return to Home\n
\n );\n }\n\n return (\n
\n {/* Header */}\n
\n

\n JOURNAL DETAILS\n

\n
\n\n {/* Main Content */}\n
\n
\n {/* Journal Info Banner */}\n {journal.section !== 'latest' && (\n
\n \n {journal.month} {journal.year} - Journal No. {journal.journalNo}\n \n
\n )}\n\n
\n {/* Title */}\n

\n {journal.title}\n

\n\n {/* Content Grid */}\n
\n {/* Left Column - Image */}\n
\n
\n {journal.title}\n\n
\n
\n\n {/* Right Column - Content */}\n
\n
\n
\n
\n
\n
\n
\n
\n\n {/* Back Button */}\n
\n \n Back to Home\n \n
\n
\n
\n );\n};\n\nexport default JournalDetail; ","import React, { useState } from 'react';\nimport axios from 'axios';\nimport { GoArrowRight } from 'react-icons/go';\n\nconst Contactus = () => {\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n const [subject, setSubject] = useState('');\n const [message, setMessage] = useState('');\n const [status, setStatus] = useState('');\n\n const handleSubmit = async (e) => {\n e.preventDefault();\n\n // Validate form fields\n if (!name.trim()) {\n setStatus('Please enter your name.');\n return;\n }\n\n if (!email.trim()) {\n setStatus('Please enter your email.');\n return;\n }\n\n // Basic email validation\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email.trim())) {\n setStatus('Please enter a valid email address.');\n return;\n }\n\n if (!subject.trim()) {\n setStatus('Please enter a subject.');\n return;\n }\n\n if (!message.trim()) {\n setStatus('Please enter a message.');\n return;\n }\n\n // Prepare form data\n const formData = {\n name: name.trim(),\n email: email.trim(),\n subject: subject.trim(),\n message: message.trim(),\n };\n\n try {\n const response = await axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/contact/mail`, formData);\n if (response.data.success) {\n setStatus('Message sent successfully!');\n // Optional: Clear form fields after successful submission\n setName('');\n setEmail('');\n setSubject('');\n setMessage('');\n } else {\n setStatus('Failed to send message.');\n }\n } catch (error) {\n setStatus('Failed to send message.');\n }\n };\n\n // Rest of the component remains the same...\n\n return (\n
\n
\n
\n

\n CONTACT US\n

\n
\n
\n
\n \n
\n
\n

Reach Out to Us: Connect via Email

\n
\n
\n
\n
\n
\n
\n

Your Name

\n setName(e.target.value)}\n />\n
\n\n
\n

Your Email

\n setEmail(e.target.value)}\n />\n
\n\n
\n

Subject

\n setSubject(e.target.value)}\n />\n
\n\n
\n

Your Message

\n setMessage(e.target.value)}\n />\n
\n\n
\n
\n \n Send \n \n
\n
\n\n\n {/* Display status message */}\n {status && (\n
\n

{status}

\n
\n )}\n
\n
\n
\n
\n
\n );\n};\n\nexport default Contactus;","import React from 'react'\nimport { GoArrowRight } from 'react-icons/go'\nimport { Link } from 'react-router-dom'\n\nconst Footer = () => {\n return (\n
\n {/* Top Section */}\n
\n {/* Left Section */}\n
\n \n
\n \n {/* Right Section */}\n
\n {/* First Row */}\n \n\n{/* Second Row */}\n\n \n {/* External Links Button */}\n
\n {/* \n EXTERNAL Links \n */}\n
\n \n \n\n \n \n \n External Links \n \n\n\n
\n
\n
\n
\n \n {/* Bottom Section */}\n
\n
\n

\n THE AERONAUTICAL SOCIETY OF INDIA\n

\n
\n
\n
\n \n )\n}\n\nexport default Footer\n","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport { Link } from 'react-router-dom';\n\nconst ExecutiveCouncil = () => {\n const [members, setMembers] = useState({\n chairman: [],\n chairmanelect: [],\n vicechairman: [],\n honsecretary: [],\n hontreasurer: [], // Fixed casing\n jointsecretary: [], // Fixed casing\n itmember: [],\n propertymember: [],\n activitymember: [],\n activitycoordinator: [],\n executivemember: []\n });\n\n useEffect(() => {\n const fetchMembers = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/executive`);\n \n // Group members by designation\n const groupedMembers = response.data.reduce((acc, member) => {\n const designation = member.designation.toLowerCase()\n .replace(/[0-9]/g, '')\n .replace(/\\s+/g, '')\n .replace('-', '');\n \n if (!acc[designation]) {\n acc[designation] = [];\n }\n acc[designation].push(member);\n return acc;\n }, {});\n\n setMembers(groupedMembers);\n } catch (error) {\n console.error('Error fetching members:', error);\n }\n };\n\n fetchMembers();\n }, []);\n\n const capitalizeDesignation = (designation) => {\n if (!designation) return 'Position Vacant';\n \n return designation\n .replace(/[0-9]/g, '')\n .split(' ')\n .map(word => {\n \n if (word.toLowerCase() === 'it') return 'IT';\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n })\n .join(' ');\n };\n\n const MemberCard = ({ member }) => {\n const [imageLoading, setImageLoading] = useState(true);\n\n return (\n
\n
\n {imageLoading && (\n
\n
\n
\n )}\n {member?.name} setImageLoading(false)}\n onError={() => setImageLoading(false)}\n />\n
\n
\n

\n {member?.name || 'Position Vacant'}\n

\n
\n
\n

\n {capitalizeDesignation(member?.designation)}\n

\n
\n
\n );\n };\n\n return (\n
\n
\n

\n EXECUTIVE COUNCIL\n

\n
\n
\n
\n \n {/* Chairman and Chairman-Elect Row */}\n
\n \n \n
\n\n {/* Vice-Chairman Row */}\n
\n {[0, 1, 2].map((index) => (\n \n ))}\n
\n\n {/* Hon Secretary, Hon Treasurer, Joint Secretary Row */}\n
\n \n \n \n
\n\n {/* IT, Property, Activity Members Row */}\n
\n \n \n \n \n
\n\n {/* Executive Members Row */}\n
\n {[0, 1, 2].map((index) => (\n \n ))}\n
\n
\n
\n\n
\n \n Back to Home\n \n
\n
\n );\n};\n\nexport default ExecutiveCouncil;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport { FaExternalLinkAlt, FaLink, FaSearch, FaChevronDown, FaChevronUp } from 'react-icons/fa';\nimport { Link } from 'react-router-dom';\n\nconst ExternalLinks = () => {\n const [groups, setGroups] = useState([]);\n const [loading, setLoading] = useState(true);\n const [searchTerm, setSearchTerm] = useState('');\n const [expandedGroup, setExpandedGroup] = useState(null);\n const [groupLinks, setGroupLinks] = useState({});\n const [links, setLinks] = useState([]);\n const [imageStates, setImageStates] = useState({});\n const [groupLoadingStates, setGroupLoadingStates] = useState({});\n\n // Fetch all groups\n const fetchGroups = async (search = '') => {\n try {\n setLoading(true);\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/external-links`, {\n params: { search }\n });\n setGroups(Array.isArray(response.data) ? response.data : []);\n } catch (error) {\n console.error('Error fetching external links:', error);\n setGroups([]);\n } finally {\n setLoading(false);\n }\n };\n\n // Fetch links for a specific group\n const fetchGroupLinks = async (groupId) => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/${groupId}`);\n setGroupLinks(prev => ({\n ...prev,\n [groupId]: response.data.links\n }));\n } catch (error) {\n console.error('Error fetching group links:', error);\n }\n };\n\n useEffect(() => {\n const delayDebounceFn = setTimeout(() => {\n fetchGroups(searchTerm);\n }, 300);\n\n return () => clearTimeout(delayDebounceFn);\n }, [searchTerm]);\n\n const handleGroupClick = async (groupId) => {\n if (expandedGroup === groupId) {\n setExpandedGroup(null);\n } else {\n setExpandedGroup(groupId);\n if (!groupLinks[groupId]) {\n // Set loading state for this group\n setGroupLoadingStates(prev => ({\n ...prev,\n [groupId]: true\n }));\n \n await fetchGroupLinks(groupId);\n \n // Remove loading state after fetch\n setGroupLoadingStates(prev => ({\n ...prev,\n [groupId]: false\n }));\n }\n }\n };\n\n useEffect(() => {\n const fetchLinks = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/external-links`);\n setLinks(response.data);\n // Initialize loading states for each link's image\n const initialStates = {};\n response.data.forEach(link => {\n initialStates[link._id] = { loading: true };\n });\n setImageStates(initialStates);\n } catch (error) {\n console.error('Error fetching links:', error);\n } finally {\n setLoading(false);\n }\n };\n\n fetchLinks();\n }, []);\n\n const handleImageLoad = (linkId) => {\n setImageStates(prev => ({\n ...prev,\n [linkId]: { loading: false }\n }));\n };\n\n // Loading state with skeleton UI\n if (loading) {\n return (\n
\n
\n
\n
\n {[1, 2, 3].map((n) => (\n
\n
\n
\n ))}\n
\n
\n
\n );\n }\n\n return (\n
\n
\n
\n
\n {/* Title */}\n
\n

\n External Links\n

\n \n
\n \n {/* Search Bar */}\n
\n setSearchTerm(e.target.value)}\n placeholder=\"Search groups...\"\n className=\"w-full pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-[#008CFF] focus:border-transparent\"\n />\n \n
\n
\n\n
\n \n {groups.length === 0 ? (\n
\n \n

No external links available at the moment.

\n

Please check back later.

\n
\n ) : (\n
\n {groups.map((group) => (\n
\n handleGroupClick(group._id)}\n className=\"w-full p-6 flex items-center justify-between text-left\"\n >\n

\n {group.name}\n

\n {expandedGroup === group._id ? (\n \n ) : (\n \n )}\n \n \n {expandedGroup === group._id && (\n
\n {groupLoadingStates[group._id] ? (\n // Loading skeleton\n
\n {[1, 2, 3].map((n) => (\n
\n
\n
\n
\n ))}\n
\n ) : groupLinks[group._id]?.length > 0 ? (\n groupLinks[group._id].map((link) => (\n \n {link.label}\n \n \n ))\n ) : (\n

\n No links available in this group\n

\n )}\n
\n )}\n
\n ))}\n
\n )}\n\n {groups.length > 0 && (\n
\n

These links are provided for reference purposes. We are not responsible for the content of external websites.

\n
\n )}\n
\n
\n\n Back to Home\n \n
\n
\n );\n};\n\nexport default ExternalLinks;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport { useNavigate } from 'react-router-dom';\nimport { getAdminAuthToken } from '../../utilis/auth';\n\nconst AdminHome = () => {\n const [loading, setLoading] = useState(false);\n const [message, setMessage] = useState({ type: '', text: '' });\n const [images, setImages] = useState([]);\n const [membershipLink, setMembershipLink] = useState('');\n const [marquee, setmarquee] = useState('');\n const [newPassword, setNewPassword] = useState(''); \n const [MembId, setMembId] = useState(''); \n const [passwordLoading, setPasswordLoading] = useState(false);\n\n const navigate = useNavigate();\n\n useEffect(() => {\n const adminToken = getAdminAuthToken();\n if (!adminToken) {\n navigate('/admin/login');\n }\n }, [navigate]);\n\n // Load Cloudinary Widget\n useEffect(() => {\n const script = document.createElement('script');\n script.src = 'https://upload-widget.cloudinary.com/global/all.js';\n script.async = true;\n document.body.appendChild(script);\n return () => {\n document.body.removeChild(script);\n };\n }, []);\n\n const handlePasswordChange = (e) => {\n setNewPassword(e.target.value);\n };\n\n const handleMembershipId = (e) => {\n setMembId(e.target.value);\n };\n\n // Updated password change handler\n const handlePasswordSubmit = async (e) => {\n e.preventDefault();\n \n // Validation checks\n if (!MembId) {\n setMessage({ type: 'error', text: 'Please enter Membership ID.' });\n return;\n }\n\n if (!newPassword) {\n setMessage({ type: 'error', text: 'Please enter a new password.' });\n return;\n }\n\n // Password strength validation (optional but recommended)\n if (newPassword.length < 8) {\n setMessage({ type: 'error', text: 'Password must be at least 8 characters long.' });\n return;\n }\n\n try {\n setPasswordLoading(true);\n\n // Get admin token for authorization\n const token = getAdminAuthToken();\n\n const response = await axios.patch(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/update-password`,\n { \n membershipId: MembId, \n newPassword \n }, \n {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n }\n );\n\n // Handle successful response\n if (response.status === 200) {\n setMessage({ \n type: 'success', \n text: 'Password successfully updated!' \n });\n alert(\"Password successfully updated!\")\n \n // Clear input fields\n setMembId('');\n setNewPassword('');\n } else {\n setMessage({ \n type: 'error', \n text: 'Failed to update password.' \n });\n alert(\"Password Updation failed!\")\n }\n\n // Clear message after 3 seconds\n setTimeout(() => setMessage({ type: '', text: '' }), 3000);\n } catch (error) {\n // More detailed error handling\n const errorMsg = error.response?.data?.message || \n 'Error updating password. Please try again.';\n \n setMessage({ \n type: 'error', \n text: errorMsg \n });\n } finally {\n setPasswordLoading(false);\n }\n };\n\n // Initialize upload widget\n const initializeWidget = (oldImageUrl = null) => {\n if (!window.cloudinary) {\n showMessage('error', 'Upload widget is not ready. Please refresh the page.');\n return null;\n }\n\n return window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n folder: 'landing-page',\n maxFiles: 999,\n sources: ['local', 'url', 'camera'],\n resourceType: 'image',\n maxImageFileSize: 5000000,\n clientAllowedFormats: ['jpg', 'jpeg', 'png'],\n },\n async (error, result) => {\n if (error) {\n showMessage('error', `Upload failed: ${error.message}`);\n setLoading(false);\n return;\n }\n\n if (result.event === 'success') {\n await handleImageUploadSuccess(result.info.secure_url, oldImageUrl);\n }\n }\n );\n };\n\n // Show message helper\n const showMessage = (type, text) => {\n setMessage({ type, text });\n setTimeout(() => setMessage({ type: '', text: '' }), 5000);\n };\n\n // Handle successful image upload\n const handleImageUploadSuccess = async (newImageUrl, oldImageUrl = null) => {\n try {\n setLoading(true);\n const endpoint = `${import.meta.env.VITE_API_BASE_URL}/api/landing-page`;\n let response;\n\n if (oldImageUrl) {\n // Replace existing image\n response = await axios.put(endpoint, {\n updates: { oldUrl: oldImageUrl, newUrl: newImageUrl }\n });\n } else {\n // Add new image\n response = await axios.put(endpoint, {\n addImages: [newImageUrl]\n });\n }\n\n if (response.data.landingPage) {\n setImages(response.data.landingPage.images);\n showMessage('success', oldImageUrl ? 'Image replaced successfully!' : 'Image uploaded successfully!');\n }\n } catch (err) {\n showMessage('error', `Error saving image: ${err.response?.data?.message || err.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n // Handle image upload\n const handleUpload = (oldImageUrl = null) => {\n setLoading(true);\n const widget = initializeWidget(oldImageUrl);\n if (widget) {\n widget.open();\n } else {\n setLoading(false);\n }\n };\n\n // Fetch images\n const fetchImages = async () => {\n try {\n setLoading(true);\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/landing-page`);\n if (response.data.images) {\n setImages(response.data.images);\n }\n } catch (error) {\n showMessage('error', `Error fetching images: ${error.response?.data?.message || error.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n // Fetch current membership link\n const fetchCurrentLink = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/landing-page/membership-link`);\n if (response.data.membershipLink) {\n setMembershipLink(response.data.membershipLink);\n }\n } catch (error) {\n showMessage('error', `Error fetching current link: ${error.response?.data?.message || error.message}`);\n }\n };\n\n // Initial data fetch\n useEffect(() => {\n fetchImages();\n fetchCurrentLink();\n }, []);\n\n // Fetch and set marquee text\n useEffect(() => {\n const fetchMarqueeText = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page/`\n );\n setmarquee(response.data.marqueeText);\n } catch (error) {\n console.log('Error fetching marquee text:', error);\n } \n };\n fetchMarqueeText();\n },[]);\n\n // Handle membership link update\n const handleLinkUpdate = async (e) => {\n e.preventDefault();\n setLoading(true);\n try {\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page/membership-link`,\n { link: membershipLink }\n );\n \n if (response.data.message) {\n showMessage('success', response.data.message);\n }\n } catch (error) {\n showMessage('error', `Error updating link: ${error.response?.data?.message || error.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n // Handle marquee text update\n const HandleMarqueeText = async (e) => {\n e.preventDefault();\n setLoading(true);\n try {\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page/`,\n { marqueeText: marquee }\n );\n \n if (response.data.message) {\n showMessage('success', response.data.message);\n }\n } catch (error) {\n showMessage('error', `Error updating marquee text: ${error.response?.data?.message || error.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n // Handle single image deletion\n const handleDeleteImage = async (imageUrl) => {\n if (!window.confirm('Are you sure you want to delete this image?')) return;\n \n try {\n setLoading(true);\n const updatedImages = images.filter(img => img !== imageUrl);\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page`,\n { images: updatedImages }\n );\n \n if (response.status === 200) {\n setImages(updatedImages);\n showMessage('success', 'Image deleted successfully!');\n }\n } catch (error) {\n showMessage('error', `Error deleting image: ${error.response?.data?.message || error.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n // Handle deletion of all images\n const handleDeleteAllImages = async () => {\n if (!window.confirm('Are you sure you want to delete all images? This action cannot be undone.')) return;\n \n try {\n setLoading(true);\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/landing-page`,\n { images: [] }\n );\n \n if (response.status === 200) {\n setImages([]);\n showMessage('success', 'All images deleted successfully!');\n }\n } catch (error) {\n showMessage('error', `Error deleting images: ${error.response?.data?.message || error.message}`);\n } finally {\n setLoading(false);\n }\n };\n\n return (\n
\n

Admin Dashboard

\n \n {/* Message Display */}\n {message.text && (\n
\n {message.text}\n
\n )}\n \n {/* Landing Page Image Management */}\n
\n

Landing Page Images

\n \n
\n \n\n \n
\n \n {images.length === 0 ? (\n
\n No images uploaded yet. Click \"Upload New Image\" to add some.\n
\n ) : (\n
\n {images.map((url, index) => (\n url !== 'NA' && (\n
\n \n
\n \n \n
\n
\n )\n ))}\n
\n )}\n
\n\n {/* Marquee Text Management */}\n
\n

Marquee Text

\n
\n setmarquee(e.target.value)} \n className=\"w-full p-2 border rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none\" \n placeholder=\"Enter Marquee Text\"\n required\n />\n \n
\n
\n \n {/* Membership Link Management */}\n
\n

Membership Link

\n
\n setMembershipLink(e.target.value)} \n className=\"w-full p-2 border rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none\" \n placeholder=\"Enter membership link\"\n required\n />\n \n
\n
\n\n {/* Change User Password Section */}\n
\n
\n

Change Password

\n \n {/* Membership ID Input */}\n
\n \n Membership ID\n \n \n
\n\n {/* New Password Input */}\n
\n \n New Password\n \n \n
\n\n {/* Submit Button */}\n
\n \n {passwordLoading ? 'Updating...' : 'Update Password'}\n \n
\n
\n
\n
\n );\n};\n\nexport default AdminHome;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\n\n// Utility function to format the date and time\nconst formatDateTime = (timestamp) => {\n const date = new Date(timestamp);\n\n const day = date.getDate();\n const month = date.toLocaleString('default', { month: 'long' });\n const year = date.getFullYear();\n const hours = date.getHours();\n const minutes = date.getMinutes().toString().padStart(2, '0');\n\n // Add suffix for day (st, nd, rd, th)\n const suffix = day % 10 === 1 && day !== 11 ? 'st' :\n day % 10 === 2 && day !== 12 ? 'nd' :\n day % 10 === 3 && day !== 13 ? 'rd' : 'th';\n\n // Format: \"11th June 2024, 14:05\"\n return `${day}${suffix} ${month} ${year}, ${hours}:${minutes}`;\n};\n\nconst formatFieldName = (field) => {\n return field\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, str => str.toUpperCase());\n};\n\nconst NotificationDetails = ({ notification }) => {\n if (!notification || !notification.details) {\n return null;\n }\n\n if (notification.type === 'PASSWORD_CHANGE') {\n return (\n

Password was changed

\n );\n }\n\n if (notification.type === 'DETAILS_UPDATE' && notification.details.changes) {\n // Filter out memId from updatedFields if it exists\n const updatedFields = notification.details.updatedFields?.filter(field => field !== 'memId');\n \n if (!updatedFields || updatedFields.length === 0) return null;\n\n return (\n
\n

Changed fields:

\n
    \n {updatedFields.map(field => {\n const changes = notification.details.changes[field];\n if (!changes) return null;\n \n return (\n
  • \n {formatFieldName(field)}:\n \n {changes.from || 'empty'}\n \n \n → {changes.to || 'empty'}\n \n
  • \n );\n })}\n
\n
\n );\n }\n\n return null;\n};\n\nconst Notifications = () => {\n const [notifications, setNotifications] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n const fetchNotifications = async () => {\n try {\n setLoading(true);\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/notifications`\n );\n setNotifications(response.data || []);\n } catch (err) {\n setError('Unable to load notifications. Please try again later.');\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchNotifications();\n const interval = setInterval(fetchNotifications, 60000);\n return () => clearInterval(interval);\n }, []);\n\n return (\n
\n

Notifications

\n \n {loading && (\n
\n
\n
\n )}\n\n {error && (\n
\n {error}\n
\n )}\n\n
\n {notifications.map((notification) => (\n \n {/* Top-right date and time */}\n \n {formatDateTime(notification.details?.timestamp)}\n \n\n
\n
\n

\n {notification.type === 'PASSWORD_CHANGE' ? '🔑 Password Changed' : '📝 Details Updated'}\n

\n

{notification.message}

\n {notification.details?.membershipId && (\n

\n Member ID: {notification.details.membershipId}\n

\n )}\n \n
\n
\n
\n ))}\n\n {notifications.length === 0 && !loading && (\n
\n No notifications to display\n
\n )}\n
\n
\n );\n};\n\nexport default Notifications;\n","import React, { useState } from 'react';\nimport { Link, useNavigate } from 'react-router-dom';\nimport { clearAdminAuthToken } from '../../utilis/auth'; // Adjust the path based on your project structure\nimport Notifications from './Notifications';\n\nconst AdminNavbar = () => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [showNotifications, setShowNotifications] = useState(false);\n const navigate = useNavigate();\n\n const handleLogout = () => {\n clearAdminAuthToken(); // Clear admin-specific authentication cookies\n navigate('/admin/login'); // Redirect to the login page\n };\n\n return (\n \n );\n};\n\nexport default AdminNavbar;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\n\nconst AdminAboutUs = () => {\n const [loading, setLoading] = useState(false);\n const [message, setMessage] = useState('');\n const [previewUrls, setPreviewUrls] = useState({\n topImage: '/default-top.jpg',\n middleImage: '/default-middle.jpg',\n bottomImage: '/default-bottom.jpg',\n });\n\n useEffect(() => {\n // Load the Cloudinary widget script\n const script = document.createElement('script');\n script.src = 'https://upload-widget.cloudinary.com/global/all.js';\n script.async = true;\n document.body.appendChild(script);\n\n script.onerror = () => {\n console.error('Failed to load Cloudinary widget script');\n };\n\n // Fetch current images from the backend\n const fetchCurrentImages = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/aboutus`\n );\n\n if (response.data) {\n setPreviewUrls({\n topImage: response.data.topImage || '/default-top.jpg',\n middleImage: response.data.middleImage || '/default-middle.jpg',\n bottomImage: response.data.bottomImage || '/default-bottom.jpg',\n });\n }\n } catch (error) {\n console.error('Error fetching current images:', error);\n setMessage('Failed to load current images.');\n }\n };\n\n fetchCurrentImages();\n\n return () => {\n document.body.removeChild(script);\n };\n }, []);\n\n const handleUpload = (imageType) => {\n if (!window.cloudinary) {\n console.error('Cloudinary widget is not loaded');\n return;\n }\n\n const widget = window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n folder: 'aboutus',\n maxFiles: 1,\n sources: ['local', 'url', 'camera'],\n clientAllowedFormats: ['jpg', 'jpeg', 'png'],\n resourceType: 'image',\n maxImageFileSize: 5000000, // 5MB\n },\n (error, result) => {\n if (error) {\n console.error('Upload error:', error);\n setMessage('Upload failed: ' + error.message);\n return;\n }\n\n if (result.event === 'success') {\n const uploadedUrl = result.info.secure_url;\n\n axios\n .post(`${import.meta.env.VITE_API_BASE_URL}/api/aboutus`, {\n [imageType]: uploadedUrl,\n })\n .then((response) => {\n setPreviewUrls((prevState) => ({\n ...prevState,\n [imageType]: response.data.imageUrl || uploadedUrl,\n }));\n setMessage(`${imageType} uploaded successfully!`);\n })\n .catch((err) => {\n console.error('Backend error:', err.response?.data || err.message);\n setMessage(`Error saving ${imageType}: ${err.message}`);\n });\n }\n }\n );\n\n widget.open();\n };\n\n return (\n
\n

\n Manage About Us Images\n

\n\n
\n {['topImage', 'middleImage', 'bottomImage'].map((type) => (\n \n

\n {type.replace('Image', '')} Image\n

\n\n
\n \n
\n\n handleUpload(type)}\n className=\"bg-blue-600 text-white px-5 py-2 rounded-md hover:bg-blue-700 transition-colors duration-200\"\n >\n Upload Image\n \n
\n ))}\n
\n\n {loading &&

Saving...

}\n\n {message && (\n \n {message}\n

\n )}\n \n );\n};\n\nexport default AdminAboutUs;","import React, { useState, useEffect, useCallback } from 'react';\nimport axios from 'axios';\nimport { GripVertical } from 'lucide-react';\n\nconst AdminActivitiesPage = () => {\n const [activities, setActivities] = useState([]);\n const [newActivity, setNewActivity] = useState({ name: '' });\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n const [draggedItem, setDraggedItem] = useState(null);\n\n const sortCompletedActivities = useCallback(async (activitiesList) => {\n try {\n const completedActivities = activitiesList\n .filter(activity => activity.completed)\n .sort((a, b) => {\n const dateA = new Date(a.completedAt || a.endDate);\n const dateB = new Date(b.completedAt || b.endDate);\n return dateB - dateA;\n });\n \n const incompleteActivities = activitiesList\n .filter(activity => !activity.completed)\n .sort((a, b) => a.precedence - b.precedence);\n \n return [...incompleteActivities, ...completedActivities];\n } catch (error) {\n console.error(\"Error sorting activities:\", error);\n return activitiesList;\n }\n }, []);\n\n const fetchActivities = useCallback(async () => {\n try {\n setLoading(true);\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/activities`);\n const activitiesData = Array.isArray(response.data) ? response.data : response.data.activities || [];\n \n const completedActivities = activitiesData.filter(activity => activity.completed);\n const sortedCompleted = await sortCompletedActivities(completedActivities);\n const incompleteActivities = activitiesData.filter(activity => !activity.completed);\n \n setActivities([...incompleteActivities, ...sortedCompleted]);\n setError(null);\n } catch (error) {\n console.error(\"Error fetching activities:\", error);\n setError(\"Failed to load activities\");\n setActivities([]);\n } finally {\n setLoading(false);\n }\n }, [sortCompletedActivities]);\n\n useEffect(() => {\n fetchActivities();\n }, [fetchActivities]);\n\n const handleCreateActivity = async (e) => {\n e.preventDefault();\n try {\n const response = await axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/activities`, newActivity);\n setActivities(prev => [...prev, response.data]);\n setNewActivity({ name: '' });\n setError(null);\n } catch (error) {\n console.error(\"Error creating activity:\", error);\n setError(\"Failed to create activity\");\n }\n };\n\n const handleCompleteActivity = async (id) => {\n try {\n const response = await axios.patch(`${import.meta.env.VITE_API_BASE_URL}/api/activities/${id}/complete`);\n const updatedActivities = activities.map(activity =>\n activity._id === id ? { \n ...activity, \n completed: true, \n endDate: response.data.endDate,\n completedAt: response.data.completedAt \n } : activity\n );\n const sortedActivities = await sortCompletedActivities(updatedActivities);\n setActivities(sortedActivities);\n setError(null);\n } catch (error) {\n console.error(\"Error completing activity:\", error);\n setError(\"Failed to complete activity\");\n }\n };\n\n const handleDeleteActivity = async (id) => {\n try {\n await axios.delete(`${import.meta.env.VITE_API_BASE_URL}/api/activities/${id}`);\n setActivities(prev => prev.filter(activity => activity._id !== id));\n setError(null);\n } catch (error) {\n console.error(\"Error deleting activity:\", error);\n setError(\"Failed to delete activity\");\n }\n };\n\n const handleDragStart = (e, activity) => {\n if (activity.completed) {\n e.preventDefault();\n return;\n }\n setDraggedItem(activity);\n };\n\n const handleDragOver = (e) => {\n e.preventDefault();\n };\n\n const handleDrop = async (e, targetActivity) => {\n e.preventDefault();\n if (!draggedItem || draggedItem._id === targetActivity._id || targetActivity.completed) return;\n\n const draggedIndex = activities.findIndex(item => item._id === draggedItem._id);\n const targetIndex = activities.findIndex(item => item._id === targetActivity._id);\n \n try {\n const newActivities = [...activities];\n newActivities.splice(draggedIndex, 1);\n newActivities.splice(targetIndex, 0, draggedItem);\n \n const incompleteActivities = newActivities.filter(activity => !activity.completed);\n incompleteActivities.forEach((activity, index) => {\n activity.precedence = index + 1;\n });\n \n setActivities(newActivities);\n\n await axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/activities/reorder`, {\n activityId: draggedItem._id,\n targetPosition: targetIndex,\n newOrder: incompleteActivities.map(activity => ({\n id: activity._id,\n precedence: activity.precedence\n }))\n });\n\n setError(null);\n } catch (error) {\n console.error(\"Error reordering activities:\", error);\n setError(\"Failed to reorder activities\");\n await fetchActivities();\n }\n\n setDraggedItem(null);\n };\n\n if (loading) {\n return (\n
\n

Admin: Manage Activities

\n

Loading activities...

\n
\n );\n }\n\n return (\n
\n
\n

Manage Activities

\n\n {error && (\n
\n {error}\n
\n )}\n\n
\n
\n
\n \n setNewActivity({ ...newActivity, name: e.target.value })}\n className=\"w-full p-2 border rounded focus:ring-blue-500 focus:border-blue-500\"\n required\n />\n
\n\n \n Add Activity\n \n
\n
\n\n
\n {activities.map((activity) => (\n handleDragStart(e, activity)}\n onDragOver={handleDragOver}\n onDrop={(e) => handleDrop(e, activity)}\n className={`bg-white p-4 rounded-lg shadow ${\n activity.completed ? 'opacity-75' : 'hover:shadow-lg transition-shadow'\n }`}\n >\n
\n
\n {!activity.completed && (\n \n \n \n )}\n {activity.name}\n
\n
\n {!activity.completed && (\n handleCompleteActivity(activity._id)}\n className=\"bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600\"\n >\n Complete\n \n )}\n handleDeleteActivity(activity._id)}\n className=\"bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600\"\n >\n Delete\n \n
\n
\n {activity.completed && (\n
\n Completed on: {new Date(activity.endDate).toLocaleDateString()}\n
\n )}\n
\n ))}\n
\n
\n \n );\n};\n\nexport default AdminActivitiesPage;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\n\nconst AdminExecutiveCouncil = () => {\n const [loading, setLoading] = useState(false);\n const [message, setMessage] = useState('');\n const [editMode, setEditMode] = useState(false);\n const [editingMember, setEditingMember] = useState(null);\n\n const designationCounts = {\n Chairman: 1,\n ChairmanElect: 1,\n ViceChairman: 3,\n HonSecretary: 1, // We'll modify this to 1 but handle 3 positions later\n HonTreasurer: 1, // New designation\n JointSecretary: 1, // New designation\n ItMember: 1,\n PropertyMember: 1,\n ActivityMember: 1,\n ActivityCoordinator: 1,\n ExecutiveMember: 3,\n };\n\n const generateInitialFormData = () => {\n const data = {};\n Object.entries(designationCounts).forEach(([designation, count]) => {\n data[designation] = Array(count).fill().map(() => ({ name: '', imageUrl: '', _id: null }));\n });\n return data;\n };\n\n const generateInitialPreviewUrls = () => {\n const urls = {};\n Object.entries(designationCounts).forEach(([designation, count]) => {\n urls[designation] = Array(count).fill('/default-image.jpg');\n });\n return urls;\n };\n\n const [formData, setFormData] = useState(generateInitialFormData());\n const [previewUrls, setPreviewUrls] = useState(generateInitialPreviewUrls());\n const [members, setMembers] = useState([]);\n\n useEffect(() => {\n const script = document.createElement('script');\n script.src = 'https://upload-widget.cloudinary.com/global/all.js';\n script.async = true;\n document.body.appendChild(script);\n\n script.onerror = () => console.error('Failed to load Cloudinary widget script');\n\n fetchMembers();\n\n return () => {\n if (document.body.contains(script)) {\n document.body.removeChild(script);\n }\n };\n }, []);\n\n useEffect(() => {\n if (Array.isArray(members) && members.length > 0) {\n const newFormData = generateInitialFormData();\n const newPreviewUrls = generateInitialPreviewUrls();\n\n members.forEach(member => {\n const baseDesignation = member.designation.replace(/\\d+$/, '').trim().replace(/\\s+/g, '');\n const capitalizedDesignation = baseDesignation.charAt(0).toUpperCase() + baseDesignation.slice(1);\n const positionIndex = parseInt(member.designation.match(/\\d+$/)?.[0] || 1) - 1;\n\n if (newFormData[capitalizedDesignation] && newFormData[capitalizedDesignation][positionIndex]) {\n newFormData[capitalizedDesignation][positionIndex] = {\n name: member.name,\n imageUrl: member.imageUrl,\n _id: member._id\n };\n newPreviewUrls[capitalizedDesignation][positionIndex] = member.imageUrl;\n }\n });\n\n setFormData(newFormData);\n setPreviewUrls(newPreviewUrls);\n }\n }, [members]);\n\n const fetchMembers = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/executive`);\n const membersData = Array.isArray(response.data) ? response.data : [];\n setMembers(membersData);\n } catch (error) {\n console.error('Error fetching members:', error);\n setMessage('Error fetching members');\n setMembers([]);\n }\n };\n\n const handleChange = (designation, index, e) => {\n const { name, value } = e.target;\n setFormData(prevState => ({\n ...prevState,\n [designation]: prevState[designation].map((item, i) =>\n i === index ? { ...item, [name]: value } : item\n ),\n }));\n };\n\n const handleImageUpload = (designation, index) => {\n const widget = window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n sources: ['local', 'url', 'camera'],\n clientAllowedFormats: ['jpg', 'jpeg', 'png'],\n maxFiles: 1,\n resourceType: 'image',\n maxImageFileSize: 5000000,\n folder: 'executive-council',\n },\n (error, result) => {\n if (!error && result && result.event === 'success') {\n setFormData(prevState => ({\n ...prevState,\n [designation]: prevState[designation].map((item, i) =>\n i === index ? { ...item, imageUrl: result.info.secure_url } : item\n ),\n }));\n setPreviewUrls(prevState => ({\n ...prevState,\n [designation]: prevState[designation].map((url, i) =>\n i === index ? result.info.secure_url : url\n ),\n }));\n } else if (error) {\n console.error('Upload error:', error);\n setMessage('Error uploading image');\n }\n }\n );\n\n widget.open();\n };\n\n const handleSubmit = async (event) => {\n event.preventDefault();\n setLoading(true);\n setMessage('');\n\n try {\n const membersToSubmit = [];\n Object.entries(formData).forEach(([designation, members]) => {\n members.forEach((member, index) => {\n if (member.name && member.imageUrl) {\n membersToSubmit.push({\n _id: member._id,\n name: member.name,\n imageUrl: member.imageUrl,\n designation: `${designation.replace(/([A-Z])/g, ' $1').trim()}${index + 1}`\n });\n }\n });\n });\n\n for (const memberData of membersToSubmit) {\n if (memberData._id) {\n await axios.patch(\n `${import.meta.env.VITE_API_BASE_URL}/api/executive/${memberData._id}`,\n memberData,\n { headers: { 'Content-Type': 'application/json' } }\n );\n } else {\n await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/executive`,\n memberData,\n { headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n setMessage('Upload successful');\n setEditMode(false);\n setEditingMember(null);\n await fetchMembers();\n } catch (error) {\n console.error(\"Upload error:\", error);\n setMessage(error.response?.data?.message || 'Error uploading data');\n } finally {\n setLoading(false);\n }\n };\n\n const handleDelete = async (id) => {\n try {\n await axios.delete(`${import.meta.env.VITE_API_BASE_URL}/api/executive/${id}`);\n setMessage('Deleted successfully');\n await fetchMembers();\n } catch (error) {\n console.error('Delete error:', error);\n setMessage('Error deleting data');\n }\n };\n\n return (\n
\n

Executive Council Details

\n {message && (\n
\n {message}\n
\n )}\n \n
\n {Object.entries(formData).map(([designation, members]) => (\n
\n

\n {designation.replace(/([A-Z])/g, ' $1').trim()} ({members.length} position{members.length > 1 ? 's' : ''})\n

\n {members.map((member, index) => (\n
\n

Position {index + 1}

\n
\n
\n handleChange(designation, index, e)}\n className=\"w-full p-2 border border-gray-300 rounded\"\n />\n
\n handleImageUpload(designation, index)}\n className=\"bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded transition-colors\"\n >\n Upload Image\n \n
\n
\n \n
\n
\n ))}\n
\n ))}\n \n {loading ? 'Uploading...' : (editMode ? 'Update' : 'Submit All')}\n \n \n\n
\n

Existing Members

\n
\n {Array.isArray(members) && members.map(member => (\n
\n
\n

{member.name}

\n
\n handleDelete(member._id)}\n className=\"bg-red-500 hover:bg-red-600 text-white px-3 py-1 rounded text-sm transition-colors\"\n >\n Delete\n \n
\n
\n

{member.designation}

\n \n
\n ))}\n
\n
\n
\n );\n};\n\nexport default AdminExecutiveCouncil;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\n\nconst AdminPhotogallery = () => {\n const [loading, setLoading] = useState(false);\n const [message, setMessage] = useState('');\n const [photos, setPhotos] = useState([]);\n const [selectedYear, setSelectedYear] = useState('');\n const [isEditModalOpen, setIsEditModalOpen] = useState(false);\n const [editingPhoto, setEditingPhoto] = useState(null);\n const [formData, setFormData] = useState({\n name: '',\n description: '',\n eventName: '',\n programName: '',\n year: new Date().getFullYear(),\n mainImage: '',\n additionalImages: [],\n });\n\n const currentYear = new Date().getFullYear();\n const years = Array.from(\n { length: currentYear - 2019 },\n (_, i) => currentYear - i\n );\n\n useEffect(() => {\n const loadCloudinaryScript = () => {\n const script = document.createElement('script');\n script.src = 'https://upload-widget.cloudinary.com/global/all.js';\n script.async = true;\n document.body.appendChild(script);\n return script;\n };\n\n const script = loadCloudinaryScript();\n return () => {\n document.body.removeChild(script);\n };\n }, []);\n\n useEffect(() => {\n fetchPhotos();\n }, [selectedYear]);\n\n const fetchPhotos = async () => {\n try {\n setLoading(true);\n const url = selectedYear\n ? `${import.meta.env.VITE_API_BASE_URL}/api/photos?year=${selectedYear}`\n : `${import.meta.env.VITE_API_BASE_URL}/api/photos`;\n const response = await axios.get(url);\n setPhotos(response.data.photos || []);\n } catch (error) {\n console.error('Error fetching photos:', error);\n setMessage(\n 'Error fetching photos: ' +\n (error.response?.data?.message || error.message)\n );\n } finally {\n setLoading(false);\n }\n };\n\n const handleUpload = (isEditMode = false) => {\n if (typeof window.cloudinary === 'undefined') {\n setMessage('Upload widget is still loading. Please try again in a moment.');\n return;\n }\n\n const widget = window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n folder: 'photo-gallery',\n maxFiles: 1,\n sources: ['local', 'url', 'camera'],\n clientAllowedFormats: ['jpg', 'jpeg', 'png'],\n resourceType: 'image',\n },\n (error, result) => {\n if (error) {\n setMessage('Upload failed: ' + error.message);\n return;\n }\n\n if (result.event === 'success') {\n setFormData((prev) => ({\n ...prev,\n mainImage: result.info.secure_url,\n }));\n setMessage(\n isEditMode\n ? 'Photo updated. Click Update to save changes.'\n : 'Main photo selected. Click Submit Event to save.'\n );\n }\n }\n );\n\n widget.open();\n };\n\n const handleUploadAdditionalImages = () => {\n const widget = window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n folder: 'photo-gallery',\n maxFiles: 10,\n sources: ['local', 'url', 'camera'],\n clientAllowedFormats: ['jpg', 'jpeg', 'png'],\n resourceType: 'image',\n multiple: true,\n },\n (error, result) => {\n if (error) {\n setMessage('Upload failed: ' + error.message);\n return;\n }\n\n if (result.event === 'success') {\n setFormData((prev) => ({\n ...prev,\n additionalImages: [...prev.additionalImages, result.info.secure_url],\n }));\n }\n }\n );\n widget.open();\n };\n\n const handleEdit = (photo) => {\n setEditingPhoto(photo);\n setFormData({\n name: photo.name,\n description: photo.description || '',\n eventName: photo.eventName,\n programName: photo.programName || '',\n year: photo.year,\n mainImage: photo.imageUrl,\n additionalImages: photo.additionalImages || [],\n });\n setIsEditModalOpen(true);\n };\n\n const handleUpdate = async (e) => {\n e.preventDefault();\n if (!formData.name || !formData.eventName || !formData.year || !formData.mainImage) {\n setMessage('Please fill in all required fields');\n return;\n }\n\n try {\n setLoading(true);\n const updateData = {\n name: formData.name,\n description: formData.description,\n eventName: formData.eventName,\n programName: formData.programName,\n year: formData.year,\n imageUrl: formData.mainImage,\n additionalImages: formData.additionalImages || [],\n };\n\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/photos/${editingPhoto._id}`,\n updateData\n );\n\n setPhotos(photos.map(photo => \n photo._id === editingPhoto._id ? response.data : photo\n ));\n setMessage('Photo updated successfully!');\n setIsEditModalOpen(false);\n setEditingPhoto(null);\n resetForm();\n } catch (error) {\n setMessage(\n 'Error updating photo: ' +\n (error.response?.data?.message || error.message)\n );\n } finally {\n setLoading(false);\n }\n };\n\n const handleSubmit = async (e) => {\n e.preventDefault();\n if (!formData.name || !formData.eventName || !formData.year || !formData.mainImage) {\n setMessage('Please fill in all required fields and upload a main photo');\n return;\n }\n \n try {\n setLoading(true);\n const photoData = {\n photos: [\n {\n name: formData.name,\n description: formData.description,\n eventName: formData.eventName,\n programName: formData.programName,\n year: formData.year,\n imageUrl: formData.mainImage,\n additionalImages: formData.additionalImages, // This should now work correctly\n },\n ],\n };\n \n const response = await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/photos/event`,\n photoData,\n {\n params: {\n eventName: formData.eventName,\n year: formData.year\n }\n }\n );\n \n setPhotos((prev) => [...prev, ...response.data]);\n setMessage('Event photos uploaded successfully!');\n resetForm();\n } catch (error) {\n setMessage(\n 'Error uploading photos: ' +\n (error.response?.data?.message || error.message)\n );\n } finally {\n setLoading(false);\n }\n };\n\n\n\n const handleDeletePhoto = async (photoId) => {\n if (!window.confirm('Are you sure you want to delete this photo?')) {\n return;\n }\n\n try {\n setLoading(true);\n const response = await axios.delete(\n `${import.meta.env.VITE_API_BASE_URL}/api/photos/${photoId}`\n );\n\n if (response.status === 200) {\n setPhotos(prevPhotos => prevPhotos.filter(photo => photo._id !== photoId));\n setMessage('Photo deleted successfully!');\n }\n } catch (error) {\n console.error('Error deleting photo:', error);\n setMessage(\n 'Error deleting photo: ' +\n (error.response?.data?.message || error.message)\n );\n } finally {\n setLoading(false);\n }\n };\n\n const resetForm = () => {\n setFormData({\n name: '',\n description: '',\n eventName: '',\n programName: '',\n year: new Date().getFullYear(),\n mainImage: '',\n additionalImages: [],\n });\n };\n\n const handleFormChange = (e) => {\n const { name, value } = e.target;\n requestAnimationFrame(() => {\n setFormData(prev => ({\n ...prev,\n [name]: value\n }));\n });\n };\n\n const EditModal = () => (\n
\n
\n
\n

Edit Photo

\n {\n setIsEditModalOpen(false);\n setEditingPhoto(null);\n resetForm();\n }}\n className=\"text-gray-500 hover:text-gray-700\"\n >\n ×\n \n
\n\n
\n
\n \n \n \n \n \n
\n\n {formData.mainImage && (\n
\n \n setFormData({ ...formData, mainImage: '' })}\n className=\"absolute top-2 right-2 bg-red-500 text-white p-1 rounded-full\"\n >\n ×\n \n
\n )}\n\n {formData.additionalImages.length > 0 && (\n
\n {formData.additionalImages.map((url, index) => (\n
\n \n {\n const newImages = formData.additionalImages.filter(\n (_, i) => i !== index\n );\n setFormData({ ...formData, additionalImages: newImages });\n }}\n className=\"absolute top-1 right-1 bg-red-500 text-white p-1 rounded-full text-sm\"\n >\n ×\n \n
\n ))}\n
\n )}\n\n
\n handleUpload(true)}\n disabled={loading}\n className=\"bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition-colors\"\n >\n Change Main Photo\n \n \n Add Additional Photos\n \n
\n\n \n {loading ? 'Updating...' : 'Update Photo'}\n \n \n
\n
\n );\n\n if (loading && photos.length === 0) {\n return
Loading...
;\n }\n\n return (\n
\n

Photo Gallery Management

\n\n {message && (\n \n {message}\n
\n )}\n\n
\n \n setSelectedYear(e.target.value)}\n className=\"p-2 border rounded\"\n >\n \n {years.map((year) => (\n \n ))}\n \n
\n\n
\n

Add New Event Photos

\n
\n \n \n \n \n \n\n {formData.mainImage && (\n
\n \n setFormData({ ...formData, mainImage: '' })}\n className=\"absolute top-2 right-2 bg-red-500 text-white p-1 rounded-full\"\n >\n ×\n \n
\n )}\n\n {formData.additionalImages.length > 0 && (\n
\n {formData.additionalImages.map((url, index) => (\n
\n \n {\n const newImages = formData.additionalImages.filter(\n (_, i) => i !== index\n );\n setFormData({ ...formData, additionalImages: newImages });\n }}\n className=\"absolute top-1 right-1 bg-red-500 text-white p-1 rounded-full text-sm\"\n >\n ×\n \n
\n ))}\n
\n )}\n\n
\n handleUpload(false)}\n disabled={loading}\n className=\"bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition-colors\"\n >\n Upload Main Photo\n \n \n Add Additional Photos\n \n
\n\n \n {loading ? 'Uploading...' : 'Submit Event'}\n \n \n
\n\n
\n {photos.map((photo) => (\n \n \n

{photo.name}

\n

\n {photo.eventName} - {photo.year}\n

\n {photo.programName && (\n

\n Program: {photo.programName}\n

\n )}\n {photo.description && (\n

\n {photo.description}\n

\n )}\n\n {photo.additionalImages && photo.additionalImages.length > 0 && (\n
\n {photo.additionalImages.map((url, index) => (\n \n ))}\n
\n )}\n\n
\n handleEdit(photo)}\n className=\"bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600\"\n >\n Edit\n \n handleDeletePhoto(photo._id)}\n className=\"bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600\"\n >\n Delete Photo\n \n
\n
\n ))}\n \n\n {isEditModalOpen && }\n\n {loading && (\n
\n
\n
\n

Processing...

\n
\n
\n )}\n \n );\n};\n\nexport default AdminPhotogallery;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\n\nconst AdminJournal = () => {\n const [journals, setJournals] = useState({ latest: [], journals: {} });\n const [loading, setLoading] = useState(false);\n const [message, setMessage] = useState('');\n const [selectedJournal, setSelectedJournal] = useState(null);\n const [cloudinaryLoaded, setCloudinaryLoaded] = useState(false);\n const [formData, setFormData] = useState({\n month: '',\n year: new Date().getFullYear(),\n journalNo: 1,\n description: '',\n pdfUrl: '',\n image: '',\n section: 'regular'\n });\n\n // Load Cloudinary script\n useEffect(() => {\n if (!window.cloudinary) {\n const script = document.createElement('script');\n script.src = 'https://upload-widget.cloudinary.com/global/all.js';\n script.async = true;\n \n script.onload = () => {\n console.log('Cloudinary script loaded');\n setCloudinaryLoaded(true);\n };\n\n script.onerror = () => {\n console.error('Failed to load Cloudinary script');\n setMessage('Error loading upload widget');\n };\n\n document.body.appendChild(script);\n\n return () => {\n document.body.removeChild(script);\n };\n } else {\n setCloudinaryLoaded(true);\n }\n }, []);\n\n // Fetch all journals\n const fetchJournals = async () => {\n try {\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/journal`);\n setJournals(response.data);\n \n } catch (error) {\n setMessage('Error fetching journals: ' + error.message);\n }\n };\n\n useEffect(() => {\n fetchJournals();\n }, []);\n\n // Handle form input changes\n const handleInputChange = (e) => {\n const { name, value } = e.target;\n setFormData(prev => ({\n ...prev,\n [name]: value\n }));\n };\n\n // Handle image upload using Cloudinary widget\n const handleImageUpload = () => {\n if (!cloudinaryLoaded) {\n setMessage('Image upload widget is still loading...');\n return;\n }\n\n const widget = window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n folder: 'journals/images',\n sources: ['local'],\n multiple: false,\n maxFileSize: 5000000, // 5MB\n clientAllowedFormats: ['jpg', 'jpeg', 'png'],\n },\n (error, result) => {\n if (error) {\n setMessage('Error uploading image: ' + error.message);\n return;\n }\n \n if (result && result.event === \"success\") {\n setFormData(prev => ({\n ...prev,\n image: result.info.secure_url\n }));\n setMessage('Image uploaded successfully!');\n }\n }\n );\n widget.open();\n };\n\n // Cloudinary Widget for PDF\n const handlePdfUpload = () => {\n if (!cloudinaryLoaded) {\n setMessage('PDF upload widget is still loading...');\n return;\n }\n\n const widget = window.cloudinary.createUploadWidget(\n {\n cloudName: import.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n uploadPreset: import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET,\n folder: 'journals/pdfs',\n sources: ['local'],\n multiple: false,\n resourceType: 'image',\n clientAllowedFormats: ['pdf'],\n maxFileSize: 10000000, // 10MB\n },\n (error, result) => {\n if (error) {\n setMessage('Error uploading PDF: ' + error.message);\n return;\n }\n\n if (result && result.event === \"success\") {\n // Transform the URL to make it viewable\n const publicId = result.info.public_id;\n const pdfUrl = `https://res.cloudinary.com/${import.meta.env.VITE_CLOUDINARY_CLOUD_NAME}/image/upload/${publicId}.pdf`;\n setFormData(prev => ({\n ...prev,\n pdfUrl: pdfUrl\n }));\n setMessage('PDF uploaded successfully!');\n }\n }\n );\n widget.open();\n };\n\n // Handle form submission\n const handleSubmit = async (e) => {\n e.preventDefault();\n if (!formData.pdfUrl || !formData.image) {\n setMessage('Please upload both PDF and image files');\n return;\n }\n \n setLoading(true);\n try {\n const journalData = {\n month: formData.month,\n year: formData.year,\n journalNo: formData.journalNo,\n description: formData.description,\n pdfUrl: formData.pdfUrl,\n image: formData.image,\n section: formData.section\n };\n \n if (selectedJournal) {\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/journal/${selectedJournal.id}`,\n journalData\n );\n \n // Check if the update was successful\n if (response.status === 200) {\n setMessage('Journal updated successfully!');\n } else {\n // This is a fallback, but the catch block would likely handle most error cases\n setMessage('Update failed. Please try again.');\n return;\n }\n } else {\n await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/journal`,\n journalData\n );\n setMessage('Journal created successfully!');\n }\n \n fetchJournals();\n resetForm();\n } catch (error) {\n // More specific error handling\n if (selectedJournal) {\n setMessage(`Failed to update journal: ${error.response?.data?.message || error.message}`);\n } else {\n setMessage(`Failed to create journal: ${error.response?.data?.message || error.message}`);\n }\n } finally {\n setLoading(false);\n }\n };\n\n // Reset form\n const resetForm = () => {\n setSelectedJournal(null);\n setFormData({\n month: '',\n year: new Date().getFullYear(),\n journalNo: 1,\n description: '',\n pdfUrl: '',\n image: '',\n section: 'regular'\n });\n };\n\n // Add this helper function at the top of your component\n const getPdfUrl = (url) => {\n if (!url) return '#';\n // If it's already a PDF URL, return as is\n if (url.endsWith('.pdf')) return url;\n \n // If it's an image URL, convert to PDF URL\n return url.replace(/\\.[^/.]+$/, '.pdf');\n };\n\n // Handle journal deletion\n const handleDelete = async (id) => {\n if (!window.confirm('Are you sure you want to delete this journal?')) {\n return;\n }\n\n setLoading(true);\n try {\n await axios.delete(`${import.meta.env.VITE_API_BASE_URL}/api/journal/${id}`);\n setMessage('Journal deleted successfully!');\n fetchJournals(); // Refresh the list\n } catch (error) {\n setMessage('Error deleting journal: ' + error.message);\n } finally {\n setLoading(false);\n }\n };\n\n // Handle journal edit\n const handleEdit = (journal) => {\n let month = '';\n let year = new Date().getFullYear();\n let section = 'latest';\n\n // First, check in regular journals\n const regularJournalDetails = Object.values(journals.journals)\n .find(yearData => \n yearData.journal.some(j => j.id === journal.id)\n );\n\n if (regularJournalDetails) {\n month = regularJournalDetails.month;\n year = regularJournalDetails.year;\n section = 'regular';\n }\n\n setSelectedJournal(journal);\n setFormData({\n month: month,\n year: year,\n journalNo: journal.journalNo || 1,\n description: journal.description || '',\n pdfUrl: journal.pdfUrl || '',\n image: journal.image || '',\n section: section\n });\n \n // Scroll to the form\n window.scrollTo({\n top: 0,\n behavior: 'smooth'\n });\n };\n\n return (\n
\n {message && (\n
\n {message}\n
\n )}\n

Journal Management

\n\n {/* Journal Form */}\n
\n

{selectedJournal ? 'Edit Journal' : 'Create New Journal'}

\n
\n
\n \n \n \n \n\n {formData.section === 'regular' && (\n <>\n \n \n {['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'].map(month => (\n \n ))}\n \n\n \n\n \n \n )}\n
\n\n \n\n
\n \n
\n \n Upload Image\n \n {formData.image && (\n
\n ✓ Image Uploaded\n \"Preview\"\n
\n )}\n
\n
\n\n
\n \n
\n \n Upload PDF\n \n {formData.pdfUrl && (\n
\n ✓ PDF Uploaded\n \n View PDF\n \n
\n )}\n
\n
\n\n
\n \n {loading ? 'Saving...' : (selectedJournal ? 'Update' : 'Create')}\n \n {selectedJournal && (\n \n Cancel\n \n )}\n
\n \n
\n\n {/* Journals List */}\n
\n

Existing Journals

\n \n {/* Latest Journal */}\n {journals.latest.length > 0 && (\n
\n

Latest Journal

\n {journals.latest.map(journal => (\n
\n
\n
{journal.description}
\n
\n \"Cover\"\n \n View PDF\n \n
\n
\n
\n {\n console.log('Editing journal:', journal);\n handleEdit(journal);\n }}\n className=\"bg-yellow-500 text-white px-3 py-1 rounded\"\n>\n Edit\n\n handleDelete(journal.id)}\n disabled={loading}\n className={`bg-red-500 text-white px-3 py-1 rounded ${\n loading ? 'opacity-50 cursor-not-allowed' : 'hover:bg-red-600'\n }`}\n >\n Delete\n \n
\n
\n ))}\n
\n )}\n\n {/* Regular Journals */}\n {Object.entries(journals.journals).map(([key, monthData]) => (\n
\n

{`${monthData.month} ${monthData.year}`}

\n {monthData.journal.map(journal => (\n
\n
\n
{journal.description}
\n
\n \"Cover\"\n \n View PDF\n \n
\n
\n
\n handleEdit(journal)}\n className=\"bg-yellow-500 text-white px-3 py-1 rounded\"\n >\n Edit\n \n handleDelete(journal.id)}\n disabled={loading}\n className={`bg-red-500 text-white px-3 py-1 rounded ${\n loading ? 'opacity-50 cursor-not-allowed' : 'hover:bg-red-600'\n }`}\n >\n Delete\n \n
\n
\n ))}\n
\n ))}\n
\n\n {message && (\n
\n {message}\n
\n )}\n
\n );\n};\n\nexport default AdminJournal;","import React, { useEffect, useState } from 'react';\nimport { useNavigate, Link } from 'react-router-dom';\nimport axios from 'axios';\nimport { setAdminAuthToken, getAdminAuthToken } from '../../utilis/auth';\nimport { FaEye, FaEyeSlash } from \"react-icons/fa\";\n\nconst AdminLogin = () => {\n const [credentials, setCredentials] = useState({\n email: '',\n password: ''\n });\n const [error, setError] = useState('');\n const navigate = useNavigate();\n\n const [reveal, setReveal] = useState(false)\n\n const handleReveal = () => {\n setReveal((prev)=>!prev)\n console.log(reveal)\n }\n\n useEffect(() => {\n // Check if admin is already logged in\n const adminToken = getAdminAuthToken();\n if (adminToken) {\n // Redirect to the authorized page\n navigate('/authorized');\n }\n }, [navigate]);\n\n const handleSubmit = async (e) => {\n e.preventDefault();\n setError('');\n\n try {\n const response = await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/admin/login`,\n credentials\n );\n\n // Extract user and token details from the response\n const { token, user } = response.data;\n setAdminAuthToken(token);\n\n // Navigate to the authorized page with user details\n navigate('/authorized', { state: { user, token } });\n } catch (err) {\n const errorMessage =\n err.response?.data?.message || 'An error occurred. Please try again.';\n setError(errorMessage);\n }\n };\n\n const handleChange = (e) => {\n setCredentials({\n ...credentials,\n [e.target.name]: e.target.value\n });\n };\n\n return (\n
\n
\n

Admin Login

\n\n
\n
\n
\n \n
\n
\n\n
\n
\n \n
\n {reveal ? : }\n
\n
\n
\n\n {error && (\n
\n {error}\n
\n )}\n\n \n Login\n \n \n \n
\n \n
\n \n Back to Home\n \n
\n
\n \n );\n};\n\nexport default AdminLogin;","import React from 'react';\n\nconst members = [\n {\n id: \"MEM-001\",\n name: \"John Doe\",\n contactNo: \"555-123-4567\",\n unit: \"Unit 101\",\n address: \"Building A, 123 Main St\",\n profession: \"Software Engineer\",\n email: \"john.doe@email.com\",\n yearJoined: 2020\n },\n {\n id: \"MEM-002\",\n name: \"Jane Smith\",\n contactNo: \"555-234-5678\",\n unit: \"Unit 205\",\n address: \"Building B, 456 Oak Ave\",\n profession: \"Architect\",\n email: \"jane.smith@email.com\",\n yearJoined: 2021\n },\n {\n id: \"MEM-003\",\n name: \"Michael Johnson\",\n contactNo: \"555-345-6789\",\n unit: \"Unit 308\",\n address: \"Building C, 789 Pine Rd\",\n profession: \"Doctor\",\n email: \"michael.j@email.com\",\n yearJoined: 2019\n },\n {\n id: \"MEM-004\",\n name: \"Sarah Williams\",\n contactNo: \"555-456-7890\",\n unit: \"Unit 402\",\n address: \"Building D, 321 Elm St\",\n profession: \"Teacher\",\n email: \"sarah.w@email.com\",\n yearJoined: 2022\n }\n];\n\nconst MemberTable = () => {\n return (\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {members.map((member) => (\n \n \n \n \n \n \n \n \n \n \n ))}\n \n
Membership IDNameContact NoUnitAddressProfessionEmailYear Joined
\n \n {member.id}\n \n \n {member.name}\n \n \n {member.contactNo}\n \n \n {member.unit}\n \n {member.address}\n \n {member.profession}\n \n {member.email}\n \n {member.yearJoined}\n
\n
\n
\n );\n};\n\nexport default MemberTable;","import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\nimport { FaTrash, FaEdit, FaSave, FaTimes } from 'react-icons/fa';\n\nconst AdminExternalLinks = () => {\n const [groups, setGroups] = useState([]);\n const [newGroup, setNewGroup] = useState('');\n const [newLink, setNewLink] = useState({ group: '', label: '', url: '' });\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n const [editMode, setEditMode] = useState({\n type: null, // 'group' or 'link'\n id: null,\n data: null\n });\n\n // Fetch all groups and their links\n const fetchGroups = async () => {\n try {\n setLoading(true);\n const response = await axios.get(`${import.meta.env.VITE_API_BASE_URL}/api/external-links`);\n setGroups(Array.isArray(response.data) ? response.data : []);\n } catch (error) {\n console.error('Error fetching groups:', error);\n setError('Failed to fetch groups');\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchGroups();\n }, []);\n\n // Start editing group or link\n const startEdit = (type, id, data) => {\n setEditMode({ type, id, data: { ...data } });\n };\n\n // Cancel editing\n const cancelEdit = () => {\n setEditMode({ type: null, id: null, data: null });\n };\n\n // Update group name\n const handleUpdateGroup = async (groupId) => {\n try {\n await axios.put(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/groups/${groupId}`, {\n name: editMode.data.name\n });\n cancelEdit();\n fetchGroups();\n } catch (error) {\n console.error('Error updating group:', error);\n setError('Failed to update group');\n }\n };\n\n // Update link\n const handleUpdateLink = async (linkId) => {\n try {\n await axios.put(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/links/${linkId}`, editMode.data);\n cancelEdit();\n fetchGroups();\n } catch (error) {\n console.error('Error updating link:', error);\n setError('Failed to update link');\n }\n };\n\n // Add new group\n const handleAddGroup = async (e) => {\n e.preventDefault();\n try {\n await axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/groups`, { name: newGroup });\n setNewGroup('');\n fetchGroups();\n } catch (error) {\n console.error('Error adding group:', error);\n setError('Failed to add group');\n }\n };\n\n // Add new link\n const handleAddLink = async (e) => {\n e.preventDefault();\n try {\n await axios.post(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/links`, newLink);\n setNewLink({ group: '', label: '', url: '' });\n fetchGroups();\n } catch (error) {\n console.error('Error adding link:', error);\n setError('Failed to add link');\n }\n };\n\n // Delete group\n const handleDeleteGroup = async (groupId) => {\n if (window.confirm('Are you sure you want to delete this group?')) {\n try {\n await axios.delete(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/groups/${groupId}`);\n fetchGroups();\n } catch (error) {\n console.error('Error deleting group:', error);\n setError('Failed to delete group');\n }\n }\n };\n\n // Delete link\n const handleDeleteLink = async (linkId) => {\n if (window.confirm('Are you sure you want to delete this link?')) {\n try {\n await axios.delete(`${import.meta.env.VITE_API_BASE_URL}/api/external-links/links/${linkId}`);\n fetchGroups();\n } catch (error) {\n console.error('Error deleting link:', error);\n setError('Failed to delete link');\n }\n }\n };\n\n if (loading) return
Loading...
;\n if (error) return
{error}
;\n\n return (\n
\n

Manage External Links

\n\n {/* Add New Group Form */}\n
\n

Add New Group

\n
\n setNewGroup(e.target.value)}\n placeholder=\"Enter group name\"\n className=\"flex-1 p-2 border rounded\"\n required\n />\n \n Add Group\n \n \n
\n\n {/* Add New Link Form */}\n
\n

Add New Link

\n
\n setNewLink({ ...newLink, group: e.target.value })}\n className=\"w-full p-2 border rounded\"\n required\n >\n \n {groups.map((group) => (\n \n ))}\n \n setNewLink({ ...newLink, label: e.target.value })}\n placeholder=\"Enter link label\"\n className=\"w-full p-2 border rounded\"\n required\n />\n setNewLink({ ...newLink, url: e.target.value })}\n placeholder=\"Enter URL\"\n className=\"w-full p-2 border rounded\"\n required\n />\n \n Add Link\n \n \n
\n\n {/* Display Groups and Links */}\n
\n {groups.length === 0 ? (\n

No groups added yet.

\n ) : (\n groups.map((group) => (\n
\n
\n {editMode.type === 'group' && editMode.id === group._id ? (\n
\n setEditMode({\n ...editMode,\n data: { ...editMode.data, name: e.target.value }\n })}\n className=\"p-2 border rounded\"\n />\n handleUpdateGroup(group._id)}\n className=\"text-green-500 hover:text-green-700\"\n >\n \n \n \n \n \n
\n ) : (\n

\n {group.name}\n

\n )}\n
\n {editMode.type !== 'group' && (\n startEdit('group', group._id, { name: group.name })}\n className=\"text-blue-500 hover:text-blue-700\"\n >\n \n \n )}\n handleDeleteGroup(group._id)}\n className=\"text-red-500 hover:text-red-700\"\n >\n \n \n
\n
\n
\n {group.links && group.links.length > 0 ? (\n group.links.map((link) => (\n \n {editMode.type === 'link' && editMode.id === link._id ? (\n
\n setEditMode({\n ...editMode,\n data: { ...editMode.data, label: e.target.value }\n })}\n className=\"p-2 border rounded w-1/3\"\n placeholder=\"Label\"\n />\n setEditMode({\n ...editMode,\n data: { ...editMode.data, url: e.target.value }\n })}\n className=\"p-2 border rounded w-1/2\"\n placeholder=\"URL\"\n />\n handleUpdateLink(link._id)}\n className=\"text-green-500 hover:text-green-700\"\n >\n \n \n \n \n \n
\n ) : (\n <>\n \n {link.label}\n \n
\n startEdit('link', link._id, {\n label: link.label,\n url: link.url\n })}\n className=\"text-blue-500 hover:text-blue-700\"\n >\n \n \n handleDeleteLink(link._id)}\n className=\"text-red-500 hover:text-red-700\"\n >\n \n \n
\n \n )}\n
\n ))\n ) : (\n

No links in this group.

\n )}\n
\n
\n ))\n )}\n
\n \n );\n};\n\nexport default AdminExternalLinks;","import React, { useState } from \"react\";\nimport axios from \"axios\";\nimport ExcelJS from \"exceljs\";\n\nconst ExcelUploader = () => {\n const [isLoading, setIsLoading] = useState(false);\n const [uploadStatus, setUploadStatus] = useState({ type: \"\", message: \"\" });\n const [userData, setUserData] = useState([]);\n const [validationErrors, setValidationErrors] = useState([]);\n const [showPreview, setShowPreview] = useState(false);\n\n const generatePassword = (name) => {\n if (!name) return \"User@123\";\n const firstName = name.replace(/\\s+/g, \" \").substring(0, 4).toLowerCase();\n return `${firstName}@123`;\n };\n\n const cleanEmailFormat = (email) => {\n if (email === null || email === undefined) return null;\n if (typeof email === 'number') return email.toString();\n if (typeof email !== 'string') return null;\n \n try {\n return email.replace(/\\s+/g, \"\").toLowerCase();\n } catch (error) {\n return null;\n }\n };\n\n const cleanPhoneFormat = (phone) => {\n if (phone === null || phone === undefined) return null;\n try {\n const phoneStr = String(phone);\n return phoneStr.replace(/\\D/g, '');\n } catch (error) {\n return null;\n }\n };\n\n const validateUserData = (data) => {\n const errors = [];\n const validUsers = [];\n \n data.forEach((user, index) => {\n try {\n const rowErrors = [];\n if (!user.memid || user.memid.toString().trim() === \"\") return;\n\n let cleanedEmail = null;\n if (user.email) {\n cleanedEmail = cleanEmailFormat(user.email);\n if (cleanedEmail && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(cleanedEmail)) {\n rowErrors.push(`Invalid email: ${user.email}`);\n }\n }\n\n const cleanedUser = {\n memid: String(user.memid || '').trim(),\n name: user.name ? String(user.name).trim() : null,\n email: cleanedEmail,\n contactno: cleanPhoneFormat(user.contactno),\n unit: user.unit ? String(user.unit).trim() : null,\n address: user.address ? String(user.address).trim() : null,\n profession: user.profession ? String(user.profession).trim() : null,\n yearjoined: user.yearjoined ? parseInt(String(user.yearjoined)) : null,\n };\n\n if (rowErrors.length > 0) {\n errors.push({ row: index + 1, errors: rowErrors });\n } else {\n validUsers.push(cleanedUser);\n }\n } catch (error) {\n errors.push({ \n row: index + 1, \n errors: [`Error processing row: ${error.message}`] \n });\n }\n });\n return { errors, validUsers };\n };\n\n const handleFileUpload = async (e) => {\n const file = e.target.files[0];\n if (!file) {\n setUploadStatus({ type: \"error\", message: \"No file selected.\" });\n return;\n }\n\n setIsLoading(true);\n setUploadStatus({ type: \"\", message: \"\" });\n setValidationErrors([]);\n setUserData([]);\n setShowPreview(false);\n\n try {\n const workbook = new ExcelJS.Workbook();\n const buffer = await file.arrayBuffer();\n await workbook.xlsx.load(buffer);\n\n const worksheet = workbook.worksheets[0];\n const rawSheetData = [];\n \n const headers = {};\n worksheet.getRow(1).eachCell((cell, colNumber) => {\n let headerValue = cell.value;\n if (typeof headerValue === 'object' && headerValue.text) {\n headerValue = headerValue.text;\n }\n headers[colNumber] = String(headerValue).toLowerCase().trim();\n });\n\n worksheet.eachRow((row, rowIndex) => {\n if (rowIndex === 1) return;\n \n const rowData = {};\n row.eachCell((cell, colNumber) => {\n const header = headers[colNumber];\n let value = cell.value;\n \n if (value && typeof value === 'object') {\n if (value.text) value = value.text;\n else if (value.result) value = value.result;\n else value = null;\n }\n \n rowData[header] = value;\n });\n rawSheetData.push(rowData);\n });\n\n const transformedData = rawSheetData.map((row) => ({\n memid: row[\"mem id\"] || row[\"memid\"] || null,\n name: row[\"name\"] || null,\n email: row[\"email\"],\n contactno: row[\"contact no\"] || row[\"contactno\"],\n unit: row[\"unit\"] || null,\n address: row[\"address\"] || null,\n profession: row[\"profession\"] || null,\n yearjoined: row[\"yearjoined\"] || row[\"year joined\"] || null,\n password: generatePassword(row[\"name\"]),\n }));\n\n const { errors, validUsers } = validateUserData(transformedData);\n\n if (errors.length > 0) {\n setValidationErrors(errors);\n setUserData(transformedData);\n setShowPreview(true);\n setUploadStatus({ \n type: \"error\", \n message: `${errors.length} validation errors found.` \n });\n } else {\n await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/uploaduserdata`,\n validUsers\n );\n setUploadStatus({ \n type: \"success\", \n message: `Uploaded ${validUsers.length} users.` \n });\n }\n } catch (error) {\n setUploadStatus({ \n type: \"error\", \n message: \"Error processing file: \" + (error.message || \"Unknown error\") \n });\n } finally {\n setIsLoading(false);\n }\n };\n\n return (\n
\n

Upload Excel File

\n\n
\n \n {isLoading &&

Processing file...

}\n
\n\n {uploadStatus.message && (\n \n

{uploadStatus.type === \"error\" ? \"Error\" : \"Success\"}

\n

{uploadStatus.message}

\n
\n )}\n\n {showPreview && (\n
\n

Validation Errors

\n \n \n \n \n \n \n \n \n {validationErrors.map((error, index) => (\n \n \n \n \n ))}\n \n
RowErrors
{error.row}{error.errors.join(\", \")}
\n
\n )}\n \n );\n};\n\nexport default ExcelUploader;","import React, { useEffect, useState } from 'react';\nimport axios from 'axios';\nimport Cookies from 'js-cookie';\n\nconst MemberModification = () => {\n const [formData, setFormData] = useState({\n memId: '',\n name: '',\n contactNo: '',\n unit: '',\n address: '',\n profession: '',\n });\n\n const [newPassword, setNewPassword] = useState(''); // Separate state for password\n const [message, setMessage] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [passwordLoading, setPasswordLoading] = useState(false); // Loading for password update\n\n const userId = Cookies.get('Userid');\n const token = Cookies.get('AuthToken');\n\n useEffect(() => {\n const fetchMemberData = async () => {\n if (!userId || !token) {\n setMessage('Authentication required. Please log in.');\n return;\n }\n\n try {\n setIsLoading(true);\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/members/${userId}`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n }\n );\n\n const userData = response.data.user || {};\n setFormData((prev) => ({\n ...prev,\n memId: userData.membershipId || '',\n name: userData.name || '',\n contactNo: userData.contactNo || '',\n unit: userData.unit || '',\n address: userData.address || '',\n profession: userData.profession || '',\n }));\n\n setMessage('');\n } catch (error) {\n setMessage(\n error.response?.data?.message ||\n 'Failed to fetch member information. Please try again.'\n );\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchMemberData();\n }, [userId, token]);\n\n const handleChange = (e) => {\n const { name, value } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: value,\n }));\n };\n\n const handlePasswordChange = (e) => {\n setNewPassword(e.target.value);\n };\n\n const handleProfileSubmit = async (e) => {\n e.preventDefault();\n try {\n setIsLoading(true);\n const response = await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/members/${userId}`,\n formData,\n {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n }\n );\n\n setMessage('Profile successfully updated!');\n setTimeout(() => setMessage(''), 3000);\n } catch (error) {\n setMessage(\n error.response?.data?.message ||\n error.response?.data?.error ||\n 'Error updating profile. Please try again.'\n );\n } finally {\n setIsLoading(false);\n }\n };\n\n const handlePasswordSubmit = async (e) => {\n e.preventDefault();\n if (!newPassword) {\n setMessage('Please enter a new password.');\n return;\n }\n\n try {\n setPasswordLoading(true);\n\n const response = await axios.patch(\n `${import.meta.env.VITE_API_BASE_URL}/api/users/update-password`,\n { newPassword, userId }, // Ensure this matches the backend's expected field name\n {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n }\n );\n\n if (response.status === 200) {\n setMessage('Password successfully updated!');\n } else {\n setMessage('Failed to update password.');\n }\n\n setNewPassword(''); // Clear password field\n setTimeout(() => setMessage(''), 3000);\n } catch (error) {\n setMessage(\n error.response?.data?.message ||\n 'Error updating password. Please try again.'\n );\n } finally {\n setPasswordLoading(false);\n }\n };\n\n return (\n \n
\n

\n Member Information\n

\n\n {message && (\n \n {message}\n
\n )}\n\n
\n
\n
\n \n Member ID\n \n \n
\n\n
\n \n Name\n \n \n
\n\n
\n \n Contact Number\n \n \n
\n\n
\n \n Unit\n \n \n
\n\n
\n \n Address\n \n \n
\n\n
\n \n Profession\n \n \n
\n
\n\n
\n \n {isLoading ? 'Submitting...' : 'Update Profile'}\n \n
\n
\n\n
\n

Change Password

\n
\n \n New Password\n \n \n
\n\n
\n \n {passwordLoading ? 'Updating...' : 'Update Password'}\n \n
\n
\n \n \n );\n};\n\nexport default MemberModification;","import React, { useEffect, useState } from 'react';\nimport axios from 'axios';\n\nconst MembershipHandle = () => {\n const [categories, setCategories] = useState([]);\n const [newCategory, setNewCategory] = useState({\n category: '',\n abbreviation: '',\n });\n const [editingId, setEditingId] = useState(null);\n const [editValue, setEditValue] = useState('');\n const [loading, setLoading] = useState(false);\n const [message, setMessage] = useState('');\n\n useEffect(() => {\n fetchCategories();\n }, []);\n\n const fetchCategories = async () => {\n try {\n const response = await axios.get(\n `${import.meta.env.VITE_API_BASE_URL}/api/membership/categories`\n );\n setCategories(response.data);\n } catch (error) {\n console.error('Error fetching categories:', error);\n setMessage('Error loading categories');\n }\n };\n\n const handleChange = (e) => {\n setNewCategory((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\n }));\n };\n\n const handleSubmit = async (e) => {\n e.preventDefault();\n setLoading(true);\n setMessage('');\n\n try {\n await axios.post(\n `${import.meta.env.VITE_API_BASE_URL}/api/membership/add-abbreviation`,\n newCategory\n );\n\n setMessage('Category added successfully!');\n setNewCategory({ category: '', abbreviation: '' });\n fetchCategories();\n } catch (error) {\n console.error('Error saving data:', error);\n setMessage(error.response?.data?.message || 'Error saving data');\n } finally {\n setLoading(false);\n }\n };\n\n const startEditing = (category) => {\n setEditingId(category._id);\n setEditValue(category.abbreviation);\n };\n\n const cancelEditing = () => {\n setEditingId(null);\n setEditValue('');\n };\n\n const handleUpdateAbbreviation = async (id) => {\n if (!editValue.trim()) return;\n\n try {\n await axios.put(\n `${import.meta.env.VITE_API_BASE_URL}/api/membership/categories/${id}`,\n { abbreviation: editValue }\n );\n setMessage('Category updated successfully!');\n setEditingId(null);\n fetchCategories();\n } catch (error) {\n console.error('Error updating abbreviation:', error);\n setMessage('Error updating abbreviation');\n }\n };\n\n const handleDelete = async (id, categoryName) => {\n if (window.confirm('Are you sure you want to delete this category?')) {\n try {\n await axios.delete(\n `${import.meta.env.VITE_API_BASE_URL}/api/membership/delete`,\n {\n data: {\n category: categoryName,\n abbreviation: '', // Sending empty abbreviation to delete entire category\n },\n }\n );\n setMessage('Category deleted successfully!');\n fetchCategories();\n } catch (error) {\n console.error('Error deleting category:', error);\n setMessage('Error deleting category');\n }\n }\n };\n\n return (\n
\n
\n

Membership Categories

\n {message && (\n \n {message}\n
\n )}\n\n
\n
\n
\n \n \n
\n
\n \n \n
\n \n {loading ? 'Adding...' : 'Add Category'}\n \n
\n
\n\n
\n

Existing Categories

\n
\n {categories.map((category) => (\n
\n
\n

{category.category}

\n
\n {editingId !== category._id ? (\n startEditing(category)}\n className=\"px-3 py-1 text-sm bg-blue-100 text-blue-600 rounded hover:bg-blue-200\"\n >\n Edit\n \n ) : (\n <>\n handleUpdateAbbreviation(category._id)}\n className=\"px-3 py-1 text-sm bg-green-100 text-green-600 rounded hover:bg-green-200\"\n >\n Save\n \n \n Cancel\n \n \n )}\n \n handleDelete(category._id, category.category)\n }\n className=\"px-3 py-1 text-sm bg-red-100 text-red-600 rounded hover:bg-red-200\"\n >\n Delete\n \n
\n
\n
\n {editingId === category._id ? (\n setEditValue(e.target.value)}\n className=\"w-full p-1 text-sm border rounded focus:border-blue-500 focus:ring-blue-500\"\n placeholder=\"Enter new abbreviation\"\n />\n ) : (\n

\n {category.abbreviation}\n

\n )}\n
\n
\n ))}\n
\n
\n
\n \n );\n};\n\nexport default MembershipHandle;","import { \n BrowserRouter, \n Routes, \n Route,\n} from 'react-router-dom';\n\n// import './App.css';\nimport Navbar from './Components/Navbar';\nimport Home from './Components/Home';\nimport AboutUs from './Components/About';\nimport ActivityPage from './Components/Activity';\nimport Member_ship from './Components/Member_ship';\nimport Photogallery from './Components/Photogallery';\nimport Journal from './Components/Journal';\nimport JournalDetail from './Components/JournalDetail';\nimport Contactus from './Components/Contactus';\nimport Footer from './Components/Footer';\nimport ExecutiveCouncil from './Components/ExecutiveCouncil';\nimport ExternalLinks from './Components/ExternalLinks';\n\n// Admin imports\nimport AdminHome from './Components/admin/Home';\nimport AdminNavbar from './Components/admin/Navbar';\nimport AdminAboutUs from './Components/admin/Aboutus';\nimport AdminActivitiesPage from './Components/admin/Activity';\nimport AdminExecutiveCouncil from './Components/admin/Executive';\nimport AdminPhotogallery from './Components/admin/Photogallery';\nimport AdminJournal from './Components/admin/Journal';\nimport AdminLogin from './Components/admin/Adminlogin';\nimport MemberTable from './Components/admin/MemberTable';\nimport AdminExternalLinks from './Components/admin/ExternalLinks';\nimport ExcelUploader from './Components/admin/Login';\n\nimport MemberModification from './Components/MemberModification';\nimport MembershipHandle from './Components/admin/MembershipHandle';\n\nfunction App() {\n return (\n \n \n {/* Main Website Routes */}\n \n \n \n \n \n \n \n \n
\n \n
\n