From 287759757a2b22a4defaf87da4090910c2b95cb6 Mon Sep 17 00:00:00 2001 From: wkim10 Date: Sun, 27 Apr 2025 02:11:43 -0400 Subject: [PATCH 1/4] small fixes --- prisma/schema.prisma | 12 - src/app/api/event/route.client.ts | 53 ----- src/app/api/event/route.ts | 142 ------------ src/app/api/user/route.ts | 64 +++++- src/app/private/events/page.tsx | 356 ++++++++++++++++-------------- src/app/private/page.tsx | 2 +- src/components/Calendar.tsx | 3 + src/components/CheckInOut.tsx | 7 + src/components/SignUp.tsx | 15 +- 9 files changed, 266 insertions(+), 388 deletions(-) delete mode 100644 src/app/api/event/route.client.ts delete mode 100644 src/app/api/event/route.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e89796c..f66deb5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -35,8 +35,6 @@ model User { organizationId String? @db.ObjectId organization Organization? @relation(fields: [organizationId], references: [id]) code Code? - eventIds String[] @db.ObjectId - events Event[] @relation(fields: [eventIds], references: [id]) timeSlots TimeSlot[] volunteerSessions VolunteerSession[] } @@ -82,16 +80,6 @@ model Code { userId String @unique @db.ObjectId } -model Event { - id String @id @default(auto()) @map("_id") @db.ObjectId - userIds String[] @db.ObjectId - users User[] @relation(fields: [userIds], references: [id]) - eventName String @default("") - dateTime DateTime @default(now()) - description String @default("") - maxPeople Int @default(0) -} - model Organization { id String @id @default(auto()) @map("_id") @db.ObjectId name String @unique diff --git a/src/app/api/event/route.client.ts b/src/app/api/event/route.client.ts deleted file mode 100644 index 323f119..0000000 --- a/src/app/api/event/route.client.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Event } from "@prisma/client"; - -type CreateEventInput = Pick< - Event, - "eventName" | "description" | "maxPeople" | "dateTime" ->; - -export const fetchApi = async ( - endpoint: string, - method: "POST" | "GET" | "DELETE" | "PATCH", - body?: Record -) => { - const response = await fetch(endpoint, { - method, - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(body), - }); - - const responseData = await response.json(); - - if (!response.ok) { - throw new Error( - JSON.stringify({ - code: responseData.code, - message: responseData.message, - }) - ); - } - - return responseData; -}; - -export const addEvent = async (event: CreateEventInput) => { - fetchApi("/api/event", "POST", { event }); -}; - -export const getEvent = async (eventID: string) => { - const url = `/api/event?id=${eventID}`; - return fetchApi(url, "GET"); -}; - -export const getAllEvents = async () => { - return fetchApi("/api/event", "GET"); -}; - -export const updateEvent = async (event: Event) => { - return fetchApi("/api/event", "PATCH", { event }); -}; - -export const deleteEvent = async (eventID: string) => { - const url = `/api/user?id=${eventID}`; - return fetchApi(url, "DELETE"); -}; diff --git a/src/app/api/event/route.ts b/src/app/api/event/route.ts deleted file mode 100644 index e91f9cc..0000000 --- a/src/app/api/event/route.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import { NextRequest, NextResponse } from "next/server"; - -const prisma = new PrismaClient(); - -export const POST = async (request: NextRequest) => { - try { - /* @TODO: Add auth */ - - const { event } = await request.json(); - - const savedEvent = await prisma.event.create({ - data: event, - }); - - return NextResponse.json( - { - code: "SUCCESS", - message: savedEvent.eventName, - }, - { status: 201 } - ); - } catch (error) { - console.error("Error:", error); - return NextResponse.json( - { - code: "ERROR", - message: error, - }, - { status: 500 } - ); - } -}; - -export const PATCH = async (request: NextRequest) => { - try { - const { event } = await request.json(); - - const updatedEvent = await prisma.event.update({ - where: { - id: event.id, - }, - data: { - ...event, - id: undefined, - }, - }); - return NextResponse.json( - { - code: "SUCCESS", - message: updatedEvent.eventName, - }, - { status: 200 } - ); - } catch (error) { - console.error("Error:", error); - return NextResponse.json( - { - code: "ERROR", - message: error, - }, - { status: 500 } - ); - } -}; - -export const DELETE = async (request: NextRequest) => { - const { searchParams } = new URL(request.url); - const id = searchParams.get("id"); - - // Check if id is null - if (!id) { - return NextResponse.json( - { - code: "BAD_REQUEST", - message: "Event ID is required.", - }, - { status: 400 } - ); - } - - try { - const deletedEvent = await prisma.event.delete({ - where: { id }, - }); - - return NextResponse.json( - { - code: "SUCCESS", - message: "Event deleted successfully", - data: deletedEvent, - }, - { status: 200 } - ); - } catch (error) { - console.error("Error:", error); - return NextResponse.json( - { - code: "ERROR", - message: error, - }, - { status: 500 } - ); - } -}; - -export const GET = async (request: NextRequest) => { - const { searchParams } = new URL(request.url); - const eventId = searchParams.get("id"); - - try { - if (eventId) { - const fetchedEvent = await prisma.event.findUnique({ - where: { id: eventId }, - }); - - if (!fetchedEvent) { - return NextResponse.json( - { code: "NOT_FOUND", message: "No event found" }, - { status: 404 } - ); - } - - return NextResponse.json( - { code: "SUCCESS", data: fetchedEvent }, - { status: 200 } - ); - } else { - const fetchedEvents = await prisma.event.findMany(); - return NextResponse.json( - { code: "SUCCESS", data: fetchedEvents }, - { status: 200 } - ); - } - } catch (error) { - console.error("Error fetching events:", error); - return NextResponse.json( - { code: "ERROR", message: "An error occurred while fetching events" }, - { status: 500 } - ); - } -}; diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts index 6cb692a..b14afbb 100644 --- a/src/app/api/user/route.ts +++ b/src/app/api/user/route.ts @@ -96,23 +96,69 @@ export const DELETE = async (request: NextRequest) => { } try { - await prisma.code.delete({ - where: { userId: id }, - }); + await prisma.$transaction(async (tx) => { + // Delete TimeSlots where organizationId is null + await tx.timeSlot.deleteMany({ + where: { + userId: id, + organizationId: null, + }, + }); - await prisma.volunteerDetails.delete({ - where: { userId: id }, - }); + // Delete VolunteerSessions where organizationId is null + await tx.volunteerSession.deleteMany({ + where: { + userId: id, + organizationId: null, + }, + }); - const deletedUser = await prisma.user.delete({ - where: { id }, + // For TimeSlots with organizationId, nullify userId + await tx.timeSlot.updateMany({ + where: { + userId: id, + NOT: { + organizationId: null, + }, + }, + data: { + userId: undefined, // nullify userId + }, + }); + + // For VolunteerSessions with organizationId, nullify userId + await tx.volunteerSession.updateMany({ + where: { + userId: id, + NOT: { + organizationId: null, + }, + }, + data: { + userId: undefined, // nullify userId + }, + }); + + // Delete related Code + await tx.code.deleteMany({ + where: { userId: id }, + }); + + // Delete related VolunteerDetails + await tx.volunteerDetails.deleteMany({ + where: { userId: id }, + }); + + // Delete the User + await tx.user.delete({ + where: { id }, + }); }); return NextResponse.json( { code: "SUCCESS", message: "User deleted successfully", - data: deletedUser, }, { status: 200 } ); diff --git a/src/app/private/events/page.tsx b/src/app/private/events/page.tsx index 3a2102e..55f0d53 100644 --- a/src/app/private/events/page.tsx +++ b/src/app/private/events/page.tsx @@ -25,7 +25,7 @@ export default function EventsPage() { const date = searchParams.get("date"); const [selectedDate, setSelectedDate] = React.useState( - getStandardDate(date ?? "") + undefined ); const [timeSlots, setTimeSlots] = React.useState([ { start: "", end: "", submitted: false }, @@ -109,6 +109,15 @@ export default function EventsPage() { const hasSubmittedSlot = timeSlots.some((slot) => slot.submitted); + const isPastOrToday = (date?: Date) => { + if (!date) return false; + const today = new Date(); + today.setHours(0, 0, 0, 0); + const compareDate = new Date(date); + compareDate.setHours(0, 0, 0, 0); + return compareDate <= today; + }; + const isSameSlot = ( a: { start: string; end: string }, b: { start: string; end: string } @@ -177,6 +186,16 @@ export default function EventsPage() { } }; + React.useEffect(() => { + if (date !== null) { + setSelectedDate(getStandardDate(date)); + } else { + const today = new Date(); + today.setHours(0, 0, 0, 0); + setSelectedDate(today); + } + }, [date]); + React.useEffect(() => { const fetchTimeSlots = async () => { if (!session?.user.id || !selectedDate) return; @@ -280,7 +299,8 @@ export default function EventsPage() { setPage(0)} />
@@ -299,9 +319,11 @@ export default function EventsPage() {
{customDayTitle === "" - ? `Sign up for your volunteering time! We are open from${" "} + ? !isPastOrToday(selectedDate) + ? `Sign up for your volunteering time! We are open from${" "} ${formatTime(customDayHours.start)} -${" "} ${formatTime(customDayHours.end)}.` + : "Your Volunteer Hours" : customDayTitle}
{customDayDescription !== "" && ( @@ -319,155 +341,153 @@ export default function EventsPage() { />
{formattedDate - ? `Choose Your Time (${formattedDate})` + ? !isPastOrToday(selectedDate) + ? `Choose Your Time (${formattedDate})` + : formattedDate : "Choose Your Time"}
-
+
- {timeSlots.map((slot, index) => ( -
- {slot.submitted ? ( -
-
- -
- {formatTime(slot.start)} -{" "} - {formatTime(slot.end)} + {isPastOrToday(selectedDate) && + timeSlots.length === 1 ? ( +
No time slots!
+ ) : ( + timeSlots.map((slot, index) => ( +
+ {slot.submitted ? ( +
+
+ +
+ {formatTime(slot.start)} -{" "} + {formatTime(slot.end)} +
+ {!isPastOrToday(selectedDate) ? ( + { + const newSlots = [...timeSlots]; + newSlots.splice(index, 1); + setTimeSlots(newSlots); + }} + /> + ) : null}
- { - const newSlots = [...timeSlots]; - newSlots.splice(index, 1); - setTimeSlots(newSlots); - }} - /> -
- ) : ( -
-
- { - const newSlots = [...timeSlots]; - newSlots[index].start = e.target.value; - setTimeSlots(newSlots); - }} - error={Boolean( - (slot.start && - slot.end && - slot.start > slot.end) || + ) : !isPastOrToday(selectedDate) ? ( +
+
+ { + const newSlots = [...timeSlots]; + newSlots[index].start = + e.target.value; + setTimeSlots(newSlots); + }} + error={Boolean( (slot.start && slot.end && - doesOverlap( - slot.start, - slot.end, - index - )) || - isOutOfBounds(slot.start) - )} - slotProps={{ - inputLabel: { shrink: true }, - htmlInput: { - max: slot.end || "23:59", - }, - }} - /> - { - const newSlots = [...timeSlots]; - newSlots[index].end = e.target.value; - setTimeSlots(newSlots); - }} - error={Boolean( - (slot.start && - slot.end && - slot.end < slot.start) || + slot.start > slot.end) || + (slot.start && + slot.end && + doesOverlap( + slot.start, + slot.end, + index + )) || + isOutOfBounds(slot.start) + )} + slotProps={{ + inputLabel: { shrink: true }, + htmlInput: { + max: slot.end || "23:59", + }, + }} + /> + { + const newSlots = [...timeSlots]; + newSlots[index].end = e.target.value; + setTimeSlots(newSlots); + }} + error={Boolean( (slot.start && slot.end && - doesOverlap( - slot.start, - slot.end, - index - )) || - isOutOfBounds(slot.end) - )} - slotProps={{ - inputLabel: { shrink: true }, - htmlInput: { - min: slot.start || "00:00", - }, + slot.end < slot.start) || + (slot.start && + slot.end && + doesOverlap( + slot.start, + slot.end, + index + )) || + isOutOfBounds(slot.end) + )} + slotProps={{ + inputLabel: { shrink: true }, + htmlInput: { + min: slot.start || "00:00", + }, + }} + /> +
+
- -
- )} -
- ))} + ) : null} +
+ )) + )}
-
-
- -
Any comments?
-
-