From 0ee97678590fbcbde824dcf85578ae667d848b11 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 30 Sep 2025 12:08:16 -0400 Subject: [PATCH] More fixes. Signed-off-by: Cliff Hill --- frontend/eslint.config.js | 1 + .../__tests__/components/BookingForm.test.tsx | 7 +- .../__tests__/components/RoomSelect.test.tsx | 8 +- .../src/__tests__/pages/BookingPage.test.tsx | 7 +- .../__tests__/pages/ConfirmationPage.test.tsx | 7 +- .../src/__tests__/pages/LandingPage.test.tsx | 7 +- frontend/src/apis/sse.ts | 10 +- .../src/components/BookingConfirmation.tsx | 4 +- frontend/src/components/BookingForm.tsx | 100 +++++++++++++----- frontend/src/components/RoomDetailsModal.tsx | 4 +- frontend/src/components/RoomSelect.tsx | 1 + frontend/src/constants.ts | 3 +- frontend/src/context/BookingContext.tsx | 9 +- frontend/src/context/RoomContext.tsx | 4 +- frontend/src/context/UserContext.tsx | 7 +- frontend/src/helpers/booking.ts | 8 +- frontend/src/helpers/bookingList.ts | 20 +++- frontend/src/helpers/validation.ts | 44 ++++++-- frontend/src/interfaces.ts | 6 +- frontend/src/pages/BookingPage.tsx | 22 ++-- frontend/src/pages/ConfirmationPage.tsx | 18 +++- frontend/src/pages/LandingPage.tsx | 2 +- frontend/src/schemas.ts | 4 +- frontend/src/utils/date.ts | 4 +- 24 files changed, 213 insertions(+), 94 deletions(-) diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 4165d8d0..647ee808 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -65,6 +65,7 @@ export default [ jest: "readonly", test: "readonly", global: "readonly", + console: "readonly", }, }, rules: { diff --git a/frontend/src/__tests__/components/BookingForm.test.tsx b/frontend/src/__tests__/components/BookingForm.test.tsx index a95e8543..7f7bbfac 100644 --- a/frontend/src/__tests__/components/BookingForm.test.tsx +++ b/frontend/src/__tests__/components/BookingForm.test.tsx @@ -37,10 +37,9 @@ describe("BookingForm", () => { dispatchEvent() { return true; } - onmessage: ((this: EventSource, ev: MessageEvent) => unknown) | null = - null; - onerror: ((this: EventSource, ev: Event) => unknown) | null = null; - onopen: ((this: EventSource, ev: Event) => unknown) | null = null; + onmessage: (() => unknown) | null = null; + onerror: (() => unknown) | null = null; + onopen: (() => unknown) | null = null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any window.EventSource = EventSourceMock as any; diff --git a/frontend/src/__tests__/components/RoomSelect.test.tsx b/frontend/src/__tests__/components/RoomSelect.test.tsx index fff12cbc..eaecd083 100644 --- a/frontend/src/__tests__/components/RoomSelect.test.tsx +++ b/frontend/src/__tests__/components/RoomSelect.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, screen, fireEvent } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; import RoomSelect from "../../components/RoomSelect"; describe("RoomSelect", () => { @@ -9,10 +9,10 @@ describe("RoomSelect", () => { }); it("calls onChange when a room is selected", () => { - const onChange = jest.fn(); - render(); + const _onChange = jest.fn(); + render(); // Simulate selection change if options exist // fireEvent.change(screen.getByLabelText(/room/i), { target: { value: "2" } }); - // expect(onChange).toHaveBeenCalledWith("2"); + // expect(_onChange).toHaveBeenCalledWith("2"); }); }); diff --git a/frontend/src/__tests__/pages/BookingPage.test.tsx b/frontend/src/__tests__/pages/BookingPage.test.tsx index 5f0f210f..6d3efa5d 100644 --- a/frontend/src/__tests__/pages/BookingPage.test.tsx +++ b/frontend/src/__tests__/pages/BookingPage.test.tsx @@ -38,10 +38,9 @@ describe("BookingPage", () => { dispatchEvent() { return true; } - onmessage: ((this: EventSource, ev: MessageEvent) => unknown) | null = - null; - onerror: ((this: EventSource, ev: Event) => unknown) | null = null; - onopen: ((this: EventSource, ev: Event) => unknown) | null = null; + onmessage: (() => unknown) | null = null; + onerror: (() => unknown) | null = null; + onopen: (() => unknown) | null = null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any window.EventSource = EventSourceMock as any; diff --git a/frontend/src/__tests__/pages/ConfirmationPage.test.tsx b/frontend/src/__tests__/pages/ConfirmationPage.test.tsx index 89a3dcf4..866d191b 100644 --- a/frontend/src/__tests__/pages/ConfirmationPage.test.tsx +++ b/frontend/src/__tests__/pages/ConfirmationPage.test.tsx @@ -37,10 +37,9 @@ describe("ConfirmationPage", () => { dispatchEvent() { return true; } - onmessage: ((this: EventSource, ev: MessageEvent) => unknown) | null = - null; - onerror: ((this: EventSource, ev: Event) => unknown) | null = null; - onopen: ((this: EventSource, ev: Event) => unknown) | null = null; + onmessage: (() => unknown) | null = null; + onerror: (() => unknown) | null = null; + onopen: (() => unknown) | null = null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any window.EventSource = EventSourceMock as any; diff --git a/frontend/src/__tests__/pages/LandingPage.test.tsx b/frontend/src/__tests__/pages/LandingPage.test.tsx index c2b075a7..31cde570 100644 --- a/frontend/src/__tests__/pages/LandingPage.test.tsx +++ b/frontend/src/__tests__/pages/LandingPage.test.tsx @@ -39,10 +39,9 @@ describe("LandingPage", () => { dispatchEvent() { return true; } - onmessage: ((this: EventSource, ev: MessageEvent) => unknown) | null = - null; - onerror: ((this: EventSource, ev: Event) => unknown) | null = null; - onopen: ((this: EventSource, ev: Event) => unknown) | null = null; + onmessage: (() => unknown) | null = null; + onerror: (() => unknown) | null = null; + onopen: (() => unknown) | null = null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any window.EventSource = EventSourceMock as any; diff --git a/frontend/src/apis/sse.ts b/frontend/src/apis/sse.ts index b10d1c7f..a2eb6cf2 100644 --- a/frontend/src/apis/sse.ts +++ b/frontend/src/apis/sse.ts @@ -25,7 +25,9 @@ export function connectRoomsAvailabilityStream({ onError, }: { url?: string; + /* eslint-disable-next-line no-unused-vars */ onMessage: (data: RoomsAvailabilitySSEPayload) => void; + /* eslint-disable-next-line no-unused-vars */ onError?: (error: unknown) => void; }) { const es = new window.EventSource(url); @@ -34,12 +36,16 @@ export function connectRoomsAvailabilityStream({ const data: RoomsAvailabilitySSEPayload = JSON.parse(event.data); onMessage(data); } catch (e) { - if (onError) onError(e); + if (onError) { + onError(e); + } } }; es.onerror = (err) => { es.close(); - if (onError) onError(err); + if (onError) { + onError(err); + } }; return es; } diff --git a/frontend/src/components/BookingConfirmation.tsx b/frontend/src/components/BookingConfirmation.tsx index 4e781358..ec27b0ea 100644 --- a/frontend/src/components/BookingConfirmation.tsx +++ b/frontend/src/components/BookingConfirmation.tsx @@ -32,7 +32,9 @@ const BookingConfirmation: React.FC = ({ onEdit, onBack, }) => { - if (!booking) return null; + if (!booking) { + return null; + } const { room, start_time, end_time, title, invitees } = booking; return ( void; editBooking?: Booking; slotInfo?: { room_id?: string | number; start?: string }; - onBookingSuccess?: (booking?: Booking) => void; + onBookingSuccess?: () => void; } const BookingForm: React.FC = ({ @@ -97,9 +97,15 @@ const BookingForm: React.FC = ({ // Room selection const initialRoomId = useMemo(() => { - if (isEdit && editBooking) return getEditBookingRoomId(editBooking); - if (slotInfo?.room_id) return String(slotInfo.room_id); - if (rooms.length > 0) return String((rooms[0] as ConferenceRoom).id); + if (isEdit && editBooking) { + return getEditBookingRoomId(editBooking); + } + if (slotInfo?.room_id) { + return String(slotInfo.room_id); + } + if (rooms.length > 0) { + return String((rooms[0] as ConferenceRoom).id); + } return ""; }, [isEdit, editBooking, slotInfo, rooms]); const [room_id, setRoomId] = useState(initialRoomId); @@ -138,13 +144,17 @@ const BookingForm: React.FC = ({ const currentCandidate = candidate; const currentCandidateEnd = candidateEnd; const overlaps = roomBookings.some(function (b) { - if (isEdit && b.id === editBooking?.id) return false; + if (isEdit && b.id === editBooking?.id) { + return false; + } return ( currentCandidate < new Date(b.end_time) && currentCandidateEnd > new Date(b.start_time) ); }); - if (!overlaps && candidate > now) return candidate; + if (!overlaps && candidate > now) { + return candidate; + } candidate = candidateEnd; } return candidate; @@ -153,10 +163,12 @@ const BookingForm: React.FC = ({ ); const initialStart = useMemo(() => { - if (isEdit && editBooking?.start_time) + if (isEdit && editBooking?.start_time) { return new Date(editBooking.start_time); - if (slotInfo?.start) + } + if (slotInfo?.start) { return findNextAvailableStart(room_id, new Date(slotInfo.start)); + } return findNextAvailableStart(room_id, now); }, [isEdit, editBooking, slotInfo, room_id, findNextAvailableStart, now]); const [start, setStart] = useState(initialStart.toISOString()); @@ -164,7 +176,9 @@ const BookingForm: React.FC = ({ // End time const [customInterval, setCustomInterval] = useState(null); const initialEnd = useMemo(() => { - if (isEdit && editBooking?.end_time) return new Date(editBooking.end_time); + if (isEdit && editBooking?.end_time) { + return new Date(editBooking.end_time); + } return new Date( initialStart.getTime() + ENV.DEFAULT_BOOKING_INTERVAL_MINUTES * 60000 ); @@ -179,19 +193,27 @@ const BookingForm: React.FC = ({ const newErrors: { [key: string]: string } = {}; // Room const roomIdError = validateRoomId(room_id); - if (roomIdError) newErrors.room_id = roomIdError; + if (roomIdError) { + newErrors.room_id = roomIdError; + } // Start const startError = validateStart(start); - if (startError) newErrors.start = startError; + if (startError) { + newErrors.start = startError; + } // End const endError = validateEnd(end, start); - if (endError) newErrors.end = endError; + if (endError) { + newErrors.end = endError; + } // Overlap const roomBookings = bookings.filter( (b: Booking) => String(b.room_id) === String(room_id) ); const overlaps = roomBookings.some((b: Booking) => { - if (isEdit && b.id === editBooking?.id) return false; + if (isEdit && b.id === editBooking?.id) { + return false; + } return ( new Date(start) < new Date(b.end_time) && new Date(end) > new Date(b.start_time) @@ -203,7 +225,9 @@ const BookingForm: React.FC = ({ } // Invitees const inviteesError = validateInvitees(invitees, roomCapacity); - if (inviteesError) newErrors.invitees = inviteesError; + if (inviteesError) { + newErrors.invitees = inviteesError; + } setErrors(newErrors); return Object.keys(newErrors).length === 0; } @@ -224,7 +248,9 @@ const BookingForm: React.FC = ({ setEnd(value); const interval = (new Date(value).getTime() - new Date(start).getTime()) / 60000; - if (interval > 0) setCustomInterval(interval); + if (interval > 0) { + setCustomInterval(interval); + } validateFields(); } function handleInviteeChange(event: SelectChangeEvent) { @@ -245,7 +271,9 @@ const BookingForm: React.FC = ({ onClose(); return; } - if (!validateFields()) return; + if (!validateFields()) { + return; + } try { let bookingResult; if (!isEdit) { @@ -269,9 +297,11 @@ const BookingForm: React.FC = ({ logger.info("[BookingForm] updateBooking payload:", updatePayload); bookingResult = await updateBooking(editBooking.id, updatePayload); } - if (onBookingSuccess && bookingResult) onBookingSuccess(bookingResult); + if (onBookingSuccess && bookingResult) { + onBookingSuccess(bookingResult); + } onClose(); - } catch (err: any) { + } catch (err: unknown) { let errorMsg = "Failed to save booking. Please try again."; if (err?.response?.data?.detail) { errorMsg = err.response.data.detail; @@ -308,7 +338,7 @@ const BookingForm: React.FC = ({ - handleRoomChange({ target: { value: id } } as any) + handleRoomChange({ target: { value: id } } as unknown) } label="Room" minWidth={180} @@ -332,9 +362,13 @@ const BookingForm: React.FC = ({ label="Start Time" type="datetime-local" value={(() => { - if (!start) return ""; + if (!start) { + return ""; + } const d = new Date(start); - if (isNaN(d.getTime())) return ""; + if (isNaN(d.getTime())) { + return ""; + } const pad = (n: number) => n.toString().padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad( d.getDate() @@ -356,9 +390,13 @@ const BookingForm: React.FC = ({ label="End Time" type="datetime-local" value={(() => { - if (!end) return ""; + if (!end) { + return ""; + } const d = new Date(end); - if (isNaN(d.getTime())) return ""; + if (isNaN(d.getTime())) { + return ""; + } const pad = (n: number) => n.toString().padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad( d.getDate() @@ -387,7 +425,9 @@ const BookingForm: React.FC = ({ renderValue={(selected) => ( {(selected as string[]).map((value, idx) => { - const user = users.find((u: any) => u.email === value); + const user = users.find( + (u: import("../interfaces").User) => u.email === value + ); return ( = ({ } left` : `${Math.abs(remainingSlots)} over room capacity`} - {users.map((user: any) => { + {users.map((user: import("../interfaces").User) => { const email = user.email ?? user.name; const isSelected = invitees.includes(email); // User unavailable if booked for another event at this time const unavailable = bookings.some((b: Booking) => { - if (isEdit && b.id === editBooking?.id) return false; + if (isEdit && b.id === editBooking?.id) { + return false; + } return ( b.invitees?.includes(email) && new Date(start) < new Date(b.end_time) && @@ -486,9 +528,11 @@ const BookingForm: React.FC = ({ if (editBooking) { try { await deleteBooking(editBooking.id); - if (onBookingSuccess) onBookingSuccess(); + if (onBookingSuccess) { + onBookingSuccess(); + } onClose(); - } catch (err: any) { + } catch (err: unknown) { let errorMsg = "Failed to delete booking. Please try again."; if (err?.response?.data?.detail) { diff --git a/frontend/src/components/RoomDetailsModal.tsx b/frontend/src/components/RoomDetailsModal.tsx index 7911a4ef..9bca6b15 100644 --- a/frontend/src/components/RoomDetailsModal.tsx +++ b/frontend/src/components/RoomDetailsModal.tsx @@ -32,7 +32,9 @@ const RoomDetailsModal: React.FC = ({ room, }) => { // Lifecycle and debug logging removed - if (!room) return null; + if (!room) { + return null; + } return ( Room Details diff --git a/frontend/src/components/RoomSelect.tsx b/frontend/src/components/RoomSelect.tsx index 283147aa..81a0cda9 100644 --- a/frontend/src/components/RoomSelect.tsx +++ b/frontend/src/components/RoomSelect.tsx @@ -25,6 +25,7 @@ import { logger } from "../utils/logger"; export interface RoomSelectProps { selectedRoomId: string | number; + /* eslint-disable-next-line no-unused-vars */ onChange: (roomId: string | number) => void; label?: string; minWidth?: number; diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts index d2af0fc2..44fd7e4f 100644 --- a/frontend/src/constants.ts +++ b/frontend/src/constants.ts @@ -41,8 +41,9 @@ export const ENV = { * @returns {string} The CSS variable value, or the fallback if not found. */ function getCssVar(name: string, fallback: string): string { - if (typeof window === "undefined" || !document.documentElement) + if (typeof window === "undefined" || !document.documentElement) { return fallback; + } const value = getComputedStyle(document.documentElement) .getPropertyValue(name) .trim(); diff --git a/frontend/src/context/BookingContext.tsx b/frontend/src/context/BookingContext.tsx index e12f3929..9c135f4e 100644 --- a/frontend/src/context/BookingContext.tsx +++ b/frontend/src/context/BookingContext.tsx @@ -21,6 +21,7 @@ import type { Booking } from "../interfaces"; interface BookingContextType { bookings: Booking[]; + /* eslint-disable-next-line no-unused-vars */ fetchMonth: (month: string) => Promise; } @@ -36,11 +37,12 @@ export const BookingContext = createContext( */ export const BookingProvider = ({ children }: { children: ReactNode }) => { // Fetch bookings for a given month and merge into state + const fetchMonth = async (month: string) => { try { const newBookings = await getMonthBookings(month); setBookingsByMonth((prev) => ({ ...prev, [month]: newBookings })); - } catch (err) { + } catch { // Optionally handle error } }; @@ -75,8 +77,9 @@ export const BookingProvider = ({ children }: { children: ReactNode }) => { const month = `${d.getFullYear()}-${String( d.getMonth() + 1 ).padStart(2, "0")}`; - if (!bookingsByMonthFromSSE[month]) + if (!bookingsByMonthFromSSE[month]) { bookingsByMonthFromSSE[month] = []; + } bookingsByMonthFromSSE[month].push(b); }); // SSE received bookings @@ -158,7 +161,7 @@ export const BookingProvider = ({ children }: { children: ReactNode }) => { }); } }, - onError: (err) => { + onError: () => { // Optionally handle SSE errors // SSE error }, diff --git a/frontend/src/context/RoomContext.tsx b/frontend/src/context/RoomContext.tsx index 16e6c689..7fde4abf 100644 --- a/frontend/src/context/RoomContext.tsx +++ b/frontend/src/context/RoomContext.tsx @@ -60,6 +60,8 @@ export const RoomProvider: React.FC<{ children: ReactNode }> = ({ export const useRooms = () => { const context = useContext(RoomContext); - if (!context) throw new Error("useRooms must be used within a RoomProvider"); + if (!context) { + throw new Error("useRooms must be used within a RoomProvider"); + } return context; }; diff --git a/frontend/src/context/UserContext.tsx b/frontend/src/context/UserContext.tsx index 77b6596e..0ab34502 100644 --- a/frontend/src/context/UserContext.tsx +++ b/frontend/src/context/UserContext.tsx @@ -40,8 +40,9 @@ export function useAvailableUsers(params: { const start = new Date(params.start_time).getTime(); const end = new Date(params.end_time).getTime(); const overlappingBookings = bookings.filter((b) => { - if (params.exclude_booking_id && b.id === params.exclude_booking_id) + if (params.exclude_booking_id && b.id === params.exclude_booking_id) { return false; + } const bStart = new Date(b.start_time).getTime(); const bEnd = new Date(b.end_time).getTime(); return bStart < end && bEnd > start; @@ -95,6 +96,8 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ export const useUsers = () => { const context = useContext(UserContext); - if (!context) throw new Error("useUsers must be used within a UserProvider"); + if (!context) { + throw new Error("useUsers must be used within a UserProvider"); + } return context; }; diff --git a/frontend/src/helpers/booking.ts b/frontend/src/helpers/booking.ts index bdebc036..38df7ecf 100644 --- a/frontend/src/helpers/booking.ts +++ b/frontend/src/helpers/booking.ts @@ -20,7 +20,9 @@ import type { Booking } from "../interfaces"; export function getEditBookingRoomId( editBooking: Booking | undefined | null ): string { - if (!editBooking) return ""; + if (!editBooking) { + return ""; + } // Only support standard Booking shape return editBooking.room_id ? String(editBooking.room_id) : ""; } @@ -31,7 +33,9 @@ export function getEditBookingRoomId( * @returns User-friendly error message */ export function formatBookingError(detail: string): string { - if (!detail) return "An unknown error occurred."; + if (!detail) { + return "An unknown error occurred."; + } const lower = detail.toLowerCase(); if (lower.includes("overlap")) { return "The selected room is already booked for the chosen time. Please pick a different time or room."; diff --git a/frontend/src/helpers/bookingList.ts b/frontend/src/helpers/bookingList.ts index 733c44c7..4ef4a419 100644 --- a/frontend/src/helpers/bookingList.ts +++ b/frontend/src/helpers/bookingList.ts @@ -53,12 +53,22 @@ export function formatBookingTime(dateStr: string): string { * @returns Invitee name string */ export function getInviteeName(inv: Invitee | string): string { - if (typeof inv === "string") return inv; + if (typeof inv === "string") { + return inv; + } if (inv && typeof inv === "object") { - if (typeof inv.name === "string") return inv.name; - if (typeof inv.username === "string") return inv.username; - if (typeof inv.displayName === "string") return inv.displayName; - if (inv.user && typeof inv.user.name === "string") return inv.user.name; + if (typeof inv.name === "string") { + return inv.name; + } + if (typeof inv.username === "string") { + return inv.username; + } + if (typeof inv.displayName === "string") { + return inv.displayName; + } + if (inv.user && typeof inv.user.name === "string") { + return inv.user.name; + } } return ""; } diff --git a/frontend/src/helpers/validation.ts b/frontend/src/helpers/validation.ts index 64f7226a..df75d378 100644 --- a/frontend/src/helpers/validation.ts +++ b/frontend/src/helpers/validation.ts @@ -14,7 +14,9 @@ import type { Booking } from "../interfaces"; * @returns Error message if invalid, otherwise null */ export function validateRoomId(room_id: string | null): string | null { - if (!room_id) return "Room is required."; + if (!room_id) { + return "Room is required."; + } return null; } @@ -24,10 +26,16 @@ export function validateRoomId(room_id: string | null): string | null { * @returns Error message if invalid, otherwise null */ export function validateStart(start: string | null): string | null { - if (!start) return "Start time is required."; + if (!start) { + return "Start time is required."; + } const d = new Date(start); - if (isNaN(d.getTime())) return "Invalid start time."; - if (d < new Date()) return "Start time cannot be in the past."; + if (isNaN(d.getTime())) { + return "Invalid start time."; + } + if (d < new Date()) { + return "Start time cannot be in the past."; + } return null; } @@ -41,15 +49,25 @@ export function validateEnd( end: string | null, start?: string | null ): string | null { - if (!end) return "End time is required."; + if (!end) { + return "End time is required."; + } const dEnd = new Date(end); - if (isNaN(dEnd.getTime())) return "Invalid end time."; + if (isNaN(dEnd.getTime())) { + return "Invalid end time."; + } if (start) { const dStart = new Date(start); - if (isNaN(dStart.getTime())) return "Invalid start time."; - if (dStart >= dEnd) return "End time must be after start time."; + if (isNaN(dStart.getTime())) { + return "Invalid start time."; + } + if (dStart >= dEnd) { + return "End time must be after start time."; + } + } + if (dEnd < new Date()) { + return "End time cannot be in the past."; } - if (dEnd < new Date()) return "End time cannot be in the past."; return null; } @@ -103,10 +121,14 @@ export function validateRoomAvailability( filteredBookings.forEach((b: Booking) => { const bStart = new Date(b.start_time); const bEnd = new Date(b.end_time); - if (isNaN(bStart.getTime()) || isNaN(bEnd.getTime())) return; + if (isNaN(bStart.getTime()) || isNaN(bEnd.getTime())) { + return; + } const dStart = new Date(start); const dEnd = new Date(end); - if (isNaN(dStart.getTime()) || isNaN(dEnd.getTime())) return; + if (isNaN(dStart.getTime()) || isNaN(dEnd.getTime())) { + return; + } if (dStart < bEnd && dEnd > bStart) { if (dStart < bEnd && dStart >= bStart) { startConflict = true; diff --git a/frontend/src/interfaces.ts b/frontend/src/interfaces.ts index fb9cc83c..9dbc32ac 100644 --- a/frontend/src/interfaces.ts +++ b/frontend/src/interfaces.ts @@ -34,8 +34,8 @@ export interface SlotInfo { */ export interface CalendarViewProps { events: import("./types").BookingEvent[]; - onEventClick?: (event: import("./types").BookingEvent) => void; - onSlotSelect?: (slotInfo: SlotInfo) => void; + onEventClick?: () => void; + onSlotSelect?: () => void; selectedRoomId?: string | number; officeStartHour?: number; officeEndHour?: number; @@ -133,11 +133,13 @@ export interface RoomDetailsModalProps { export interface BookingListProps { bookings: Booking[]; + /* eslint-disable-next-line no-unused-vars */ onSelect?: (booking: Booking) => void; } export interface RoomListProps { rooms: ConferenceRoom[]; selectedRoomId?: number; + /* eslint-disable-next-line no-unused-vars */ onSelectRoom?: (roomId: number) => void; } diff --git a/frontend/src/pages/BookingPage.tsx b/frontend/src/pages/BookingPage.tsx index e100e41b..88dbbcdf 100644 --- a/frontend/src/pages/BookingPage.tsx +++ b/frontend/src/pages/BookingPage.tsx @@ -86,7 +86,9 @@ const BookingPage: React.FC = () => { * Flatten all bookings from all rooms into a single array. */ const allBookings: Booking[] = React.useMemo(() => { - if (!bookings) return []; + if (!bookings) { + return []; + } return bookings; }, [bookings]); @@ -94,7 +96,9 @@ const BookingPage: React.FC = () => { * Map all bookings to calendar event objects for display. */ const events = React.useMemo(() => { - if (!bookings || rooms.length === 0) return []; + if (!bookings || rooms.length === 0) { + return []; + } const mappedEvents = bookings.map((booking: Booking) => { const room = rooms.find( (r: ConferenceRoom) => String(r.id) === String(booking.room_id) @@ -206,14 +210,18 @@ const BookingPage: React.FC = () => { // Clamp to office hours (local time) const officeStart = 8; const officeEnd = 18; - if (selectedDay.getHours() < officeStart) + if (selectedDay.getHours() < officeStart) { selectedDay.setHours(officeStart, 0, 0, 0); - if (selectedDay.getHours() >= officeEnd) + } + if (selectedDay.getHours() >= officeEnd) { selectedDay.setHours(officeEnd - 1, 45, 0, 0); + } // 2. Find all bookings for this room on this day const roomBookings = events.filter((e: BookingEvent) => { - if (e.room_id !== selectedRoomId) return false; + if (e.room_id !== selectedRoomId) { + return false; + } const eventStart = e.start && (typeof e.start === "string" || @@ -237,7 +245,9 @@ const BookingPage: React.FC = () => { typeof b.end === "string" || typeof b.end === "number" || b.end instanceof Date; - if (!validStart || !validEnd) return undefined; + if (!validStart || !validEnd) { + return undefined; + } const s = new Date(b.start as string | number | Date); const e = new Date(b.end as string | number | Date); return [s.getTime(), e.getTime()]; diff --git a/frontend/src/pages/ConfirmationPage.tsx b/frontend/src/pages/ConfirmationPage.tsx index 4593349a..9f973f4f 100644 --- a/frontend/src/pages/ConfirmationPage.tsx +++ b/frontend/src/pages/ConfirmationPage.tsx @@ -67,10 +67,14 @@ const ConfirmationPage: React.FC = () => { */ React.useEffect(() => { const bookingId = location.state?.booking?.id || booking?.id; - if (!bookingId) return; + if (!bookingId) { + return; + } // Always get latest booking from context const latestBooking = bookings.find((b: Booking) => b.id === bookingId); - if (latestBooking) setBooking(latestBooking); + if (latestBooking) { + setBooking(latestBooking); + } const es = connectRoomsAvailabilityStream({ onMessage: (data) => { if (Array.isArray(data.bookings)) { @@ -84,7 +88,7 @@ const ConfirmationPage: React.FC = () => { } } }, - onError: (err) => { + onError: () => { // Optionally handle SSE errors }, }); @@ -115,8 +119,12 @@ const ConfirmationPage: React.FC = () => { navigate("/booking"); return null; } - if (roomsLoading) return
Loading rooms...
; - if (roomsError) return
Error loading rooms.
; + if (roomsLoading) { + return
Loading rooms...
; + } + if (roomsError) { + return
Error loading rooms.
; + } const handleEdit = () => setEditing(true); const handleBack = () => navigate("/booking"); diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index ab15ed7e..99cdd294 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -44,7 +44,7 @@ const LandingPage: FC = () => { * Debug: log before page unload. */ useEffect(() => { - const handler = (e: BeforeUnloadEvent) => { + const handler = () => { // ...removed debug log... }; window.addEventListener("beforeunload", handler); diff --git a/frontend/src/schemas.ts b/frontend/src/schemas.ts index 01f48734..fcf245e3 100644 --- a/frontend/src/schemas.ts +++ b/frontend/src/schemas.ts @@ -69,7 +69,7 @@ export interface RoomDetailsModalProps { */ export interface BookingListProps { bookings: Booking[]; - onSelect?: (booking: Booking) => void; + onSelect?: () => void; } /** @@ -78,5 +78,5 @@ export interface BookingListProps { export interface RoomListProps { rooms: ConferenceRoom[]; selectedRoomId?: number; - onSelectRoom?: (roomId: number) => void; + onSelectRoom?: () => void; } diff --git a/frontend/src/utils/date.ts b/frontend/src/utils/date.ts index d5cb9415..660b770c 100644 --- a/frontend/src/utils/date.ts +++ b/frontend/src/utils/date.ts @@ -22,7 +22,9 @@ export function roundToStrictlyFutureQuarter(dt: Date): Date { const copy = new Date(dt.getTime()); copy.setSeconds(0, 0); let add = 15 - (copy.getMinutes() % 15); - if (add === 0) add = 15; + if (add === 0) { + add = 15; + } copy.setMinutes(copy.getMinutes() + add); return copy; }