From dab1e270c23ea3b1ec7f5b1836f637510c7ee6e0 Mon Sep 17 00:00:00 2001 From: lucia <51058748+lucia-gomez@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:49:23 -0400 Subject: [PATCH] auto format sheet data into JSON --- .../routes/admin/components/Bookings.tsx | 6 +- .../client/routes/booking/approval_email.html | 4 +- .../routes/booking/components/FormInput.tsx | 26 ++--- .../routes/booking/hooks/useSubmitBooking.tsx | 4 +- .../src/client/routes/components/Provider.tsx | 110 +++--------------- media_commons_booking_app/src/server/admin.ts | 43 +------ .../src/server/calendars.ts | 7 +- media_commons_booking_app/src/server/db.ts | 77 ++++++++---- media_commons_booking_app/src/types.ts | 6 +- 9 files changed, 94 insertions(+), 189 deletions(-) diff --git a/media_commons_booking_app/src/client/routes/admin/components/Bookings.tsx b/media_commons_booking_app/src/client/routes/admin/components/Bookings.tsx index 8ca44287..6ea65fe8 100644 --- a/media_commons_booking_app/src/client/routes/admin/components/Bookings.tsx +++ b/media_commons_booking_app/src/client/routes/admin/components/Bookings.tsx @@ -108,11 +108,9 @@ export const Bookings: React.FC = ({ showNnumber = false }) => { {booking.sponsorFirstName} {booking.sponsorLastName} {booking.sponsorEmail} - - {booking.reservationTitle} - + {booking.title} - {booking.reservationDescription} + {booking.description} {booking.expectedAttendance} diff --git a/media_commons_booking_app/src/client/routes/booking/approval_email.html b/media_commons_booking_app/src/client/routes/booking/approval_email.html index 5b9c882e..625466f1 100644 --- a/media_commons_booking_app/src/client/routes/booking/approval_email.html +++ b/media_commons_booking_app/src/client/routes/booking/approval_email.html @@ -116,11 +116,11 @@

Room Reservation Request

Reservation Title: - +

Reservation Description: - +

Expected Attendance: diff --git a/media_commons_booking_app/src/client/routes/booking/components/FormInput.tsx b/media_commons_booking_app/src/client/routes/booking/components/FormInput.tsx index 068aaa2d..1c9413cc 100644 --- a/media_commons_booking_app/src/client/routes/booking/components/FormInput.tsx +++ b/media_commons_booking_app/src/client/routes/booking/components/FormInput.tsx @@ -51,8 +51,6 @@ const FormInput = ({ handleParentSubmit }) => { const [showTextbox, setShowTextbox] = useState(false); const roomNumber = selectedRooms.map((room) => room.roomId); - console.log(selectedRooms); - const maxCapacity = selectedRooms.reduce((sum, room) => { return sum + parseInt(room.capacity); }, 0); @@ -333,38 +331,36 @@ const FormInput = ({ handleParentSubmit }) => { )}

- {errors.reservationTitle && ( - - )} + {errors.title && }
- {errors.reservationDescription && ( - + {errors.description && ( + )}
diff --git a/media_commons_booking_app/src/client/routes/booking/hooks/useSubmitBooking.tsx b/media_commons_booking_app/src/client/routes/booking/hooks/useSubmitBooking.tsx index ced8d97c..7e90f7ec 100644 --- a/media_commons_booking_app/src/client/routes/booking/hooks/useSubmitBooking.tsx +++ b/media_commons_booking_app/src/client/routes/booking/hooks/useSubmitBooking.tsx @@ -168,8 +168,8 @@ const order: (keyof Inputs)[] = [ 'sponsorFirstName', 'sponsorLastName', 'sponsorEmail', - 'reservationTitle', - 'reservationDescription', + 'title', + 'description', 'expectedAttendance', 'attendeeAffiliation', 'roomSetup', diff --git a/media_commons_booking_app/src/client/routes/components/Provider.tsx b/media_commons_booking_app/src/client/routes/components/Provider.tsx index 0a31e240..bc5f335b 100644 --- a/media_commons_booking_app/src/client/routes/components/Provider.tsx +++ b/media_commons_booking_app/src/client/routes/components/Provider.tsx @@ -3,7 +3,6 @@ import { Ban, Booking, BookingStatus, - DevBranch, LiaisonType, PaUser, PagePermission, @@ -108,94 +107,62 @@ export const DatabaseProvider = ({ children }) => { console.log('CURRENT BRANCH:', process.env.BRANCH_NAME); const bookingRows = await serverFunctions .getActiveBookingsFutureDates() - .then((rows) => - rows - .map((row) => mappingBookingRows(row)) - .filter((booking) => booking.devBranch === process.env.BRANCH_NAME) - ); + .then((rows) => { + return (JSON.parse(rows) as Booking[]).filter((booking) => { + return booking.devBranch === process.env.BRANCH_NAME; + }); + }); setBookings(bookingRows); }; const fetchBookingStatuses = async () => { const bookingStatusRows = await serverFunctions .getAllActiveSheetRows(TableNames.BOOKING_STATUS) - .then((rows) => rows.map((row) => mappingBookingStatusRow(row))); + .then((rows) => JSON.parse(rows) as BookingStatus[]); setBookingStatuses(bookingStatusRows); }; const fetchAdminUsers = async () => { const admins = await serverFunctions .getAllActiveSheetRows(TableNames.ADMINS) - .then((rows) => - rows.map((row) => ({ - email: row[0], - createdAt: row[1], - })) - ); + .then((rows) => JSON.parse(rows) as AdminUser[]); setAdminUsers(admins); }; const fetchPaUsers = async () => { const pas = await serverFunctions .getAllActiveSheetRows(TableNames.PAS) - .then((rows) => - rows.map((row) => ({ - email: row[0], - createdAt: row[1], - })) - ); + .then((rows) => JSON.parse(rows) as PaUser[]); setPaUsers(pas); }; const fetchSafetyTrainedUsers = async () => { const trained = await serverFunctions .getAllActiveSheetRows(TableNames.SAFETY_TRAINING) - .then((rows) => - rows.map((row) => ({ - email: row[0], - completedAt: row[1], - })) - ); + .then((rows) => JSON.parse(rows) as SafetyTraining[]); setSafetyTrainedUsers(trained); }; const fetchBannedUsers = async () => { const banned = await serverFunctions .getAllActiveSheetRows(TableNames.BANNED) - .then((rows) => - rows.map((row) => ({ - email: row[0], - bannedAt: row[1], - })) - ); + .then((rows) => JSON.parse(rows) as Ban[]); setBannedUsers(banned); }; const fetchLiaisonUsers = async () => { const liaisons = await serverFunctions .getAllActiveSheetRows(TableNames.LIAISONS) - .then((rows) => - rows.map((row) => ({ - email: row[0], - department: row[1], - createdAt: row[2], - })) - ); + .then((rows) => JSON.parse(rows) as LiaisonType[]); setLiaisonUsers(liaisons); }; const fetchRoomSettings = async () => { const settings = await serverFunctions .getAllActiveSheetRows(TableNames.ROOMS) - .then((rows) => - rows.map((roomRow) => ({ - roomId: roomRow[0], - name: roomRow[1], - capacity: roomRow[2], - calendarId: roomRow[3], - calendarIdProd: roomRow[4], - })) - ); + .then((rows) => { + return JSON.parse(rows) as RoomSetting[]; + }); setRoomSettings(settings); }; @@ -226,52 +193,3 @@ export const DatabaseProvider = ({ children }) => { ); }; - -const mappingBookingRows = (values: string[]): Booking => { - return { - calendarEventId: values[0], - roomId: values[1], - email: values[2], - startDate: values[3], - endDate: values[4], - firstName: values[5], - lastName: values[6], - secondaryName: values[7], - nNumber: values[8], - netId: values[9], - phoneNumber: values[10], - department: values[11], - role: values[12], - sponsorFirstName: values[13], - sponsorLastName: values[14], - sponsorEmail: values[15], - reservationTitle: values[16], - reservationDescription: values[17], - expectedAttendance: values[18], - attendeeAffiliation: values[19], - roomSetup: values[20], - setupDetails: values[21], - mediaServices: values[22], - mediaServicesDetails: values[23], - catering: values[24], - cateringService: values[25], - hireSecurity: values[26], - chartFieldForCatering: values[27], - chartFieldForSecurity: values[28], - chartFieldForRoomSetup: values[29], - devBranch: values[30] as DevBranch, - }; -}; - -const mappingBookingStatusRow = (values: string[]): BookingStatus => { - return { - calendarEventId: values[0], - email: values[1], - requestedAt: values[2], - firstApprovedAt: values[3], - secondApprovedAt: values[4], - rejectedAt: values[5], - canceledAt: values[6], - checkedInAt: values[7], - }; -}; diff --git a/media_commons_booking_app/src/server/admin.ts b/media_commons_booking_app/src/server/admin.ts index e75a71cd..23e8dad5 100644 --- a/media_commons_booking_app/src/server/admin.ts +++ b/media_commons_booking_app/src/server/admin.ts @@ -6,53 +6,20 @@ import { import { approvalUrl, rejectUrl } from './ui'; import { fetchById, - fetchIndexById, fetchIndexByUniqueValue, getActiveSheetValueById, removeRowActive, updateActiveSheetValueById, } from './db'; import { inviteUserToCalendarEvent, updateEventPrefix } from './calendars'; - import { sendHTMLEmail, sendTextEmail } from './emails'; export const bookingContents = (id: string) => { - const values = fetchById(TableNames.BOOKING, id); - return { - calendarEventId: id, - roomId: values[2], - email: values[3], - startDate: values[4], - endDate: values[5], - firstName: values[6], - lastName: values[7], - secondaryName: values[8], - nNumber: values[9], - netId: values[10], - phoneNumber: values[11], - department: values[12], - role: values[13], - sponsorFirstName: values[14], - sponsorLastName: values[15], - sponsorEmail: values[16], - reservationTitle: values[17], - reservationDescription: values[18], - expectedAttendance: values[19], - attendeeAffiliation: values[20], - roomSetup: values[21], - setupDetails: values[22], - mediaServices: values[23], - mediaServicesDetails: values[24], - catering: values[25], - cateringService: values[26], - - hireSecurity: values[27], - chartFieldForCatering: values[28], - chartFieldForSecurity: values[29], - chartFieldForRoomSetup: values[30], - approvalUrl: approvalUrl(id), - rejectedUrl: rejectUrl(id), - }; + const bookingObj = fetchById(TableNames.BOOKING, id); + bookingObj.calendarEventId = id; + bookingObj.approvalUrl = approvalUrl(id); + bookingObj.rejectedUrl = rejectUrl(id); + return bookingObj; }; export const approveInstantBooking = (id: string) => { diff --git a/media_commons_booking_app/src/server/calendars.ts b/media_commons_booking_app/src/server/calendars.ts index 1412cd23..324a0d5e 100644 --- a/media_commons_booking_app/src/server/calendars.ts +++ b/media_commons_booking_app/src/server/calendars.ts @@ -3,8 +3,8 @@ // const startDate = new Date(startCalendarDate); // const endDate = new Date(endCalendarDate); -import { ActiveSheetRoomsColumns, TableNames } from '../policy'; - +import { RoomSetting } from '../types'; +import { TableNames } from '../policy'; import { getAllActiveSheetRows } from './db'; export const addEventToCalendar = ( @@ -63,8 +63,7 @@ export const getCalendarEvents = (calendarId: string) => { const allRoomIds = () => { const rows = getAllActiveSheetRows(TableNames.ROOMS); - const ids = rows.map((row) => row[ActiveSheetRoomsColumns.CALENDAR_ID]); - // ids.shift(); // remove header row + const ids = JSON.parse(rows).map((row: RoomSetting) => row.calendarId); return ids; }; diff --git a/media_commons_booking_app/src/server/db.ts b/media_commons_booking_app/src/server/db.ts index cfa93cd1..44fd4bdf 100644 --- a/media_commons_booking_app/src/server/db.ts +++ b/media_commons_booking_app/src/server/db.ts @@ -6,54 +6,81 @@ import { TableNames, } from '../policy'; +function formatHeaderString(header: string): string { + // replace all spaces with underscores + let snakeCase = header.replace(/ /g, '_'); + // convert snake_case_string to camelCaseString + let camelCase = snakeCase.replace(/([-_][a-z])/gi, ($1) => { + return $1.toUpperCase().replace('-', '').replace('_', ''); + }); + // ensure first letter is lowercased + return camelCase.charAt(0).toLowerCase() + camelCase.slice(1); +} + const sheetToStrings = (rows: any[][] | undefined) => (rows || []).map((row) => row.map((cell) => `${cell}`)); -const fetchRows_ = ( - sheetId: string, - sheetName: string, - includeHeaders: boolean = false -) => { +function sheetRowToJSON(headers: string[], row: any[]) { + const rowObject: any = {}; + headers + .filter((header) => header.length > 0) + .forEach((header, index) => { + rowObject[formatHeaderString(header)] = `${row[index]}`; + }); + return rowObject; +} + +export function sheetToJSON(headers: string[], rows: any[][]): string { + const jsonArray: string[] = []; + rows.forEach((row) => { + const rowObject = sheetRowToJSON(headers, row); + jsonArray.push(rowObject); + }); + return JSON.stringify(jsonArray); +} + +// always includes header row +const fetchRows_ = (sheetId: string, sheetName: string) => { const values = SpreadsheetApp.openById(sheetId) .getSheetByName(sheetName) .getDataRange() - .getValues() - .slice(includeHeaders ? 0 : 1); // potentially ignore the header row + .getValues(); return sheetToStrings(values); }; -export const getAllActiveSheetRows = (sheetName: TableNames) => - fetchRows_(ACTIVE_SHEET_ID, sheetName); +export const getAllActiveSheetRows = (sheetName: TableNames) => { + const rows = fetchRows_(ACTIVE_SHEET_ID, sheetName); + const headers = rows[0]; + const values = rows.slice(1); + return sheetToJSON(headers, values); +}; export const getActiveBookingsFutureDates = () => { - const values = fetchRows_(ACTIVE_SHEET_ID, TableNames.BOOKING); + const rows = fetchRows_(ACTIVE_SHEET_ID, TableNames.BOOKING); + const headers = rows[0]; + const values = rows.slice(1); var today = new Date(); today.setHours(0, 0, 0, 0); // set hours 00:00:00.000 - var filteredData = values.filter(function (row, index) { + console.log('VALUES', values); + + var filteredData = values.filter(function (row) { var startDate = new Date(row[3]); // 'start date' column return startDate > today; // 'start date' is after today }); Logger.log('getFutureDates', filteredData); - return filteredData; + return sheetToJSON(headers, filteredData); }; -// TODO FIX ME headValues ? export const fetchById = (sheetName: TableNames, id: string) => { - const row = fetchRows_(ACTIVE_SHEET_ID, sheetName).find( - (row) => row[0] === id - ); + const rows = fetchRows_(ACTIVE_SHEET_ID, sheetName); + const row = rows.find((row) => row[0] === id); if (!row) throw `Invalid conversation ID: ${id}`; - // const messages = row.flatMap((row) => { - // const dataObj = {}; - // // headValues.forEach((head, cnt) => { - // // dataObj[head] = row[cnt]; - // // }); - // return dataObj; - // }); - return row; + + const headers = rows[0]; + return sheetRowToJSON(headers, row); }; export const fetchIndexByUniqueValue = ( @@ -61,7 +88,7 @@ export const fetchIndexByUniqueValue = ( column: number, value: string ) => { - const rowIndex = fetchRows_(ACTIVE_SHEET_ID, sheetName, true).findIndex( + const rowIndex = fetchRows_(ACTIVE_SHEET_ID, sheetName).findIndex( (row) => row[column] === value ); if (rowIndex === -1) throw 'Invalid unique value: ' + value; diff --git a/media_commons_booking_app/src/types.ts b/media_commons_booking_app/src/types.ts index ab31bad3..864c73f9 100644 --- a/media_commons_booking_app/src/types.ts +++ b/media_commons_booking_app/src/types.ts @@ -14,7 +14,7 @@ export type Booking = Inputs & { startDate: string; endDate: string; roomId: string; - devBranch: DevBranch; + devBranch: string; }; export type BookingStatus = { @@ -68,8 +68,8 @@ export type Inputs = { sponsorFirstName: string; sponsorLastName: string; sponsorEmail: string; - reservationTitle: string; - reservationDescription: string; + title: string; + description: string; attendeeAffiliation: string; roomSetup: string; setupDetails: string;