「app\controllers\rooms_controller.rb」ファイルを編集します。
1.記述追加 app\controllers\rooms_controller.rb(71行目)
# 予約 開始日のAJAX def preload today = Date.today reservations = @room.reservations.where("start_date >= ? OR end_date >= ?", today, today) render json: reservations end # 予約 終了日のAJAX def preview start_date = Date.parse(params[:start_date]) end_date = Date.parse(params[:end_date]) output = { conflict: is_conflict(start_date, end_date, @room) } render json: output end
2.記述追加 app\controllers\rooms_controller.rb(105行目)
# 予約 プライベートメソッド def is_conflict(start_date, end_date, room) check = room.reservations.where("? < start_date AND end_date < ?", start_date, end_date) check.size > 0? true : false end
app\controllers\rooms_controller.rb
class RoomsController < ApplicationController protect_from_forgery except: [:upload_photo] before_action :set_room, except: [:index, :new, :create] before_action :authenticate_user!, except: [:show] before_action :is_authorised, only: [:listing, :pricing, :description, :photo_upload, :amenities, :location, :update] def index @rooms = current_user.rooms end def new @room = current_user.rooms.build end def create @room = current_user.rooms.build(room_params) if @room.save redirect_to listing_room_path(@room), notice: "保存しました。" else flash[:alert] = "問題が発生しました。" render :new end end def show @photos = @room.photos end def listing end def pricing end def description end def photo_upload end def amenities end def location end def update new_params = room_params new_params = room_params.merge(active: true) if is_ready_room if @room.update(new_params) flash[:notice] = "保存しました。" else flash[:alert] = "問題が発生しました。" end redirect_back(fallback_location: request.referer) end def upload_photo @room.photos.attach(params[:file]) render json: { success: true } end def delete_photo @image = ActiveStorage::Attachment.find(params[:photo_id]) @image.purge redirect_to photo_upload_room_path(@room) end # 予約 開始日のAJAX def preload today = Date.today reservations = @room.reservations.where("start_date >= ? OR end_date >= ?", today, today) render json: reservations end # 予約 終了日のAJAX def preview start_date = Date.parse(params[:start_date]) end_date = Date.parse(params[:end_date]) output = { conflict: is_conflict(start_date, end_date, @room) } render json: output end private def set_room @room = Room.find(params[:id]) end def room_params params.require(:room).permit(:home_type, :room_type, :accommodate, :bed_room, :bath_room, :listing_name, :summary, :address, :is_tv, :is_kitchen, :is_air, :is_heating, :is_internet, :price, :active, :description) end def is_authorised redirect_to root_path, alert: "権限がありません。" unless current_user.id == @room.user_id end def is_ready_room !@room.active && !@room.price.blank? && !@room.listing_name.blank? && !@room.photos.blank? && !@room.address.blank? end # 予約 プライベートメソッド def is_conflict(start_date, end_date, room) check = room.reservations.where("? < start_date AND end_date < ?", start_date, end_date) check.size > 0? true : false end end
記述追加 config\routes.rb
「get 'preload'」の記述追加(25行目)と「get 'preview'」の記述追加(26行目)
Rails.application.routes.draw do # ルートを app\views\pages\home.html.erb に設定 root 'pages#home' devise_for :users, path: '', path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'}, controllers: {registrations: 'registrations'} get 'pages/home' get '/dashboard', to: 'users#dashboard' get '/users/:id', to: 'users#show', as: 'user' post '/users/edit', to: 'users#update' resources :rooms, except: [:edit] do member do get 'listing' get 'pricing' get 'description' get 'photo_upload' get 'amenities' get 'location' get 'preload' get 'preview' delete :delete_photo post :upload_photo end resources :reservations, only: [:create] end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
記述更新 app\views\reservations\_form.html.erb
コードをコピーして内容を置き換えてください。
<div class="card"> <header class="card-header"> <p class="card-header-title"> <span><i class="fas fa-chess-king" style="color: #ffb400"></i></span> <span class="pull-right">1泊 <%= number_to_currency(@room.price) %></span> </p> </header> <div class="card-content"> <div class="content"> <%= form_for([@room, @room.reservations.new]) do |f| %> <div class="field is-horizontal"> <div class="field-body"> <div class="field"> <p class="control is-expanded has-icons-left"> <%= f.text_field :start_date, readonly: true, placeholder: "イン", class: "input is-primary is-rounded" %> <span class="icon is-small is-left"> <i class="far fa-calendar-alt"></i> </span> </p> </div> <div class="field"> <p class="control is-expanded has-icons-left has-icons-right"> <%= f.text_field :end_date, readonly: true, placeholder: "アウト", class: "input is-primary is-rounded" %> <span class="icon is-small is-left"> <i class="far fa-calendar-alt"></i> </span> </p> </div> </div> </div> <h4 class="message-alert text-center"><span id="message"></span></h4> <div id="preview" style="display: none"> <table class="reservation-table"> <tbody> <tr> <td>宿泊費用</td> <td class="text-right", style="white-space: nowrap"><%= number_to_currency(@room.price) %></td> </tr> <tr> <td>宿泊日数</td> <td class="text-right"><span id="reservation_nights"></span>泊</td> </tr> <tr> <td class="total">合計金額</td> <td class="text-right", style="white-space: nowrap"><span id="reservation_total"></span>円</td> </tr> </tbody> </table> </div> <%= f.submit "予約する", class: "button is-danger is-fullwidth m-t-10 m-b-10" %> <% end %> </div> </div> </div> <script> function checkDate(date) { dmy = date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear(); return [$.inArray(dmy, unavailableDates) == -1]; } $(function() { unavailableDates = []; $.ajax({ url: '<%= preload_room_path(@room) %>', dateTyp: 'json', success: function(data) { $.each(data, function(arrID, arrValue) { for(var d = new Date(arrValue.start_date); d <= new Date(arrValue.end_date); d.setDate(d.getDate() + 1)) { unavailableDates.push($.datepicker.formatDate('d-m-yy', d)); } }); $('#reservation_start_date').datepicker({ dateFormat: 'dd-mm-yy', //今日から3ヶ月先まで予約可能 minDate: 0, maxDate: '3m', beforeShowDay: checkDate, onSelect: function(selected) { $('#reservation_end_date').datepicker("option", "minDate", selected); $('#reservation_end_date').attr("disabled", false); var start_date = $('#reservation_start_date').datepicker('getDate'); var end_date = $('#reservation_end_date').datepicker('getDate'); //2日選択すると1泊になる var nights = (end_date - start_date)/1000/60/60/24; var input = { 'start_date': start_date, 'end_date': end_date } $.ajax({ url: '<%= preview_room_path(@room) %>', data: input, success: function(data) { if(data.conflict) { $('#message').text("この日付はご利用できません。"); $('#preview').hide(); $('#btn_book').attr('disabled', true); } else { $('#message').text(""); $('#preview').show(); $('#btn_book').attr('disabled', false); var total = nights * <%= @room.price %> $('#reservation_nights').text(nights); $('#reservation_total').text(total); } } }); } }); $('#reservation_end_date').datepicker({ dateFormat: 'dd-mm-yy', //今日から3ヶ月先まで予約可能 minDate: 0, maxDate: '3m', beforeShowDay: checkDate, onSelect: function(selected) { $('#reservation_start_date').datepicker("option", "maxDate", selected); var start_date = $('#reservation_start_date').datepicker('getDate'); var end_date = $('#reservation_end_date').datepicker('getDate'); var nights = (end_date - start_date)/1000/60/60/24; var input = { 'start_date': start_date, 'end_date': end_date } $.ajax({ url: '<%= preview_room_path(@room) %>', data: input, success: function(data) { if(data.conflict) { $('#message').text("この日付ではご予約できません。"); $('#preview').hide(); $('#btn_book').attr('disabled', true); } else { $('#message').text(""); $('#preview').show(); $('#btn_book').attr('disabled', false); var total = nights * <%= @room.price %> $('#reservation_nights').text(nights); $('#reservation_total').text(total); } } }); } }); } }); }); </script>
「app\views\rooms\show.html.erb」ファイルの記述を変更します。
記述変更 app\views\rooms\show.html.erb
265行目から267行目の記述を変更しています。
<section class="section"> <div class="container"> <div class="columns"> <!-- 写真 --> <div class="card"> <div class="card-content"> <div class="content"> <%= image_tag room_cover(@room) %> </div> </div> </div> <br/> </div> </div> </section> <section class="section"> <div class="container"> <div class="columns"> <!-- 左側 --> <div class="column is-two-thirds"> <div class="columns is-multiline"> <div class="column"> <div class="card"> <div class="card-content"> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <!-- お部屋の名前 --> <div class="row"> <div class="col-md-8"> <h1><%= @room.listing_name %></h1> <h2><%= @room.address %></h2> </div> </div> </div> </div> </article> </div> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <span class="title is-5"><%= @room.user.full_name %></span> <figure class="image is-96x96"> <%= image_tag avatar_url(@room.user), class: "is-rounded" %> </figure> </div> </div> </article> </div> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <!-- 部屋のインフォメーション --> <div style="white-space: nowrap"> <span class="col-md-3"> <i class="fas fa-home fa-3x" style="color: #1dbf73"></i> <span class="col-md-3"><%= @room.home_type %></span> </span> <span class="col-md-3"> <i class="fas fa-user fa-3x" style="color: #1dbf73"></i> <span class="col-md-3"><%= pluralize(@room.accommodate, "人") %></span> </span> <span class="col-md-3"> <i class="fas fa-bed fa-3x" style="color: #1dbf73"></i> <span class="col-md-3"><%= pluralize(@room.bed_room, "台") %></span> </span> <span class="col-md-3"> <i class="fas fa-bath fa-3x" style="color: #1dbf73"></i> <span class="col-md-3"><%= pluralize(@room.bath_room, "部屋") %></span> </span> </div> </div> </div> </article> </div> <!-- お部屋の詳細 --> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <h3>お部屋の詳細</h3> <p><%= @room.description %></p> </div> </div> </article> </div> <!-- アメニティー --> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <div class="row"> <div class="col-md-3"> <h4>アメニティ</h4> </div> <div class="col-md-9"> <div class="row"> <div class="col-md-6"> <ul class="amenities"> <li class="<%= 'text-line-through' if !@room.is_tv %>">テレビ</li> <li class="<%= 'text-line-through' if !@room.is_kitchen %>">キッチン</li> <li class="<%= 'text-line-through' if !@room.is_internet %>">インターネット</li> </ul> </div> <div class="col-md-6"> <ul class="amenities"> <li class="<%= 'text-line-through' if !@room.is_heating %>">暖房</li> <li class="<%= 'text-line-through' if !@room.is_air %>">エアコン</li> </ul> </div> </div> </div> </div> </div> </div> </article> </div> </div> </div> </div> <!-- カルーセル表示 --> <div class="column is-full"> <div class="card"> <div class="card-content"> <div class="hero-carousel" id="carousel-photo"> <% @room.photos.each do |photo| %> <div class="carousel-item has-background image is-16by9"> <%= image_tag url_for(photo), class: "is-background", width: "100%" %> </div> <% end %> </div> </div> </div> </div> <div class="column"> <div class="card"> <div class="card-content"> <!-- googleマップ --> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <!-- GOOGLE マップ --> <div class="row"> <div id="map" style="width: 100%; height: 400px"></div> <script src="https://maps.googleapis.com/maps/api/js"></script> <script> function initialize() { var location = {lat: <%= @room.latitude %>, lng: <%= @room.longitude %>}; var map = new google.maps.Map(document.getElementById('map'), { center: location, zoom: 14 }); var marker = new google.maps.Marker({ position: location, map: map }); var infoWindow = new google.maps.InfoWindow({ content: '<div id="content" class="image is-128x128"><%= image_tag room_cover(@room) %></div>' }); infoWindow.open(map, marker); } google.maps.event.addDomListener(window, 'load', initialize); </script> <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBojDcZmScBkIOISjoYREjgid99iZUL2Tk&callback=initMap" type="text/javascript"></script> </div> </div> </div> </article> </div> <!-- 近くのお部屋 --> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <!-- 近くのお部屋を検索 --> <div class="row"> <h3>お近くのお部屋</h3> <br/> <% for room in @room.nearbys(10) %> <div class="col-md-4"> <div class="image is-128x128"> <%= image_tag room_cover(@room) %> </div> <span> <%= link_to room.listing_name, room %><br/> (距離:<%= room.distance.round(2) %> Km) </span> </div> <% end %> </div> </div> </div> </article> </div> </div> </div> </div> </div> </div> <!-- 右側 --> <div class="column"> <div class="columns is-multiline"> <!-- 予約 --> <div class="column is-full"> <div class="media"> <!-- 予約フォーム --> <%= render 'reservations/form' %> </div> </div> </div> </div> </div> </div> </section> <script> BulmaCarousel.attach('#carousel-photo', { slidesToScroll: 1, slidesToShow: 1 }); $(document).ready(function() { $('#tabs li').on('click', function() { var type = $(this).data('tab'); $('#tabs li').removeClass('is-active'); $(this).addClass('is-active'); $('.tab-content').hide(); $('#tab-' + type).show(); }) }) </script>
ブラウザ確認
ログインしないと予約カレンダーが出ないようになっています。
実際に予約できるか確認してください。
http://localhost:3000/rooms/1