Loading calendar...
;
- }
- if (roomsError || bookingsError || inviteesError) {
- return (
-
+
Book a Room
- {usingPollingFallback && (
-
- Warning: Your browser does not support real-time
- updates. The calendar will refresh every 10 seconds.
-
- )}
+ {/* ...existing code... */}
Room
@@ -509,17 +366,32 @@ const BookingPage: React.FC = () => {
label="Room"
onChange={(e) => setSelectedRoomId(e.target.value)}
>
- {rooms.map((room: ConferenceRoom) => (
-
- ))}
+ {rooms.map((room: ConferenceRoom) => {
+ const roomIdx = Number(room.id) % 20 || 0;
+ const bgColor = `var(--room-color-${roomIdx + 1})`;
+ return (
+
+ );
+ })}
setRoomModalOpen(false)}
+ onClose={() => {
+ logger.info("[BookingPage] Closing room modal");
+ setRoomModalOpen(false);
+ }}
room={
rooms.find((r: ConferenceRoom) => r.id === selectedRoomId) ?? rooms[0]
}
@@ -543,18 +418,25 @@ const BookingPage: React.FC = () => {
open={formOpen}
onClose={() => handleFormClose(true)}
slotInfo={
- formSlot ?? {
- start: new Date(),
- end: new Date(),
- room_id: selectedRoomId,
- }
+ formSlot
+ ? {
+ ...formSlot,
+ start:
+ typeof formSlot.start === "string"
+ ? formSlot.start
+ : formSlot.start instanceof Date
+ ? formSlot.start.toISOString()
+ : new Date().toISOString(),
+ }
+ : {
+ start: new Date().toISOString(),
+ room_id: selectedRoomId,
+ }
}
- rooms={rooms}
editBooking={editBooking ?? undefined}
- allInvitees={allInvitees.map(
- (u: import("../interfaces").User) => u.email ?? u.name
- )}
- onBookingSuccess={() => handleFormClose(true)}
+ onBookingSuccess={(booking?: Booking) =>
+ handleFormClose(true, booking)
+ }
/>
)}
diff --git a/frontend/src/pages/ConfirmationPage.tsx b/frontend/src/pages/ConfirmationPage.tsx
index 69e87f60..890071d8 100644
--- a/frontend/src/pages/ConfirmationPage.tsx
+++ b/frontend/src/pages/ConfirmationPage.tsx
@@ -1,24 +1,36 @@
/**
- * ConfirmationPage.tsx
+ * ConfirmationPage component
* Shows booking confirmation and allows editing or returning to booking form.
* Expects booking data via location.state.
- *
- * Author: Cliff Hill
- * Last updated: 2025-09-05
* @component
* @returns {JSX.Element} Confirmation page UI
*/
+// External imports
import React, { useState } from "react";
-import { Box } from "@mui/material";
import { useLocation, useNavigate } from "react-router-dom";
+
+// MUI imports
+import { Box } from "@mui/material";
+
+// Third-party imports
import { useQuery } from "@tanstack/react-query";
+// Internal component imports
import BookingConfirmation from "../components/BookingConfirmation";
import BookingForm from "../components/BookingForm";
+
+// API imports
import { connectRoomsAvailabilityStream } from "../apis/sse";
import { getRooms } from "../apis/rooms";
+// Context imports
+import { useBookings } from "../context/BookingContext";
+
+// Utility/helper imports
+import { logger } from "../utils/logger";
+
+// Type-only imports
import type { Booking } from "../interfaces";
/**
@@ -36,19 +48,35 @@ const ConfirmationPage: React.FC = () => {
/**
* Booking state, updated via SSE for live status.
*/
+ const { bookings } = useBookings();
const [booking, setBooking] = useState
(
location.state?.booking || null
);
+
+ React.useEffect(() => {
+ logger.info("[ConfirmationPage] Mounted");
+ return () => {
+ logger.info("[ConfirmationPage] Unmounted");
+ };
+ }, []);
+
+ React.useEffect(() => {
+ logger.debug("[ConfirmationPage] booking updated", booking);
+ }, [booking]);
/**
* Subscribe to SSE for live booking status updates.
*/
React.useEffect(() => {
- if (!booking) return;
+ let bookingId = location.state?.booking?.id || booking?.id;
+ if (!bookingId) return;
+ // Always get latest booking from context
+ const latestBooking = bookings.find((b: Booking) => b.id === bookingId);
+ if (latestBooking) setBooking(latestBooking);
const es = connectRoomsAvailabilityStream({
onMessage: (data) => {
if (Array.isArray(data.bookings)) {
const updated = data.bookings.find(
- (b: Booking) => b.id === booking?.id
+ (b: Booking) => b.id === bookingId
);
if (updated) {
setBooking((prev: Booking | null) =>
@@ -64,7 +92,7 @@ const ConfirmationPage: React.FC = () => {
return () => {
es.close();
};
- }, [booking, booking?.id]);
+ }, [bookings, location.state, booking?.id]);
/**
* Editing state for toggling between confirmation and edit form.
*/
@@ -75,7 +103,7 @@ const ConfirmationPage: React.FC = () => {
* Fetch all rooms for editing.
*/
const {
- data: rooms = [],
+ // ...existing code...
isLoading: roomsLoading,
error: roomsError,
} = useQuery({
@@ -114,16 +142,13 @@ const ConfirmationPage: React.FC = () => {
onClose={handleFormClose}
slotInfo={{
start: booking.start_time,
- end: booking.end_time,
room_id: booking.room_id,
}}
- rooms={rooms}
editBooking={{
...booking,
start: booking.start_time,
end: booking.end_time,
}}
- allInvitees={booking.invitees || []}
onBookingSuccess={() => {
setEditing(false);
// Optionally update booking state
diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx
index 5b9ce6a0..c28f6a87 100644
--- a/frontend/src/pages/LandingPage.tsx
+++ b/frontend/src/pages/LandingPage.tsx
@@ -1,51 +1,33 @@
/**
- * LandingPage.tsx
+ * LandingPage component
* Main landing page for the conference room booking system frontend.
* Displays available rooms, today's bookings, and navigation to booking form.
* Handles data fetching, error states, and loading skeletons.
- *
- * Author: Cliff Hill
- * Last updated: 2025-09-05
* @component
* @returns {JSX.Element} Landing page UI
*/
+// External imports
import React, { FC, useEffect, useState } from "react";
-import {
- Box,
- Button,
- Card,
- CardContent,
- Skeleton,
- Typography,
-} from "@mui/material";
-import EventNoteIcon from "@mui/icons-material/EventNote";
import { useNavigate } from "react-router-dom";
-import { useQuery } from "@tanstack/react-query";
+// MUI imports
+import { Box, Button, Card, CardContent, Typography } from "@mui/material";
+import EventNoteIcon from "@mui/icons-material/EventNote";
+
+// Styles
+import "../styles/LandingPage.css";
+
+// Internal component imports
import BookingList from "../components/BookingList";
import RoomList from "../components/RoomList";
-import { connectRoomsAvailabilityStream } from "../apis/sse";
-import { getRooms } from "../apis/rooms";
+
+// Context imports
+import { useRooms } from "../context/RoomContext";
+import { useBookings } from "../context/BookingContext";
+
+// Utility/helper imports
import { logger } from "../utils/logger";
-
-import type { Booking } from "../interfaces";
-import type { ConferenceRoom } from "../interfaces";
-
-/**
- * LandingPage component
- * Main landing page for the conference room booking system frontend.
- * Displays available rooms, today's bookings, and navigation to booking form.
- * Handles data fetching, error states, and loading skeletons.
- * @component
- * @returns {JSX.Element} Landing page UI
- */
-/**
- * LandingPage component
- * Renders the main UI for conference room booking, including available rooms, today's bookings, and navigation to booking form.
- * Handles data fetching, error states, and loading skeletons.
- * @returns {JSX.Element} Landing page UI
- */
const LandingPage: FC = () => {
// Log when LandingPage mounts/unmounts
/**
@@ -71,30 +53,8 @@ const LandingPage: FC = () => {
}, []);
const navigate = useNavigate();
- // Fetch available rooms from backend
- /**
- * Fetch available rooms from backend.
- */
- const {
- data: rooms = [],
- isLoading: roomsLoading,
- error: roomsError,
- refetch: refetchRooms,
- } = useQuery({
- queryKey: ["rooms"],
- queryFn: async () => {
- logger.info("[LandingPage] Fetching rooms");
- const rooms = await getRooms();
- logger.info("[LandingPage] Rooms response", rooms); // Log backend return value
- return rooms as ConferenceRoom[];
- },
- });
- // Log rooms result after fetch
- useEffect(() => {
- if (!roomsLoading && roomsError == null) {
- logger.info("[LandingPage] Rooms loaded", rooms);
- }
- }, [rooms, roomsLoading, roomsError]);
+ // Get rooms from context
+ const { rooms } = useRooms();
// Room selection state
const [selectedRoomId, setSelectedRoomId] = useState(
@@ -106,108 +66,34 @@ const LandingPage: FC = () => {
if (rooms.length > 0 && selectedRoomId === undefined) {
setSelectedRoomId(rooms[0].id);
}
+ // Fetch bookings for today for the selected room if not already fetched
+ // Bookings are now managed by BookingContext
}, [rooms, selectedRoomId]);
// Real-time bookings state via SSE
- const [bookingsByRoom, setBookingsByRoom] = useState<
- Record
- >({});
- useEffect(() => {
- const es = connectRoomsAvailabilityStream({
- onMessage: (data) => {
- if (Array.isArray(data.bookings)) {
- const grouped: Record = {};
- data.bookings.forEach((b: Booking) => {
- if (typeof b.room_id === "undefined" || b.room_id === null) return;
- const roomId = Number(b.room_id);
- if (!grouped[roomId]) grouped[roomId] = [];
- grouped[roomId].push(b);
- });
- setBookingsByRoom(grouped);
- logger.debug("[LandingPage] SSE update received", grouped);
- }
- },
- onError: (err) => {
- logger.error("[LandingPage] SSE connection error", err);
- },
- });
- return () => {
- es.close();
- };
- }, []);
+ // Unified bookings state from context
+ const { bookings } = useBookings();
// Select bookings for the selected room
- const bookings = selectedRoomId ? bookingsByRoom[selectedRoomId] || [] : [];
+ // Only show bookings for today
+ function isToday(dateStr: string): boolean {
+ const d = new Date(dateStr);
+ const now = new Date();
+ return (
+ d.getFullYear() === now.getFullYear() &&
+ d.getMonth() === now.getMonth() &&
+ d.getDate() === now.getDate()
+ );
+ }
+ const filteredBookings = selectedRoomId
+ ? bookings.filter(
+ (b: import("../interfaces").Booking) =>
+ String(b.room_id) === String(selectedRoomId) &&
+ b.start_time &&
+ isToday(b.start_time)
+ )
+ : [];
// Display skeleton loaders during data fetching
- if (roomsLoading) {
- return (
-
-
-
-
- {[...Array(3)].map((_, i) => (
-
- ))}
-
-
-
-
-
- {[...Array(3)].map((_, i) => (
-
- ))}
-
-
-
- );
- }
-
- // Display error message with retry option
- if (roomsError) {
- logger.error("[LandingPage] Rooms error", roomsError);
- return (
-
-
-
- {roomsError?.message || "Failed to fetch data."}
-
-
-
-
- );
- }
return (
@@ -260,7 +146,7 @@ const LandingPage: FC = () => {
display: "flex",
alignItems: "center",
fontWeight: 600,
- color: "var(--color-on-primary)",
+ color: "var(--color-white)",
}}
>
{
height: "100%",
}}
>
-
+
diff --git a/frontend/src/styles/App.css b/frontend/src/styles/App.css
index ff82b5dd..ad71d4a9 100644
--- a/frontend/src/styles/App.css
+++ b/frontend/src/styles/App.css
@@ -18,19 +18,19 @@
/* Header styles */
.App-header {
- background-color: #282c34;
+ background-color: var(--color-app-bg, #282c34);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
- color: white;
+ color: var(--color-app-text, white);
}
/* Link styles */
.App-link {
- color: #61dafb;
+ color: var(--color-app-accent, #61dafb);
}
/* Keyframes for logo spin animation */
diff --git a/frontend/src/styles/BookingForm.css b/frontend/src/styles/BookingForm.css
index b76dd53e..3506dcdd 100644
--- a/frontend/src/styles/BookingForm.css
+++ b/frontend/src/styles/BookingForm.css
@@ -13,14 +13,14 @@
/* Unavailable menu item styles */
.booking-form-menu-item-unavailable {
- color: #b0b0b0;
+ color: var(--color-form-disabled, #b0b0b0);
margin-left: 8px;
font-size: 13px;
}
/* Invitee chip styles */
.booking-form-invitee-chip {
- color: #1976d2;
+ color: var(--color-primary, #1976d2);
margin-right: 8px;
}
diff --git a/frontend/src/styles/BookingList.css b/frontend/src/styles/BookingList.css
index f42c07ed..445d943c 100644
--- a/frontend/src/styles/BookingList.css
+++ b/frontend/src/styles/BookingList.css
@@ -12,7 +12,7 @@
/* Booking time styles */
.booking-list-time {
font-weight: 600;
- color: #23272f;
+ color: var(--color-on-surface, #23272f);
font-size: 1rem;
}
@@ -20,7 +20,7 @@
.booking-list-invitees {
display: block;
font-size: 0.85em;
- color: #555;
+ color: var(--color-on-surface-secondary, #555);
font-style: italic;
margin-top: 2px;
}
diff --git a/frontend/src/styles/BookingPage.css b/frontend/src/styles/BookingPage.css
new file mode 100644
index 00000000..3db57bb3
--- /dev/null
+++ b/frontend/src/styles/BookingPage.css
@@ -0,0 +1,12 @@
+.bookingpage-loading {
+ padding: 24px;
+}
+
+.bookingpage-error {
+ padding: 24px;
+ color: var(--color-error, red);
+}
+
+.bookingpage-content {
+ padding: 24px;
+}
diff --git a/frontend/src/styles/CalendarView.css b/frontend/src/styles/CalendarView.css
index 66a5b0b7..cfd38ae5 100644
--- a/frontend/src/styles/CalendarView.css
+++ b/frontend/src/styles/CalendarView.css
@@ -1,3 +1,22 @@
+/* Override inline styles if present */
+.fc-event[style],
+.fc-daygrid-event[style],
+.fc-event-main[style],
+.fc-event-main-frame[style] {
+ background: inherit !important;
+ background-color: inherit !important;
+}
+/* Force FullCalendar event blocks to use the correct background and text color */
+.fc-event,
+.fc-daygrid-event {
+ background-color: var(
+ --fc-event-bg-color,
+ var(--color-primary, #1976d2)
+ ) !important;
+ color: var(--color-white, #fff) !important;
+ border-radius: 4px !important;
+ border: none !important;
+}
/* Inline event styles from CalendarViewInline.css */
.calendar-event-content {
width: 100%;
@@ -8,6 +27,8 @@
border-radius: 4px;
padding-left: 4px;
padding-right: 4px;
+ background-color: var(--fc-event-bg-color, var(--color-primary, #1976d2));
+ color: var(--color-white, #fff);
}
.fc-event-title-ellipsis {
@@ -23,7 +44,7 @@
/* Non-business hours styling */
.fc-timegrid-col-bg .fc-non-business,
.fc-non-business {
- background: #888 !important;
+ background: var(--color-calendar-nonbusiness, #888) !important;
opacity: 0.5 !important;
}
@@ -53,54 +74,57 @@
.fc .fc-daygrid-day.fc-day-sun .fc-daygrid-day-frame,
.fc .fc-timegrid-col.fc-day-sat,
.fc .fc-timegrid-col.fc-day-sun {
- background: #888 !important; /* unified non-business color */
- border-color: #bbb !important;
+ background: var(
+ --color-calendar-nonbusiness,
+ #888
+ ) !important; /* unified non-business color */
+ border-color: var(--color-calendar-border, #bbb) !important;
}
.fc .fc-daygrid-day.fc-day-sat .fc-daygrid-day-number,
.fc .fc-daygrid-day.fc-day-sun .fc-daygrid-day-number {
- color: #e0e0e0 !important;
+ color: var(--color-calendar-title, #e0e0e0) !important;
font-weight: 500 !important;
opacity: 0.7 !important;
}
/* Weekday non-business hours */
.fc .fc-non-business {
- background: #888 !important;
+ background: var(--color-calendar-nonbusiness, #888) !important;
opacity: 0.5 !important;
}
/* Workday all-day and time slots */
.fc .fc-daygrid-day .fc-daygrid-day-frame,
.fc .fc-timegrid-col {
- background: #c0c0c0 !important;
+ background: var(--color-calendar-blocked, #c0c0c0) !important;
}
/* Lighter border for all-day row on weekends */
.fc .fc-daygrid-day.fc-day-sat,
.fc .fc-daygrid-day.fc-day-sun {
- border-color: #bbb !important;
+ border-color: var(--color-calendar-border, #bbb) !important;
}
/* Out-of-month days styling */
.fc .fc-daygrid-day.fc-day-other .fc-daygrid-day-frame {
- background: #aaa !important;
+ background: var(--color-calendar-muted, #aaa) !important;
opacity: 1 !important;
}
/* =============================== */
/* Calendar cell backgrounds */
/* =============================== */
:root {
- --fc-page-bg-color: #a0a0a0;
- --fc-neutral-bg-color: #e3f2fd;
- --fc-today-bg-color: #e3f2fd;
- --fc-event-bg-color: #1976d2;
- --fc-border-color: #707070;
+ --fc-page-bg-color: var(--color-page-bg, #a0a0a0);
+ --fc-neutral-bg-color: var(--color-neutral-bg, #e3f2fd);
+ --fc-today-bg-color: var(--color-today-bg, #e3f2fd);
+ --fc-event-bg-color: var(--color-event-bg, #1976d2);
+ --fc-border-color: var(--color-border, #707070);
}
.calendarContainer {
padding: 2rem;
- background: #f8fafc !important;
+ background: var(--color-calendar-bg, #f8fafc) !important;
border-radius: 16px !important;
box-shadow: 0 4px 24px rgba(30, 64, 175, 0.08) !important;
/* Responsive container, no fixed height */
@@ -118,8 +142,8 @@
}
.fc .fc-toolbar {
- background: #1976d2 !important;
- color: #fff !important;
+ background: var(--color-primary, #1976d2) !important;
+ color: var(--color-white, #fff) !important;
border-radius: 12px 12px 0 0 !important;
padding: 0.5rem 1rem !important;
}
@@ -130,8 +154,8 @@
}
.fc .fc-button {
- background: #1976d2 !important;
- color: #fff !important;
+ background: var(--color-primary, #1976d2) !important;
+ color: var(--color-white, #fff) !important;
border: none !important;
border-radius: 6px !important;
padding: 0.3rem 0.8rem !important;
@@ -157,14 +181,15 @@
.fc .fc-daygrid-day-number {
font-weight: 600 !important;
- color: #1976d2 !important;
+ color: var(--color-calendar-event, #1976d2) !important;
}
.fc .fc-event {
- color: #fff !important;
+ color: var(--color-white, #fff) !important;
padding: 2px 6px !important;
font-size: 0.95rem !important;
box-shadow: 0 2px 8px rgba(30, 64, 175, 0.12) !important;
+ overflow: hidden !important;
}
.fc .fc-event:hover {
@@ -193,7 +218,7 @@
}
.fc .fc-daygrid-day.fc-day-other .fc-daygrid-day-number {
- color: #fff !important;
+ color: var(--color-white, #fff) !important;
opacity: 1 !important;
font-weight: 600 !important;
}
@@ -217,61 +242,61 @@
* These classes are mapped in the frontend logic (getRoomClass).
*/
.room-color-1 {
- background: var(--room-color-1) !important;
+ background: var(--room-color-1, #1976d2) !important;
}
.room-color-2 {
- background: var(--room-color-2) !important;
+ background: var(--room-color-2, #388e3c) !important;
}
.room-color-3 {
- background: var(--room-color-3) !important;
+ background: var(--room-color-3, #fbc02d) !important;
}
.room-color-4 {
- background: var(--room-color-4) !important;
+ background: var(--room-color-4, #d32f2f) !important;
}
.room-color-5 {
- background: var(--room-color-5) !important;
+ background: var(--room-color-5, #7b1fa2) !important;
}
.room-color-6 {
- background: var(--room-color-6) !important;
+ background: var(--room-color-6, #0288d1) !important;
}
.room-color-7 {
- background: var(--room-color-7) !important;
+ background: var(--room-color-7, #c2185b) !important;
}
.room-color-8 {
- background: var(--room-color-8) !important;
+ background: var(--room-color-8, #ffa000) !important;
}
.room-color-9 {
- background: var(--room-color-9) !important;
+ background: var(--room-color-9, #009688) !important;
}
.room-color-10 {
- background: var(--room-color-10) !important;
+ background: var(--room-color-10, #8bc34a) !important;
}
.room-color-11 {
- background: var(--room-color-11) !important;
+ background: var(--room-color-11, #e91e63) !important;
}
.room-color-12 {
- background: var(--room-color-12) !important;
+ background: var(--room-color-12, #00bcd4) !important;
}
.room-color-13 {
- background: var(--room-color-13) !important;
+ background: var(--room-color-13, #ff5722) !important;
}
.room-color-14 {
- background: var(--room-color-14) !important;
+ background: var(--room-color-14, #9c27b0) !important;
}
.room-color-15 {
- background: var(--room-color-15) !important;
+ background: var(--room-color-15, #3f51b5) !important;
}
.room-color-16 {
- background: var(--room-color-16) !important;
+ background: var(--room-color-16, #4caf50) !important;
}
.room-color-17 {
- background: var(--room-color-17) !important;
+ background: var(--room-color-17, #ff9800) !important;
}
.room-color-18 {
- background: var(--room-color-18) !important;
+ background: var(--room-color-18, #607d8b) !important;
}
.room-color-19 {
- background: var(--room-color-19) !important;
+ background: var(--room-color-19, #795548) !important;
}
/* Ensure event text is readable on colored backgrounds */
@@ -295,5 +320,5 @@
.room-color-17,
.room-color-18,
.room-color-19 {
- color: #fff !important;
+ color: var(--color-white, #fff) !important;
}
diff --git a/frontend/src/styles/LandingPage.css b/frontend/src/styles/LandingPage.css
index 262bc545..df72ee1b 100644
--- a/frontend/src/styles/LandingPage.css
+++ b/frontend/src/styles/LandingPage.css
@@ -1,7 +1,10 @@
/* Selected room button styles */
.MuiButtonBase-root.MuiListItemButton-root.Mui-selected {
- background-color: #afafaf !important; /* Only use !important if necessary for Material UI override */
- border: 1px solid #23272f;
+ background-color: var(
+ --color-landing-bg,
+ #afafaf
+ ) !important; /* Only use !important if necessary for Material UI override */
+ border: 1px solid var(--color-on-surface, #23272f);
border-radius: 16px;
margin: 1px;
}
@@ -69,7 +72,7 @@
padding: 2rem 1.5rem 1.5rem 1.5rem;
margin-bottom: 0;
transition: none;
- color: #222;
+ color: var(--color-white, #fff);
flex: 1 1 0;
min-height: 60vh;
display: flex;
@@ -94,7 +97,7 @@
.landing-hr-wide {
width: 420px;
border: none;
- border-top: 2px solid #444;
+ border-top: 2px solid var(--color-landing-divider, #444);
margin: 0.5rem auto 1.5rem auto;
background: none;
}
@@ -106,7 +109,11 @@
width: 100vw;
text-align: center;
padding-bottom: 2rem;
- background: linear-gradient(to top, #fff 80%, rgba(255, 255, 255, 0));
+ background: linear-gradient(
+ to top,
+ var(--color-white, #fff) 80%,
+ rgba(255, 255, 255, 0)
+ );
z-index: 10;
display: flex;
flex-direction: column;
@@ -121,7 +128,7 @@
.landing-hr {
width: 220px;
border: none;
- border-top: 2px solid #e0e0e0;
+ border-top: 2px solid var(--color-calendar-title, #e0e0e0);
margin: 0.5rem auto 1.5rem auto;
background: none;
}
@@ -160,9 +167,9 @@
border-radius: 0;
box-shadow: none;
border: none;
- background: #fff;
+ background: var(--color-white, #fff);
padding: 1.2rem 0.5rem 1.5rem 0.5rem;
- color: #23272f;
+ color: var(--color-on-surface, #23272f);
}
.landing-book-btn-desktop {
display: none;
@@ -221,6 +228,6 @@
.landing-hr-wide {
width: 80vw;
margin: 0.5rem auto 1.5rem auto;
- border-top: 2px solid #e0e0e0;
+ border-top: 2px solid var(--color-calendar-title, #e0e0e0);
}
}
diff --git a/frontend/src/styles/RoomList.css b/frontend/src/styles/RoomList.css
index 49269fba..4e07fad7 100644
--- a/frontend/src/styles/RoomList.css
+++ b/frontend/src/styles/RoomList.css
@@ -11,7 +11,7 @@
.room-list-header {
flex: 0 0 auto;
border-bottom: 1px solid #e0e0e0;
- background-color: #1976d2;
+ background-color: var(--color-primary, #1976d2);
margin-bottom: 0;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
@@ -30,7 +30,7 @@
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
- background-color: #f7f8fa;
+ background-color: var(--color-roomlist-bg, #f7f8fa);
color: #23272f;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
@@ -38,7 +38,7 @@
/* Selected room list item styles */
.room-list-item-selected {
- background-color: #ececec !important;
+ background-color: var(--color-roomlist-selected, #ececec) !important;
font-weight: 700;
color: #23272f;
border: 1px solid #23272f;
diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts
index 488e4a11..ebc95225 100644
--- a/frontend/src/theme.ts
+++ b/frontend/src/theme.ts
@@ -1,12 +1,13 @@
/**
* theme.ts
* MUI theme configuration for the frontend.
- *
- * Author: Cliff Hill
- * Last updated: 2025-09-08
+ * @module
*/
+// MUI imports
import { createTheme } from "@mui/material/styles";
+
+// Internal helper imports
import { getColors } from "./constants";
/**
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index f7229a26..eee8223a 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -1,10 +1,9 @@
/**
* types.ts
* Centralized TypeScript types for frontend application.
- * Author: Cliff Hill
- * Last updated: 2025-09-09
+ * @module
*/
-
+// ...no imports in this file...
/**
* Represents a booking event for display in the calendar.
* @property originalBooking - Optional reference to the original booking object
diff --git a/frontend/src/utils/logger.ts b/frontend/src/utils/logger.ts
index f869d4f6..1662ab71 100644
--- a/frontend/src/utils/logger.ts
+++ b/frontend/src/utils/logger.ts
@@ -1,13 +1,22 @@
/**
* logger.ts
* Centralized logger using loglevel, with level set from FRONTEND_LOG_LEVEL.
- *
- * Author: Cliff Hill
- * Last updated: 2025-09-08
+ * @module
*/
+
+// External imports
import log, { LogLevelDesc } from "loglevel";
-const level = (process.env.FRONTEND_LOG_LEVEL || "info") as LogLevelDesc;
+/**
+ * Gets the log level from environment and sets it for loglevel.
+ * @type {LogLevelDesc}
+ */
+const level: LogLevelDesc = (process.env.FRONTEND_LOG_LEVEL ||
+ "info") as LogLevelDesc;
log.setLevel(level);
+/**
+ * Centralized logger instance for frontend logging.
+ * @type {log.Logger}
+ */
export const logger = log;