How to assign start and end date from range using Flatpickr in Ruby on Rails

294 Views Asked by At

I have been struggling for 3 hours as I have added mode: range in the Stimulus JS datepicker controller which works on display.

On the logic though, I do not know how to reduce from two to one simple form input since on my bookings table, I have a start_date and end_date columns and I don't know how to extract the date range to assign it correctly.

My booking_form partial:

<div class="row justify-content-center mt-7">
  <div class="col-6 mb-3 form-inline justify-content-center" data-controller="datepicker"

      data-datepicker-price-value="<%= @room.price_per_night.to_json %>">

    <%= simple_form_for ([ @room, @booking]) do |f| %>
    <%= f.error_notification %>
    <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>

    <div data-controller="datepicker">
      <%= f.input :date_range, as: :string, input_html: { data: { datepicker_target: "range", action: "change->datepicker#dateRangeInput" } } %>
    </div>
          <div class="d-flex col-9 justify-content-between flex-grow-1">
            <p> Total price:  </p>
            <strong data-datepicker-target="price"></strong>
          </div>

        <div class="col-9 d-flex justify-content-center" >
          <%= f.submit "Let's go!", class: "btn btn-primary" , disabled: true, data: { datepicker_target: "confirm" } %>
        </div>
      <% end %>

  </div>
</div>

As shown, I am using only one input field with the :date_range but get an expected

undefined method `date_range' for #<Booking id: nil, user_id: nil, room_id: nil, starts_at: nil, ends_at: nil, created_at: nil, updated_at: nil>

My Stimulus JS controller:

import { Controller } from "@hotwired/stimulus"
import flatpickr from "flatpickr";

// Connects to data-controller="datepicker"
export default class extends Controller {
  static values = {
    dates: Array,
    price: Number
  };
  static targets = ["range", "confirm", "price"]

  connect() {
    console.log("datepicker active")
    console.log(this.rangeTarget);

    this.endTarget.disabled = true
    flatpickr(this.startTarget, {

      dateFormat: "d F Y",
      disable: this.datesValue,

    })
  }

  DateInput() {

    flatpickr(this.endTarget, {
      mode: "range",
      dateFormat: "d F Y",
      disable: this.datesValue,
      minDate: this.startTarget.value,
      minDate: "today"
    })
  }

  endDateInput() {
    let splitStartDate = this.startTarget.value.split("-")
    let splitEndDate = this.endTarget.value.split("-")

    let startDate = new Date([splitStartDate[1], splitStartDate[0], splitStartDate[2]].join("/"))
    let endDate = new Date([splitEndDate[1], splitEndDate[0], splitEndDate[2]].join("/"))

    let price = parseInt((endDate - startDate) / (1000 * 60 * 60 * 24), 10) * Number(this.priceValue)
    this.priceTarget.innerText = `${price}`
    console.log(price)

    this.confirmTarget.disabled = false
  }
}

Sorry for the mess due to some trial and errors.

Finally, my bookings controller:

class BookingsController < ApplicationController
  before_action :select_room, only: %i[new create edit]
  before_action :select_booking, only: %i[edit update destroy]

  def new
    @booking = Booking.new
  end

  def create
    @booking = Booking.new(booking_params)
    @booking.room = @room
    @booking.user = current_user

    start_date, end_date = params[:booking][:date_range].split(' to ')
    @booking.starts_at = Date.parse(start_date)
    @booking.ends_at = Date.parse(end_date)
    if @booking.save
      redirect_to room_path(@room)
    else
      render 'rooms/show', status: :unprocessable_entity
    end
  end

  def destroy
    @booking.destroy
    redirect_to pages_dashboard_path
  end

  private

  def select_room
    @room = Room.find(params[:room_id])
  end

  def select_booking
    @booking = Booking.find(params[:id])
  end

  def booking_params
    params.require(:booking).permit(:starts_at, :ends_at)
  end
end

I have tried to change the simple form input which generated an error, then tried to change the controller to handle this change, all in order to implement the date range which only requires one field.

1

There are 1 best solutions below

0
Vishwani patlan On

Your form HTML and stimulus controller should be like code below:

_form.html.erb

<%= simple_form_for(@modelname, data: {controller: 'stimulus_controller_name'}) do |form| %>
   <%= form.datetime_field :field_name, class: 'date-range' %>
   <%= form.button :submit %>
<% end %>

stimulus_controller.js

import { Controller } from "@hotwired/stimulus"
import flatpickr from "flatpickr";

// Connects to data-controller="flatpickr"
export default class extends Controller {
  connect() {
    console.log('connected')
    flatpickr(".date-range", {
      mode: "range"
    })
  }
}

Please note that the input field class name should match the stimulus flatpickr('.class-name') which is date-range in this case.