From d7252a572212ac938c53011fc6af2921abfd84c5 Mon Sep 17 00:00:00 2001 From: "riho.takagi" Date: Fri, 27 Sep 2024 14:11:14 -0400 Subject: [PATCH 1/3] Remove prisma --- booking-app/package-lock.json | 19 ++- booking-app/package.json | 1 - .../migration.sql | 140 ------------------ .../migration.sql | 8 - .../prisma/migrations/migration_lock.toml | 3 - booking-app/prisma/schema.prisma | 119 --------------- booking-app/prisma/seed.mjs | 124 ---------------- booking-app/tsconfig.json | 2 - 8 files changed, 12 insertions(+), 404 deletions(-) delete mode 100644 booking-app/prisma/migrations/20240628194231_initial_migration/migration.sql delete mode 100644 booking-app/prisma/migrations/20240628223926_initial_migration/migration.sql delete mode 100644 booking-app/prisma/migrations/migration_lock.toml delete mode 100644 booking-app/prisma/schema.prisma delete mode 100644 booking-app/prisma/seed.mjs 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"] From 0dc53cf82d19a13d15409e752099038dd9571b6d Mon Sep 17 00:00:00 2001 From: "riho.takagi" Date: Fri, 27 Sep 2024 16:31:13 -0400 Subject: [PATCH 2/3] Sync pregame calendar events to database --- booking-app/app/api/syncCalendars/route.ts | 154 ++++++++++++++++++ .../routes/admin/components/Settings.tsx | 3 + .../routes/admin/components/SyncCalendars.tsx | 60 +++++++ 3 files changed, 217 insertions(+) create mode 100644 booking-app/app/api/syncCalendars/route.ts create mode 100644 booking-app/components/src/client/routes/admin/components/SyncCalendars.tsx diff --git a/booking-app/app/api/syncCalendars/route.ts b/booking-app/app/api/syncCalendars/route.ts new file mode 100644 index 00000000..c5a21c53 --- /dev/null +++ b/booking-app/app/api/syncCalendars/route.ts @@ -0,0 +1,154 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { Booking, MediaServices } from "@/components/src/types"; +import admin from "@/firebaseAdmin"; +import { getCalendarClient } from "@/lib/googleClient"; +import { toFirebaseTimestampFromString } from "@/components/src/client/utils/serverDate"; +import { Timestamp } from "@firebase/firestore"; +import { NextResponse } from "next/server"; +import { TableNames } from "@/components/src/policy"; + +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; + for (const resource of resources) { + try { + // Fetch events for each calendar + const now = new Date(); + const events = await calendar.events.list({ + calendarId: resource.calendarId, + timeMin: now.toISOString(), + maxResults: 100, // Adjust as needed + singleEvents: true, + orderBy: "startTime", + }); + + for (const event of events.data.items || []) { + const bookingRef = db + .collection("bookings") + .where("calendarEventId", "==", event.id); + const bookingSnapshot = await bookingRef.get(); + + if (bookingSnapshot.empty) { + // Create a new booking + const calendarEventId = event.id; + const nyuEmail = findNyuEmail(event); + 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, + }); + 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(), + }; + const statusDocRef = await db + .collection(TableNames.BOOKING_STATUS) + .add(newBookingStatus); + console.log( + `New BookingStatus created with ID: ${statusDocRef.id}`, + ); + + totalNewBookings++; + } + } + } 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/components/src/client/routes/admin/components/Settings.tsx b/booking-app/components/src/client/routes/admin/components/Settings.tsx index 4add3488..f1654c19 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() { @@ -52,6 +54,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; From 97809e193d88df6df0ae8a2363b194de610c1b22 Mon Sep 17 00:00:00 2001 From: "riho.takagi" Date: Fri, 27 Sep 2024 17:09:47 -0400 Subject: [PATCH 3/3] Except unavailable events --- booking-app/app/api/syncCalendars/route.ts | 37 +++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/booking-app/app/api/syncCalendars/route.ts b/booking-app/app/api/syncCalendars/route.ts index c5a21c53..ba488394 100644 --- a/booking-app/app/api/syncCalendars/route.ts +++ b/booking-app/app/api/syncCalendars/route.ts @@ -1,11 +1,10 @@ -import { NextApiRequest, NextApiResponse } from "next"; +import { toFirebaseTimestampFromString } from "@/components/src/client/utils/serverDate"; +import { TableNames } from "@/components/src/policy"; import { Booking, MediaServices } from "@/components/src/types"; -import admin from "@/firebaseAdmin"; +import admin from "@/lib/firebase/server/firebaseAdmin"; import { getCalendarClient } from "@/lib/googleClient"; -import { toFirebaseTimestampFromString } from "@/components/src/client/utils/serverDate"; import { Timestamp } from "@firebase/firestore"; import { NextResponse } from "next/server"; -import { TableNames } from "@/components/src/policy"; const db = admin.firestore(); const createBookingWithDefaults = ( @@ -67,16 +66,28 @@ export async function POST(request: Request) { })); 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: now.toISOString(), - maxResults: 100, // Adjust as needed + 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 || []) { @@ -84,11 +95,16 @@ export async function POST(request: Request) { .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) { + if (bookingSnapshot.empty && nyuEmail) { // Create a new booking const calendarEventId = event.id; - const nyuEmail = findNyuEmail(event); const newBooking = createBookingWithDefaults({ title: event.summary || "", description: event.description || "", @@ -104,6 +120,7 @@ export async function POST(request: Request) { roomId: resource.roomId, mediaServices: MediaServices.CHECKOUT_EQUIPMENT, }); + console.log("newBooking", newBooking); const bookingDocRef = await db .collection(TableNames.BOOKING) .add(newBooking); @@ -117,6 +134,7 @@ export async function POST(request: Request) { firstApprovedAt: admin.firestore.FieldValue.serverTimestamp(), finalApprovedAt: admin.firestore.FieldValue.serverTimestamp(), }; + console.log("newBookingStatus", newBookingStatus); const statusDocRef = await db .collection(TableNames.BOOKING_STATUS) .add(newBookingStatus); @@ -126,7 +144,10 @@ export async function POST(request: Request) { totalNewBookings++; } + pageToken = events.data.nextPageToken; } + while (pageToken); + console.log("targetBookings", targetBookings); } catch (error) { console.error( `Error processing calendar ${resource.calendarId}:`,