↓↓クリックして頂けると励みになります。
【31 | 予約確認】 << 【ホーム】 >> 【33 | Ransack】
raty-jsは、スター評価を作成するためのJavaScriptプラグインです。
Rails6.1でraty-jsを利用するには、以下のステップに従って設定を行う必要があります。
「star-on.png」「star-off.png」「star-half.png」の3ファイルを「app/assets/images」フォルダにコピーしておいてください。
画像は下記のリンクにあります。
github.com
「raty-js」をインストールします。
Rails6.1ではyarnを利用していましたが、Rails7.1ではimportmapを利用しまし。
しかしながら、使用するJQueryがimportmapでうまく動作しないため、raty-jsも「app/views/layouts/application.html.erb」ファイルのheadタグ内で直接CDNのスクリプトを読み込ませます。
記述追加 「app/views/layouts/application.html.erb」43行目
<!-- ratyjs 3.1.1 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/raty/3.1.1/jquery.raty.min.js" integrity="sha512-Isj3SyFm+B8u/cErwzYj2iEgBorGyWqdFVb934Y+jajNg9kiYQQc9pbmiIgq/bDcar9ijmw4W+bd72UK/tzcsA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
記述追加 【app/views/layouts/application.html.erb】43行目
<!DOCTYPE html> <html> <head> <title>VacationRental7</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> <!-- noty3.1.4 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.min.js" integrity="sha512-lOrm9FgT1LKOJRUXF3tp6QaMorJftUjowOWiDcG5GFZ/q7ukof19V0HKx/GWzXCdt9zYju3/KhBNdCLzK8b90Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.min.css" integrity="sha512-0p3K0H3S6Q4bEWZ/WmC94Tgit2ular2/n0ESdfEX8l172YyQj8re1Wu9s/HT9T/T2osUw5Gx/6pAZNk3UKbESw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <!-- Google Fonts --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Kaisei+Opti&family=Kosugi+Maru&family=Rampart+One&display=swap" rel="stylesheet"> <!-- Dropzone5.5.1 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.js" integrity="sha512-jytq61HY3/eCNwWirBhRofDxujTCMFEiQeTe+kHR4eYLNTXrUq7kY2qQDKOUnsVAKN5XGBJjQ3TvNkIkW/itGw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.css" integrity="sha512-zoIoZAaHj0iHEOwZZeQnGqpU8Ph4ki9ptyHZFPe+BmILwqAksvwm27hR9dYH4WXjYY/4/mz8YDBCgVqzc2+BJA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <!-- JQuery 3.7.1 --> <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script> <!-- JQuery-UI 1.13.2 --> <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js" integrity="sha256-lSjKY0/srUM9BE3dPm+c4fBo1dky2v27Gdjm2uoZaL0=" crossorigin="anonymous"></script> <!-- 日付ピッカー デザインsunny--> <link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/sunny/jquery-ui.css"> <!-- 日付ピッカーの日本語化--> <script src="https://rawgit.com/jquery/jquery-ui/master/ui/i18n/datepicker-ja.js"></script> <!-- ratyjs 3.1.1 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/raty/3.1.1/jquery.raty.min.js" integrity="sha512-Isj3SyFm+B8u/cErwzYj2iEgBorGyWqdFVb934Y+jajNg9kiYQQc9pbmiIgq/bDcar9ijmw4W+bd72UK/tzcsA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> </head> <body> <!-- ナビゲーションバー --> <%= render "shared/navbar" %> <!-- noty --> <%= render 'shared/notification' %> <%= yield %> </body> </html>
レビューモデルを作成します。
まず、ゲスト用レビューモデルを作成します。
コマンド
rails g model GuestReview --parent=Review --migration=false
次に、ホスト用レビューモデルを作成します。
コマンド
rails g model HostReview --parent=Review --migration=false
レビューモデルを作成します。
コマンド
一文です。
rails g model Review review:text stars:bigint room:references reservation:references guest:references host:references type
記述更新 db\migrate\20200727021130_create_reviews.rb
「,default: 1」の記述追加(5行目)をしています。
また、8,9行目に「, foreign_key: { to_table: :users }」の記述を追加しています。
class CreateReviews < ActiveRecord::Migration[7.1] def change create_table :reviews do |t| t.text :review t.bigint :stars,default: 1 t.references :room, null: false, foreign_key: true t.references :reservation, null: false, foreign_key: true t.references :guest, null: false, foreign_key: true, foreign_key: { to_table: :users } t.references :host, null: false, foreign_key: true, foreign_key: { to_table: :users } t.string :type t.timestamps end end end
マイグレーションを適用します。
コマンド マイグレーション
rails db:migrate
ゲストレビューモデル、ホストレビューモデルをユーザーモデルに関連付けします。
記述追加 app\models\guest_review.rb
「belongs_to :guest, class_name: "User"」の追加(2行目)
class GuestReview < Review belongs_to :guest, class_name: "User" end
記述追加 app\models\host_review.rb
「belongs_to :host, class_name: "User"」の追加(2行目)
class HostReview < Review belongs_to :host, class_name: "User" end
「app\models\review.rb」ファイルを以下のように編集します。
記述追加 app\models\review.rb
4行目と5行目に「, class_name: "User"」の記述を追加しています。
class Review < ApplicationRecord belongs_to :room belongs_to :reservation belongs_to :guest, class_name: "User" belongs_to :host, class_name: "User" end
「app\models\room.rb」ファイルを編集していきます。
1.6行目に「has_many :guest_reviews」の記述追加
2.7行目に「has_many :reviews」の記述追加
3.21行目に「average_rating()」メソッドの追加
def average_rating guest_reviews.count == 0 ? 0 : guest_reviews.average(:stars).round(2).to_i end
記述追加 app\models\room.rb
class Room < ApplicationRecord belongs_to :user has_many :reservations has_many :guest_reviews has_many :reviews has_many_attached :photos has_rich_text :description geocoded_by :address after_validation :geocode, if: :address_changed? validates :home_type, presence: true validates :room_type, presence: true validates :accommodate, presence: true validates :bed_room, presence: true validates :bath_room, presence: true def average_rating guest_reviews.count == 0 ? 0 : guest_reviews.average(:stars).round(2).to_i end end
「app\models\reservation.rb」ファイルを編集します。
記述追加 app\models\reservation.rb
5行目に「has_many :reviews」の記述追加
class Reservation < ApplicationRecord belongs_to :user belongs_to :room has_many :reviews end
「app\models\user.rb」ファイルを編集していきます。
5, 6行目に以下の記述を追加します。
has_many :guest_reviews, class_name: "GuestReview", foreign_key: "guest_id" has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id"
記述追加 app\models\user.rb
class User < ApplicationRecord has_many :rooms has_many :reservations has_many :guest_reviews, class_name: "GuestReview", foreign_key: "guest_id" has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id" has_one_attached :avatar validates :full_name, presence: true, length: {maximum: 50} # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :omniauthable def self.from_omniauth(auth) user = User.where(email: auth.info.email).first if user return user else where(provider: auth.provider, uid: auth.uid).first_or_create do |user| user.email = auth.info.email user.password = Devise.friendly_token[0, 20] user.full_name = auth.info.name # ユーザーモデルに名前があると仮定 user.image = auth.info.image # ユーザーモデルに画像があると仮定 user.uid = auth.uid user.provider = auth.provider end end end end
レビューコントローラを作成します。
「app\controllers」フォルダに「host_reviews_controller.rb」ファイルを新規作成してください。
app\controllers\host_reviews_controller.rb(新規作成したファイル)
class HostReviewsController < ApplicationController def create # ステップ 1: 予約が存在するかどうかを確認します(room_id、guest_id、host_id) # ステップ 2: 現在のホストがこの予約のゲストをすでにレビューしているかどうかを確認します。 @reservation = Reservation.where( id: host_review_params[:reservation_id], room_id: host_review_params[:room_id], user_id: host_review_params[:guest_id] ).first if !@reservation.nil? @has_reviewed = HostReview.where( reservation_id: @reservation.id, guest_id: host_review_params[:guest_id] ).first if @has_reviewed.nil? # レビューを許可 @host_review = current_user.host_reviews.create(host_review_params) flash[:success] = "レビューを作成しました。" else # レビュー済み flash[:alert] = "この予約はレビュー済みです。" end else flash[:alert] = "予約がありません。" end redirect_back(fallback_location: request.referer) end def destroy @host_review = Review.find(params[:id]) @host_review.destroy redirect_back(fallback_location: request.referer, notice: "削除しました。") end private def host_review_params params.require(:host_review).permit(:review, :stars, :room_id, :reservation_id, :guest_id) end end
「app\controllers」フォルダに「guest_reviews_controller.rb」ファイルを新規作成してください。
app\controllers\guest_reviews_controller.rb(新規作成したファイル)
class GuestReviewsController < ApplicationController def create # ステップ 1: 予約が存在するかどうかを確認します(room_id、host_id、host_id) # ステップ 2: 現在のホストがこの予約のゲストをすでにレビューしているかどうかを確認します。 @reservation = Reservation.where( id: guest_review_params[:reservation_id], room_id: guest_review_params[:room_id] ).first if !@reservation.nil? && @reservation.room.user.id == guest_review_params[:host_id].to_i @has_reviewed = GuestReview.where( reservation_id: @reservation.id, host_id: guest_review_params[:host_id] ).first if @has_reviewed.nil? # レビューを許可 @guest_review = current_user.guest_reviews.create(guest_review_params) flash[:success] = "レビューを作成しました。" else # レビュー済み flash[:alert] = "この予約はレビュー済みです。" end else flash[:alert] = "予約がありません。" end redirect_back(fallback_location: request.referer) end def destroy @guest_review = Review.find(params[:id]) @guest_review.destroy redirect_back(fallback_location: request.referer, notice: "削除しました。") end private def guest_review_params params.require(:guest_review).permit(:review, :stars, :room_id, :reservation_id, :host_id) end end
記述追加 app\controllers\rooms_controller.rb
「show()」メソッドに「@guest_reviews = @room.guest_reviews」の記述を追加してます。(30行目)
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, :photo_upload, :delete_photo, :amenities, :location, :update] def index @rooms = Room.all 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 @i = 0 @guest_reviews = @room.guest_reviews 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, :latitude, :longitude, :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
「app\controllers\users_controller.rb」ファイルを編集します。
show()メソッドの11行目に以下の記述を追加します。
# ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: @user.id)
dashboad()メソッドの6行目にも以下の記述を追加しています。
# ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: current_user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: current_user.id)
app\controllers\users_controller.rb
class UsersController < ApplicationController before_action :authenticate_user! def dashboard # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: current_user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: current_user.id) end def show @user = User.find(params[:id]) # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: @user.id) end def update @user = current_user if @user.update(current_user_params) flash[:notice] = "保存しました" else flash[:alert] = "更新できません" end redirect_to dashboard_path end private def current_user_params params.require(:user).permit(:about, :status, :avatar) end end
ルートを設定します。
32,33行目に以下の記述を追加します。
resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy]
記述追加 config\routes.rb
Rails.application.routes.draw do # ルートを app\views\pages\home.html.erb に設定 root 'pages#home' # get get '/dashboard', to: 'users#dashboard' get 'pages/home' get '/users/:id', to: 'users#show', as: 'user' get '/your_trips' => 'reservations#your_trips' get '/your_reservations' => 'reservations#your_reservations' # post 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 resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy] # device devise_for :users, path: '', path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'}, controllers: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'} # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
ビューを作成します。
レビューフォームのレンダーファイルを作成します。
「app\views」フォルダに「reviews」フォルダを新規作成します。
作成した「reviews」フォルダに「_guest_form.html.erb」ファイルを新規作成してください。
app\views\reviews\_guest_form.html.erb(新規作成したファイル)
<!-- Button trigger modal --> <button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#exampleModal<%= reservation.id %>"> <font style="font-size: 0.9rem;">レビュー</font> </button> <!-- Modal --> <div class="modal fade" id="exampleModal<%= reservation.id %>" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <%= form_for GuestReview.new do |f| %> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabel"><div id="stars<%= reservation.id %>"></div></h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <%= f.text_area :review, class: "textarea", required: true, style: "width: 20rem; height: 10rem; margin-top: -1rem;" %> <%= f.hidden_field :reservation_id, value: reservation.id %> <%= f.hidden_field :room_id, value: reservation.room.id %> <%= f.hidden_field :reservation_id, value: reservation.id %> <%= f.hidden_field :host_id, value: reservation.room.user.id %> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉じる</button> <%= f.submit "レビューを投稿する", class: "btn btn-warning" %> </div> <% end %> </div> </div> </div> <script> $('#stars<%= reservation.id %>').raty({ path: '/assets', scoreName: 'guest_review[stars]', score: 1 }); </script>
「app\views\reviews」フォルダに「_host_form.html.erb」ファイルを新規作成してください。
app\views\reviews\_host_form.html.erb(新規作成したファイル)
<!-- Button trigger modal --> <button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#exampleModal<%= reservation.id %>"> <font style="font-size: 0.9rem;">レビュー</font> </button> <!-- Modal --> <div class="modal fade" id="exampleModal<%= reservation.id %>" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <%= form_for HostReview.new do |f| %> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabel"><div id="stars<%= reservation.id %>"></div></h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <%= f.text_area :review, class: "textarea", required: true, style: "width: 20rem; height: 10rem; margin-top: -1rem;" %> <%= f.hidden_field :reservation_id, value: reservation.id %> <%= f.hidden_field :room_id, value: reservation.room.id %> <%= f.hidden_field :reservation_id, value: reservation.id %> <%= f.hidden_field :guest_id, value: reservation.user.id %> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉じる</button> <%= f.submit "レビューを投稿する", class: "btn btn-warning" %> </div> <% end %> </div> </div> </div> <script> $('#stars<%= reservation.id %>').raty({ path: '/assets', scoreName: 'host_review[stars]', score: 1 }); </script>
「app\views\reservations\your_trips.html.erb」ファイルを編集します。
記述追加 app\views\reservations\your_trips.html.erb(41行目)
<li class="list-group-item mt-4" style="border: none;"> <% if trip.end_date < Date.today %> <%= render partial: "reviews/guest_form", locals: {reservation: trip} %> <% else %> <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span> <% end %> </li>
記述追加 app\views\reservations\your_trips.html.erb(41行目)
<div class="container mt-4"> <div class="card"> <div class="card-body"> <h5 class="card-title text-danger h3 font1">予約内容(ゲスト)</h5> <% if @trips.blank? %> <h5 class="font1">表示できる予約はありません。</h5> <% end %> <% @trips.each do |trip| %> <div class="card mt-4"> <div class="card-body"> <ul class="list-group"> <li class="list-group-item" style="border: none;"> <span class="font1">申込日:</span><%= I18n.l(trip.created_at, format: :full_date) %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">部屋名:</span> <%= link_to room_path(trip.room), style: "text-decoration: none;", data: { turbolinks: false} do %> <span class="btn btn-light"><%= trip.room.listing_name %></span> <% end %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">ホスト:</span> <%= link_to user_path(trip.room.user), class: "tootip", style: "text-decoration: none;" do %> <span class="btn btn-light"><%= trip.room.user.full_name %></span> <% end %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">宿泊日:</span> <%= I18n.l(trip.start_date, format: :full_date) %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">ご出発:</span> <%= I18n.l(trip.end_date, format: :full_date) %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">泊数:</span><%=trip.total/trip.price %>泊 </li> <li class="list-group-item" style="border: none;"> <span class="font1">料金:</span><%= number_to_currency(trip.total) %> </li> <li class="list-group-item mt-4" style="border: none;"> <% if trip.end_date < Date.today %> <%= render partial: "reviews/guest_form", locals: {reservation: trip} %> <% else %> <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span> <% end %> </li> </ul> </div> </div> <% end %> </div> </div> </div>
レビューができるか確認します。
現在の日付よりチェックアウト日が過ぎていないとレビューボタンが出ないようになっています。
確認するには、Posticoでチェックアウト日を編集してください。
ブラウザ確認
http://localhost:3000/your_trips
「app\views\reservations\your_reservations.html.erb」ファイルを編集します。
記述追加(42行目)
<li class="list-group-item mt-4" style="border: none;"> <% if reservation.end_date < Date.today %> <%= render partial: "reviews/host_form", locals: {reservation: reservation} %> <% else %> <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span> <% end %> </li>
記述追加 【app\views\reservations\your_reservations.html.erb】42行目
<div class="container mt-4"> <div class="card"> <div class="card-body"> <h5 class="card-title text-danger h3 font1">予約内容(ホスト)</h5> <% if @rooms.blank? %> <h5 class="font1">表示できる予約はありません。</h5> <% end %> <% @rooms.each do |room| %> <% room.reservations.each do |reservation| %> <div class="card mt-4"> <div class="card-body"> <ul class="list-group"> <li class="list-group-item" style="border: none;"> <span class="font1">申込日:</span><%= I18n.l(reservation.created_at, format: :full_date) %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">部屋名:</span> <%= link_to room_path(reservation.room), style: "text-decoration: none;", data: { turbolinks: false} do %> <span class="btn btn-light"><%= reservation.room.listing_name %></span> <% end %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">ゲスト:</span> <%= link_to user_path(reservation.room.user), class: "tootip", style: "text-decoration: none;" do %> <span class="btn btn-light"><%= reservation.user.full_name %></span> <% end %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">宿泊日:</span> <%= I18n.l(reservation.start_date, format: :full_date) %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">ご出発:</span> <%= I18n.l(reservation.end_date, format: :full_date) %> </li> <li class="list-group-item" style="border: none;"> <span class="font1">泊数:</span><%=reservation.total/reservation.price %>泊 </li> <li class="list-group-item" style="border: none;"> <span class="font1">料金:</span><%= number_to_currency(reservation.total) %> </li> <li class="list-group-item mt-4" style="border: none;"> <% if reservation.end_date < Date.today %> <%= render partial: "reviews/host_form", locals: {reservation: reservation} %> <% else %> <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span> <% end %> </li> </ul> </div> </div> <% end %> <% end %> </div> </div> </div>
ホストでログインし、動作を確認してください。
ブラウザ確認
http://localhost:3000/your_reservations
レビューの内容を表示するレンダーファイルを作成します。
ゲストからのレビューとホストからのレビューを分けて作成します。
「app\views\reviews」フォルダに「_guest_list.html.erb」ファイルを新規作成します。
app\views\reviews\_guest_list.html.erb(新規作成したファイル)
<% @guest_reviews.each do |r| %> <div style="border: solid 1px #c0c0c0; margin-bottom: 0.3rem; border-radius: 5px;"> <ul class="list-group list-group-horizontal"> <li class="list-group-item" style="border: none;"> <div class="font1"><%= r.guest.full_name %></div> <figure class="figure"> <%= link_to user_path(r.guest), style: "text-decoration:none;" do %> <%= image_tag avatar_url(r.guest), style: "width: 40px;", class: "figure-img img-fluid rounded-pill" %> <% end %> <div><i class="fa fa-star" style="color: gold;"></i></span><span><%= r.stars %></div> </figure> </li> <li class="list-group-item bn" style="width: 100%; border: none;"> <span><%= r.review %></span> <span class="text-secondary" style="font-size: 0.7rem;"><%= time_ago_in_words(r.created_at) %></span> </li> <% if (user_signed_in?) %> <% if current_user && current_user == r.guest %> <li class="list-group-item bn"> <div> <%= button_to r, method: :delete, form: { onSubmit: "return check()" }, class: "btn btn-light" do %> <i class="fas fa-trash-alt" style="color: red;"></i> <% end %> </div> </li> <% end %> <% end %> </ul> </div> <% end %> <script> function check(){ if(window.confirm('本当に削除してよろしいですか?')){ return true; } else{ window.alert('キャンセルされました'); return false; } } </script>
「app\views\reviews」フォルダに「_host_list.html.erb」ファイルを新規作成します。
app\views\reviews\_host_list.html.erb(新規作成したファイル)
<% @host_reviews.each do |r| %> <div style="border: solid 1px #c0c0c0; margin-bottom: 0.3rem; border-radius: 5px;"> <ul class="list-group list-group-horizontal"> <li class="list-group-item" style="border: none;"> <div class="font1"><%= r.host.full_name %></div> <figure class="figure"> <%= link_to user_path(r.host), style: "text-decoration:none;" do %> <%= image_tag avatar_url(r.host), style: "width: 40px;", class: "figure-img img-fluid rounded-pill" %> <% end %> <div><i class="fa fa-star" style="color: gold;"></i></span><span><%= r.stars %></div> </figure> </li> <li class="list-group-item bn" style="width: 100%; border: none;"> <span><%= r.review %></span> <span class="text-secondary" style="font-size: 0.7rem;"><%= time_ago_in_words(r.created_at) %></span> </li> <% if (user_signed_in?) %> <% if current_user && current_user == r.host %> <li class="list-group-item bn"> <div> <%= button_to r, method: :delete, form: { onSubmit: "return check()" }, class: "btn btn-light" do %> <i class="fas fa-trash-alt" style="color: red;"></i> <% end %> </div> </li> <% end %> <% end %> </ul> </div> <% end %> <script> function check(){ if(window.confirm('本当に削除してよろしいですか?')){ return true; } else{ window.alert('キャンセルされました'); return false; } } </script>
作成したレンダーファイルを実際に表示させていきます。
45行目にスターの平均値を表示する記述も追加しています。
記述追加 app\views\rooms\show.html.erb45, 112行目
<div class="container mt-4"> <div class="row"> <!-- 右側 --> <div class="col-md-3"> <!-- 予約フォーム --> <%= render 'reservations/form' %> </div> <!-- 左側 --> <div class="col-md-9 mb-4"> <!-- カルーセル表示 --> <div class="card"> <div class="card-body"> <div id="carouselExampleIndicators" class="carousel slide" data-bs-ride="carousel"> <div class="carousel-indicators"> <% @photos.each do |photo| %> <button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="<%= @i %>" class="<%= 'active' if photo.id == @photos[0].id %>" aria-current="true" aria-label="Slide <%= @i+1 %>"></button> <% @i = @i +1 %> <% end %> </div> <div class="carousel-inner"> <% @photos.each do |photo| %> <div class="carousel-item <%= 'active' if photo.id == @photos[0].id %>"> <%= image_tag url_for(photo), class: "d-block w-100", style: "border-radius: 10px;" %> </div> <% end %> </div> <button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="visually-hidden">Previous</span> </button> <button class="carousel-control-next" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="visually-hidden">Next</span> </button> </div> </div> </div> <div class="card mb-2 mt-2"> <div class="card-body"> <i class="fa fa-star fa-2x" style="color: gold;"></i><span style="font-size: 1.6rem;"><%= pluralize(@room.average_rating, "") %></span> <h2 class="mb-4 mt-4"><%= @room.listing_name %></h2> <div class="fs-5"><%= @room.address %></div> <div class="mt-4"> <%= link_to user_path(@room.user), style: "text-decoration:none;" do %> <%= image_tag avatar_url(@room.user), class: "bd-placeholder-img figure-img img-fluid rounded-pill", style: "width: 40px; height: 30px;" %> <span class="badge bg-light text-dark"><%= @room.user.full_name %></span> <% end %> </div> </div> </div> <!-- 部屋のインフォメーション --> <div class="card mb-2"> <div class="card-body"> <div class="row"> <div class="col-3"> <i class="fas fa-home fa-2x" style="color: cadetblue"></i><br/> <%= @room.home_type %> </div> <div class="col-3"> <i class="fas fa-user fa-2x" style="color: cadetblue"></i><br/> <%= pluralize(@room.accommodate, "人宿泊可能") %> </div> <div class="col-3"> <i class="fas fa-bed fa-2x" style="color: cadetblue"></i><br/> <%= pluralize(@room.bed_room, "台") %> </div> <div class="col-3"> <i class="fas fa-door-closed fa-2x" style="color: cadetblue"></i><br/> <%= pluralize(@room.bath_room, "部屋") %> </div> </div> </div> </div> <div class="card"> <div class="card-body"> <div class="badge bg-secondary mb-4">部屋の詳細</div> <p><%= @room.summary %></p> </div> </div> <!-- アメニティ --> <div class="card mt-2"> <div class="card-body"> <div class="badge bg-secondary mb-4">アメニティ</div> <div class="row"> <div class="col-6"> <ul style="list-style:none;"> <li class="<%= 'text-line-through' if !@room.is_tv %>"><span style="color: black;"><i class="fas fa-tv"></i> テレビ</span></li> <li class="<%= 'text-line-through' if !@room.is_kitchen %>"><span style="color: black;"><i class="fas fa-blender"></i> キッチン</span></li> <li class="<%= 'text-line-through' if !@room.is_internet %>"><span style="color: black;"><i class="fas fa-wifi"></i> Wi-Fi</span></li> </ul> </div> <div class="col-6"> <ul style="list-style:none;"> <li class="<%= 'text-line-through' if !@room.is_heating %>"><span style="color: black;"><i class="fab fa-hotjar"></i> 暖房</span></li> <li class="<%= 'text-line-through' if !@room.is_air %>"><span style="color: black;"><i class="fas fa-temperature-low"></i> エアコン</span></li> </ul> </div> </div> </div> </div> <!-- レビュー --> <div class="card mt-2"> <div class="card-body"> <div class="badge bg-warning mb-4">レビュー(<%= @guest_reviews.count %>)</div> <!-- レンダー --> <%= render "reviews/guest_list" %> </div> </div> <!-- GOOGLE マップ --> <div class="card mt-4"> <div class="card-body"> <div id="map" style="width: 100%; height: 400px"></div> <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDoAh6S8kEpArgzGFEvjT_0xK_VeUL5SEs&callback=initMap" type="text/javascript"></script> <script> function initMap() { const uluru = { lat: <%= @room.latitude %>, lng: <%= @room.longitude %> }; const map = new google.maps.Map(document.getElementById("map"), { zoom: 14, center: uluru, }); const contentString = '<div id="content">' + '<%= image_tag room_cover(@room), style: "width: 200px;" %>' + "</div>" + "<h5>" + '<%= @room.listing_name %>' + "</h5>" + "<div>" + '<%= @room.address %>' + "</div>"; const infowindow = new google.maps.InfoWindow({ content: contentString, ariaLabel: "<%= @room.listing_name %>", }); const marker = new google.maps.Marker({ position: uluru, map, title: "<%= @room.listing_name %>", }); marker.addListener("click", () => { infowindow.open({ anchor: marker, map, }); }); } window.initMap = initMap; </script> </div> </div> <!-- 近くのお部屋を検索 --> <div class="card mt-4 mb-4"> <div class="card-body"> <h3 class="badge bg-secondary">近くの部屋</h3> <div class="row"> <% for room in @room.nearbys(10) %> <div class="col-md-4"> <div class="card mb-2"> <%= link_to room_path(room), data: { turbo: false} do %> <%= image_tag room_cover(room), class: "card-image-top", style: "width: 100%;" %> <% end %> <div class="card-body"> <div> <%= link_to room.listing_name, room, class: "btn btn-light", data: { turbo: false} %> </div> <div class="badge bg-danger">距離:<%= room.distance.round(2) %> Km</div> </div> </div> </div> <% end %> </div> </div> </div> </div> </div> </div>
ブラウザを確認します。
コメントを投稿した人のみ、削除することができます。
動作を確認して下さい。
http://localhost:3000/rooms/1
ホストからのレビューをユーザー詳細ページに表示させます。
「app\views\users\show.html.erb」ファイルを編集します。
記述編集 【app\views\users\show.html.erb】
<div class="container mt-4 mb-4"> <div class="row"> <!-- 左側 --> <div class="col-md-4 mb-4"> <div class="card"> <div class="card-body"> <!-- ステータス --> <div> <% if @user.status %> <span class="badge bg-success"><i class="fa-regular fa-bell"></i>オンライン</span> <% else %> <span class="badge bg-secondary"><i class="fa-regular fa-bell-slash"></i>オフライン</span> <% end %> </div> <!-- アバター --> <%= image_tag avatar_url(@user), class: "img-fluid img-thumbnail rounded-pill" %> <h4 class="text-center"><%= @user.full_name %></h4> <!-- 自己紹介 --> <div class="h5 text-center"><%= @user.about %></div> </div> </div> </div> <!-- 右側 --> <div class="col-md-8"> <!-- 登録している部屋 --> <div class="card mb-4"> <div class="card-body"> <h5 class="card-title"><%= @user.full_name %>さんが登録している部屋</h5> <div class="container mt-4"> <div class="row"> <% @user.rooms.each do |room| %> <% if room.active? %> <div class="col-md-4"> <div class="card"> <div class="card-body"> <span><i class="fa fa-star fa-1x" style="color: gold;"></i><%= pluralize(room.average_rating, "") %></span> <%= link_to room_path(room), data: { turbolinks: false} do %> <%= image_tag room_cover(room), style: "width: 100%;" %> <h5 class="card-title"> <span class="btn btn-light"><%= room.listing_name %></span> </h5> <% end %> <div class="card-text" style="margin-left: 0.5rem;"> <p style="font-size: 0.8rem; margin-bottom: -0.3rem;">Address</p> <p style="margin-bottom: 2rem;"><%= room.address %></p> </div> <h5 class="badge rounded-pill bg-danger text-light" style="font-size: 1rem;">1泊<%= number_to_currency(room.price) %></h5> </div> </div> </div> <% end %> <% end %> </div> </div> </div> </div> <!-- レビュー --> <div class="card"> <div class="card-body"> <h5 class="card-title"><%= @user.full_name %>さんへのレビュー</h5> <%= render "reviews/guest_list" %> <%= render "reviews/host_list" %> </div> </div> </div> </div> </div>
ページを確認します。
ブラウザ確認
http://localhost:3000/users/2
同じくダッシュボードページにもレビューを表示させます。
記述追加 「app/views/users/dashboard.html.erb」
<div class="container mt-4 mb-4"> <div class="row"> <!-- 左側 --> <div class="col-md-4"> <div class="card mb-4"> <div class="card-body"> <!-- アバター --> <%= image_tag avatar_url(current_user), class: "img-fluid img-thumbnail rounded-pill" %> <h4 class="text-center"><%= current_user.full_name %></h4> <!-- 画像アップロードボタン --> <button class="btn btn-info text-light w-100" type="button" data-bs-toggle="collapse" data-bs-target="#collapse1" aria-expanded="false" aria-controls="collapse1"> <i class="fa-solid fa-cloud-arrow-up"></i>アバター画像アップロード </button> <div class="collapse" id="collapse1"> <div class="card card-body"> <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %> <%= f.file_field :avatar, class: "input-group-text", onchange: "this.form.submit();" %> <% end %> </div> </div> <hr/> <div> <%= link_to "部屋新規登録", new_room_path, class: "btn btn-danger" %> </div> <hr/> <!-- ステータス --> <div type="button" data-bs-toggle="collapse" data-bs-target="#collapse2" aria-expanded="false" aria-controls="collapse2"> <% if current_user.status %> <span class="btn btn-success"><i class="toggle far fa-edit"></i>オンライン</span> <% else %> <span class="btn btn-secondary"><i class="toggle far fa-edit"></i>オフライン</span> <% end %> </div> <div class="collapse" id="collapse2"> <div class="card card-body"> <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %> <%= f.select(:status, options_for_select([["オンライン", true], ["オフライン", false]]), {}, {class: "custom-select"}) %> <%= f.submit "保存", class: "btn btn-dark" %> <% end %> </div> </div> <hr/> <!-- 自己紹介 --> <div class="h5"><%= current_user.about %></div> <button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#collapse3" aria-expanded="false" aria-controls="collapse3"> 自己紹介編集 </button> <div class="collapse" id="collapse3"> <div class="card card-body"> <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %> <div><%= f.text_area :about, autofocus: true, autocomplete: 'form'%></div> <%= f.submit "保存", class: "btn btn-dark" %> <% end %> </div> </div> <hr/> <!-- 電話番号 --> <div> <% if !current_user.phone_number.blank? %> <span class="pull-right icon-babu"><i class="far fa-check-circle" style="color:green;"></i></span> 電話番号 <% else %> <div class="text-danger">電話番号が登録されていません</div> <%= link_to "電話番号登録", edit_user_registration_path, class: "btn btn-danger" %> <% end %> </div> <hr/> <!-- アカウント登録日 --> 登録:<%= I18n.l(current_user.created_at, format: :full_date) %> </div> </div> </div> <!-- 右側 --> <div class="col-md-8"> <!-- お知らせ --> <div class="card mb-2"> <div class="card-body"> <h5 class="card-title">お知らせ</h5> <h6 class="card-subtitle mb-2 text-body-secondary"> </h6> <p class="card-text"> </p> </div> </div> <!-- 登録している部屋 --> <div class="card"> <div class="card-body"> <h5 class="card-title">登録している部屋</h5> <h6 class="card-subtitle mb-2 text-body-secondary"> </h6> <div class="row"> <% current_user.rooms.each do |room| %> <% if room.active? %> <div class="col-md-4"> <div class="card mb-2"> <div class="card-body"> <span><i class="fa fa-star fa-1x" style="color: gold;"></i><%= pluralize(room.average_rating, "") %></span> <%= link_to room_path(room), data: { turbolinks: false} do %> <%= image_tag room_cover(room), style: "width: 100%;" %> <h5 class="card-title"> <span class="btn btn-light"><%= room.listing_name %></span> </h5> <% end %> <div class="card-text" style="margin-left: 0.5rem;"> <p style="font-size: 0.8rem; margin-bottom: -0.3rem;">Address</p> <p><%= room.address %></p> <h5 class="badge rounded-pill bg-danger text-light mb-4" style="font-size: 1rem;">1泊<%= number_to_currency(room.price) %></h5> <%= link_to listing_room_path(room), class: "btn btn-outline-primary w-100 mb-4" do %> 登録内容編集 <% end %> </div> </div> </div> </div> <% end %> <% end %> </div> </div> </div> <!-- レビュー --> <div class="card mt-2"> <div class="card-body"> <h5 class="card-title"><%= current_user.full_name %>さんへのレビュー</h5> <%= render "reviews/guest_list" %> <%= render "reviews/host_list" %> </div> </div> </div> </div> </div>
ダッシュボードページを確認します。
ブラウザ確認
http://localhost:3000/dashboard
【31 | 予約確認】 << 【ホーム】 >> 【33 | Ransack】
↓↓クリックして頂けると励みになります。