diff --git a/booking-app/app/api/syncCalendars/route.ts b/booking-app/app/api/syncCalendars/route.ts new file mode 100644 index 00000000..ba488394 --- /dev/null +++ b/booking-app/app/api/syncCalendars/route.ts @@ -0,0 +1,175 @@ +import { toFirebaseTimestampFromString } from "@/components/src/client/utils/serverDate"; +import { TableNames } from "@/components/src/policy"; +import { Booking, MediaServices } from "@/components/src/types"; +import admin from "@/lib/firebase/server/firebaseAdmin"; +import { getCalendarClient } from "@/lib/googleClient"; +import { Timestamp } from "@firebase/firestore"; +import { NextResponse } from "next/server"; + +const db = admin.firestore(); +const createBookingWithDefaults = ( + partialBooking: Partial, +): Booking => { + return { + title: "", + description: "", + email: "", + firstName: "", + lastName: "", + secondaryName: "", + nNumber: "", + netId: "", + phoneNumber: "", + department: "", + role: "", + sponsorFirstName: "", + sponsorLastName: "", + sponsorEmail: "", + bookingType: "", + attendeeAffiliation: "", + roomSetup: "", + setupDetails: "", + mediaServices: "", + mediaServicesDetails: "", + catering: "", + hireSecurity: "", + expectedAttendance: "", + cateringService: "", + chartFieldForCatering: "", + chartFieldForSecurity: "", + chartFieldForRoomSetup: "", + calendarEventId: "", + roomId: "", + requestNumber: 0, + equipmentCheckedOut: false, + startDate: null, + endDate: null, + ...partialBooking, + }; +}; +const findNyuEmail = (event: any): string => { + const attendees = event.attendees || []; + const nyuEmail = attendees.find( + (attendee: any) => attendee.email && attendee.email.endsWith("@nyu.edu"), + ); + return nyuEmail ? nyuEmail.email : ""; +}; +export async function POST(request: Request) { + try { + const calendar = await getCalendarClient(); + // Fetch all calendar IDs from the Resource table + const resourcesSnapshot = await db.collection("resources").get(); + const resources = resourcesSnapshot.docs.map(doc => ({ + id: doc.id, + calendarId: doc.data().calendarId, + roomId: doc.data().roomId, + })); + + let totalNewBookings = 0; + let targetBookings = 0; + for (const resource of resources) { + try { + // Fetch events for each calendar + let pageToken: string | undefined; + const now = new Date(); + const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth(), 1); + const threeMonthsLater = new Date( + now.getFullYear(), + now.getMonth() + 4, + 0, + ); + const timeMin = threeMonthsAgo.toISOString(); + const timeMax = threeMonthsLater.toISOString(); + const events = await calendar.events.list({ + calendarId: resource.calendarId, + timeMin: timeMin, + timeMax: timeMax, + maxResults: 500, // Maximum allowed by Google Calendar API + singleEvents: true, + orderBy: "startTime", + pageToken: pageToken, + }); + + for (const event of events.data.items || []) { + const bookingRef = db + .collection("bookings") + .where("calendarEventId", "==", event.id); + const bookingSnapshot = await bookingRef.get(); + const nyuEmail = findNyuEmail(event); + if (bookingSnapshot.empty && nyuEmail) { + targetBookings++; + console.log("calendarEventId", event.id); + console.log("title", event.summary); + } + + if (bookingSnapshot.empty && nyuEmail) { + // Create a new booking + const calendarEventId = event.id; + const newBooking = createBookingWithDefaults({ + title: event.summary || "", + description: event.description || "", + email: nyuEmail || "", + startDate: toFirebaseTimestampFromString( + event.start?.dateTime, + ) as Timestamp, + endDate: toFirebaseTimestampFromString( + event.end?.dateTime, + ) as Timestamp, + calendarEventId: calendarEventId || "", + equipmentCheckedOut: true, + roomId: resource.roomId, + mediaServices: MediaServices.CHECKOUT_EQUIPMENT, + }); + console.log("newBooking", newBooking); + const bookingDocRef = await db + .collection(TableNames.BOOKING) + .add(newBooking); + + console.log(`New Booking created with ID: ${bookingDocRef.id}`); + + const newBookingStatus = { + calendarEventId: calendarEventId, + email: nyuEmail, + requestedAt: admin.firestore.FieldValue.serverTimestamp(), + firstApprovedAt: admin.firestore.FieldValue.serverTimestamp(), + finalApprovedAt: admin.firestore.FieldValue.serverTimestamp(), + }; + console.log("newBookingStatus", newBookingStatus); + const statusDocRef = await db + .collection(TableNames.BOOKING_STATUS) + .add(newBookingStatus); + console.log( + `New BookingStatus created with ID: ${statusDocRef.id}`, + ); + + totalNewBookings++; + } + pageToken = events.data.nextPageToken; + } + while (pageToken); + console.log("targetBookings", targetBookings); + } catch (error) { + console.error( + `Error processing calendar ${resource.calendarId}:`, + error, + ); + // Continue with the next calendar + } + } + + return NextResponse.json( + { + message: `${totalNewBookings} new bookings have been synchronized.`, + }, + { status: 200 }, + ); + } catch (error) { + console.error("Error syncing calendars:", error); + return NextResponse.json( + { + error: "An error occurred while syncing calendars.", + }, + { status: 500 }, + ); + } +} diff --git a/booking-app/app/book/confirmation/page.tsx b/booking-app/app/book/confirmation/page.tsx index da2bd04e..8bca06a6 100644 --- a/booking-app/app/book/confirmation/page.tsx +++ b/booking-app/app/book/confirmation/page.tsx @@ -1,7 +1,10 @@ // app/book/confirmation/page.tsx import BookingFormConfirmationPage from "@/components/src/client/routes/booking/formPages/BookingFormConfirmationPage"; +import { FormContextLevel } from "@/components/src/types"; import React from "react"; -const Role: React.FC = () => ; +const Role: React.FC = () => ( + +); export default Role; diff --git a/booking-app/app/liaison/page.tsx b/booking-app/app/liaison/page.tsx new file mode 100644 index 00000000..59799d7f --- /dev/null +++ b/booking-app/app/liaison/page.tsx @@ -0,0 +1,9 @@ +// app/liaison/page.tsx + +"use client"; + +import Liaison from "@/components/src/client/routes/liaison/Liaison"; + +const LiaisonPage: React.FC = () => ; + +export default LiaisonPage; diff --git a/booking-app/app/walk-in/confirmation/page.tsx b/booking-app/app/walk-in/confirmation/page.tsx new file mode 100644 index 00000000..b0e9127f --- /dev/null +++ b/booking-app/app/walk-in/confirmation/page.tsx @@ -0,0 +1,10 @@ +// app/walk-in/confirmation/page.tsx +import BookingFormConfirmationPage from "@/components/src/client/routes/booking/formPages/BookingFormConfirmationPage"; +import { FormContextLevel } from "@/components/src/types"; +import React from "react"; + +const Role: React.FC = () => ( + +); + +export default Role; diff --git a/booking-app/components/src/client/routes/admin/components/BookingActions.tsx b/booking-app/components/src/client/routes/admin/components/BookingActions.tsx index 3a6a8a7a..10437974 100644 --- a/booking-app/components/src/client/routes/admin/components/BookingActions.tsx +++ b/booking-app/components/src/client/routes/admin/components/BookingActions.tsx @@ -204,10 +204,15 @@ export default function BookingActions({ options.push(Actions.MODIFICATION); } else if (status === BookingStatusLabel.NO_SHOW) { options.push(Actions.CHECK_IN); + } else if (status === BookingStatusLabel.WALK_IN) { + options.push(Actions.CHECK_OUT); + options.push(Actions.MODIFICATION); } return options; }, [status]); + const liaisonOptions = [Actions.FIRST_APPROVE, Actions.DECLINE]; + const adminOptions = useMemo(() => { if ( status === BookingStatusLabel.CANCELED || @@ -236,6 +241,8 @@ export default function BookingActions({ return userOptions; case PageContextLevel.PA: return paOptions; + case PageContextLevel.LIAISON: + return liaisonOptions; default: return adminOptions; } diff --git a/booking-app/components/src/client/routes/admin/components/Settings.tsx b/booking-app/components/src/client/routes/admin/components/Settings.tsx index 4add3488..ce20c5d4 100644 --- a/booking-app/components/src/client/routes/admin/components/Settings.tsx +++ b/booking-app/components/src/client/routes/admin/components/Settings.tsx @@ -11,6 +11,7 @@ import Grid from "@mui/material/Unstable_Grid2"; import { Liaisons } from "./Liaisons"; import { PAUsers } from "./PAUsers"; import SafetyTrainedUsers from "./SafetyTraining"; +import SyncCalendars from "./SyncCalendars"; const tabs = [ { label: "Safety Training", id: "safetyTraining" }, @@ -22,6 +23,7 @@ const tabs = [ { label: "Booking Types", id: "bookingTypes" }, { label: "Policy Settings", id: "policy" }, { label: "Export", id: "export" }, + { label: "Sync Calendars", id: "syncCalendars" }, ]; export default function Settings() { @@ -33,10 +35,13 @@ export default function Settings() { divider={} sx={{ border: "1px solid #21212114", borderRadius: "4px" }} > - {tabs.map((tab) => ( -
- setTab(tab.id)}> - + {tabs.map((currentTab) => ( +
+ setTab(currentTab.id)} + selected={tab === currentTab.id} + > +
))} @@ -52,6 +57,7 @@ export default function Settings() { {tab === "bookingTypes" && } {tab === "policy" && } {tab === "export" && } + {tab === "syncCalendars" && } ); diff --git a/booking-app/components/src/client/routes/admin/components/SyncCalendars.tsx b/booking-app/components/src/client/routes/admin/components/SyncCalendars.tsx new file mode 100644 index 00000000..6f8d6def --- /dev/null +++ b/booking-app/components/src/client/routes/admin/components/SyncCalendars.tsx @@ -0,0 +1,60 @@ +import { Box, Button, Typography } from "@mui/material"; +import React, { useState } from "react"; +import AlertToast from "../../components/AlertToast"; + +const SyncCalendars = () => { + const [loading, setLoading] = useState(false); + const [showAlert, setShowAlert] = useState(false); + const [alertSeverity, setAlertSeverity] = useState<"error" | "success">( + "success" + ); + const [message, setMessage] = useState(""); + + const handleSync = async () => { + setLoading(true); + setShowAlert(false); + try { + const response = await fetch("/api/syncCalendars", { method: "POST" }); + const data = await response.json(); + if (response.ok) { + setMessage(`Sync successful: ${data.message}`); + setAlertSeverity("success"); + } else { + setMessage(`Error: ${data.error}`); + setAlertSeverity("error"); + } + } catch (error) { + setMessage("An error occurred while syncing calendars."); + setAlertSeverity("error"); + } finally { + setLoading(false); + setShowAlert(true); + } + }; + + return ( + + + {" "} + Sync Current Semester Calendar Events + +

+ This function saves existing events from the current semester's calendar + to the database. +

+ + + + setShowAlert(false)} + /> +
+ ); +}; + +export default SyncCalendars; diff --git a/booking-app/components/src/client/routes/booking/components/BookingStatusBar.tsx b/booking-app/components/src/client/routes/booking/components/BookingStatusBar.tsx index 92e72998..1878d427 100644 --- a/booking-app/components/src/client/routes/booking/components/BookingStatusBar.tsx +++ b/booking-app/components/src/client/routes/booking/components/BookingStatusBar.tsx @@ -56,7 +56,14 @@ export default function BookingStatusBar({ formContext, ...props }: Props) { message: (

You have not taken safety training, which is required for at least - one of the rooms you have selected + one of the rooms you have selected.{" "} + + Sign up here +

), severity: "error", diff --git a/booking-app/components/src/client/routes/booking/components/FormInput.tsx b/booking-app/components/src/client/routes/booking/components/FormInput.tsx index 6e3c7415..8ccc59b9 100644 --- a/booking-app/components/src/client/routes/booking/components/FormInput.tsx +++ b/booking-app/components/src/client/routes/booking/components/FormInput.tsx @@ -186,8 +186,7 @@ export default function FormInput({ calendarEventId, formContext }: Props) { // setFormData(data); registerEvent(data, isAutoApproval, calendarEventId); - // // TODO confirmation page for walk-in - router.push("/book/confirmation"); + router.push(isWalkIn ? "/walk-in/confirmation" : "/book/confirmation"); }; const fullFormFields = ( diff --git a/booking-app/components/src/client/routes/booking/formPages/BookingFormConfirmationPage.tsx b/booking-app/components/src/client/routes/booking/formPages/BookingFormConfirmationPage.tsx index 2802332e..79d72f06 100644 --- a/booking-app/components/src/client/routes/booking/formPages/BookingFormConfirmationPage.tsx +++ b/booking-app/components/src/client/routes/booking/formPages/BookingFormConfirmationPage.tsx @@ -5,6 +5,7 @@ import { Error, Event } from "@mui/icons-material"; import React, { useContext } from "react"; import { BookingContext } from "../bookingProvider"; +import { FormContextLevel } from "@/components/src/types"; import Loading from "../../components/Loading"; import { styled } from "@mui/system"; import { useRouter } from "next/navigation"; @@ -18,11 +19,17 @@ const Centered = styled(Box)` height: 55vh; `; -export default function BookingFormConfirmationPage() { +interface Props { + formContext: FormContextLevel; +} + +export default function BookingFormConfirmationPage({ formContext }: Props) { const { submitting } = useContext(BookingContext); const router = useRouter(); const theme = useTheme(); + const isWalkIn = formContext === FormContextLevel.WALK_IN; + // don't submit form via useEffect here b/c it submits twice in development strict mode if (submitting === "submitting") { @@ -38,7 +45,9 @@ export default function BookingFormConfirmationPage() { > - Submitting your booking request... + + Submitting {isWalkIn ? "walk-in" : "your booking"} request... + ); } @@ -59,7 +68,9 @@ export default function BookingFormConfirmationPage() { - Yay! We've received your booking request + {isWalkIn + ? "Walk-in submitted" + : "Yay! We've received your booking request"} @@ -99,7 +110,7 @@ export default function BookingFormConfirmationPage() { - Sorry, an error occured while submitting your booking + Sorry, an error occured while submitting this request ); diff --git a/booking-app/components/src/client/routes/components/EmailListTable.tsx b/booking-app/components/src/client/routes/components/EmailListTable.tsx index 3b928d89..32476dd7 100644 --- a/booking-app/components/src/client/routes/components/EmailListTable.tsx +++ b/booking-app/components/src/client/routes/components/EmailListTable.tsx @@ -1,8 +1,8 @@ -import React, { useMemo } from 'react'; +import React, { useMemo } from "react"; -import AddEmail from './AddEmail'; -import ListTable from './ListTable'; -import { TableNames } from '../../../policy'; +import AddEmail from "./AddEmail"; +import ListTable from "./ListTable"; +import { TableNames } from "../../../policy"; interface EmailField { email: string; @@ -18,6 +18,7 @@ interface Props { export default function EmailListTable(props: Props) { const addEmail = useMemo(() => , [props]); + props.userList.sort((a, b) => a.email.localeCompare(b.email)); return ( (() => { if (!userEmail) return PagePermission.BOOKING; - if (adminUsers.map((admin) => admin.email).includes(userEmail)) return PagePermission.ADMIN; - else if (paUsers.map((pa) => pa.email).includes(userEmail)) + if (liaisonUsers.map((liaison) => liaison.email).includes(userEmail)) { + return PagePermission.LIAISON; + } else if (paUsers.map((pa) => pa.email).includes(userEmail)) return PagePermission.PA; else return PagePermission.BOOKING; }, [userEmail, adminUsers, paUsers]); diff --git a/booking-app/components/src/client/routes/components/bookingTable/BookingTableRow.tsx b/booking-app/components/src/client/routes/components/bookingTable/BookingTableRow.tsx index b0183fd0..188c9adf 100644 --- a/booking-app/components/src/client/routes/components/bookingTable/BookingTableRow.tsx +++ b/booking-app/components/src/client/routes/components/bookingTable/BookingTableRow.tsx @@ -50,7 +50,7 @@ export default function BookingTableRow({ {booking.requestNumber ?? "--"} - + = ({ pageContext }) => { bookings, bookingsLoading, bookingStatuses, + liaisonUsers, userEmail, reloadBookings, reloadBookingStatuses, @@ -73,15 +74,16 @@ export const Bookings: React.FC = ({ pageContext }) => { }, []); const allowedStatuses: BookingStatusLabel[] = useMemo(() => { - const paViewStatuses = [ - BookingStatusLabel.APPROVED, - BookingStatusLabel.CHECKED_IN, - BookingStatusLabel.CHECKED_OUT, - BookingStatusLabel.NO_SHOW, - BookingStatusLabel.WALK_IN, - ]; if (pageContext === PageContextLevel.PA) { - return paViewStatuses; + return [ + BookingStatusLabel.APPROVED, + BookingStatusLabel.CHECKED_IN, + BookingStatusLabel.CHECKED_OUT, + BookingStatusLabel.NO_SHOW, + BookingStatusLabel.WALK_IN, + ]; + } else if (pageContext === PageContextLevel.LIAISON) { + return [BookingStatusLabel.REQUESTED]; } else { return Object.values(BookingStatusLabel); } @@ -106,12 +108,24 @@ export const Bookings: React.FC = ({ pageContext }) => { ); // filter based on user view - if (pageContext === PageContextLevel.USER) + if (pageContext === PageContextLevel.USER) { filtered = rows.filter((row) => row.email === userEmail); - else if (pageContext === PageContextLevel.PA) { - filtered = rows.filter((row) => allowedStatuses.includes(row.status)); + } else if (pageContext === PageContextLevel.LIAISON) { + const liaisonMatches = liaisonUsers.filter( + (user) => user.email === userEmail + ); + if (liaisonMatches.length > 0) { + const liaisonDepartments = liaisonMatches.map( + (user) => user.department + ); + filtered = rows.filter((row) => + liaisonDepartments.includes(row.department) + ); + } } + filtered = filtered.filter((row) => allowedStatuses.includes(row.status)); + if (pageContext >= PageContextLevel.PA) { // PA and Admin // filter by selected PA date range @@ -154,6 +168,22 @@ export const Bookings: React.FC = ({ pageContext }) => { ); } + + if (pageContext === PageContextLevel.LIAISON) { + return ( + + Department Requests + + ); + } + return ( { if (disabled) { return "rgba(0, 0, 0, 0.38)"; @@ -100,7 +105,32 @@ export default function StatusChip({ status, disabled = false }: Props) { } }, [status]); - return ( + const tooltipText = useMemo(() => { + switch (status) { + case BookingStatusLabel.APPROVED: + return "Your request has been fully approved!"; + case BookingStatusLabel.CANCELED: + return "You have canceled your request"; + case BookingStatusLabel.CHECKED_IN: + return "Your reservation has begun, thank you for checking in at the front desk"; + case BookingStatusLabel.CHECKED_OUT: + return "Your reservation has ended"; + case BookingStatusLabel.NO_SHOW: + return "You did not show up in time for your reservation. Your booking has been forfeited"; + case BookingStatusLabel.PENDING: + return "Your request has been partially approved, still pending final approval"; + case BookingStatusLabel.DECLINED: + return "Your request has been declined"; + case BookingStatusLabel.REQUESTED: + return "Your request has been received and is pending approval"; + case BookingStatusLabel.UNKNOWN: + return "Unable to determine the status of this request"; + case BookingStatusLabel.WALK_IN: + return "This request was booked as a walk-in"; + } + }, [status]); + + const chip = ( ); + + if (!allowTooltip) return chip; + return ( + + {chip} + + ); } diff --git a/booking-app/components/src/client/routes/components/navBar.tsx b/booking-app/components/src/client/routes/components/navBar.tsx index 32d5f084..d4ad284b 100644 --- a/booking-app/components/src/client/routes/components/navBar.tsx +++ b/booking-app/components/src/client/routes/components/navBar.tsx @@ -76,6 +76,9 @@ export default function NavBar() { case PagePermission.ADMIN: router.push("/admin"); break; + case PagePermission.LIAISON: + router.push("/liaison"); + break; } }; @@ -100,6 +103,8 @@ export default function NavBar() { setSelectedView(PagePermission.PA); } else if (pathname.includes("/admin")) { setSelectedView(PagePermission.ADMIN); + } else if (pathname.includes("/liaison")) { + setSelectedView(PagePermission.LIAISON); } }, [pathname]); @@ -117,22 +122,28 @@ export default function NavBar() { const dropdown = useMemo(() => { if ( pagePermission !== PagePermission.ADMIN && - pagePermission !== PagePermission.PA + pagePermission !== PagePermission.PA && + pagePermission !== PagePermission.LIAISON ) { return null; } + const showPA = + pagePermission === PagePermission.PA || + pagePermission === PagePermission.ADMIN; + const showLiaison = + pagePermission === PagePermission.LIAISON || + pagePermission === PagePermission.ADMIN; + const showAdmin = pagePermission === PagePermission.ADMIN; + return ( ); }, [pagePermission, selectedView]); diff --git a/booking-app/components/src/client/routes/liaison/Liaison.tsx b/booking-app/components/src/client/routes/liaison/Liaison.tsx new file mode 100644 index 00000000..e808e304 --- /dev/null +++ b/booking-app/components/src/client/routes/liaison/Liaison.tsx @@ -0,0 +1,50 @@ +import { Box, Tab, Tabs } from "@mui/material"; +import { PageContextLevel, PagePermission } from "../../../types"; +import React, { useContext, useMemo, useState } from "react"; + +import { Bookings } from "../components/bookingTable/Bookings"; +import { CenterLoading } from "../components/Loading"; +import { DatabaseContext } from "../components/Provider"; + +const Liaison = () => { + const { liaisonUsers, pagePermission, userEmail } = + useContext(DatabaseContext); + const [tab, setTab] = useState("bookings"); + + const liaisonEmails = useMemo( + () => liaisonUsers.map((user) => user.email), + [liaisonUsers] + ); + + const userHasPermission = + pagePermission === PagePermission.ADMIN || + pagePermission === PagePermission.LIAISON; + + if (liaisonEmails.length === 0 || userEmail === null) { + return ; + } + + return ( + + {!userHasPermission ? ( +
You do not have permission to view this page.
+ ) : ( +
+ setTab(newVal)} + textColor="primary" + indicatorColor="primary" + > + + + {tab === "bookings" && ( + + )} +
+ )} +
+ ); +}; + +export default Liaison; diff --git a/booking-app/components/src/types.ts b/booking-app/components/src/types.ts index ed195f43..77db896c 100644 --- a/booking-app/components/src/types.ts +++ b/booking-app/components/src/types.ts @@ -160,9 +160,10 @@ export type PaUser = { }; export enum PagePermission { - ADMIN = "Admin", - BOOKING = "User", - PA = "PA", + BOOKING = 0, + PA, + LIAISON, + ADMIN, } export enum PageContextLevel { diff --git a/booking-app/package-lock.json b/booking-app/package-lock.json index eaf44726..95842cf0 100644 --- a/booking-app/package-lock.json +++ b/booking-app/package-lock.json @@ -61,7 +61,6 @@ "eslint-plugin-prettier": "^5.2.1", "postcss": "^8", "prettier": "^3.3.3", - "prisma": "^5.16.1", "tailwindcss": "^3.4.1", "ts-node": "^10.9.2", "typescript": "^5.5.3" @@ -3672,14 +3671,16 @@ "version": "5.17.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.17.0.tgz", "integrity": "sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@prisma/engines": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.17.0.tgz", "integrity": "sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg==", - "devOptional": true, "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "@prisma/debug": "5.17.0", "@prisma/engines-version": "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053", @@ -3691,13 +3692,15 @@ "version": "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053.tgz", "integrity": "sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@prisma/fetch-engine": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.17.0.tgz", "integrity": "sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@prisma/debug": "5.17.0", "@prisma/engines-version": "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053", @@ -3708,7 +3711,8 @@ "version": "5.17.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.17.0.tgz", "integrity": "sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@prisma/debug": "5.17.0" } @@ -9352,8 +9356,9 @@ "version": "5.17.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.17.0.tgz", "integrity": "sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA==", - "devOptional": true, "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "@prisma/engines": "5.17.0" }, diff --git a/booking-app/package.json b/booking-app/package.json index b600f1f7..1d12beeb 100644 --- a/booking-app/package.json +++ b/booking-app/package.json @@ -64,7 +64,6 @@ "eslint-plugin-prettier": "^5.2.1", "postcss": "^8", "prettier": "^3.3.3", - "prisma": "^5.16.1", "tailwindcss": "^3.4.1", "ts-node": "^10.9.2", "typescript": "^5.5.3" diff --git a/booking-app/prisma/migrations/20240628194231_initial_migration/migration.sql b/booking-app/prisma/migrations/20240628194231_initial_migration/migration.sql deleted file mode 100644 index 0f821b20..00000000 --- a/booking-app/prisma/migrations/20240628194231_initial_migration/migration.sql +++ /dev/null @@ -1,140 +0,0 @@ --- CreateTable -CREATE TABLE `Room` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `roomId` INTEGER NOT NULL, - `name` VARCHAR(191) NOT NULL, - `capacity` VARCHAR(191) NOT NULL, - `calendarIdDev` VARCHAR(191) NOT NULL, - `calendarIdProd` VARCHAR(191) NOT NULL, - - UNIQUE INDEX `Room_roomId_key`(`roomId`), - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `Booking` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `roomId` INTEGER NOT NULL, - `email` VARCHAR(191) NOT NULL, - `startDate` DATETIME(3) NOT NULL, - `endDate` DATETIME(3) NOT NULL, - `firstName` VARCHAR(191) NOT NULL, - `lastName` VARCHAR(191) NOT NULL, - `secondaryName` VARCHAR(191) NOT NULL, - `nNumber` VARCHAR(191) NOT NULL, - `netId` VARCHAR(191) NOT NULL, - `phoneNumber` VARCHAR(191) NOT NULL, - `department` VARCHAR(191) NOT NULL, - `role` VARCHAR(191) NOT NULL, - `sponsorFirstName` VARCHAR(191) NOT NULL, - `sponsorLastName` VARCHAR(191) NOT NULL, - `sponsorEmail` VARCHAR(191) NOT NULL, - `title` VARCHAR(191) NOT NULL, - `description` VARCHAR(191) NOT NULL, - `reservationType` VARCHAR(191) NOT NULL, - `expectedAttendance` INTEGER NOT NULL, - `attendeeAffiliation` VARCHAR(191) NOT NULL, - `roomSetup` VARCHAR(191) NOT NULL, - `setupDetails` VARCHAR(191) NOT NULL, - `mediaServices` VARCHAR(191) NOT NULL, - `mediaServicesDetails` VARCHAR(191) NOT NULL, - `catering` VARCHAR(191) NOT NULL, - `cateringService` VARCHAR(191) NOT NULL, - `hireSecurity` VARCHAR(191) NOT NULL, - `chartFieldForCatering` VARCHAR(191) NOT NULL, - `chartFieldForSecurity` VARCHAR(191) NOT NULL, - `chartFieldForRoomSetup` VARCHAR(191) NOT NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `BookingStatus` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `email` VARCHAR(191) NOT NULL, - `requestedAt` DATETIME(3) NOT NULL, - `firstApprovedAt` DATETIME(3) NOT NULL, - `secondApprovedAt` DATETIME(3) NOT NULL, - `rejectedAt` DATETIME(3) NOT NULL, - `canceledAt` DATETIME(3) NOT NULL, - `checkedInAt` DATETIME(3) NOT NULL, - `noShowedAt` DATETIME(3) NOT NULL, - `finalApprovedAt` DATETIME(3) NOT NULL, - `bookingId` INTEGER NOT NULL, - - UNIQUE INDEX `BookingStatus_bookingId_key`(`bookingId`), - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `AdminUser` ( - `email` VARCHAR(191) NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - UNIQUE INDEX `AdminUser_email_key`(`email`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `PaUser` ( - `email` VARCHAR(191) NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - UNIQUE INDEX `PaUser_email_key`(`email`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `SafetyTrainingUser` ( - `email` VARCHAR(191) NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - UNIQUE INDEX `SafetyTrainingUser_email_key`(`email`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `BannedUser` ( - `email` VARCHAR(191) NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - UNIQUE INDEX `BannedUser_email_key`(`email`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `Liaison` ( - `email` VARCHAR(191) NOT NULL, - `departmentId` INTEGER NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - UNIQUE INDEX `Liaison_email_key`(`email`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `Department` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `name` VARCHAR(191) NOT NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `reservationType` ( - `name` VARCHAR(191) NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - UNIQUE INDEX `reservationType_name_key`(`name`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `Setting` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `key` VARCHAR(191) NOT NULL, - `value` VARCHAR(191) NOT NULL, - `createdAt` DATETIME(3) NOT NULL, - - PRIMARY KEY (`id`) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- AddForeignKey -ALTER TABLE `BookingStatus` ADD CONSTRAINT `BookingStatus_bookingId_fkey` FOREIGN KEY (`bookingId`) REFERENCES `Booking`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE `Liaison` ADD CONSTRAINT `Liaison_departmentId_fkey` FOREIGN KEY (`departmentId`) REFERENCES `Department`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/booking-app/prisma/migrations/20240628223926_initial_migration/migration.sql b/booking-app/prisma/migrations/20240628223926_initial_migration/migration.sql deleted file mode 100644 index 34c21aa7..00000000 --- a/booking-app/prisma/migrations/20240628223926_initial_migration/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* - Warnings: - - - You are about to alter the column `capacity` on the `Room` table. The data in that column could be lost. The data in that column will be cast from `VarChar(191)` to `Int`. - -*/ --- AlterTable -ALTER TABLE `Room` MODIFY `capacity` INTEGER NOT NULL; diff --git a/booking-app/prisma/migrations/migration_lock.toml b/booking-app/prisma/migrations/migration_lock.toml deleted file mode 100644 index e5a788a7..00000000 --- a/booking-app/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "mysql" \ No newline at end of file diff --git a/booking-app/prisma/schema.prisma b/booking-app/prisma/schema.prisma deleted file mode 100644 index f639f763..00000000 --- a/booking-app/prisma/schema.prisma +++ /dev/null @@ -1,119 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "mysql" - url = env("DATABASE_URL") - shadowDatabaseUrl = env("SHADOW_DATABASE_URL") -} - -model Room { - id Int @id @default(autoincrement()) - roomId Int @unique - name String - capacity Int - calendarIdDev String - calendarIdProd String -} - -model Booking { - id Int @id @default(autoincrement()) - roomId Int - email String - startDate DateTime - endDate DateTime - firstName String - lastName String - secondaryName String - nNumber String - netId String - phoneNumber String - department String - role String - sponsorFirstName String - sponsorLastName String - sponsorEmail String - title String - description String - reservationType String - expectedAttendance Int - attendeeAffiliation String - roomSetup String - setupDetails String - mediaServices String - mediaServicesDetails String - catering String - cateringService String - hireSecurity String - chartFieldForCatering String - chartFieldForSecurity String - chartFieldForRoomSetup String - bookingStatus BookingStatus? -} - -model BookingStatus { - id Int @id @default(autoincrement()) - email String - requestedAt DateTime - firstApprovedAt DateTime - secondApprovedAt DateTime - rejectedAt DateTime - canceledAt DateTime - checkedInAt DateTime - noShowedAt DateTime - finalApprovedAt DateTime - booking Booking @relation(fields: [bookingId], references: [id]) - bookingId Int @unique -} - -model AdminUser { - email String @unique - createdAt DateTime -} - -model PaUser { - email String @unique - createdAt DateTime -} - -model SafetyTrainingUser { - email String @unique - createdAt DateTime -} - -model BannedUser { - email String @unique - createdAt DateTime -} - -model Liaison { - email String @unique - department Department @relation(fields: [departmentId], references: [id]) - departmentId Int - createdAt DateTime -} - -model Department { - id Int @id @default(autoincrement()) - name String - liaisons Liaison[] -} - -model reservationType { - name String @unique - createdAt DateTime -} - -model Setting { - id Int @id @default(autoincrement()) - key String - value String - createdAt DateTime -} \ No newline at end of file diff --git a/booking-app/prisma/seed.mjs b/booking-app/prisma/seed.mjs deleted file mode 100644 index 0a5c1fac..00000000 --- a/booking-app/prisma/seed.mjs +++ /dev/null @@ -1,124 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -const prisma = new PrismaClient(); - -async function main() { - const rooms = [ - { - roomId: 103, - name: "The Garage", - capacity: 74, - calendarIdDev: - "c_96d0951fc59bf720396cb997a62564ef6f1f9d45ec5db6cded4a4bb95bfae02b@group.calendar.google.com", - calendarIdProd: "c_oea6k9fs8p6nvsai6f25uiue1c@group.calendar.google.com", - }, - { - roomId: 202, - name: "Lecture Hall", - capacity: 210, - calendarIdDev: - "c_cadf2be353a6162aab2c58b8b30ff75ea35b5f6c5163ed4fd57df71c00f03f6b@group.calendar.google.com", - calendarIdProd: - "nyu.edu_qii7e5htheep643hq8m5mu3ngg@group.calendar.google.com", - }, - { - roomId: 220, - name: "Black Box", - capacity: 30, - calendarIdDev: - "c_e8f6e893fa24c23f848d98b802902e92c52b665771542d8a89d2284456ad73f3@group.calendar.google.com", - calendarIdProd: "c_dbn41lc126ghnl8f98gop59kfg@group.calendar.google.com", - }, - { - roomId: 221, - name: "Ballroom A", - capacity: 12, - calendarIdDev: - "c_9d58b3e286524a0b044af7b9a92da0b266e4105039650b0bd557bdcd76d5d1db@group.calendar.google.com", - calendarIdProd: - "nyu.edu_iif5nf5n543lav8tgka3td9u2s@group.calendar.google.com", - }, - { - roomId: 222, - name: "Ballroom B", - capacity: 12, - calendarIdDev: - "c_4dc286f20a5855fcbf80f9c6476547e4fce3841e4a7c855034ed9df074b857d0@group.calendar.google.com", - calendarIdProd: - "nyu.edu_0s1704spc5sqd5ra0epiks6r0o@group.calendar.google.com", - }, - { - roomId: 223, - name: "Ballroom C", - capacity: 12, - calendarIdDev: - "c_4f55a0dbe715b86b87fd4bae480d794895982b096cd2df0dd449de7b27d31e61@group.calendar.google.com", - calendarIdProd: - "nyu.edu_pkci0d2mp33rhpied80126cot4@group.calendar.google.com", - }, - { - roomId: 224, - name: "Ballroom D", - capacity: 12, - calendarIdDev: - "c_dc7ff82c6f3b5217883fc7c512b97618cd16ac2564047f8a2f7d87dd142b7c63@group.calendar.google.com", - calendarIdProd: - "nyu.edu_u29cf5805g3qviu36tucn76ihk@group.calendar.google.com", - }, - { - roomId: 230, - name: "Audio Lab", - capacity: 13, - calendarIdDev: - "c_83eebf9ac5f6cf46d583b777e6805e548949a27aa88ce56775681b6aa139a16f@group.calendar.google.com", - calendarIdProd: "c_us27q4ttapcgdt9dc38lfsnn9s@group.calendar.google.com", - }, - { - roomId: 233, - name: "Co-Lab", - capacity: 50, - calendarIdDev: - "c_334de9eac5f1951dd22f53176d70b0752b192445eb5fcf0104f242d9383f90ee@group.calendar.google.com", - calendarIdProd: "c_s93e1dmilqo8ol6cd9ugh64bl4@group.calendar.google.com", - }, - { - roomId: 260, - name: "Post Production Lab", - capacity: 20, - calendarIdDev: - "c_ed4919e8a07e1338ed3df9c7d7bceaece5a466b8d669a8a70d83b30a8627089e@group.calendar.google.com", - calendarIdProd: "c_fdq6oqq965ge7k3gv6k2epsme0@group.calendar.google.com", - }, - { - roomId: 1201, - name: "Seminar Room", - capacity: 100, - calendarIdDev: - "c_f6f425226ac4db1d71237245f6b4cebc1631f59d55bae8a483465c47c24d3c48@group.calendar.google.com", - calendarIdProd: - "nyu.edu_aa15nlviffkp3tkt86ehe80n9o@group.calendar.google.com", - }, - ]; - - for (const room of rooms) { - const createdRoom = await prisma.room.create({ - data: { - roomId: room.roomId, - name: room.name, - capacity: room.capacity, - calendarIdDev: room.calendarIdDev, // Assuming default values for missing properties - calendarIdProd: room.calendarIdProd, // Assuming default values for missing properties - }, - }); - console.log({ createdRoom }); - } -} - -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - }); diff --git a/booking-app/tsconfig.json b/booking-app/tsconfig.json index 4f3520a3..77573062 100644 --- a/booking-app/tsconfig.json +++ b/booking-app/tsconfig.json @@ -29,8 +29,6 @@ "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", - "prisma/**/*.ts", - "prisma/seed.mjs", "scripts/getToken.js" ], "exclude": ["node_modules"]