diff --git a/Gemfile b/Gemfile
index 422fa177..138bcd22 100644
--- a/Gemfile
+++ b/Gemfile
@@ -13,6 +13,7 @@ gem 'image_processing', '>= 1.2'
gem 'jbuilder'
gem 'jsbundling-rails'
gem 'puma', '~> 6.0'
+gem 'simple_calendar'
gem 'sprockets-rails'
gem 'sqlite3', '~> 1.4'
gem 'stimulus-rails'
diff --git a/Gemfile.lock b/Gemfile.lock
index af58d89c..299f4645 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -275,6 +275,8 @@ GEM
ruby-progressbar (1.13.0)
ruby-vips (2.2.1)
ffi (~> 1.12)
+ simple_calendar (3.0.4)
+ rails (>= 6.1)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
@@ -335,6 +337,7 @@ DEPENDENCIES
rspec-rails
rubocop
rubocop-rails
+ simple_calendar
simplecov
sprockets-rails
sqlite3 (~> 1.4)
diff --git a/README.md b/README.md
index 8b0d734c..5946a1b0 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,8 @@
Tanto administradores quanto moradores podem ver a página de listagem e detalhes de condomínio. Tendo sua exibição alterada para cada tipo de usuário.
+Moradores podem fazer uma reserva de área comum, bem como cancelar essa reserva.
+
(voltar ao topo)
diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss
index 34f3ad0a..0fddef45 100644
--- a/app/assets/stylesheets/application.bootstrap.scss
+++ b/app/assets/stylesheets/application.bootstrap.scss
@@ -1,3 +1,4 @@
@import 'custom_spacers';
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
+@import 'simple_calendar';
diff --git a/app/assets/stylesheets/simple_calendar.scss b/app/assets/stylesheets/simple_calendar.scss
new file mode 100644
index 00000000..9d03b505
--- /dev/null
+++ b/app/assets/stylesheets/simple_calendar.scss
@@ -0,0 +1,82 @@
+.simple-calendar {
+ table {
+ -webkit-border-horizontal-spacing: 0px;
+ -webkit-border-vertical-spacing: 0px;
+ background-color: rgba(0, 0, 0, 0);
+ border: 1px solid rgb(221, 221, 221);
+ border-collapse: collapse;
+ box-sizing: border-box;
+ max-width: 100%;
+ width: 100%;
+ }
+
+ tr {
+ border-collapse: collapse;
+ }
+
+ th {
+ padding: 6px;
+ border-bottom: 2px solid rgb(221, 221, 221);
+ border-collapse: collapse;
+ border-left: 1px solid rgb(221, 221, 221);
+ border-right: 1px solid rgb(221, 221, 221);
+ border-top: 0px none rgb(51, 51, 51);
+ box-sizing: border-box;
+ text-align: left;
+ }
+
+ td {
+ padding: 6px;
+ vertical-align: top;
+ width: 14%;
+
+ border: 1px solid #ddd;
+ border-top-color: rgb(221, 221, 221);
+ border-top-style: solid;
+ border-top-width: 1px;
+ border-right-color: rgb(221, 221, 221);
+ border-right-style: solid;
+ border-right-width: 1px;
+ border-bottom-color: rgb(221, 221, 221);
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-left-color: rgb(221, 221, 221);
+ border-left-style: solid;
+ border-left-width: 1px;
+ }
+
+ .calendar-heading nav {
+ display: inline-block;
+ }
+
+ .day {
+ height: 80px;
+ }
+
+ .wday-0 {}
+ .wday-1 {}
+ .wday-2 {}
+ .wday-3 {}
+ .wday-4 {}
+ .wday-5 {}
+ .wday-6 {}
+
+ .today {
+ background: #FFFFC0
+ }
+
+ .past {}
+ .future {}
+
+ .start-date {}
+
+ .prev-month {
+ background: #DDD;
+ }
+ .next-month {
+ background: #DDD;
+ }
+ .current-month {}
+
+ .has-events {}
+}
\ No newline at end of file
diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb
new file mode 100644
index 00000000..9057979e
--- /dev/null
+++ b/app/controllers/reservations_controller.rb
@@ -0,0 +1,101 @@
+class ReservationsController < ApplicationController
+ before_action :set_reservation, only: %i[show canceled]
+ before_action :set_common_area, only: %i[new create]
+ before_action :set_breadcrumbs, only: [:new]
+ before_action :block_manager_from_resident_sign_in, only: %i[new create]
+ before_action :authenticate_resident!, only: %i[new create]
+ before_action :authenticate_for_cancelation, only: [:canceled]
+ before_action :authorize_user, only: [:show]
+
+ def show
+ @common_area = @reservation.common_area
+ set_breadcrumbs
+ end
+
+ def new
+ @reservation = Reservation.new common_area: @common_area
+ end
+
+ def create
+ @reservation = Reservation.new reservation_params
+ @reservation.resident = current_resident
+ @reservation.common_area = @common_area
+
+ return redirect_to @reservation, notice: t('notices.reservation.created') if @reservation.save
+
+ flash.now[:alert] = I18n.t 'alerts.reservation.not_created'
+ render 'new', status: :unprocessable_entity
+ end
+
+ def canceled
+ @reservation.canceled!
+ redirect_to @reservation, notice: t('notices.reservation.canceled')
+ end
+
+ private
+
+ def reservation_params
+ params.require(:reservation).permit :date
+ end
+
+ def set_common_area
+ @common_area = CommonArea.find params[:common_area_id]
+ end
+
+ def authenticate_resident!
+ @residents = @common_area.condo.residents
+
+ if manager_signed_in? || (resident_signed_in? && !@common_area.access_allowed?(current_resident))
+ return redirect_to root_path,
+ alert: t('alerts.reservation.not_authorized')
+ end
+
+ super
+ end
+
+ def authenticate_for_cancelation
+ unless resident_signed_in? || manager_signed_in?
+ return redirect_to new_resident_session_path,
+ alert: t('alerts.reservation.access_denied')
+ end
+
+ return unless manager_signed_in? || (resident_signed_in? && @reservation.resident != current_resident)
+
+ redirect_to root_path, alert: t('alerts.reservation.not_authorized')
+ end
+
+ def authorize_user
+ return if !authenticate_user || super_manager? || can_access_condo? || reservation_owner?
+
+ redirect_to root_path, alert: t('alerts.reservation.not_authorized')
+ end
+
+ def authenticate_user
+ return true if manager_signed_in? || resident_signed_in?
+
+ redirect_to signup_choice_path
+ false
+ end
+
+ def super_manager?
+ manager_signed_in? && current_manager.is_super
+ end
+
+ def can_access_condo?
+ manager_signed_in? && current_manager.condos.include?(@reservation.common_area.condo)
+ end
+
+ def reservation_owner?
+ resident_signed_in? && @reservation.resident == current_resident
+ end
+
+ def set_reservation
+ @reservation = Reservation.find params[:id]
+ end
+
+ def set_breadcrumbs
+ add_breadcrumb @common_area.condo.name, @common_area.condo
+ add_breadcrumb @common_area.name, @common_area
+ add_breadcrumb I18n.t "breadcrumb.reservation.#{action_name}"
+ end
+end
diff --git a/app/controllers/visitor_entries_controller.rb b/app/controllers/visitor_entries_controller.rb
index 4573e076..19f481aa 100644
--- a/app/controllers/visitor_entries_controller.rb
+++ b/app/controllers/visitor_entries_controller.rb
@@ -51,7 +51,7 @@ def set_breadcrumbs_for_index
end
def authenticate_manager!
- return redirect_to root_path, notice: I18n.t('alerts.visitor_entry.access_denied') if resident_signed_in?
+ return redirect_to root_path, alert: I18n.t('alerts.visitor_entry.access_denied') if resident_signed_in?
super
end
diff --git a/app/models/common_area.rb b/app/models/common_area.rb
index 82e18880..5b7c4582 100644
--- a/app/models/common_area.rb
+++ b/app/models/common_area.rb
@@ -1,6 +1,11 @@
class CommonArea < ApplicationRecord
belongs_to :condo
+ has_many :reservations, dependent: :destroy
validates :name, :description, :max_occupancy, presence: true
validates :max_occupancy, numericality: { greater_than: 0 }
+
+ def access_allowed?(resident)
+ condo.residents.include?(resident)
+ end
end
diff --git a/app/models/reservation.rb b/app/models/reservation.rb
new file mode 100644
index 00000000..0e999e96
--- /dev/null
+++ b/app/models/reservation.rb
@@ -0,0 +1,19 @@
+class Reservation < ApplicationRecord
+ belongs_to :common_area
+ belongs_to :resident
+
+ enum status: { confirmed: 0, canceled: 3 }
+
+ validates :date, presence: true
+ validate :check_availability, :date_must_be_actual_or_future, on: :create
+
+ def check_availability
+ common_area.reservations.confirmed.each do |reservation|
+ errors.add(:date, "#{I18n.l date} já está reservada para esta área comum") if reservation[:date] == date
+ end
+ end
+
+ def date_must_be_actual_or_future
+ errors.add(:date, 'deve ser atual ou futura') if date&.past?
+ end
+end
diff --git a/app/models/resident.rb b/app/models/resident.rb
index c7fb99e4..3cb3eec3 100644
--- a/app/models/resident.rb
+++ b/app/models/resident.rb
@@ -1,6 +1,7 @@
class Resident < ApplicationRecord
- has_one :residence, class_name: 'Unit', foreign_key: 'tenant_id', dependent: :destroy, inverse_of: :tenant
- has_many :properties, class_name: 'Unit', foreign_key: 'owner_id', dependent: :destroy, inverse_of: :owner
+ has_one :residence, class_name: 'Unit', foreign_key: 'tenant_id', dependent: :nullify, inverse_of: :tenant
+ has_many :properties, class_name: 'Unit', foreign_key: 'owner_id', dependent: :nullify, inverse_of: :owner
+ has_many :reservations, dependent: :destroy
has_many :visitors, dependent: :destroy
devise :database_authenticatable, :recoverable, :rememberable, :validatable
diff --git a/app/views/common_areas/show.html.erb b/app/views/common_areas/show.html.erb
index fe1a8bd3..4b38db30 100644
--- a/app/views/common_areas/show.html.erb
+++ b/app/views/common_areas/show.html.erb
@@ -5,5 +5,6 @@
Capacidade Máxima: <%= @common_area.max_occupancy %> pessoas
Regras de Uso: <%= @common_area.rules %>
- <%= link_to 'Editar', edit_common_area_path(@common_area), class: 'btn btn-dark rounded-pill px-4' %>
+ <%= link_to 'Editar', edit_common_area_path(@common_area), class: 'btn btn-dark rounded-pill px-4' if manager_signed_in? %>
+ <%= link_to 'Reservar', new_common_area_reservation_path(@common_area), class: 'btn btn-dark rounded-pill px-4' if resident_signed_in? %>
\ No newline at end of file
diff --git a/app/views/reservations/new.html.erb b/app/views/reservations/new.html.erb
new file mode 100644
index 00000000..a80109e3
--- /dev/null
+++ b/app/views/reservations/new.html.erb
@@ -0,0 +1,18 @@
+<%= render 'shared/errors', model: @reservation %>
+
+
+
Escolha a data da sua reserva
+
+ <%= form_with model: [@common_area, @reservation] do |f| %>
+
+
+
+ <%= f.submit 'Reservar', class: 'btn btn-dark rounded-pill px-4 mt-3' %>
+
+ <% end %>
+
diff --git a/app/views/reservations/show.html.erb b/app/views/reservations/show.html.erb
new file mode 100644
index 00000000..c708d28b
--- /dev/null
+++ b/app/views/reservations/show.html.erb
@@ -0,0 +1,12 @@
+
+
Sua Reserva
+
+
Data: <%= I18n.l @reservation.date %>
+
Local: <%= @common_area.name %>
+
Status: <%= t(@reservation.status) %>
+
+
Atenção às Regras de Uso!
+
<%= @common_area.rules %>
+
+ <%= button_to 'Cancelar', canceled_reservation_path(@reservation), data: { turbo_confirm: 'Você tem certeza que deseja cancelar a reserva?' }, class: 'btn btn-dark rounded-pill px-4' if resident_signed_in? && @reservation.confirmed? %>
+
\ No newline at end of file
diff --git a/config/locales/breadcrumb.pt-BR.yml b/config/locales/breadcrumb.pt-BR.yml
index ec70cded..9fc70839 100644
--- a/config/locales/breadcrumb.pt-BR.yml
+++ b/config/locales/breadcrumb.pt-BR.yml
@@ -1,6 +1,10 @@
pt-BR:
breadcrumb:
edit: 'Editar'
+ reservation:
+ index: 'Reservas'
+ new: 'Nova Reserva'
+ show: 'Detalhes da Reserva'
common_area:
new: 'Cadastrar Área Comum'
condo:
diff --git a/config/locales/models/reservation.pt-BR.yml b/config/locales/models/reservation.pt-BR.yml
new file mode 100644
index 00000000..14c0bfb5
--- /dev/null
+++ b/config/locales/models/reservation.pt-BR.yml
@@ -0,0 +1,25 @@
+pt-BR:
+ confirmed: 'Confirmado'
+ canceled: 'Cancelado'
+ notices:
+ reservation:
+ created: 'Reserva realizada com sucesso!'
+ canceled: 'Reserva cancelada com sucesso!'
+ alerts:
+ reservation:
+ not_created: 'Não foi possível realizar a reserva'
+ unavailable: 'Essa data já está reservada para outro morador'
+ access_denied: 'Para continuar, faça login ou registre-se.'
+ not_authorized: 'Você não tem permissão para fazer isso.'
+ activerecord:
+ models:
+ reservation:
+ one: 'Reserva'
+ other: 'Reservas'
+ attributes:
+ reservation:
+ date: 'Data'
+ common_area: 'Área comum'
+ common_area_id: 'Área comum'
+ resident: 'Morador'
+ resident_id: 'Morador'
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 1ed4818b..56384afb 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -4,10 +4,12 @@
devise_for :managers
devise_for :residents
+
resources :managers, only: [:new, :create] do
get 'edit_photo', on: :member
patch 'update_photo', on: :member
end
+
resources :residents, only: [:new, :create, :show, :update] do
resources :visitors, only: [:new, :create, :index]
resources :tenants, only: [:new, :create], on: :collection
@@ -22,7 +24,14 @@
get 'find_units', on: :collection
end
- resources :common_areas, only: [:show, :edit, :update]
+ resources :common_areas, only: [:show, :edit, :update] do
+ resources :reservations, only: [:new, :create, :update]
+ end
+
+ resources :reservations, only: [:show] do
+ post 'canceled', on: :member
+ end
+
resources :unit_types, only: [:show, :edit, :update]
resources :condos, only: [:new, :create, :show, :edit, :update] do
diff --git a/db/migrate/20240716143712_create_reservations.rb b/db/migrate/20240716143712_create_reservations.rb
new file mode 100644
index 00000000..81a42eab
--- /dev/null
+++ b/db/migrate/20240716143712_create_reservations.rb
@@ -0,0 +1,12 @@
+class CreateReservations < ActiveRecord::Migration[7.1]
+ def change
+ create_table :reservations do |t|
+ t.date :date, null: false
+ t.integer :status, default: 0, null: false
+ t.references :common_area, null: false, foreign_key: true
+ t.references :resident, null: false, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 161532a3..6de7408c 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -104,6 +104,17 @@
t.index ["reset_password_token"], name: "index_managers_on_reset_password_token", unique: true
end
+ create_table "reservations", force: :cascade do |t|
+ t.date "date", null: false
+ t.integer "status", default: 0, null: false
+ t.integer "common_area_id", null: false
+ t.integer "resident_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["common_area_id"], name: "index_reservations_on_common_area_id"
+ t.index ["resident_id"], name: "index_reservations_on_resident_id"
+ end
+
create_table "residents", force: :cascade do |t|
t.string "full_name"
t.string "registration_number"
@@ -184,6 +195,8 @@
add_foreign_key "condo_managers", "managers"
add_foreign_key "condos", "addresses"
add_foreign_key "floors", "towers"
+ add_foreign_key "reservations", "common_areas"
+ add_foreign_key "reservations", "residents"
add_foreign_key "towers", "condos"
add_foreign_key "unit_types", "condos"
add_foreign_key "units", "floors"
diff --git a/spec/factories/reservations.rb b/spec/factories/reservations.rb
new file mode 100644
index 00000000..c43b7125
--- /dev/null
+++ b/spec/factories/reservations.rb
@@ -0,0 +1,8 @@
+FactoryBot.define do
+ factory :reservation do
+ date { Date.current + 1.week }
+ status { 0 }
+ common_area { build :common_area }
+ resident { build :resident }
+ end
+end
diff --git a/spec/factories/residents.rb b/spec/factories/residents.rb
index a6c7b9b4..18921839 100644
--- a/spec/factories/residents.rb
+++ b/spec/factories/residents.rb
@@ -5,5 +5,13 @@
sequence(:email) { |n| "João#{n}@example.com" }
password { '123456' }
status { :mail_confirmed }
+
+ trait :with_residence do
+ transient { condo { create :condo } }
+
+ after(:create) do |resident, evaluator|
+ resident.residence = create(:unit, floor: create(:floor, tower: create(:tower, condo: evaluator.condo)))
+ end
+ end
end
end
diff --git a/spec/models/reservation_spec.rb b/spec/models/reservation_spec.rb
new file mode 100644
index 00000000..f9190b28
--- /dev/null
+++ b/spec/models/reservation_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+RSpec.describe Reservation, type: :model do
+ describe '#valid' do
+ it 'date must be present' do
+ reservation = build :reservation, date: nil
+
+ expect(reservation).not_to be_valid
+ expect(reservation.errors).to include :date
+ end
+
+ it 'date cannot have multiple reservations' do
+ common_area = create :common_area, rules: 'Não pode subir no escorregador.'
+ first_resident = build :resident
+ second_resident = build :resident, email: 'morador@mail.com'
+
+ first_reservation = build :reservation, common_area:, date: Date.current + 1.week, resident: first_resident
+ common_area.reservations << first_reservation
+
+ second_reservation = build :reservation, common_area:, date: Date.current + 1.week, resident: second_resident
+
+ expect(Reservation.all.size).to eq 1
+ expect(second_reservation).not_to be_valid
+ expect(second_reservation.errors.full_messages).to include "Data #{I18n.l Date.current + 1.week} " \
+ 'já está reservada para esta área comum'
+ end
+
+ it 'date must be current or future' do
+ reservation = build :reservation, date: Date.current - 1.day
+
+ expect(Reservation.all.empty?).to be true
+ expect(reservation).not_to be_valid
+ expect(reservation.errors.full_messages).to include 'Data deve ser atual ou futura'
+ end
+ end
+end
diff --git a/spec/requests/reservation_spec.rb b/spec/requests/reservation_spec.rb
new file mode 100644
index 00000000..ba1d2f66
--- /dev/null
+++ b/spec/requests/reservation_spec.rb
@@ -0,0 +1,79 @@
+require 'rails_helper'
+
+describe 'Reservation' do
+ context 'POST /reservations' do
+ it 'must be authenticated as resident to make a reservation' do
+ common_area = create :common_area
+
+ post common_area_reservations_path common_area, params: { reservation: { date: I18n.l(Date.current + 1.week) } }
+
+ expect(response).to redirect_to new_resident_session_path
+ expect(flash[:alert]).to eq 'Para continuar, faça login ou registre-se.'
+ expect(Reservation.all.empty?).to be true
+ end
+
+ it 'and administrator cannot make a reservation' do
+ common_area = create :common_area
+ manager = build :manager
+
+ login_as manager, scope: :manager
+ post common_area_reservations_path common_area, params: { reservation: { date: I18n.l(Date.current + 1.week) } }
+
+ expect(response).to redirect_to root_path
+ expect(flash[:alert]).to eq 'Você não tem permissão para fazer isso.'
+ expect(Reservation.all.empty?).to be true
+ end
+
+ it 'and resident must live at related condo of the common area' do
+ first_condo = create :condo
+ first_resident = create :resident, :with_residence, condo: first_condo
+
+ second_condo = create :condo
+ common_area = create :common_area, condo: second_condo
+
+ login_as first_resident, scope: :resident
+ post common_area_reservations_path common_area, params: { reservation: { date: I18n.l(Date.current + 1.week) } }
+
+ expect(response).to redirect_to root_path
+ expect(flash[:alert]).to eq 'Você não tem permissão para fazer isso.'
+ expect(Reservation.all.empty?).to be true
+ end
+ end
+
+ context 'POST /canceled_reservation' do
+ it 'must be authenticated as resident to cancel a reservation' do
+ reservation = create :reservation
+
+ post canceled_reservation_path reservation
+
+ expect(response).to redirect_to new_resident_session_path
+ expect(flash[:alert]).to eq 'Para continuar, faça login ou registre-se.'
+ expect(reservation.confirmed?).to be true
+ end
+
+ it 'and residents can only cancel their own reservations' do
+ first_resident = create :resident
+ second_resident = create :resident, email: 'second@email.com'
+ reservation = create(:reservation, resident: first_resident)
+
+ login_as second_resident, scope: :resident
+ post canceled_reservation_path reservation
+
+ expect(response).to redirect_to root_path
+ expect(flash[:alert]).to eq 'Você não tem permissão para fazer isso.'
+ expect(reservation.confirmed?).to be true
+ end
+
+ it 'and administrator cannot cancel a reservation' do
+ reservation = create :reservation
+ manager = build :manager
+
+ login_as manager, scope: :manager
+ post canceled_reservation_path reservation
+
+ expect(response).to redirect_to root_path
+ expect(flash[:alert]).to eq 'Você não tem permissão para fazer isso.'
+ expect(reservation.confirmed?).to be true
+ end
+ end
+end
diff --git a/spec/system/common_area/manager_edits_common_area_spec.rb b/spec/system/common_area/manager_edits_common_area_spec.rb
index 19ada9de..bbbb2143 100644
--- a/spec/system/common_area/manager_edits_common_area_spec.rb
+++ b/spec/system/common_area/manager_edits_common_area_spec.rb
@@ -1,7 +1,25 @@
require 'rails_helper'
describe 'Manager edits common area' do
- it 'succesfully' do
+ it 'and must be authenticated' do
+ common_area = create :common_area
+
+ visit edit_common_area_path common_area
+
+ expect(current_path).to eq new_manager_session_path
+ end
+
+ it 'and does not see edit button if is a resident' do
+ common_area = create :common_area
+ resident = create :resident
+
+ login_as resident, scope: :resident
+ visit new_common_area_reservation_path common_area
+
+ expect(page).not_to have_link 'Editar'
+ end
+
+ it 'successfully' do
manager = create(:manager)
common_area = create(:common_area)
@@ -38,12 +56,4 @@
expect(page).to have_content 'Descrição não pode ficar em branco'
expect(page).to have_content 'Capacidade Máxima não pode ficar em branco'
end
-
- it 'and must be authenticated' do
- common_area = create(:common_area)
-
- visit edit_common_area_path(common_area)
-
- expect(current_path).to eq new_manager_session_path
- end
end
diff --git a/spec/system/common_area/reservation/resident_cancel_common_area_reservation_spec.rb b/spec/system/common_area/reservation/resident_cancel_common_area_reservation_spec.rb
new file mode 100644
index 00000000..e2fbabc7
--- /dev/null
+++ b/spec/system/common_area/reservation/resident_cancel_common_area_reservation_spec.rb
@@ -0,0 +1,19 @@
+require 'rails_helper'
+
+describe 'Resident cancel common area reservation' do
+ it 'from the reservation details page' do
+ resident = create :resident
+ reservation = create :reservation, resident:, status: :confirmed
+
+ login_as resident, scope: :resident
+ visit reservation_path reservation
+ click_on 'Cancelar'
+ reservation.reload
+
+ expect(current_path).to eq reservation_path reservation
+ expect(page).to have_content 'Reserva cancelada com sucesso'
+ expect(page).to have_content 'Status: Cancelado'
+ expect(page).not_to have_button 'Cancelar'
+ expect(reservation.canceled?).to be true
+ end
+end
diff --git a/spec/system/common_area/reservation/resident_reserves_common_area_spec.rb b/spec/system/common_area/reservation/resident_reserves_common_area_spec.rb
new file mode 100644
index 00000000..fab21b1a
--- /dev/null
+++ b/spec/system/common_area/reservation/resident_reserves_common_area_spec.rb
@@ -0,0 +1,92 @@
+require 'rails_helper'
+
+describe 'Resident reserves common area' do
+ it 'only if authenticated' do
+ common_area = create :common_area
+
+ visit new_common_area_reservation_path common_area
+
+ expect(current_path).to eq new_resident_session_path
+ end
+
+ it 'and does not see reserve button if is a manager' do
+ common_area = create :common_area
+ manager = create :manager
+
+ login_as manager, scope: :manager
+ visit new_common_area_reservation_path common_area
+
+ expect(page).not_to have_link 'Reservar'
+ end
+
+ it "only if has a residence at common area's condo" do
+ first_condo = create :condo
+ first_resident = create :resident, :with_residence, condo: first_condo
+
+ second_condo = create :condo
+ common_area = create :common_area, condo: second_condo
+
+ login_as first_resident, scope: :resident
+ visit new_common_area_reservation_path common_area
+
+ expect(current_path).to eq root_path
+ expect(page).to have_content 'Você não tem permissão para fazer isso'
+ end
+
+ it 'successfully if there is no confirmed reservation on the same date' do
+ common_area = create :common_area, rules: 'Não pode subir no escorregador.'
+ first_resident = create :resident, :with_residence, condo: common_area.condo
+ second_resident = create :resident
+
+ create :reservation,
+ common_area:,
+ resident: second_resident,
+ date: Date.current + 1.week,
+ status: :canceled
+
+ login_as first_resident, scope: :resident
+ visit common_area_path common_area
+ click_on 'Reservar'
+ fill_in 'Data', with: Date.current + 1.week
+ click_on 'Reservar'
+
+ expect(current_path).to eq reservation_path Reservation.last
+ expect(page).to have_content 'Reserva realizada com sucesso!'
+ expect(page).to have_content "Data: #{I18n.l Date.current + 1.week}"
+ expect(page).to have_content 'Status: Confirmado'
+ expect(page).to have_content 'Não pode subir no escorregador'
+ expect(Reservation.last.resident).to eq first_resident
+ end
+
+ it "fail if date isn't selected" do
+ common_area = create :common_area
+ resident = create :resident, :with_residence, condo: common_area.condo
+
+ login_as resident, scope: :resident
+ visit new_common_area_reservation_path common_area
+ fill_in 'Data', with: ''
+ click_on 'Reservar'
+
+ expect(current_path).to eq new_common_area_reservation_path common_area
+ expect(page).to have_content 'Não foi possível realizar a reserva'
+ expect(page).to have_content 'Data não pode ficar em branco'
+ expect(Reservation.all.empty?).to be true
+ end
+
+ it 'fail if the date already has a reservation confirmed' do
+ common_area = create :common_area, rules: 'Não pode subir no escorregador.'
+ first_resident = create :resident
+ second_resident = create :resident, :with_residence, condo: common_area.condo
+ create :reservation, common_area:, date: Date.current + 1.week, resident: first_resident, status: :confirmed
+
+ login_as second_resident, scope: :resident
+ visit new_common_area_reservation_path common_area
+ fill_in 'Data', with: Date.current + 1.week
+ click_on 'Reservar'
+
+ expect(current_path).to eq new_common_area_reservation_path common_area
+ expect(page).to have_content 'Não foi possível realizar a reserva'
+ expect(page).to have_content "Data #{I18n.l Date.current + 1.week} já está reservada para esta área comum"
+ expect(Reservation.all.size).to eq 1
+ end
+end
diff --git a/spec/system/common_area/reservation/user_sees_reservation_details_spec.rb b/spec/system/common_area/reservation/user_sees_reservation_details_spec.rb
new file mode 100644
index 00000000..6c2a0bf5
--- /dev/null
+++ b/spec/system/common_area/reservation/user_sees_reservation_details_spec.rb
@@ -0,0 +1,50 @@
+require 'rails_helper'
+
+describe 'User sees reservation details' do
+ it 'and must be authenticated' do
+ reservation = create :reservation
+
+ visit reservation_path reservation
+
+ expect(current_path).to eq signup_choice_path
+ end
+
+ it 'and administrators can see any reservation details from their condo' do
+ condo = create :condo
+ manager = create :manager
+ condo.managers << manager
+ common_area = create(:common_area, condo:)
+ reservation = create(:reservation, common_area:)
+
+ login_as manager, scope: :manager
+ visit reservation_path reservation
+
+ expect(current_path).to eq reservation_path reservation
+ expect(page).not_to have_link 'Cancelar'
+ end
+
+ it 'and administrators cannot see reservation details from another condos' do
+ condo = create :condo
+ manager = create :manager, is_super: false
+ common_area = create(:common_area, condo:)
+ reservation = create(:reservation, common_area:)
+
+ login_as manager, scope: :manager
+ visit reservation_path reservation
+
+ expect(current_path).to eq root_path
+ expect(page).to have_content 'Você não tem permissão para fazer isso.'
+ end
+
+ it 'and residents can only see their own reservation details' do
+ first_resident = create :resident
+ second_resident = create :resident, email: 'second@email.com'
+ reservation = create :reservation, resident: first_resident
+
+ login_as second_resident, scope: :resident
+ visit reservation_path reservation
+
+ expect(current_path).to eq root_path
+ expect(page).to have_content 'Você não tem permissão para fazer isso.'
+ end
+end
diff --git a/spec/system/resident/manager_sets_resident_as_tenant_spec.rb b/spec/system/resident/manager_sets_resident_as_tenant_spec.rb
index 337aa8dd..d13c7741 100644
--- a/spec/system/resident/manager_sets_resident_as_tenant_spec.rb
+++ b/spec/system/resident/manager_sets_resident_as_tenant_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe 'managers access page to set a resident as tenant' do
+describe 'managers sets resident as tenant' do
it 'and is not authenticated' do
resident = create :resident
@@ -36,6 +36,7 @@
select '2', from: 'Unidade'
click_on 'Atualizar Morador'
+ sleep 3
expect(current_path).to eq resident_path resident
expect(mail).to have_received(:deliver).once
expect(page).to have_content 'Atualizado com sucesso!'
@@ -65,8 +66,9 @@
select '2', from: 'Unidade'
click_on 'Não reside neste condomínio'
- expect(page).to have_content 'Atualizado com sucesso!'
+ sleep 3
expect(current_path).to eq resident_path resident
+ expect(page).to have_content 'Atualizado com sucesso!'
resident.reload
expect(resident.residence).to eq nil
end