diff --git a/.rubocop.yml b/.rubocop.yml index a94c563..5a6a40e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,6 +8,8 @@ Metrics/CyclomaticComplexity: Max: 15 Metrics/ClassLength: Max: 250 +Metrics/ParameterLists: + Max: 10 AllCops: NewCops: enable TargetRubyVersion: 3.3.x diff --git a/app/assets/stylesheets/room_bookings.css b/app/assets/stylesheets/room_bookings.css index e2d9e95..af6fcdd 100644 --- a/app/assets/stylesheets/room_bookings.css +++ b/app/assets/stylesheets/room_bookings.css @@ -1,4 +1,11 @@ .is-hover { --bs-bg-opacity: 0.1; background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; +} + +.section-item:hover { + background-color: #f0f9ff; /* Light blue shade to indicate hover */ + transition: background-color 0.3s; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Subtle shadow effect */ + cursor: pointer; } \ No newline at end of file diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index 533c36f..8aa2fc9 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -7,17 +7,84 @@ def index @schedule = Schedule.find(params[:schedule_id]) @rooms = @schedule.rooms.where(is_active: true).where.not(building_code: 'ONLINE') @tabs = TimeSlot.distinct.pluck(:day) - @active_tab = params[:active_tab] || @tabs[0] + @active_tab = params[:active_tab] || session[:active_rb_tab] || @tabs[0] + session[:active_rb_tab] = @active_tab @time_slots = TimeSlot.where(time_slots: { day: @active_tab }).to_a @time_slots.sort_by! { |ts| Time.parse(ts.start_time) } + @instructors = @schedule.instructors # Fetch room bookings only for the specified schedule - @room_bookings = RoomBooking.joins(:room, :time_slot) - .where(rooms: { schedule_id: }, time_slots: { day: @active_tab }) + @room_bookings = RoomBooking.includes( + room: {}, + section: [:course], + time_slot: {}, + instructor: {} + ).where(rooms: { schedule_id: }, time_slots: { day: @active_tab }) # Organize room bookings in a hash with room_id and time_slot_id as keys @bookings_matrix = @room_bookings.each_with_object({}) do |booking, hash| hash[[booking.room_id, booking.time_slot_id]] = booking end end + + def create + room_booking = RoomBooking.new(room_booking_params) + @schedule = Schedule.find(params[:schedule_id]) + + respond_to do |format| + if room_booking.save + format.html { redirect_to schedule_room_bookings_path(@schedule), notice: 'Movie was successfully created.' } + format.json { render :index, status: :created } + else + flash[:alert] = 'Did not work' + render json: { error: 'Failed to create room booking', details: room_booking.errors.full_messages }, status: :unprocessable_entity + end + end + end + + def destroy + @schedule = Schedule.find(params[:schedule_id]) + @room_booking = RoomBooking.find_by(id: params[:id]) + + if @room_booking + @room_booking.destroy + flash[:notice] = 'Room booking deleted successfully.' + else + flash[:alert] = 'Room booking not found.' + end + + redirect_to schedule_room_bookings_path(@schedule) # Redirect to the list of room bookings or another appropriate page + end + + def toggle_lock + @room_booking = RoomBooking.find(params[:id]) + + # Toggle the `is_locked` value + @room_booking.update(is_locked: !@room_booking.is_locked) + + # Optionally add a notice or alert to show success or failure + flash[:notice] = 'Room booking lock status updated successfully.' + + # Redirect to the previous page or another relevant page + redirect_back(fallback_location: room_bookings_path) + end + + def update_instructor + @booking = RoomBooking.find(params[:id]) + + if @booking.update(instructor_id: params[:room_booking][:instructor_id]) + flash[:notice] = 'Instructor updated successfully.' + else + flash[:alert] = 'Failed to update instructor.' + end + + # Redirect to the previous page or another relevant page + redirect_back(fallback_location: room_bookings_path) + end + + private + + def room_booking_params + params.require(:room_booking).permit(:room_id, :time_slot_id, :is_available, :is_lab, :instructor_id, :section_id) + end end diff --git a/app/helpers/room_bookings_helper.rb b/app/helpers/room_bookings_helper.rb index 75f8f52..4c364c0 100644 --- a/app/helpers/room_bookings_helper.rb +++ b/app/helpers/room_bookings_helper.rb @@ -2,4 +2,11 @@ # Room Bookings Helper module RoomBookingsHelper + def available_sections(schedule) + @sections = Section.joins(:course) + .where(courses: { schedule_id: schedule.id }) + .left_joins(:room_booking) + .where(room_booking: { id: nil }) + render partial: '/shared/courses_list', locals: { sections: @sections } + end end diff --git a/app/javascript/room_bookings.js b/app/javascript/room_bookings.js index 543cadb..2c4c01b 100644 --- a/app/javascript/room_bookings.js +++ b/app/javascript/room_bookings.js @@ -1,4 +1,4 @@ -document.addEventListener("DOMContentLoaded", function () { +document.addEventListener("turbo:load", function () { const tds = document.querySelectorAll(".grid-view-matrix td"); tds.forEach((td) => { diff --git a/app/models/instructor.rb b/app/models/instructor.rb index 90287cd..2d821f1 100644 --- a/app/models/instructor.rb +++ b/app/models/instructor.rb @@ -4,4 +4,5 @@ class Instructor < ApplicationRecord belongs_to :schedule has_many :instructor_preferences, dependent: :destroy + has_many :room_bookings end diff --git a/app/models/room_booking.rb b/app/models/room_booking.rb index 850e9a8..ccec01b 100644 --- a/app/models/room_booking.rb +++ b/app/models/room_booking.rb @@ -3,4 +3,6 @@ class RoomBooking < ApplicationRecord belongs_to :room belongs_to :time_slot + belongs_to :section + belongs_to :instructor, optional: true end diff --git a/app/models/section.rb b/app/models/section.rb index 7e42841..f51af16 100644 --- a/app/models/section.rb +++ b/app/models/section.rb @@ -3,4 +3,5 @@ # Section model class Section < ApplicationRecord belongs_to :course + has_one :room_booking end diff --git a/app/models/time_slot.rb b/app/models/time_slot.rb index f1b1c0b..4d392bf 100644 --- a/app/models/time_slot.rb +++ b/app/models/time_slot.rb @@ -13,7 +13,7 @@ class TimeSlot < ApplicationRecord private def end_time_after_start_time - return unless end_time.present? && start_time.present? && end_time <= start_time + return unless end_time.present? && start_time.present? && Time.parse(end_time) <= Time.parse(start_time) errors.add(:end_time, 'must be after the start time') end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 7a2490c..f03c124 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -14,7 +14,7 @@ - <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css", integrity: "sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65", crossorigin: "anonymous" %> + <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", integrity: "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH", crossorigin: "anonymous" %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> <%= javascript_include_tag 'room_bookings' %> diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb index 60d55ca..b1a4139 100644 --- a/app/views/room_bookings/index.html.erb +++ b/app/views/room_bookings/index.html.erb @@ -17,49 +17,120 @@ <% if !@rooms.empty? %> - -
Times \ Rooms | - <% @rooms.each do |room| %> -<%= room.building_code %> <%= room.room_number %> | - - <% end %> -
---|---|
<%= time_slot.start_time %> - <%= time_slot.end_time %> | - - <% @rooms.each do |room| %> - <% booking = @bookings_matrix[[room.id, time_slot.id]] %> - -
- <% if booking %>
- <%= booking.is_available ? 'Available' : 'Booked' %> |
- <%= booking.is_lab ? 'Lab' : 'Lecture' %>
- <% else %>
+
+
+ |
- <% end %>
-
24 hr | + <% @rooms.each do |room| %> +<%= room.building_code %> <%= room.room_number %> | + + <% end %> +
---|---|
<%= time_slot.start_time %> - <%= time_slot.end_time %> | + + <% @rooms.each do |room| %> + <% booking = @bookings_matrix[[room.id, time_slot.id]] %> + +" data-room-id="<%= room.id %>" data-time-slot-id="<%= time_slot.id %>">
+ <% if booking %>
+
+ <%= booking.section.course.course_number %>
+ (<%= booking.section.section_number %>)
+ |
+ <%= booking.section.seats_alloted %>
+ <%= link_to "Delete",
+ schedule_room_booking_path(@schedule,booking),
+ method: :delete,
+ data: {
+ confirm: "Are you sure you want to delete this booking?",
+ turbo_method: :delete
+ },
+ class: "btn btn-danger" %>
+ <%= link_to (booking.is_locked ? "Locked" : "Unlocked"),
+ toggle_lock_schedule_room_booking_path(@schedule, booking),
+ method: :patch,
+ class: "btn btn-warning",
+ data: {
+ confirm: "Are you sure you want to toggle the lock status?",
+ turbo_method: :patch
+ } %>
+ <%= form_with model: booking,
+ url: update_instructor_schedule_room_booking_path(@schedule, booking),
+ method: :patch,
+ data: {
+ turbo_method: :patch
+ },
+ remote: true do |form| %>
+
+ <%= form.select :instructor_id, options_from_collection_for_select(@instructors, :id, :first_name, booking.instructor_id), { prompt: "Select Instructor" }, onchange: "this.form.submit()" %>
+
+ <% end %>
+
+ <% else %>
+ <% end %>
+ |
+ <% end %>
+
No rooms added to this schedule, click on View Data to Add Rooms!
-<% end %> \ No newline at end of file +<% end %> + diff --git a/app/views/shared/_courses_list.html.erb b/app/views/shared/_courses_list.html.erb new file mode 100644 index 0000000..2bfc822 --- /dev/null +++ b/app/views/shared/_courses_list.html.erb @@ -0,0 +1,35 @@ +<% if sections.present? %> +No courses available for the selected room and time slot.
+<% end %> diff --git a/config/routes.rb b/config/routes.rb index c5aa2a4..0ce1fe0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,9 +40,17 @@ post 'time_slots', to: 'time_slots#filter', as: 'filter_time_slots' get '/time_slots', to: 'time_slots#index' - resources :room_bookings, only: [:index] + + resources :room_bookings, only: %i[index create destroy] do + member do + patch :toggle_lock + patch :update_instructor + end + end end # Show Time Slot View resources :time_slots, only: [:index] + + resources :room_bookings, only: [:create] end diff --git a/db/migrate/20241031023936_add_instructor_ref_to_room_bookings.rb b/db/migrate/20241031023936_add_instructor_ref_to_room_bookings.rb new file mode 100644 index 0000000..11417d2 --- /dev/null +++ b/db/migrate/20241031023936_add_instructor_ref_to_room_bookings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddInstructorRefToRoomBookings < ActiveRecord::Migration[7.2] + def change + add_reference :room_bookings, :instructor, null: false, foreign_key: true + end +end diff --git a/db/migrate/20241031203245_change_instructor_id_in_room_bookings_to_nullable.rb b/db/migrate/20241031203245_change_instructor_id_in_room_bookings_to_nullable.rb new file mode 100644 index 0000000..f93781c --- /dev/null +++ b/db/migrate/20241031203245_change_instructor_id_in_room_bookings_to_nullable.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ChangeInstructorIdInRoomBookingsToNullable < ActiveRecord::Migration[7.2] + def change + change_column_null :room_bookings, :instructor_id, true + end +end diff --git a/db/migrate/20241031204719_add_section_to_room_bookings.rb b/db/migrate/20241031204719_add_section_to_room_bookings.rb new file mode 100644 index 0000000..b36579b --- /dev/null +++ b/db/migrate/20241031204719_add_section_to_room_bookings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddSectionToRoomBookings < ActiveRecord::Migration[7.2] + def change + add_reference :room_bookings, :section, foreign_key: true + end +end diff --git a/db/migrate/20241101020805_add_is_locked_to_room_bookings.rb b/db/migrate/20241101020805_add_is_locked_to_room_bookings.rb new file mode 100644 index 0000000..7fda6d5 --- /dev/null +++ b/db/migrate/20241101020805_add_is_locked_to_room_bookings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddIsLockedToRoomBookings < ActiveRecord::Migration[7.2] + def change + add_column :room_bookings, :is_locked, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 19dfe95..dfdf04a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 20_241_029_040_851) do +ActiveRecord::Schema[7.2].define(version: 20_241_101_020_805) do create_table 'courses', force: :cascade do |t| t.string 'course_number' t.integer 'max_seats' @@ -57,7 +57,12 @@ t.boolean 'is_lab' t.datetime 'created_at', null: false t.datetime 'updated_at', null: false + t.integer 'instructor_id' + t.integer 'section_id' + t.boolean 'is_locked' + t.index ['instructor_id'], name: 'index_room_bookings_on_instructor_id' t.index ['room_id'], name: 'index_room_bookings_on_room_id' + t.index ['section_id'], name: 'index_room_bookings_on_section_id' t.index ['time_slot_id'], name: 'index_room_bookings_on_time_slot_id' end @@ -117,7 +122,9 @@ add_foreign_key 'instructor_preferences', 'courses' add_foreign_key 'instructor_preferences', 'instructors' add_foreign_key 'instructors', 'schedules' + add_foreign_key 'room_bookings', 'instructors' add_foreign_key 'room_bookings', 'rooms' + add_foreign_key 'room_bookings', 'sections' add_foreign_key 'room_bookings', 'time_slots' add_foreign_key 'rooms', 'schedules' add_foreign_key 'sections', 'courses' diff --git a/features/room_bookings.feature b/features/room_bookings.feature index ccbdf0a..69cf090 100644 --- a/features/room_bookings.feature +++ b/features/room_bookings.feature @@ -50,4 +50,99 @@ Feature: Rooms Page When I visit the room bookings page for "Sched 1" Then I should see "Back to Schedules" When I click "Back to Schedules" - Then I should be on the schedules page \ No newline at end of file + Then I should be on the schedules page + + Scenario: User should be able to create a room booking + Given I am logged in as a user with first name "Test" + And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024" + And the following rooms exist for that schedule: + | campus | building_code | room_number | capacity | is_active | is_lab | is_learning_studio | is_lecture_hall | + | CS | BLDG1 | 101 | 30 | true | true | true | true | + | GV | BLDG2 | 102 | 50 | true | true | true | true | + | CS | BLDG3 | 102 | 50 | false | true | true | true | + And the following time slots exist for that schedule: + | day | start_time | end_time | slot_type | + | MWF | 09:00 | 10:00 | "LEC" | + | MW | 08:00 | 10:00 | "LEC" | + And the following courses and their sections exist: + | course_number | max_seats | lecture_type | num_labs | sections | + | 110 | 96 | F2F | 4 | 100,101 | + | 110 | 96 | F2F | 4 | 100 | + | 111 | 96 | F2F | 4 | 100 | + | 435/735/735D | 135 | F2F | 2 | 100 | + When I visit the room bookings page for "Sched 1" + And I book room "BLDG1" "101" in "MWF" for "09:00 - 10:00" with "100" for "110" + Then I should see "Unlocked" + + Scenario: User should be able to delete a room booking + Given I am logged in as a user with first name "Test" + And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024" + And the following rooms exist for that schedule: + | campus | building_code | room_number | capacity | is_active | is_lab | is_learning_studio | is_lecture_hall | + | CS | BLDG1 | 101 | 30 | true | true | true | true | + | GV | BLDG2 | 102 | 50 | true | true | true | true | + | CS | BLDG3 | 102 | 50 | false | true | true | true | + And the following time slots exist for that schedule: + | day | start_time | end_time | slot_type | + | MWF | 09:00 | 10:00 | "LEC" | + | MW | 08:00 | 10:00 | "LEC" | + And the following courses and their sections exist: + | course_number | max_seats | lecture_type | num_labs | sections | + | 110 | 96 | F2F | 4 | 100,101 | + | 110 | 96 | F2F | 4 | 100 | + | 111 | 96 | F2F | 4 | 100 | + | 435/735/735D | 135 | F2F | 2 | 100 | + When I visit the room bookings page for "Sched 1" + And I book room "BLDG1" "101" in "MWF" for "09:00 - 10:00" with "100" for "110" + And I click "Delete" + Then I should not see "Unlocked" + + Scenario: User should be able to lock a room booking + Given I am logged in as a user with first name "Test" + And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024" + And the following rooms exist for that schedule: + | campus | building_code | room_number | capacity | is_active | is_lab | is_learning_studio | is_lecture_hall | + | CS | BLDG1 | 101 | 30 | true | true | true | true | + | GV | BLDG2 | 102 | 50 | true | true | true | true | + | CS | BLDG3 | 102 | 50 | false | true | true | true | + And the following time slots exist for that schedule: + | day | start_time | end_time | slot_type | + | MWF | 09:00 | 10:00 | "LEC" | + | MW | 08:00 | 10:00 | "LEC" | + And the following courses and their sections exist: + | course_number | max_seats | lecture_type | num_labs | sections | + | 110 | 96 | F2F | 4 | 100,101 | + | 110 | 96 | F2F | 4 | 100 | + | 111 | 96 | F2F | 4 | 100 | + | 435/735/735D | 135 | F2F | 2 | 100 | + When I visit the room bookings page for "Sched 1" + And I book room "BLDG1" "101" in "MWF" for "09:00 - 10:00" with "100" for "110" + And I click "Unlocked" + Then I should not see "Unlocked" + And I should see "Locked" + + Scenario: User should be able to assign instructors to a room booking + Given I am logged in as a user with first name "Test" + And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024" + And the following rooms exist for that schedule: + | campus | building_code | room_number | capacity | is_active | is_lab | is_learning_studio | is_lecture_hall | + | CS | BLDG1 | 101 | 30 | true | true | true | true | + | GV | BLDG2 | 102 | 50 | true | true | true | true | + | CS | BLDG3 | 102 | 50 | false | true | true | true | + And the following time slots exist for that schedule: + | day | start_time | end_time | slot_type | + | MWF | 09:00 | 10:00 | "LEC" | + | MW | 08:00 | 10:00 | "LEC" | + And the following courses and their sections exist: + | course_number | max_seats | lecture_type | num_labs | sections | + | 110 | 96 | F2F | 4 | 100,101 | + | 110 | 96 | F2F | 4 | 100 | + | 111 | 96 | F2F | 4 | 100 | + | 435/735/735D | 135 | F2F | 2 | 100 | + And the following instructors exist: + | id_number | first_name | last_name | middle_name | email | before_9 | after_3 | beaware_of | + | 1001 | John | Doe | A | john@example.com | true | false | test | + | 1002 | Jane | Smith | B | jane@example.com | false | false | | + When I visit the room bookings page for "Sched 1" + And I book room "BLDG1" "101" in "MWF" for "09:00 - 10:00" with "100" for "110" + Then I should see "Unlocked" \ No newline at end of file diff --git a/features/step_definitions/room_bookings_steps.rb b/features/step_definitions/room_bookings_steps.rb index 51ea5e3..27a9f35 100644 --- a/features/step_definitions/room_bookings_steps.rb +++ b/features/step_definitions/room_bookings_steps.rb @@ -5,6 +5,30 @@ visit schedule_room_bookings_path(@schedule) end +Given(/^the following courses and their sections exist:$/) do |table| + table.hashes.each do |row| + # Create the course with the given name + course = Course.create!( + course_number: row['course_number'], + max_seats: row['max_seats'], + lecture_type: row['lecture_type'], + num_labs: row['num_labs'], + + schedule: @schedule + ) + + # Create sections associated with the course + row['sections'].split(',').each do |section_name| + section_data = { + course_id: course.id, + section_number: section_name, + seats_alloted: 24 + } + Section.create!(section_data) + end + end +end + When(/^I visit the room bookings page for "(.*)"$/) do |schedule_name| @schedule = Schedule.where(schedule_name:)[0] visit schedule_room_bookings_path(schedule_id: @schedule.id) @@ -20,3 +44,53 @@ ) end end + +When(/^I click on a the booking table in "(.*)" for "(.*)" in "(.*)" "(.*)"$/) do |day, time, bldg, room| + # Find the room based on schedule and building code + room = @schedule.rooms.find_by(building_code: bldg, room_number: room) + expect(room).not_to be_nil, 'Could not find room' + + # Find the time slot based on day and time + start_time, end_time = time.split(' - ') + time_slot = TimeSlot.find_by(day:, start_time:, end_time:) + expect(time_slot).not_to be_nil, "Could not find time slot for '#{day} #{time}'" + + # Locate and click on the cell + cell = find("td.table-cell[data-room-id='#{room.id}'][data-time-slot-id='#{time_slot.id}']") + cell.click + + button = find('a.btn.btn-primary.course-select-btn', match: :first) + expect(button.href).contain(room.id) +end + +When(/^I click the select "(.*)" for "(.*)"$/) do |section_number, course_number| + # Find the button using the section number + course = @schedule.courses.find_by(course_number:) + section = Section.find_by(course_id: course.id, section_number:) + button = find("a.btn.btn-primary.course-select-btn[data-section='#{section.id}']", match: :first) + button.click +end + +When(/^I book room "(.*)" "(.*)" in "(.*)" for "(.*)" with "(.*)" for "(.*)"$/) do |bldg, room, day, time, section_number, course_number| + # Find the room based on schedule and building code + room = @schedule.rooms.find_by(building_code: bldg, room_number: room) + expect(room).not_to be_nil, 'Could not find room' + + # Find the time slot based on day and time + start_time, end_time = time.split(' - ') + time_slot = TimeSlot.find_by(day:, start_time:, end_time:) + expect(time_slot).not_to be_nil, "Could not find time slot for '#{day} #{time}'" + + course = @schedule.courses.find_by(course_number:) + section = Section.find_by(course_id: course.id, section_number:) + + page.driver.post room_bookings_path(schedule_id: @schedule.id), { + room_booking: { + room_id: room.id, + time_slot_id: time_slot.id, + section_id: section.id + } + } + + visit schedule_room_bookings_path(schedule_id: @schedule.id) +end diff --git a/spec/controllers/room_bookings_controller_spec.rb b/spec/controllers/room_bookings_controller_spec.rb index d2217cb..ce9173f 100644 --- a/spec/controllers/room_bookings_controller_spec.rb +++ b/spec/controllers/room_bookings_controller_spec.rb @@ -4,13 +4,17 @@ require 'rails_helper' RSpec.describe RoomBookingsController, type: :controller do + render_views let!(:schedule) { create(:schedule) } let!(:room1) { create(:room, schedule:) } let!(:room2) { create(:room, schedule:) } let!(:time_slot1) { create(:time_slot, day: 'Monday', start_time: '09:00', end_time: '10:00') } let!(:time_slot2) { create(:time_slot, day: 'Monday', start_time: '10:00', end_time: '11:00') } - let!(:room_booking1) { create(:room_booking, room: room1, time_slot: time_slot1, is_available: true) } - let!(:room_booking2) { create(:room_booking, room: room2, time_slot: time_slot2, is_available: false) } + let(:course) { create(:course, schedule:) } + let(:section) { create(:section, course:) } + let!(:room_booking1) { create(:room_booking, room: room1, time_slot: time_slot1, section:, is_available: true) } + let!(:room_booking2) { create(:room_booking, room: room2, time_slot: time_slot2, section:, is_available: false) } + let(:instructor) { create(:instructor) } before do @user = User.create!(uid: '12345', provider: 'google_oauth2', email: 'test@example.com', first_name: 'John', @@ -48,4 +52,80 @@ end end end + + describe 'POST #create' do + context 'with valid attributes' do + it 'creates a new room booking and redirects' do + post :create, params: { + schedule_id: schedule.id, + room_booking: { + room_id: room1.id, + time_slot_id: time_slot1.id, + section_id: section.id, + instructor_id: instructor.id, + is_available: true, + is_lab: false + } + } + + expect(RoomBooking.count).to eq(3) + expect(flash[:notice]).to eq('Movie was successfully created.') + expect(response).to redirect_to(schedule_room_bookings_path(schedule)) + end + end + end + + describe 'DELETE #destroy' do + it 'deletes the room booking and redirects with a success message' do + delete :destroy, params: { schedule_id: schedule.id, id: room_booking1.id } + + expect(RoomBooking.exists?(room_booking1.id)).to be_falsey + expect(flash[:notice]).to eq('Room booking deleted successfully.') + expect(response).to redirect_to(schedule_room_bookings_path(schedule)) + end + + it 'renders an error when room booking does not exist' do + delete :destroy, params: { schedule_id: schedule.id, id: 999 } # Non-existent room_booking ID + + expect(flash[:alert]).to eq('Room booking not found.') + expect(response).to redirect_to(schedule_room_bookings_path(schedule)) + end + end + + describe 'PATCH #toggle_lock' do + it 'toggles the lock status of the room booking' do + room_booking1.update(is_locked: false) + patch :toggle_lock, params: { schedule_id: schedule.id, id: room_booking1.id } + + room_booking1.reload + expect(room_booking1.is_locked).to be_truthy + expect(flash[:notice]).to eq('Room booking lock status updated successfully.') + end + end + + describe 'PATCH #update_instructor' do + it 'updates the instructor for the room booking' do + another_instructor = create(:instructor) + patch :update_instructor, params: { + schedule_id: schedule.id, + id: room_booking1.id, + room_booking: { instructor_id: another_instructor.id } + } + + room_booking1.reload + expect(room_booking1.instructor).to eq(another_instructor) + expect(flash[:notice]).to eq('Instructor updated successfully.') + end + + it 'renders an error message when update fails' do + allow_any_instance_of(RoomBooking).to receive(:update).and_return(false) # Force failure + patch :update_instructor, params: { + schedule_id: schedule.id, + id: room_booking1.id, + room_booking: { instructor_id: nil } + } + + expect(flash[:alert]).to eq('Failed to update instructor.') + end + end end diff --git a/spec/factories.rb b/spec/factories.rb index 08c9cf8..95a709f 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -35,6 +35,10 @@ association :room association :time_slot is_available { true } + is_lab { [true, false].sample } + is_locked { false } + association :instructor + association :section end factory :time_slot do