From 7c7bdd5824e055d5d4d2b6081c33106dec6f5d73 Mon Sep 17 00:00:00 2001 From: Akash Date: Thu, 21 Nov 2024 20:44:08 +0530 Subject: [PATCH 1/4] feat: removed firebase calls from frontend, fixed events page --- app/(default)/achievements/page.tsx | 9 +- app/(default)/api/admin/route.ts | 48 ++ app/(default)/api/events/route.ts | 135 +++++ app/(default)/events/EventCard.css | 215 ------- .../{myevents => events}/page.module.css | 0 app/(default)/events/page.tsx | 524 ++++++------------ app/(default)/myevents/page.tsx | 224 -------- components/EventForm.tsx | 12 +- components/EventUpdateForm.tsx | 2 +- components/ui/footer.tsx | 2 +- components/ui/header.tsx | 4 +- package-lock.json | 15 +- package.json | 3 +- 13 files changed, 377 insertions(+), 816 deletions(-) create mode 100644 app/(default)/api/admin/route.ts create mode 100644 app/(default)/api/events/route.ts delete mode 100644 app/(default)/events/EventCard.css rename app/(default)/{myevents => events}/page.module.css (100%) delete mode 100644 app/(default)/myevents/page.tsx diff --git a/app/(default)/achievements/page.tsx b/app/(default)/achievements/page.tsx index ad0866f..bdb4505 100644 --- a/app/(default)/achievements/page.tsx +++ b/app/(default)/achievements/page.tsx @@ -4,9 +4,6 @@ import { useEffect, useState } from "react"; import axios from "axios"; import { onAuthStateChanged } from "firebase/auth"; import { auth } from "@/Firebase"; -import { db } from "@/Firebase"; -import { doc, getDoc } from "firebase/firestore"; -import Image from "next/image"; import AchievementCard from "@/components/AchievementCard"; interface Achiever { @@ -40,9 +37,9 @@ export default function AchievementsPage() { if (user) { const uid = user.uid; try { - const adminDocRef = await doc(db, "admin", uid); - const adminDocSnap = await getDoc(adminDocRef); - if (adminDocSnap.exists()) { + const resp = await fetch(`/api/admin?uid=${uid}`); + const data = await resp.json(); + if (data.isAdmin) { setIsAdmin(true); } } catch (error) { diff --git a/app/(default)/api/admin/route.ts b/app/(default)/api/admin/route.ts new file mode 100644 index 0000000..58772a2 --- /dev/null +++ b/app/(default)/api/admin/route.ts @@ -0,0 +1,48 @@ +import { db } from "@/Firebase"; +import { + doc, + getDoc, +} from "firebase/firestore"; +import { NextResponse } from "next/server"; + + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const uid = searchParams.get("uid"); + if (!uid) { + return NextResponse.json( + { error: "UID is required" }, + { status: 400 } + ); + } + const adminDocRef = doc(db, "admin", uid); + const adminDocSnap = await getDoc(adminDocRef); + if (!adminDocSnap.exists()) { + return NextResponse.json( + { error: "User is not an admin" , isAdmin: false }, + { status: 403 } + ); + } + else { + return NextResponse.json( + { message: "User is an admin" , isAdmin: true }, + { status: 200 } + ); + } + } catch (error) { + if (error instanceof Error) { + console.error("Error details:", error.message); + return NextResponse.json( + { error: "An error occurred", details: error.message }, + { status: 500 } + ); + } else { + console.error("Unknown error:", error); + return NextResponse.json( + { error: "An unknown error occurred" }, + { status: 500 } + ); + } + } +} diff --git a/app/(default)/api/events/route.ts b/app/(default)/api/events/route.ts new file mode 100644 index 0000000..ae75b3a --- /dev/null +++ b/app/(default)/api/events/route.ts @@ -0,0 +1,135 @@ +import { db } from "@/Firebase"; +import { + getDocs, + query, + collection, + orderBy, + addDoc, + updateDoc, + doc, + deleteDoc, +} from "firebase/firestore"; +import { NextResponse } from "next/server"; + + +export async function GET(request: Request) { + try { + const eventsCollection = collection(db, "events"); + const eventSnapshot = await getDocs(eventsCollection); + const data = eventSnapshot.docs.map((doc) => doc.data()); + const eventsList = data.map((event : any) => { + console.log("event id in backend is", event.id); + return { + id: event.id, + eventName: event.eventName, + description: event.description, + eventDate: event.eventDate, + lastDateOfRegistration: event.lastDateOfRegistration, + dateCreated: event.dateCreated, + dateModified: event.dateModified, + imageURL: event.imageURL, + registrationLink: event.registrationLink, + }; + }); + return NextResponse.json({ events: eventsList }, { status: 200 }); + } catch (error) { + if (error instanceof Error) { + console.error("Error details:", error.message); + return NextResponse.json( + { error: "An error occurred", details: error.message }, + { status: 500 } + ); + } else { + console.error("Unknown error:", error); + return NextResponse.json( + { error: "An unknown error occurred" }, + { status: 500 } + ); + } + } +} + +export async function POST(request: Request) { + try { + const newEvent = await request.json(); + const docRef = await addDoc(collection(db, "events"), newEvent); + return NextResponse.json({ id: docRef.id }, { status: 201 }); + } catch (error) { + if (error instanceof Error) { + console.error("Error details:", error.message); + return NextResponse.json( + { error: "An error occurred", details: error.message }, + { status: 500 } + ); + } else { + console.error("Unknown error:", error); + return NextResponse.json( + { error: "An unknown error occurred" }, + { status: 500 } + ); + } + } +} + +export async function PUT(request: Request) { + try{ + const { searchParams } = new URL(request.url); + const eventid = searchParams.get("eventid"); + if (!eventid) { + return NextResponse.json( + { error: "Event ID is required" }, + { status: 400 } + ); + } + const updatedEvent = await request.json(); + await updateDoc(doc(db, "events", eventid), updatedEvent); + + }catch (error) { + if (error instanceof Error) { + console.error("Error details:", error.message); + return NextResponse.json( + { error: "An error occurred", details: error.message }, + { status: 500 } + ); + } else { + console.error("Unknown error:", error); + return NextResponse.json( + { error: "An unknown error occurred" }, + { status: 500 } + ); + } + } +} + +export async function DELETE(request: Request) { + try{ + const { searchParams } = new URL(request.url); + const eventid = searchParams.get("eventid"); + if (!eventid) { + return NextResponse.json( + { error: "Event ID is required" }, + { status: 400 } + ); + } + await deleteDoc(doc(db, "events", eventid)); + } + catch (error) { + if (error instanceof Error) { + console.error("Error details:", error.message); + return NextResponse.json( + { error: "An error occurred", details: error.message }, + { status: 500 } + ); + } else { + console.error("Unknown error:", error); + return NextResponse.json( + { error: "An unknown error occurred" }, + { status: 500 } + ); + } + } +} + + + + diff --git a/app/(default)/events/EventCard.css b/app/(default)/events/EventCard.css deleted file mode 100644 index 449977a..0000000 --- a/app/(default)/events/EventCard.css +++ /dev/null @@ -1,215 +0,0 @@ -h2 { - font-family: 'Arial Narrow Bold', sans-serif; - margin-top: 60px; - padding-bottom: 30px; - font-size: 2.5em; - color: #00c853; - text-align: center; -} - -.event-cards-container { - display: flex; - flex-wrap: wrap; - justify-content: space-around; -} - -.event-card { - width: 300px; - height: 400px; - perspective: 1000px; - margin: 20px; -} - -.event-card-inner { - width: 100%; - height: 100%; - position: relative; - transform-style: preserve-3d; - transition: transform 0.8s; -} - -.event-card:hover .event-card-inner { - transform: rotateY(180deg); -} - -.event-card-front, -.event-card-back { - width: 100%; - height: 100%; - position: absolute; - backface-visibility: hidden; - background-color: black; - color: white; - border-radius: 10px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); - border: 2px solid #00c853; -} - -.event-card-front { - display: flex; - justify-content: center; - align-items: center; -} - -.event-card-back { - padding: 20px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - transform: rotateY(180deg); -} - -.event-card-back img { - width: 100%; - height: auto; - max-height: 150px; - opacity: 0.2; - object-fit: cover; - position: absolute; - top: 0; - left: 0; - z-index: 0; -} - -.event-card-back p { - z-index: 1; - position: relative; - color: white; - font-size: 1.2em; - text-align: center; -} - -.event-poster-container { - position: relative; - width: 280px; - padding-bottom: 75%; - display: flex; - justify-content: center; - align-items: center; -} - -.event-poster { - position: absolute; - top: -40px; - left: 0; - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 10px; -} - -.event-content-container { - text-align: center; -} - -.event-content-container h3 { - margin-bottom: 0; - font-size: 1.4em; -} - -.event-content-container p { - margin-top: 0px; - margin-bottom: 25px; - font-size: 1em; -} - -/* Updated button styles */ -.toggle-form-button, -.toggle-edit-button, -.submit-event-button, -.update-event-button, -.remove-event-button { - margin-top: 70px !important; - background-color: black; - color: yellow; - border: 2px solid yellow; - border-radius: 5px; - padding: 10px 20px; - font-size: 1em; - cursor: pointer; - transition: background-color 0.3s, color 0.3s; - margin: 10px; -} - -.toggle-form-button:hover, -.toggle-edit-button:hover, -.submit-event-button:hover, -.update-event-button:hover, -.remove-event-button:hover { - background-color: yellow; - color: black; -} - -form { - background-color: black; - color: white; - padding: 20px; - border-radius: 10px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); - border: 2px solid yellow; - width: 80%; - margin: 40px auto; -} - -form label { - color: yellow; - font-weight: bold; - display: block; - margin-bottom: 10px; -} - -form input, -form textarea, -form select { - width: 100%; - padding: 10px; - margin-bottom: 20px; - border: 2px solid yellow; - border-radius: 5px; - background-color: black !important; - color: white !important; - font-size: 1em; -} - -form input::placeholder, -form textarea::placeholder { - color: grey !important; - opacity: 1; -} - -form input:focus, -form textarea:focus, -form select:focus { - border-color: yellow; - outline: none; -} - -input[type="text"], -input[type="date"], -textarea { - background-color: black !important; - color: white !important; -} - -input[type="text"]::placeholder, -input[type="date"]::placeholder, -textarea::placeholder { - color: grey !important; - opacity: 1; -} - -form button { - margin-top: 20px; -} - -section { - margin-bottom: 40px; -} - -.upcoming-events, -.ongoing-events, -.past-events { - margin-top: 60px; -} diff --git a/app/(default)/myevents/page.module.css b/app/(default)/events/page.module.css similarity index 100% rename from app/(default)/myevents/page.module.css rename to app/(default)/events/page.module.css diff --git a/app/(default)/events/page.tsx b/app/(default)/events/page.tsx index 17f25ef..66f8f1d 100644 --- a/app/(default)/events/page.tsx +++ b/app/(default)/events/page.tsx @@ -1,396 +1,200 @@ "use client"; -import React, { useState, useEffect } from "react"; -import { db, auth, storage } from "@/Firebase"; -import { - collection, - addDoc, - getDocs, - orderBy, - query, - updateDoc, - doc, - deleteDoc, - getDoc, -} from "firebase/firestore"; -import { ref, uploadBytes, getDownloadURL } from "firebase/storage"; +import { useState, useEffect } from "react"; +import { auth } from "../../../Firebase"; import { onAuthStateChanged } from "firebase/auth"; -import "./EventCard.css"; -import Image from "next/image"; +import EventForm from "../../../components/EventForm"; +import EventUpdateForm from "../../../components/EventUpdateForm"; +import EventCard from "../../../components/EventCard"; +import Sidebar from "../../../components/Sidebar"; + + +const EventsPage = () => { + const [showForm, setShowForm] = useState(false); + const [isAdminLoggedIn, setIsAdminLoggedIn] = useState(false); + const [events, setEvents] = useState< + { + id: string; + eventName: string; + description: string; + eventDate: string; + lastDateOfRegistration: string; + dateCreated: string; + dateModified: string; + imageURL: string; + registrationLink: string; + }[] + >([]); + + const [selectedEvent, setSelectedEvent] = useState<{ + id: string; + eventName: string; + description: string; + eventDate: string; + lastDateOfRegistration: string; + dateCreated: string; + dateModified: string; + imageURL: string; + registrationLink: string; + } | null>(null); + + const [isSidebarOpen, setIsSidebarOpen] = useState(false); -interface Event { - eventName: string; - description: string; - imageUrl: string; - startDate: string; - endDate: string; - id?: string; -} - -const EventCard: React.FC = () => { - const [events, setEvents] = useState([]); - const [eventName, setEventName] = useState(""); - const [description, setDescription] = useState(""); - const [imageUrl, setImageUrl] = useState(""); - const [imageFile, setImageFile] = useState(null); - const [startDate, setStartDate] = useState(""); - const [endDate, setEndDate] = useState(""); - const [formVisible, setFormVisible] = useState(false); - const [editFormVisible, setEditFormVisible] = useState(false); - const [eventToEdit, setEventToEdit] = useState(""); - const [eventDataToEdit, setEventDataToEdit] = useState(null); - const [isAdmin, setIsAdmin] = useState(false); - - // Fetch admin status useEffect(() => { - const checkAdminStatus = async () => { - onAuthStateChanged(auth, async (user) => { - if (user) { - const uid = user.uid; - try { - const adminDocRef = doc(db, "admin", uid); - const adminDocSnap = await getDoc(adminDocRef); - if (adminDocSnap.exists()) { - setIsAdmin(true); - } - } catch (error) { - console.log("Error getting document:", error); + onAuthStateChanged(auth, async (user) => { + if (user) { + const uid = user.uid; + try { + const resp = await fetch(`/api/admin?uid=${uid}`); + const data = await resp.json(); + if (data.isAdmin) { + setIsAdminLoggedIn(true); } + } catch (error) { + console.log("Error getting document:", error); } - }); - }; - - checkAdminStatus(); - }, []); + } + }); + }); + + const fetchEvents = async () => { + const resp = await fetch("/api/events"); + const data = await resp.json(); + const eventsList = data.events; + setEvents(eventsList); + }; - // Fetch events from Firestore - useEffect(() => { - const fetchEvents = async () => { - const q = query(collection(db, "events"), orderBy("startDate")); - const querySnapshot = await getDocs(q); - const eventsList: Event[] = querySnapshot.docs.map((doc) => ({ - ...doc.data(), - id: doc.id, - })) as Event[]; - setEvents(eventsList); - }; + useEffect(() => { fetchEvents(); }, []); - const uploadImage = async (file: File) => { - const imageRef = ref(storage, `eventImages/${file.name}`); - await uploadBytes(imageRef, file); - return await getDownloadURL(imageRef); - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - try { - const imageUrl = imageFile ? await uploadImage(imageFile) : ""; - const newEvent: Event = { - eventName, - description, - imageUrl, - startDate, - endDate, - }; - - const docRef = await addDoc(collection(db, "events"), newEvent); - setEvents((prevEvents) => - [...prevEvents, { ...newEvent, id: docRef.id }].sort( - (a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime() - ) - ); - - resetForm(); - } catch (error) { - console.error("Error adding document: ", error); - } - }; - - const handleEditSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - if (!eventDataToEdit || !eventDataToEdit.id) return; - - try { - const updatedImageUrl = imageFile ? await uploadImage(imageFile) : eventDataToEdit.imageUrl; - const updatedEvent = { - eventName, - description, - imageUrl: updatedImageUrl, - startDate, - endDate, - }; - - await updateDoc(doc(db, "events", eventDataToEdit.id), updatedEvent); - setEvents((prevEvents) => - prevEvents - .map((event) => - event.id === eventDataToEdit.id ? { ...updatedEvent, id: event.id } : event - ) - .sort((a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime()) - ); - - setEditFormVisible(false); - resetForm(); - } catch (error) { - console.error("Error updating document: ", error); - } - }; - - const handleRemoveEvent = async () => { - if (!eventDataToEdit || !eventDataToEdit.id) return; - + // Deleting an event + const deleteEvent = async (eventId: string , event : any) => { + console.log("Deleting event ", event); + console.log("Deleting event with id: ", eventId); try { - await deleteDoc(doc(db, "events", eventDataToEdit.id)); - setEvents((prevEvents) => prevEvents.filter((event) => event.id !== eventDataToEdit.id)); - setEditFormVisible(false); + await fetch(`/api/events/?eventid=${eventId}`, { + method: "DELETE", + }); + setEvents((prevEvents) => prevEvents.filter((event) => event.id !== eventId)); } catch (error) { console.error("Error deleting document: ", error); } + fetchEvents(); // Refresh event list }; - const handleEventToEditChange = (eventName: string) => { - const selectedEvent = events.find((event) => event.eventName === eventName); - if (selectedEvent) { - setEventDataToEdit(selectedEvent); - setEventName(selectedEvent.eventName); - setDescription(selectedEvent.description); - setImageUrl(selectedEvent.imageUrl); - setStartDate(selectedEvent.startDate); - setEndDate(selectedEvent.endDate); - } else { - setEventDataToEdit(null); - } + const handleEventSelect = (event: { + id: string; + eventName: string; + description: string; + eventDate: string; + lastDateOfRegistration: string; + dateCreated: string; + dateModified: string; + imageURL: string; + registrationLink: string; // Ensure registrationLink is included + }) => { + setSelectedEvent(event); + setIsSidebarOpen(true); }; - const resetForm = () => { - setEventName(""); - setDescription(""); - setImageFile(null); - setImageUrl(""); - setStartDate(""); - setEndDate(""); - setFormVisible(false); + const handleSidebarClose = () => { + setIsSidebarOpen(false); }; - const currentDate = new Date(); - const upcomingEvents = events.filter((event) => new Date(event.startDate) > currentDate); - const ongoingEvents = events.filter( - (event) => new Date(event.startDate) <= currentDate && new Date(event.endDate) >= currentDate - ); - const pastEvents = events.filter((event) => new Date(event.endDate) < currentDate); - - const renderEventCards = (events: Event[]) => { - if (!events || events.length === 0) return

No events to display

; + // Dividing events into Past, Present, and Future based on eventDate + const today = new Date(); - return ( -
- {events.map((event) => ( -
-
-
-
-
- {event.eventName} -
-
-

{event.eventName}

-

- {event.startDate} {event.endDate && ` - ${event.endDate}`} -

-
-
-
-
- {event.eventName -

{event.description || "No description available"}

-
-
-
- ))} -
- ); - }; + const pastEvents = events.filter( + (event) => new Date(event.eventDate) < today + ); + const presentEvents = events.filter( + (event) => new Date(event.eventDate).toDateString() === today.toDateString() + ); + const futureEvents = events.filter( + (event) => new Date(event.eventDate) > today + ); return ( -
- {isAdmin && ( - <> +
+

Events

+
+ {isAdminLoggedIn && ( + )} +
- {formVisible && ( -
- {/* Form fields */} -
- - setEventName(e.target.value)} - required - /> -
-
- -