Skip to content

Backend work for Smart Room Booking & Clash Detection#233

Open
gmarav05 wants to merge 3 commits intoOpenLake:mainfrom
gmarav05:main
Open

Backend work for Smart Room Booking & Clash Detection#233
gmarav05 wants to merge 3 commits intoOpenLake:mainfrom
gmarav05:main

Conversation

@gmarav05
Copy link

@gmarav05 gmarav05 commented Feb 25, 2026


name: " Pull Request"
about: Propose and submit changes to the project for review
title: "PR: [Brief Description of Changes]"
labels: ""
assignees: harshitap1305, sakshi1755

Related Issue


Changes Introduced

  • Added: Room and RoomBooking schemas, controller, and routes for room management, booking, clash detection, and status updates.
  • Fixed: N/A
  • Updated: Registered new routes in backend server, added sample room seeding logic.
  • Removed: N/A

Why This Change?

  • Problem: No centralized room booking system; manual clash detection was error-prone.
  • Solution: Introduces robust backend for booking rooms, automatic clash detection, and admin approval workflow.
  • Impact: Streamlines event coordination, prevents double-booking, and improves admin workflow.

Screenshots / Video (if applicable)

Before After

Testing

  • Ran unit tests and all passed (npm test in the relevant directory).
  • Manually tested the following scenarios:
    • Test Case 1: [Describe steps + Expected Result]
    • Test Case 2: [Describe steps + Expected Result]
  • Tested on different browsers (Chrome, Firefox) for UI changes.
  • Verified there are no new console warnings or errors.

Documentation Updates

  • Updated the README.md with new instructions.
  • Added clear code comments where logic is complex.
  • N/A

Checklist

  • I have created a new branch for this PR (git checkout -b feature/my-amazing-feature).
  • I have starred the repository.
  • My code follows the project's coding style and conventions.
  • My commit messages are clear and follow the project's guidelines.
  • I have performed a self-review of my own code.
  • I have added tests that prove my fix is effective or that my feature works.
  • All new and existing tests passed locally with my changes.
  • This PR introduces no breaking changes (or they are clearly documented).

Deployment Notes

  • Requires a database migration/schema update.
  • Requires new environment variables to be set.
  • N/A

💬 Additional Notes

  • [Add any other context or information here.]

Summary by CodeRabbit

  • New Features
    • Room management: create and view rooms with capacity, location and amenities.
    • Room booking: reserve rooms for date/time with automatic conflict detection.
    • Availability checking: view bookings for specific dates and rooms.
    • Booking administration: approve or reject pending reservations.
    • Booking cancellation: cancel reservations with role-based permissions.
    • Bookings list: retrieve bookings with filters (room, date, status).
  • Chores
    • Initial sample rooms added for onboarding.

@vercel
Copy link

vercel bot commented Feb 25, 2026

@gmarav05 is attempting to deploy a commit to the openlake's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0bccbc and 3528b0f.

📒 Files selected for processing (1)
  • backend/seed.js

Walkthrough

Adds Room and RoomBooking Mongoose models, a new roomBookingController with seven route handlers for room lifecycle and booking flows (create, list, book w/ clash prevention, availability, status update, cancel, query), Express routes mounted at /api/rooms, and seed data for rooms.

Changes

Cohort / File(s) Summary
Data Models
backend/models/schema.js
Introduced Room and RoomBooking schemas/models, timestamps, status enum, and an index on room/date/time; exported both models.
Controller
backend/controllers/roomBookingController.js
Added seven handlers: createRoom, getAllRooms, bookRoom (clash detection), getAvailability, updateBookingStatus, cancelBooking, getBookings with standard status codes and error handling.
Routes
backend/routes/roomBooking.js
New Express router registering endpoints for room creation, listing, booking, availability, booking queries, status updates, and cancellation; applies auth and role checks; exports router.
App Integration
backend/index.js
Mounted new routes: app.use("/api/rooms", roomBookingRoutes).
Seeding
backend/seed.js
Imported Room, added sampleRooms and seedRooms() routine; integrated call into seed flow.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Routes as /api/rooms Routes
    participant Controller as roomBookingController
    participant Models as Room/RoomBooking Models
    participant DB as Database

    Client->>Routes: POST /api/rooms/book<br/>(roomId, date, startTime, endTime)
    Routes->>Controller: bookRoom(req)
    Controller->>Models: RoomBooking.find(clash query)
    Models->>DB: Query bookings (pending/approved)
    DB-->>Models: Return results
    alt Clash Detected
        Models-->>Controller: conflict
        Controller-->>Routes: 409 Conflict
        Routes-->>Client: 409 Booking clash
    else No Clash
        Controller->>Models: RoomBooking.create(payload)
        Models->>DB: Insert booking
        DB-->>Models: Created booking
        Models-->>Controller: booking
        Controller-->>Routes: 201 Created
        Routes-->>Client: 201 Booking confirmed
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through models, routes, and seed,
I stitched bookings where conflicts bleed.
A nibble here, a timestamp there,
Rooms now answer every care —
Hop on, book, and take your seat! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR partially addresses issue #231 by adding room schemas and basic CRUD endpoints, but lacks required endpoints (GET /room and GET /room/:room_id) with occupancy detection and live status fields as specified in issue #231 acceptance criteria. Implement the two required endpoints (GET /room and GET /room/:room_id) with live occupancy status detection based on active bookings, including status, current_event, and occupied_until fields as defined in issue #231.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main changes introduced: backend implementation for room booking system with clash detection capabilities.
Description check ✅ Passed The PR description follows the provided template structure and includes related issue, changes introduced, problem statement, solution, and impact. However, all testing and documentation checklist items remain unchecked.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing room booking backend functionality. The addition of schemas, controllers, routes, and seeding logic align with the stated objective of supporting room management and booking workflows.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/controllers/roomBookingController.js`:
- Around line 65-66: The populated bookedBy user documents are exposing full
user data; update both populate calls (the one using
RoomBooking.find(...).populate('room event bookedBy') and the similar populate
at lines ~121-122) to restrict fields by using the object form: populate({ path:
'bookedBy', select: '_id name email' }) (or whichever minimal safe fields your
User model exposes), e.g., replace the string 'bookedBy' with the object
selector so only safe user fields are returned while keeping room and event
populations unchanged.
- Around line 19-23: getAllRooms currently returns only static Room docs from
Room.find and must be extended to include live status fields (status,
current_event, occupied_until). After fetching rooms in getAllRooms, retrieve
live occupancy data (from your RoomStatus/Occupancy store or service) for each
room (e.g., via RoomStatus.findOne({ roomId }) or
RoomStatusService.getStatus(room.id)), merge those fields into each room object,
and then send the augmented array with res.json; perform the per-room lookups
concurrently with Promise.all to avoid serial waits and handle missing status
records by supplying sensible defaults.
- Around line 31-39: The clash logic in the RoomBooking.findOne query ignores
the booking date and doesn’t validate time ranges; update the controller to
first validate that startTime < endTime (reject requests where startTime >=
endTime) and then include the booking date in the query (e.g., filter by date or
by combining date+time) so only bookings on the same date are checked; keep the
existing room: roomId and status: { $in: ['Pending','Approved'] } filters and
use the same overlap condition ({ startTime: { $lt: endTime }, endTime: { $gt:
startTime } }) but applied together with date to prevent cross-day matches.
- Around line 77-84: Before updating to 'Approved', load the target booking (use
RoomBooking.findById(id)) to get its room and time window, then if status ===
'Approved' run a query for any other booking with status 'Approved' for the same
room that overlaps the target booking's start/end (e.g. findOne({ _id: { $ne: id
}, room: booking.room, status: 'Approved', $or: [{ start: { $lt: booking.end,
$gte: booking.start } }, { end: { $gt: booking.start, $lte: booking.end } }, {
start: { $lte: booking.start }, end: { $gte: booking.end } }] }) ) and if one
exists return a 409/conflict; only then perform the update
(RoomBooking.findByIdAndUpdate or findOneAndUpdate). Ensure you reference
RoomBooking.findById and RoomBooking.findByIdAndUpdate in the change.

In `@backend/models/schema.js`:
- Around line 648-666: The roomSchema is missing the API-required fields room_id
and allowed_roles; update the mongoose roomSchema to add a room_id field
(String, required, unique — generated if absent via a default function or a
pre('save') hook using UUID logic) and an allowed_roles field (Array of String,
required or with a sensible default like []), and ensure any creation/update
code that constructs Room documents (e.g., create handlers or Model.create
usages) no longer omit room_id so responses match the API contract; keep the
symbol name roomSchema and adjust indexes/validation as needed.

In `@backend/routes/roomBooking.js`:
- Around line 11-13: The route paths are incorrect: change router.get('/rooms',
isAuthenticated, roomBookingController.getAllRooms) to router.get('/',
isAuthenticated, roomBookingController.getAllRooms) so the mounted /api/rooms
yields GET /api/rooms; add a new parameterized route router.get('/:room_id',
isAuthenticated, roomBookingController.getRoomById) (or the appropriate
controller method that returns room details/status) to expose GET
/api/rooms/:room_id; also update the other similar occurrence (the one around
line 35) to use '/' or '/:room_id' instead of duplicating "rooms". Ensure the
referenced controller method name matches an exported function in
roomBookingController.

In `@backend/seed.js`:
- Around line 26-31: The seedRooms function is defined but never invoked, so
sampleRooms are not inserted; call seedRooms from the main seeding flow (for
example inside the top-level async seed/run function or after other seed calls)
or add it to the Promise.all/await chain that runs other seed helpers so it
executes during seeding; locate the seedRooms function and ensure the script
invokes seedRooms() (or exports and calls it where other seeds like
seedUsers/seedBookings are executed) so rooms get inserted on a fresh seed.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64a42c1 and a0bccbc.

📒 Files selected for processing (5)
  • backend/controllers/roomBookingController.js
  • backend/index.js
  • backend/models/schema.js
  • backend/routes/roomBooking.js
  • backend/seed.js

Comment on lines +19 to +23
exports.getAllRooms = async (_req, res) => {
try {
const rooms = await Room.find({ is_active: true });
res.json(rooms);
} catch (err) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

getAllRooms does not return live occupancy/status fields.

The current response returns only static room docs, so it does not satisfy the required live fields (status, current_event, occupied_until) for room status APIs.

💡 Proposed fix (shape)
 exports.getAllRooms = async (_req, res) => {
   try {
-    const rooms = await Room.find({ is_active: true });
-    res.json(rooms);
+    const now = new Date();
+    const rooms = await Room.find({ is_active: true }).lean();
+    const activeBookings = await RoomBooking.find({
+      status: 'Approved',
+      startTime: { $lte: now },
+      endTime: { $gte: now },
+    }).populate('event', 'title').lean();
+
+    const activeByRoom = new Map(activeBookings.map(b => [String(b.room), b]));
+    const payload = rooms.map(room => {
+      const active = activeByRoom.get(String(room._id));
+      return {
+        ...room,
+        status: active ? 'occupied' : 'vacant',
+        current_event: active?.event?.title ?? null,
+        occupied_until: active?.endTime ?? null,
+      };
+    });
+    res.json(payload);
   } catch (err) {
     res.status(500).json({ message: 'Error fetching rooms' });
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/roomBookingController.js` around lines 19 - 23,
getAllRooms currently returns only static Room docs from Room.find and must be
extended to include live status fields (status, current_event, occupied_until).
After fetching rooms in getAllRooms, retrieve live occupancy data (from your
RoomStatus/Occupancy store or service) for each room (e.g., via
RoomStatus.findOne({ roomId }) or RoomStatusService.getStatus(room.id)), merge
those fields into each room object, and then send the augmented array with
res.json; perform the per-room lookups concurrently with Promise.all to avoid
serial waits and handle missing status records by supplying sensible defaults.

Comment on lines +31 to +39
const { roomId, eventId, date, startTime, endTime, purpose } = req.body;
// Check for clash
const clash = await RoomBooking.findOne({
room: roomId,
status: { $in: ['Pending', 'Approved'] },
$or: [
{ startTime: { $lt: endTime }, endTime: { $gt: startTime } },
],
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Clash detection is incorrect: it ignores booking date and accepts invalid time ranges.

This can report clashes across different days and allows startTime >= endTime.

💡 Proposed fix
   try {
     const { roomId, eventId, date, startTime, endTime, purpose } = req.body;
+    const bookingDate = new Date(date);
+    const start = new Date(startTime);
+    const end = new Date(endTime);
+
+    if ([bookingDate, start, end].some(d => Number.isNaN(d.getTime()))) {
+      return res.status(400).json({ message: 'Invalid date/time format' });
+    }
+    if (start >= end) {
+      return res.status(400).json({ message: 'startTime must be before endTime' });
+    }
+
     // Check for clash
     const clash = await RoomBooking.findOne({
       room: roomId,
+      date: bookingDate,
       status: { $in: ['Pending', 'Approved'] },
-      $or: [
-        { startTime: { $lt: endTime }, endTime: { $gt: startTime } },
-      ],
+      startTime: { $lt: end },
+      endTime: { $gt: start },
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/roomBookingController.js` around lines 31 - 39, The clash
logic in the RoomBooking.findOne query ignores the booking date and doesn’t
validate time ranges; update the controller to first validate that startTime <
endTime (reject requests where startTime >= endTime) and then include the
booking date in the query (e.g., filter by date or by combining date+time) so
only bookings on the same date are checked; keep the existing room: roomId and
status: { $in: ['Pending','Approved'] } filters and use the same overlap
condition ({ startTime: { $lt: endTime }, endTime: { $gt: startTime } }) but
applied together with date to prevent cross-day matches.

Comment on lines +65 to +66
const bookings = await RoomBooking.find(query).populate('room event bookedBy');
res.json(bookings);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid returning full bookedBy user documents to all authenticated callers.

Both endpoints currently populate full user objects, which can leak internal/sensitive fields unnecessarily.

💡 Proposed fix
-const bookings = await RoomBooking.find(query).populate('room event bookedBy');
+const bookings = await RoomBooking.find(query).populate('room event').populate({
+  path: 'bookedBy',
+  select: '_id role personal_info.name personal_info.email',
+});

Also applies to: 121-122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/roomBookingController.js` around lines 65 - 66, The
populated bookedBy user documents are exposing full user data; update both
populate calls (the one using RoomBooking.find(...).populate('room event
bookedBy') and the similar populate at lines ~121-122) to restrict fields by
using the object form: populate({ path: 'bookedBy', select: '_id name email' })
(or whichever minimal safe fields your User model exposes), e.g., replace the
string 'bookedBy' with the object selector so only safe user fields are returned
while keeping room and event populations unchanged.

Comment on lines +77 to +84
if (!['Approved', 'Rejected'].includes(status)) {
return res.status(400).json({ message: 'Invalid status' });
}
const booking = await RoomBooking.findByIdAndUpdate(
id,
{ status, reviewedBy: req.user._id, updated_at: new Date() },
{ new: true }
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Approval should re-check for overlapping approved bookings before status update.

Without a final overlap check on approve, edge/race scenarios can still end in double-approved slots.

💡 Proposed fix
   const { id } = req.params;
   const { status } = req.body;
@@
+  const current = await RoomBooking.findById(id);
+  if (!current) return res.status(404).json({ message: 'Booking not found' });
+
+  if (status === 'Approved') {
+    const approvedConflict = await RoomBooking.findOne({
+      _id: { $ne: current._id },
+      room: current.room,
+      date: current.date,
+      status: 'Approved',
+      startTime: { $lt: current.endTime },
+      endTime: { $gt: current.startTime },
+    });
+    if (approvedConflict) {
+      return res.status(409).json({ message: 'Cannot approve due to room clash' });
+    }
+  }
+
   const booking = await RoomBooking.findByIdAndUpdate(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/roomBookingController.js` around lines 77 - 84, Before
updating to 'Approved', load the target booking (use RoomBooking.findById(id))
to get its room and time window, then if status === 'Approved' run a query for
any other booking with status 'Approved' for the same room that overlaps the
target booking's start/end (e.g. findOne({ _id: { $ne: id }, room: booking.room,
status: 'Approved', $or: [{ start: { $lt: booking.end, $gte: booking.start } },
{ end: { $gt: booking.start, $lte: booking.end } }, { start: { $lte:
booking.start }, end: { $gte: booking.end } }] }) ) and if one exists return a
409/conflict; only then perform the update (RoomBooking.findByIdAndUpdate or
findOneAndUpdate). Ensure you reference RoomBooking.findById and
RoomBooking.findByIdAndUpdate in the change.

Comment on lines +648 to +666
const roomSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
},
capacity: {
type: Number,
required: true,
},
location: {
type: String,
required: true,
},
amenities: [
{
type: String,
},
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Room schema is missing required API fields (room_id, allowed_roles).

The linked API contract expects these fields in room responses, but the model currently does not store them.

💡 Proposed fix
 const roomSchema = new mongoose.Schema({
+  room_id: {
+    type: String,
+    required: true,
+    unique: true,
+    index: true,
+  },
   name: {
     type: String,
     required: true,
     unique: true,
   },
@@
   amenities: [
@@
   ],
+  allowed_roles: [
+    {
+      type: String,
+    },
+  ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/schema.js` around lines 648 - 666, The roomSchema is missing
the API-required fields room_id and allowed_roles; update the mongoose
roomSchema to add a room_id field (String, required, unique — generated if
absent via a default function or a pre('save') hook using UUID logic) and an
allowed_roles field (Array of String, required or with a sensible default like
[]), and ensure any creation/update code that constructs Room documents (e.g.,
create handlers or Model.create usages) no longer omit room_id so responses
match the API contract; keep the symbol name roomSchema and adjust
indexes/validation as needed.

Comment on lines +11 to +13
// Get all rooms
router.get('/rooms', isAuthenticated, roomBookingController.getAllRooms);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Route contract does not match the required room APIs.

With the /api/rooms mount, GET /rooms becomes GET /api/rooms/rooms, and there is no GET /api/rooms/:room_id endpoint for room detail/status.

💡 Proposed fix
-// Get all rooms
-router.get('/rooms', isAuthenticated, roomBookingController.getAllRooms);
+// GET /api/rooms
+router.get('/', isAuthenticated, roomBookingController.getAllRooms);
+
+// GET /api/rooms/:room_id
+router.get('/:room_id', isAuthenticated, roomBookingController.getRoomById);

Also applies to: 35-35

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/roomBooking.js` around lines 11 - 13, The route paths are
incorrect: change router.get('/rooms', isAuthenticated,
roomBookingController.getAllRooms) to router.get('/', isAuthenticated,
roomBookingController.getAllRooms) so the mounted /api/rooms yields GET
/api/rooms; add a new parameterized route router.get('/:room_id',
isAuthenticated, roomBookingController.getRoomById) (or the appropriate
controller method that returns room details/status) to expose GET
/api/rooms/:room_id; also update the other similar occurrence (the one around
line 35) to use '/' or '/:room_id' instead of duplicating "rooms". Ensure the
referenced controller method name matches an exported function in
roomBookingController.

@harshitap1305 harshitap1305 self-requested a review March 2, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FOSSOVERFLOW-25] feat: Implement Room Status & Details APIs

2 participants