Skip to content

Feature/room booking frontend#235

Open
gmarav05 wants to merge 2 commits intoOpenLake:mainfrom
gmarav05:feature/room-booking-frontend
Open

Feature/room booking frontend#235
gmarav05 wants to merge 2 commits intoOpenLake:mainfrom
gmarav05:feature/room-booking-frontend

Conversation

@gmarav05
Copy link

@gmarav05 gmarav05 commented Mar 19, 2026


name: "Pull Request"
about: Propose and submit changes to the project for review
title: "PR: Smart Room Booking Frontend Implementation"
labels: ""
assignees: harshitap1305, sakshi1755

Related Issue


Changes Introduced

  • Added: Complete Smart Room Booking frontend UI with:
    • room booking form
    • optional event linking
    • room availability view
    • booking status/actions (approve/reject/cancel)
    • create-room form for allowed roles
  • Fixed: Replaced the old placeholder Room Booking screen so the page is now fully functional.
  • Updated: Integrated Room Booking into dashboard navigation and admin route flow.
  • Removed: Outdated commented/mock Room Booking code and placeholder-only content.

Why This Change?

  • Problem: The Room Booking feature existed in backend but frontend flow was missing/incomplete, so users could not use the booking system end-to-end.
  • Solution: Built and connected a complete frontend booking workflow and linked it into role-based navigation/routing.
  • Impact: Admins and coordinators can now create and manage room bookings with clash visibility and status controls, improving event planning workflow.

Screenshots / Video (if applicable)

Screen Recording 2026-03-19 at 6.35.12 PM.zip


Testing

  • Ran unit tests and all passed (npm test in the relevant directory).
  • Manually tested the following scenarios:
  • Test Case 1: Create a booking with valid room/date/time and verify request appears in booking table with Pending status.
  • Test Case 2: Try overlapping booking slot and verify clash warning/blocked behavior.
  • Tested on different browsers (Chrome, Firefox) for UI changes.
  • Verified there are no new console warnings or errors.
  • Built frontend successfully using npm run build.

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

  • Branch pushed: feature/room-booking-frontend
  • This PR is frontend-focused.
  • Backend login test accounts were validated.

Summary by CodeRabbit

  • New Features

    • Room booking system with real-time availability checking
    • Automatic time-conflict detection during booking requests
    • Booking approval, rejection, and cancellation workflow
    • Room creation and management capabilities for administrators
  • Navigation Updates

    • Room Booking added to dashboard and main navigation menu

@vercel
Copy link

vercel bot commented Mar 19, 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 Mar 19, 2026

Walkthrough

A new Smart Room Booking system was implemented across backend and frontend. Backend introduces room and booking models, REST controllers for room management and booking workflows with clash detection, and authenticated routes with role-based access control. Frontend replaces a basic form with a comprehensive interface featuring room creation, booking management with clash warning, availability filtering, status updates, and cancellation with role-specific actions.

Changes

Cohort / File(s) Summary
Backend Room & Booking Data Models
backend/models/schema.js
Added Room and RoomBooking Mongoose models with fields for capacity, amenities, booking status tracking, timestamps, and a compound index on RoomBooking for room/date/time queries.
Backend API Controllers & Routes
backend/controllers/roomBookingController.js, backend/routes/roomBooking.js
Implemented 7 controller endpoints (createRoom, getAllRooms, bookRoom, getAvailability, updateBookingStatus, cancelBooking, getBookings) with validation, clash detection for overlapping time ranges, role-based authorization (admin-only creation/booking, president/general secretary status updates), and authenticated cancel route.
Backend Integration
backend/index.js, backend/seed.js
Mounted room booking router at /api/rooms. Added sample room data and seedRooms function (defined but not invoked in seeding flow).
Frontend Smart Room Booking Component
frontend/src/Components/RoomBooking.jsx
Replaced simple form with full-featured component: room creation with amenities, real-time availability checking, clash detection with warnings, booking submission with event linking, filtering by room/date/status, role-aware actions (approve/reject/cancel), and loading/error/success UI states.
Frontend Page & Navigation
frontend/src/pages/roomBookingPage.jsx, frontend/src/config/dashboardComponents.js, frontend/src/config/navbarConfig.js
Added RoomBookingPage wrapper component, registered roombooking route in dashboard, and added "Room Booking" navigation entry with DoorOpen icon for President and Club Coordinator roles.
Frontend Route Access Control
frontend/src/routes/AdminRoutes.js
Updated /roombooking route to render RoomBookingPage and expanded allowedRoles from ALL_ADMIN_ROLES to include CLUB_COORDINATOR.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Frontend
    participant API as Backend API
    participant DB as Database

    User->>Frontend: Selects room, date, time slot
    Frontend->>API: GET /api/rooms/availability
    API->>DB: Query bookings by room & date
    DB-->>API: Return pending/approved bookings
    API-->>Frontend: Return availability data
    
    alt Time slot clashes
        Frontend->>User: Display "Clash Warning"
    else No clash detected
        User->>Frontend: Submit booking request
        Frontend->>API: POST /api/rooms/book
        API->>API: Validate time range clash
        alt Clash found
            API-->>Frontend: 409 Conflict
            Frontend->>User: Show conflicting booking
        else No clash
            API->>DB: Create RoomBooking (Pending)
            DB-->>API: Booking created
            API-->>Frontend: 201 + booking details
            Frontend->>API: GET /api/rooms/bookings
            API->>DB: Fetch all bookings (populated)
            DB-->>API: Return bookings with room/event/user
            API-->>Frontend: Updated booking list
            Frontend->>User: Refresh & display new booking
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The review spans heterogeneous changes across backend models, controllers, routes, and a comprehensive frontend rewrite. Key areas requiring careful attention: clash detection logic correctness, authorization middleware enforcement across role-based endpoints (president vs. general secretary distinction), request body validation in booking submission, role-aware UI state management in the component, and API integration patterns with the shared api client.

Poem

🐰 Room keys click, bookings flow free,
Clashes caught before they be,
Roles align, approvals dance,
Smart bookings take their chance! ✨🚪

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR claims to close issue #234 (Style: Frontend for Smart Room Booking & Clash Detection), but the actual changes are predominantly backend implementation (models, controllers, routes, seed data) plus frontend UI/integration. The issue scope is purely frontend styling/UX improvements, not backend feature development. Verify issue #234 requirements are met (spacing improvements, clearer headers, clash warning prominence, card grouping, accessibility) and confirm the backend changes belong to this issue or should be split to a separate issue.
Out of Scope Changes check ⚠️ Warning The PR includes substantial out-of-scope backend changes: new Room and RoomBooking models, 7 controller endpoints, new routes, and seed data. Issue #234 is frontend styling-only with no backend requirements, making these backend additions out of scope. Either move backend changes (models, controllers, routes, seed.js) to a separate backend feature issue, or clarify if issue #234 should encompass backend implementation and update the issue description accordingly.
Title check ❓ Inconclusive The PR title 'Feature/room booking frontend' is too vague and generic. It lacks specificity about what was actually implemented—it doesn't convey the main changes like UI improvements, clash detection, or styling enhancements. Revise title to be more descriptive, e.g., 'Add styled Room Booking UI with clash detection and booking management' or 'Implement Smart Room Booking frontend with improved UX and warnings'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is mostly complete with all major template sections filled (Related Issue, Changes, Why, Testing, Deployment Notes). However, Testing section is only partially checked (unit tests still missing) and a video is provided but no before/after comparison images.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

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: 3

🧹 Nitpick comments (4)
frontend/src/Components/RoomBooking.jsx (2)

514-538: Consider adding explicit label-input associations for better accessibility.

The labels are nested correctly, but adding explicit id and htmlFor attributes would improve screen reader compatibility and is a best practice for form accessibility.

♿ Example for one input
             <div>
-              <label className="block text-xs font-medium text-slate-600 mb-1">
+              <label htmlFor="room-select" className="block text-xs font-medium text-slate-600 mb-1">
                 Room
               </label>
               <select
+                id="room-select"
                 value={bookingForm.roomId}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/RoomBooking.jsx` around lines 514 - 538, Add explicit
id/htmlFor attributes to associate the Room label with its select input in the
RoomBooking component: give the select a stable id (e.g., "room-select") and set
the enclosing label's htmlFor to that id so screen readers correctly link the
label to bookingForm.roomId; update the JSX around bookingForm.roomId,
setBookingForm, and the rooms mapping accordingly and ensure the id is unique
within the form.

346-376: Consider adding loading states to prevent duplicate actions.

The handleReviewBooking and handleCancelBooking functions don't disable their buttons during the API call, which could allow users to trigger duplicate requests by clicking rapidly.

🔄 Example approach using a processing state
+ const [processingId, setProcessingId] = useState(null);

  const handleReviewBooking = async (bookingId, status) => {
    try {
      setError("");
      setSuccessMessage("");
+     setProcessingId(bookingId);

      await api.put(`/api/rooms/bookings/${bookingId}/status`, { status });
      // ...
    } catch (err) {
      // ...
+   } finally {
+     setProcessingId(null);
    }
  };

Then in the button:

<button
  onClick={() => handleReviewBooking(booking._id, "Approved")}
+ disabled={processingId === booking._id}
  // ...
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/RoomBooking.jsx` around lines 346 - 376, Add a
per-booking "processing" state and use it inside handleReviewBooking and
handleCancelBooking to prevent duplicate clicks: introduce a state like
processingId (with setter setProcessingId) and at the start of each handler
setProcessingId(bookingId), then clear it in a finally block
(setProcessingId(null)) so errors still reset the flag; update the buttons that
call handleReviewBooking and handleCancelBooking to include
disabled={processingId === booking._id} (or equivalent) so the UI disables the
specific booking action while its API call is in-flight.
backend/controllers/roomBookingController.js (2)

114-126: Same date matching consideration applies here.

The date filter at line 119 has the same exact-match fragility mentioned in getAvailability. Consider applying the same date range approach for consistency.

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

In `@backend/controllers/roomBookingController.js` around lines 114 - 126, The
getBookings handler currently sets query.date = new Date(date) which requires
exact-match; change it to use a date range like in getAvailability: compute
startOfDay and endOfDay for the passed date and set query.date = { $gte:
startOfDay, $lt: endOfDay } so RoomBooking.find(query).populate('room event
bookedBy') returns all bookings for that calendar day; update getBookings to
parse the incoming date string, create start/end Date objects, and assign the
range to query.date instead of a single Date object.

99-104: Use centralized role constants instead of hardcoding.

The role list duplicates ROLE_GROUPS.ADMIN from backend/utils/roles.js. If roles change, this code would need separate updates.

♻️ Proposed refactor to use centralized roles
+const { ROLE_GROUPS } = require('../utils/roles');
+
 const { Room, RoomBooking, Event, User } = require('../models/schema');
     if (
       String(booking.bookedBy) !== String(req.user._id) &&
-      !['PRESIDENT', 'GENSEC_SCITECH', 'GENSEC_ACADEMIC', 'GENSEC_CULTURAL', 'GENSEC_SPORTS', 'CLUB_COORDINATOR'].includes(req.user.role)
+      !ROLE_GROUPS.ADMIN.includes(req.user.role)
     ) {
       return res.status(403).json({ message: 'Forbidden' });
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/roomBookingController.js` around lines 99 - 104, Replace
the hardcoded admin-role array in the authorization check with the centralized
ROLE_GROUPS.ADMIN constant from backend/utils/roles.js: import ROLE_GROUPS at
the top of roomBookingController.js, then change the condition that checks
req.user.role (the block referencing booking and req.user._id) to use
ROLE_GROUPS.ADMIN (e.g., ROLE_GROUPS.ADMIN.includes(req.user.role)) instead of
the literal array; keep the existing bookedBy vs req.user._id comparison logic
intact.
🤖 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 60-70: The getAvailability handler uses an exact date match (query
= { date: new Date(date) }) which fails when stored dates include time
components; update getAvailability to build a date range instead: parse the
incoming date into a start-of-day and end-of-day (e.g., start.setHours(0,0,0,0)
and end = new Date(start); end.setDate(end.getDate()+1)) and replace the
equality with a range query on RoomBooking.find (e.g., { date: { $gte: start,
$lt: end } }), keeping the existing optional roomId branch and populate('room
event bookedBy') behavior.

In `@backend/models/schema.js`:
- Around line 697-704: Add a schema-level validation to ensure endTime >
startTime on the booking schema: in backend/models/schema.js add a custom
validator (either on the endTime field or via a pre('validate') hook) that
compares this.endTime and this.startTime and rejects/surfaces a validation error
when endTime is <= startTime; reference the booking schema (e.g., BookingSchema
or the schema that defines startTime and endTime) so saves/creates fail early
and the bookRoom overlap logic no longer assumes invalid windows.

In `@backend/seed.js`:
- Around line 26-31: The seedRooms function is defined but never invoked, so
room records aren't created; update the seedDB function to call seedRooms
(preferably await seedRooms() inside the async seedDB) or invoke seedRooms()
where other seeding functions are run so the sampleRooms are inserted; ensure
the call uses await if seedDB is async to preserve ordering and error
propagation and reference the seedRooms symbol when adding the call.

---

Nitpick comments:
In `@backend/controllers/roomBookingController.js`:
- Around line 114-126: The getBookings handler currently sets query.date = new
Date(date) which requires exact-match; change it to use a date range like in
getAvailability: compute startOfDay and endOfDay for the passed date and set
query.date = { $gte: startOfDay, $lt: endOfDay } so
RoomBooking.find(query).populate('room event bookedBy') returns all bookings for
that calendar day; update getBookings to parse the incoming date string, create
start/end Date objects, and assign the range to query.date instead of a single
Date object.
- Around line 99-104: Replace the hardcoded admin-role array in the
authorization check with the centralized ROLE_GROUPS.ADMIN constant from
backend/utils/roles.js: import ROLE_GROUPS at the top of
roomBookingController.js, then change the condition that checks req.user.role
(the block referencing booking and req.user._id) to use ROLE_GROUPS.ADMIN (e.g.,
ROLE_GROUPS.ADMIN.includes(req.user.role)) instead of the literal array; keep
the existing bookedBy vs req.user._id comparison logic intact.

In `@frontend/src/Components/RoomBooking.jsx`:
- Around line 514-538: Add explicit id/htmlFor attributes to associate the Room
label with its select input in the RoomBooking component: give the select a
stable id (e.g., "room-select") and set the enclosing label's htmlFor to that id
so screen readers correctly link the label to bookingForm.roomId; update the JSX
around bookingForm.roomId, setBookingForm, and the rooms mapping accordingly and
ensure the id is unique within the form.
- Around line 346-376: Add a per-booking "processing" state and use it inside
handleReviewBooking and handleCancelBooking to prevent duplicate clicks:
introduce a state like processingId (with setter setProcessingId) and at the
start of each handler setProcessingId(bookingId), then clear it in a finally
block (setProcessingId(null)) so errors still reset the flag; update the buttons
that call handleReviewBooking and handleCancelBooking to include
disabled={processingId === booking._id} (or equivalent) so the UI disables the
specific booking action while its API call is in-flight.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5e8049d-f375-45e9-9157-e46ca64c690a

📥 Commits

Reviewing files that changed from the base of the PR and between 060ea48 and 2938e01.

📒 Files selected for processing (10)
  • backend/controllers/roomBookingController.js
  • backend/index.js
  • backend/models/schema.js
  • backend/routes/roomBooking.js
  • backend/seed.js
  • frontend/src/Components/RoomBooking.jsx
  • frontend/src/config/dashboardComponents.js
  • frontend/src/config/navbarConfig.js
  • frontend/src/pages/roomBookingPage.jsx
  • frontend/src/routes/AdminRoutes.js

Comment on lines +60 to +70
exports.getAvailability = async (req, res) => {
try {
const { date, roomId } = req.query;
const query = { date: new Date(date) };
if (roomId) query.room = roomId;
const bookings = await RoomBooking.find(query).populate('room event bookedBy');
res.json(bookings);
} catch (err) {
res.status(500).json({ message: 'Error fetching availability' });
}
};
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 | 🟡 Minor

Date matching may fail due to time component differences.

The exact date match { date: new Date(date) } is fragile. If the stored date has a different time component (e.g., due to timezone handling), the query won't match. Consider using a date range query.

🛡️ Proposed fix using date range
 exports.getAvailability = async (req, res) => {
   try {
     const { date, roomId } = req.query;
-    const query = { date: new Date(date) };
+    const queryDate = new Date(date);
+    const nextDate = new Date(queryDate);
+    nextDate.setDate(nextDate.getDate() + 1);
+    const query = { date: { $gte: queryDate, $lt: nextDate } };
     if (roomId) query.room = roomId;
     const bookings = await RoomBooking.find(query).populate('room event bookedBy');
     res.json(bookings);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exports.getAvailability = async (req, res) => {
try {
const { date, roomId } = req.query;
const query = { date: new Date(date) };
if (roomId) query.room = roomId;
const bookings = await RoomBooking.find(query).populate('room event bookedBy');
res.json(bookings);
} catch (err) {
res.status(500).json({ message: 'Error fetching availability' });
}
};
exports.getAvailability = async (req, res) => {
try {
const { date, roomId } = req.query;
const queryDate = new Date(date);
const nextDate = new Date(queryDate);
nextDate.setDate(nextDate.getDate() + 1);
const query = { date: { $gte: queryDate, $lt: nextDate } };
if (roomId) query.room = roomId;
const bookings = await RoomBooking.find(query).populate('room event bookedBy');
res.json(bookings);
} catch (err) {
res.status(500).json({ message: 'Error fetching availability' });
}
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/roomBookingController.js` around lines 60 - 70, The
getAvailability handler uses an exact date match (query = { date: new Date(date)
}) which fails when stored dates include time components; update getAvailability
to build a date range instead: parse the incoming date into a start-of-day and
end-of-day (e.g., start.setHours(0,0,0,0) and end = new Date(start);
end.setDate(end.getDate()+1)) and replace the equality with a range query on
RoomBooking.find (e.g., { date: { $gte: start, $lt: end } }), keeping the
existing optional roomId branch and populate('room event bookedBy') behavior.

Comment on lines +697 to +704
startTime: {
type: Date,
required: true,
},
endTime: {
type: Date,
required: 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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether any temporal guard already exists in model/controller paths.
rg -nP -C3 'startTime|endTime|validate|bookRoom|getAvailability' backend/models/schema.js backend/controllers/roomBookingController.js

Repository: OpenLake/Student_Database_COSA

Length of output: 3022


Add schema validation: endTime must be after startTime.

Invalid booking windows (e.g., endTime ≤ startTime) can be persisted, breaking the clash detection logic in bookRoom. The overlap query assumes valid time windows and would silently fail to detect conflicts.

💡 Proposed fix
 const roomBookingSchema = new mongoose.Schema({
@@
   endTime: {
     type: Date,
     required: true,
   },
@@
 });
 
+roomBookingSchema.path("endTime").validate(function (value) {
+  return this.startTime instanceof Date && value instanceof Date && value > this.startTime;
+}, "endTime must be later than startTime");
+
 roomBookingSchema.index({ room: 1, date: 1, startTime: 1, endTime: 1 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/schema.js` around lines 697 - 704, Add a schema-level
validation to ensure endTime > startTime on the booking schema: in
backend/models/schema.js add a custom validator (either on the endTime field or
via a pre('validate') hook) that compares this.endTime and this.startTime and
rejects/surfaces a validation error when endTime is <= startTime; reference the
booking schema (e.g., BookingSchema or the schema that defines startTime and
endTime) so saves/creates fail early and the bookRoom overlap logic no longer
assumes invalid windows.

Comment on lines +26 to +31
const seedRooms = async () => {
console.log("Seeding sample rooms...");
await Room.deleteMany({});
await Room.insertMany(sampleRooms);
console.log("Sample rooms seeded!");
};
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

seedRooms is never executed, so room data is never seeded.

seedRooms is defined on Line 26 but not called in seedDB(); this leaves fresh environments without room records for booking workflows.

💡 Proposed fix
 async function seedDB() {
   try {
@@
     await clearData();
     await seedOrganizationalUnits();
+    await seedRooms();
     await seedUsers();
     await seedPositions();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/seed.js` around lines 26 - 31, The seedRooms function is defined but
never invoked, so room records aren't created; update the seedDB function to
call seedRooms (preferably await seedRooms() inside the async seedDB) or invoke
seedRooms() where other seeding functions are run so the sampleRooms are
inserted; ensure the call uses await if seedDB is async to preserve ordering and
error propagation and reference the seedRooms symbol when adding the call.

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.

Style: Frontend for Smart Room Booking & Clash Detection

1 participant