From 038d6f4544d631bdc4b42b23399316e13e0e4e53 Mon Sep 17 00:00:00 2001
From: Aditya raj
Date: Thu, 27 Jun 2024 12:03:06 +0530
Subject: [PATCH] add calendar server actions, UI, home page
---
src/app/actions/EventActions.js | 167 +++++++++++++++++
src/app/actions/EventData.js | 42 +++++
src/app/dashboard/events/page.jsx | 35 +++-
src/app/page.jsx | 4 +-
src/components/Calendar.jsx | 90 ----------
src/components/Events/Calendar.jsx | 85 +++++++++
src/components/Events/create-modal.jsx | 157 ++++++++++++++++
src/components/Events/view-update-modal.jsx | 187 ++++++++++++++++++++
src/components/Home/OurProjects.jsx | 29 ---
src/components/Home/OurSchedule.jsx | 34 ++++
src/components/ui/DashboardHome.jsx | 11 +-
src/components/ui/EventDetailDialog.jsx | 35 ++++
src/components/ui/MonthCalendar.jsx | 78 ++++++++
src/models/events.js | 20 +++
14 files changed, 845 insertions(+), 129 deletions(-)
create mode 100644 src/app/actions/EventActions.js
create mode 100644 src/app/actions/EventData.js
delete mode 100644 src/components/Calendar.jsx
create mode 100644 src/components/Events/Calendar.jsx
create mode 100644 src/components/Events/create-modal.jsx
create mode 100644 src/components/Events/view-update-modal.jsx
create mode 100644 src/components/Home/OurSchedule.jsx
create mode 100644 src/components/ui/EventDetailDialog.jsx
create mode 100644 src/components/ui/MonthCalendar.jsx
create mode 100644 src/models/events.js
diff --git a/src/app/actions/EventActions.js b/src/app/actions/EventActions.js
new file mode 100644
index 0000000..cea9187
--- /dev/null
+++ b/src/app/actions/EventActions.js
@@ -0,0 +1,167 @@
+"use server";
+import { z } from "zod";
+import connectMongoDB from "@/lib/db";
+import Event from "@/models/events";
+import { revalidatePath } from "next/cache";
+import { redirect } from "next/navigation";
+import { auth } from "@/auth";
+
+// Define the schema for event validation
+const EventSchema = z.object({
+ date: z.number().min(1, "Date is required."),
+ title: z.string().min(1, "Title is required."),
+ venue: z.string().min(1, "Venue is required."),
+ time: z.string().min(1, "Time is required."),
+ about: z.string().min(1, "About is required."),
+});
+
+// Server action to add an event
+export async function addEvent(prevState, formData) {
+ const session = await auth();
+ const club = session?.user.email.split("@")[0];
+
+ const validatedFields = EventSchema.safeParse({
+ date: parseInt(formData.get("date")),
+ title: formData.get("title"),
+ venue: formData.get("venue"),
+ time: formData.get("time"), // Updated from timing to time
+ about: formData.get("about"),
+ });
+
+ // If form validation fails, return errors early. Otherwise, continue.
+ if (!validatedFields.success) {
+ return {
+ errors: validatedFields.error.flatten().fieldErrors,
+ message: "Missing or invalid fields. Failed to add event.",
+ };
+ }
+
+ // Extract validated data
+ const { date, title, venue, time, about } = validatedFields.data;
+ const eventObject = {
+ date,
+ title,
+ venue,
+ time,
+ about,
+ };
+
+ // Insert data into the database
+ try {
+ await connectMongoDB();
+ const event = await Event.findOne({ club });
+
+ if (event) {
+ event.events.push(eventObject);
+ await event.save();
+ } else {
+ await Event.create({
+ events: [eventObject],
+ club,
+ });
+ }
+ } catch (error) {
+ // If a database error occurs, return a more specific error.
+ return {
+ message: "Database Error: Failed to add event.",
+ };
+ }
+
+ // Revalidate the cache for the events page and redirect the user.
+ revalidatePath("/dashboard/events");
+}
+
+export async function updateEvent(prevState, formData) {
+ // Get the user's session and club
+ const session = await auth();
+ const club = session?.user.email.split("@")[0];
+
+ // Extract the event index and updated fields from form data
+
+ const validatedFields = EventSchema.safeParse({
+ date: parseInt(formData.get("date")),
+ title: formData.get("title"),
+ venue: formData.get("venue"),
+ time: formData.get("time"),
+ about: formData.get("about"),
+ });
+ if (!validatedFields.success) {
+ return {
+ errors: validatedFields.error.flatten().fieldErrors,
+ message: "Missing or invalid fields. Failed to add event.",
+ };
+ }
+
+ // Extract validated data
+ const { title, venue, time, about, date } = validatedFields.data;
+ const updatedEventObj = {
+ date,
+ title,
+ about,
+ venue,
+ time,
+ };
+
+ try {
+ await connectMongoDB(); // Connect to the database
+ // Find the event for the specified club
+ const event = await Event.findOne({ club });
+
+ const foundEventObjectIndex = event.events.findIndex(
+ (e) => e.date === date
+ );
+
+ event.events[foundEventObjectIndex] = updatedEventObj;
+
+ // Save the updated event
+ await event.save();
+ } catch (error) {
+ return {
+ message: "Database Error: Failed to update event.",
+ status: 500,
+ };
+ }
+
+ revalidatePath("/dashboard/events");
+}
+
+export async function deleteEventByDate(date) {
+ const session = await auth();
+ const club = session?.user.email.split("@")[0];
+
+ try {
+ await connectMongoDB();
+ const event = await Event.findOne({ club });
+
+ if (!event) {
+ return {
+ message: "Event not found for the club",
+ status: 404,
+ };
+ }
+
+ // Filter out the event at the specified index
+ const updatedEvents = event.events.filter((event) => event.date !== date);
+
+ // If the event to delete was not found
+ if (updatedEvents.length === event.events.length) {
+ return {
+ message: "Event not found",
+ status: 404,
+ };
+ }
+
+ event.events = updatedEvents;
+ await event.save();
+ } catch (error) {
+ console.error("Error deleting event:", error);
+ return {
+ message: "Database Error: Failed to delete event.",
+ status: 500,
+ };
+ }
+
+ // Revalidate the cache for the events page
+ revalidatePath("/dashboard/events");
+ redirect("/dashboard/events");
+}
diff --git a/src/app/actions/EventData.js b/src/app/actions/EventData.js
new file mode 100644
index 0000000..c91c6f8
--- /dev/null
+++ b/src/app/actions/EventData.js
@@ -0,0 +1,42 @@
+import Event from "@/models/events";
+import connectMongoDB from "@/lib/db";
+import { unstable_noStore as noStore } from "next/cache";
+
+export async function getEventsForClub(club) {
+ noStore(); // Ensure no caching is done
+ try {
+ await connectMongoDB();
+ const eventRecord = await Event.findOne({ club });
+
+ if (!eventRecord) {
+ return [];
+ }
+ return eventRecord.events;
+ } catch (error) {
+ return {
+ message: "Database Error: Failed to retrieve events.",
+ };
+ }
+}
+
+// Server action to get events for a club on a specific date
+export async function getEventsForClubAndDate(club, date) {
+ noStore(); // Ensure no caching is done
+ try {
+ await connectMongoDB();
+ const eventRecord = await Event.findOne({ club });
+
+ if (!eventRecord) {
+ return {};
+ }
+
+ const foundEventObject = eventRecord.events.findOne((e) => e.date === date);
+ console.log("founded", foundEventObject);
+
+ return foundEventObject;
+ } catch (error) {
+ return {
+ message: "Database Error: Failed to retrieve events.",
+ };
+ }
+}
diff --git a/src/app/dashboard/events/page.jsx b/src/app/dashboard/events/page.jsx
index bef1171..b092152 100644
--- a/src/app/dashboard/events/page.jsx
+++ b/src/app/dashboard/events/page.jsx
@@ -1,19 +1,38 @@
-import React from "react";
-import { MonthCalendar } from "@/components/Calendar";
+import { MonthCalendar } from "@/components/Events/Calendar";
+import { auth } from "@/auth";
+import { getEventsForClub } from "@/app/actions/EventData";
-const Page = () => {
+const Page = async () => {
const monthNames = [
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
];
const month = new Date().getMonth(); // Call the function to get the month
- const year= new Date().getFullYear()
+ const year = new Date().getFullYear();
const monthName = monthNames[month];
+ const session = await auth();
+ const club = session?.user.email.split("@")[0];
+ const events = await getEventsForClub(club);
+ // console.log(events)
return (
<>
- {monthName} {year} Calendar
-
+
+
+ {monthName} {year}
+
+
+
>
);
};
diff --git a/src/app/page.jsx b/src/app/page.jsx
index c86bb0f..4cb85d6 100644
--- a/src/app/page.jsx
+++ b/src/app/page.jsx
@@ -1,8 +1,9 @@
import Achievements from "@/components/Home/Achievements";
+import OurSchedule from "@/components/Home/OurSchedule";
import Gallery from "@/components/Home/Gallery";
import OurTeam from "@/components/Home/OurTeam";
import OurProjects from "@/components/Home/OurProjects";
-import GalleryCarousel from "@/components/Home/Carousel";
+// import GalleryCarousel from "@/components/Home/Carousel";
import Hero from "@/components/Home/Hero";
import OurBlogs from "@/components/Home/OurBlogs";
@@ -11,6 +12,7 @@ export default function Home() {
return (
<>
+
diff --git a/src/components/Calendar.jsx b/src/components/Calendar.jsx
deleted file mode 100644
index 77c0205..0000000
--- a/src/components/Calendar.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-'use client';
-import React, { useState } from 'react';
-import { DayPicker } from 'react-day-picker';
-import 'react-day-picker/dist/style.css';
-import {
- AlertDialog,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from '@/components/ui/alert-dialog';
-
-const events = [
- { date: 19, title: 'Title 19', about: 'About the event on 19th', venue: 'Venue 19' },
- { date: 25, title: 'Title 25', about: 'About the event on 25th', venue: 'Venue 25' },
- // Add more events as needed
-];
-
-/** Replace the 19th with an emoji */
-function CustomDayContent(props) {
- const event = events.find((event) => event.date === props.date.getDate());
- return (
-
- {event ? (
-
- {event.date}
-
- ) : (
- props.date.getDate()
- )}
-
- );
-}
-function CustomCaptionComponent(props) {
- return null;
-}
-
-export function MonthCalendar() {
- const [selectedEvent, setSelectedEvent] = useState(null);
- const [open, setOpen] = useState(false);
-
- const handleDayClick = (day) => {
- const dayNumber = day.getDate();
- const event = events.find((event) => event.date === dayNumber);
- if (event) {
- setSelectedEvent(event);
- setOpen(true);
- } else {
- setSelectedEvent(null);
- setOpen(false);
- }
- };
-
- return (
- <>
-
- {selectedEvent && (
-
- )}
- >
- );
-}
-
-export function AlertDialogDemo({ event, open, onOpenChange }) {
- return (
-
-
-
- {event.title}
-
- About: {event.about}
- Venue: {event.venue}
-
-
-
- Close
-
-
-
- );
-}
diff --git a/src/components/Events/Calendar.jsx b/src/components/Events/Calendar.jsx
new file mode 100644
index 0000000..2fe9253
--- /dev/null
+++ b/src/components/Events/Calendar.jsx
@@ -0,0 +1,85 @@
+"use client";
+import React, { useState, useEffect } from "react";
+import { DayPicker } from "react-day-picker";
+import "react-day-picker/dist/style.css";
+
+import { CreateEventModal } from "./create-modal";
+import { ViewUpdateEventModal } from "./view-update-modal";
+
+function CustomDayContent({ date, eventDates }) {
+ const isEventDay = eventDates.includes(date.getDate());
+ return (
+
+ {isEventDay ? (
+
+ {date.getDate()}
+
+ ) : (
+ date.getDate()
+ )}
+
+ );
+}
+
+function CustomCaptionComponent(props) {
+ return null;
+}
+
+export function MonthCalendar({ serializedEvents, club }) {
+ const [open, setOpen] = useState(false);
+ const [eventDaySelected, setEventDaySelected] = useState(false);
+ const [selectedDate, setSelectedDate] = useState(null);
+ const [selectedEvent, setSelectedEvent] = useState(null);
+ const events = JSON.parse(serializedEvents);
+ const eventDates = events.map((e) => e.date);
+ useEffect(() => {
+ setOpen(false);
+ }, [serializedEvents]);
+
+ const handleDayClick = (day) => {
+ const dayNumber = day.getDate();
+ setSelectedDate(dayNumber);
+ const isEventDay = eventDates.includes(dayNumber);
+
+ if (isEventDay) {
+ setEventDaySelected(true);
+ const eventObj = events.find((e) => e.date === dayNumber);
+ setSelectedEvent(eventObj);
+ } else {
+ setEventDaySelected(false);
+ setSelectedEvent(null);
+ }
+ setOpen(true);
+ };
+
+ return (
+ <>
+ (
+
+ ),
+ Caption: CustomCaptionComponent,
+ }}
+ className="text-xl"
+ />
+ {eventDaySelected && open && (
+
+ )}
+ {!eventDaySelected && open && (
+
+ )}
+ >
+ );
+}
diff --git a/src/components/Events/create-modal.jsx b/src/components/Events/create-modal.jsx
new file mode 100644
index 0000000..43befb6
--- /dev/null
+++ b/src/components/Events/create-modal.jsx
@@ -0,0 +1,157 @@
+"use client"
+import React from "react";
+import {
+ AlertDialog,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
+import { useFormState } from "react-dom";
+import { addEvent } from "@/app/actions/EventActions"; // Assume you have an action to create an event
+
+export function CreateEventModal({ Date, open, onOpenChange }) {
+ const initialState = { message: null, errors: {} };
+ const [state, dispatch] = useFormState(addEvent, initialState);
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ const formData = new FormData(event.target);
+ formData.set("date", Date); // Set the date in the form data
+ dispatch(formData);
+ };
+
+ return (
+
+
+
+ Create Event on {Date}
+
+
+
+
+
+ );
+}
diff --git a/src/components/Events/view-update-modal.jsx b/src/components/Events/view-update-modal.jsx
new file mode 100644
index 0000000..04ffa36
--- /dev/null
+++ b/src/components/Events/view-update-modal.jsx
@@ -0,0 +1,187 @@
+"use client";
+import React, { useState } from "react";
+import {
+ AlertDialog,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
+import { useFormState } from "react-dom";
+import { updateEvent, deleteEventByDate } from "@/app/actions/EventActions"; // Assume you have an action to create an event
+
+export function ViewUpdateEventModal({
+ Date,
+ open,
+ onOpenChange,
+ event,
+ club,
+}) {
+ const initialState = { message: null, errors: {} };
+ const [eventDetails, setEventDetails] = useState(event);
+ const [state, dispatch] = useFormState(updateEvent, initialState);
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ const formData = new FormData(event.target);
+ formData.set("date", Date); // Set the date in the form data
+ console.log("about to dispatch");
+ dispatch(formData);
+ };
+
+ const handleDelete = async () => {
+ try {
+ await deleteEventByDate(Date);
+ onOpenChange(false); // Close the modal after deletion
+ } catch (error) {
+ console.error("Failed to delete event:", error);
+ }
+ };
+
+ return (
+
+
+
+ Update Event on {Date}
+
+
+
+
+
+ );
+}
diff --git a/src/components/Home/OurProjects.jsx b/src/components/Home/OurProjects.jsx
index 9354e59..8a50028 100644
--- a/src/components/Home/OurProjects.jsx
+++ b/src/components/Home/OurProjects.jsx
@@ -2,35 +2,6 @@ import React from "react";
import ProjectCard from "../ui/ProjectCard";
import { getAllProjects } from "@/app/actions/ProjectData";
-// const projects = {
-// projects: [
-// {
-// title: "Project 1",
-// description:
-// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc felis ligula.",
-// image: "public/Home/person.jpg",
-// },
-// {
-// title: "Project 2",
-// description:
-// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc felis ligula.",
-// image: "public/Home/person.jpg",
-// },
-// {
-// title: "Project 3",
-// description:
-// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc felis ligula yayyy.",
-// image: "public/Home/person.jpg",
-// },
-// {
-// title: "Project 4",
-// description:
-// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc felis ligula.",
-// image: "public/Home/person.jpg",
-// },
-// ],
-// };
-
const OurProjects = async () => {
const projects=await getAllProjects(process.env.SUPER_ADMIN)
return (
diff --git a/src/components/Home/OurSchedule.jsx b/src/components/Home/OurSchedule.jsx
new file mode 100644
index 0000000..34fe701
--- /dev/null
+++ b/src/components/Home/OurSchedule.jsx
@@ -0,0 +1,34 @@
+import React from "react";
+import { getEventsForClub } from "@/app/actions/EventData";
+import Calendar from "../ui/MonthCalendar";
+
+const OurSchedule = async () => {
+ const fetchedEvents = await getEventsForClub(process.env.SUPER_ADMIN);
+ const monthNames = [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ ];
+ const month = new Date().getMonth(); // Call the function to get the month
+ const year = new Date().getFullYear();
+ const monthName = monthNames[month];
+ return (
+
+
+ {monthName} {year} Schedule
+
+
+
+ );
+};
+
+export default OurSchedule;
diff --git a/src/components/ui/DashboardHome.jsx b/src/components/ui/DashboardHome.jsx
index c12a8cb..3bb51b6 100644
--- a/src/components/ui/DashboardHome.jsx
+++ b/src/components/ui/DashboardHome.jsx
@@ -13,7 +13,8 @@ import {
IconArticle,
IconBrandGithub,
IconList,
- IconLogout
+ IconLogout,
+ IconCalendarEvent
} from "@tabler/icons-react";
export const DashboardHome = ({ isSuperAdmin }) => {
@@ -101,6 +102,14 @@ const AboutBlock = ({ isSuperAdmin }) => (
+
+
+ /Events{" "}
+
+ : Create, Update, delete your Events
+
+
+
{isSuperAdmin && (
diff --git a/src/components/ui/EventDetailDialog.jsx b/src/components/ui/EventDetailDialog.jsx
new file mode 100644
index 0000000..0461eab
--- /dev/null
+++ b/src/components/ui/EventDetailDialog.jsx
@@ -0,0 +1,35 @@
+"use client";
+import React, { useState } from "react";
+import {
+ AlertDialog,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
+
+export function EventDetailDialog({ Date, open, onOpenChange, event }) {
+ const [eventDetails, setEventDetails] = useState(event);
+
+ return (
+
+
+
+ {event.title}
+
+
About: {event.about}
+
Venue: {event.venue}
+
Time: {event.time}
+
+
+
+
+
+ Close
+
+
+
+
+ );
+}
diff --git a/src/components/ui/MonthCalendar.jsx b/src/components/ui/MonthCalendar.jsx
new file mode 100644
index 0000000..1006003
--- /dev/null
+++ b/src/components/ui/MonthCalendar.jsx
@@ -0,0 +1,78 @@
+"use client";
+import React, { useState, useEffect } from "react";
+import { DayPicker } from "react-day-picker";
+import "react-day-picker/dist/style.css";
+
+// import { CreateEventModal } from "./create-modal";
+import { EventDetailDialog } from "./EventDetailDialog";
+
+function CustomDayContent({ date, eventDates }) {
+ const isEventDay = eventDates.includes(date.getDate());
+ return (
+
+ {isEventDay ? (
+
+ {date.getDate()}
+
+ ) : (
+ date.getDate()
+ )}
+
+ );
+}
+
+function CustomCaptionComponent(props) {
+ return null;
+}
+
+export default function Calendar({ serializedEvents }) {
+ const [open, setOpen] = useState(false);
+ const [eventDaySelected, setEventDaySelected] = useState(false);
+ const [selectedDate, setSelectedDate] = useState(null);
+ const [selectedEvent, setSelectedEvent] = useState(null);
+ const events = JSON.parse(serializedEvents);
+ const eventDates = events.map((e) => e.date);
+ useEffect(() => {
+ setOpen(false);
+ }, [serializedEvents]);
+
+ const handleDayClick = (day) => {
+ const dayNumber = day.getDate();
+ setSelectedDate(dayNumber);
+ const isEventDay = eventDates.includes(dayNumber);
+
+ if (isEventDay) {
+ setEventDaySelected(true);
+ const eventObj = events.find((e) => e.date === dayNumber);
+ setSelectedEvent(eventObj);
+ } else {
+ setEventDaySelected(false);
+ setSelectedEvent(null);
+ }
+ setOpen(true);
+ };
+
+ return (
+ <>
+ (
+
+ ),
+ Caption: CustomCaptionComponent,
+ }}
+ className="text-lg sm:text-xl"
+ />
+ {eventDaySelected && open && (
+
+ )}
+
+ >
+ );
+}
diff --git a/src/models/events.js b/src/models/events.js
new file mode 100644
index 0000000..7ae2872
--- /dev/null
+++ b/src/models/events.js
@@ -0,0 +1,20 @@
+import mongoose, { Schema } from "mongoose";
+
+const eventSchema = new Schema(
+ {
+ events: [
+ {
+ date: Number,
+ title: String,
+ about: String,
+ venue: String,
+ time: String,
+ },
+ ],
+ club: String,
+ }
+);
+
+const Event = mongoose.models?.Event || mongoose.model("Event", eventSchema);
+
+export default Event;