I am building a Rails 7 app using bootstrap (without jquery), in which users can leave a review for various parks. One of the review fields is a 5-star rating.

Currently I have the form input as a text\_field, but I would like it to be 5 stars that the user can click on to select the rating, then submit together with the review form.

I am already displaying the park ratings after they've been submitted, but I am stuck on the input display.

I thought maybe I can somehow change the UI for a range\_field or collection\_radio\_buttons, but I can't figure out how to make it work...

**Park model**

```
class Park < ApplicationRecord
  has_many :visits, dependent: :destroy
  has_many :visited_users, through: :visits, source: :user

  has_many :favorites, dependent: :destroy
  has_many :favorited_users, through: :favorites, source: :user

  has_many :reviews, as: :reviewable

  include Translatable
  translates :name, :website_url

  validates :name_en, presence: true
  validates :name_he, presence: true
  validates :region, presence: true
  validates :latitude, presence: true
  validates :longitude, presence: true

  enum :region, { north: 0, center: 1, south: 2, west_bank: 3 }
  enum :park_system, { kkl_jnf: 0, inpa: 1 },
                      default: :inpa

  def default_image
    Review&.where(reviewable: self)&.left_joins(:images_attachments)&.where&.not(active_storage_attachments: { id: nil })&.first&.images&.first
  end

  def favorited_by?(user)
    return if user.nil?

    favorited_users.include?(user)
  end

  def visited_by?(user)
    return if user.nil?

    visited_users.include?(user)
  end

end

```

**Review model**

```
class Review < ApplicationRecord
  has_many_attached :images, dependent: :destroy

  # validates :title, presence: true
  validates :body, presence: true
  validates :rating, presence: true, numericality:
            { greater_than_or_equal_to: 1, less_than_or_equal_to: 5,
              only_integer: true }

  belongs_to :reviewable, polymorphic: true, counter_cache: true
  belongs_to :user

  after_commit :update_reviewable_rating, on: [:create, :update, :destroy]

  def update_reviewable_rating
    reviewable.update! average_rating: reviewable.reviews.average(:rating)
    # avg + (rating - avg) / count
  end

end

```

**Reviews controller**

```
# frozen_string_literal: true

class ReviewsController < ApplicationController
  before_action :authenticate_user!
  before_action :find_park
  before_action :find_review, only: %i[ edit update destroy ]

  def new
    @review = Review.new(reviewable: @park)
  end

  def create
    @review = @park.reviews.new(review_params)
    @review.reviewable_id = @park.id
    @review.user_id = current_user.id

    respond_to do |format|
      if @review.save
        format.html { redirect_to params[:previous_request], notice: "Your review for #{@park.name} was successfully added." }
        format.json { render :show, status: :created, location: @park }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @review.errors, status: :unprocessable_entity }
      end
    end
  end

  def edit
  end

  def update
    respond_to do |format|
      if @review.update(review_params)
        format.html { redirect_to params[:previous_request], notice: "Your review for #{@park.name} was successfully updated." }
        format.json { render :show, status: :ok, location: @park }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @review.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @review.destroy

    respond_to do |format|
      format.html { redirect_to params[:previous_request], notice: "Your review for #{@park.name} was successfully deleted." }
      format.json { head :no_content }
    end
  end

  private

    def review_params
      params.require(:review).permit(:rating, :body, images: [])
    end

    def find_park
      @park = Park.find(params[:park_id])
    end

    def find_review
      @review = Review.find(params[:id])
    end

end

```

**Parks show view, reviews partial**

```
<% @park.reviews.includes(:user).order("updated_at desc").each do |review| %>
  <div class="col">
    <div class="card px-0 mt-4 mb-4 border-0">
      <div class="card-body">
        <h5 class="card-title mb-4">
          <%= gravatar_for review.user, size: 50 %>
          <div class="strong">
            <%= link_to review.user.name, review.user, class: "link-dark" %>
          </div>
        </h5>
        <h6 class="card-subtitle mt-2">
          <%
            review_star_classes = ["#DEDEDE", "#DEDEDE", "#DEDEDE", "#DEDEDE", "#DEDEDE"]

            review.rating.times do |i|
              review_star_classes[i] = "#fbbc04"
            end
          %>

          <% review_star_classes.each do |star_class| %>
            <svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              fill="<%= star_class %>"
              width="16"
              height="16"
              class="w-5 h-5">
              <path
                fill-rule="evenodd"
                d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.007 5.404.433c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.433 2.082-5.006z"
                clip-rule="evenodd" />
            </svg>
          <% end %>
        </h6>
        <div class="card-text mt-4">
          <div>
            <%= review.updated_at.to_fs(:long) %>
          </div>
          <div class="mt-2">
            <%= simple_format(review.body) %>
          </div>
          <% if current_user && current_user === review.user %>
            <div>
              <%= link_to "Edit", edit_park_review_path(review.reviewable, review) %>
              <%= button_to "Delete", park_review_path(review.reviewable, review),
                                      class: "btn btn-link p-0 m-0 d-inline align-baseline text-decoration-none",
                                      form: {data: { turbo_confirm: "Are you sure?"} },
                                      method: :delete %>
            </div>
          <% end %>
        </div>
      </div>
    </div>
  </div>
<% end %>

```

**New review form**

```
<%= form_with model: @review, url: park_reviews_path, local:true do |f| %>
  <%= hidden_field_tag :previous_request, request.referer %>

  <div class="field form-group">
    <%= f.label :rating, "Rate your experience", style: "display: block" %>
    <%= f.text_field :rating, class: "form-control",
                          placeholder: "From 1 to 5 stars",
                          autofocus: true %>
  </div>
  <div class="field form-group">
    <%= f.text_area :body, class: "form-control",
                        placeholder: "Tell people about your experience" %>
  </div>
  <div class="field form-group">
    <%= f.file_field :images, multiple: true %>
  </div>
  <div class="actions">
    <%= f.submit "Submit your review", class: "btn btn-primary" %>
  </div>
<% end %>

```