Resources

Download Source Code

Summary

# Terminal
bin/rails g stimulus appointment_refresh

# appointment_controller.js
import { Controller } from "@hotwired/stimulus"
import { Calendar } from '@fullcalendar/core'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin, { Draggable } from '@fullcalendar/interaction'
// Connects to data-controller="appointment"
export default class extends Controller {
  static values = { fetched: Number }

  connect() {
    this.initializeCalendar()
    this.initializeDraggable()

    // let _this = this
    // setInterval(function() {
    //   _this.calendar.refetchEvents()
    // }, 500)
  }

  disconnect() {
    this.calendar.destroy()
    this.draggable.destroy()
  }

  fetchedValueChanged() {
    if (this.calendar) {
      this.calendar.refetchEvents()
    }
  }

  initializeCalendar() {
    let _this = this
    this.calendar = new Calendar(this.element, {
      plugins: [interactionPlugin, timeGridPlugin],
      initialView: 'timeGridWeek',
      events: '/events.json',
      headerToolbar: {
        left: 'prev,next today',
        center: 'title',
        right: ''
      },
      businessHours: {
        daysOfWeek: [1,2,3,4,5],
        startTime: '08:00',
        endTime: '17:00'
      },
      eventConstraint: 'businessHours',
      eventDurationEditable: false,
      weekends: false,
      slotDuration: "01:00:00",
      editable: true,
      eventDrop: function (info) { _this.fetch(info) },
      eventReceive: function (info) {
        _this.fetch(info)
        info.event.remove()
      }

    })
    this.calendar.render()
  }

  initializeDraggable() {
    let element = document.getElementById("external-events")
    this.draggable = new Draggable(element, {
      itemSelector: '.fc-event',
      eventData: function(event) {
        return {
          duration: "01:00"
        }
      }
    })
  }

  fetch(info) {
    let url = info.event.url ? info.event.url : '/events'
    let method = info.event.url ? 'PUT' : 'POST'
    let csrf_token = document.head.querySelector('meta[name="csrf-token"]').getAttribute('content')
    fetch(url, {
      method: method,
      headers: {
        "Content-Type": "application/json",
        Accept: "text/vnd.turbo-stream.html",
        "X-CSRF-Token": csrf_token
      },
      body: JSON.stringify({ event: { appointment: info.event.start } })
    })
  }
}

# appointment_refresh_controller.js
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="appointment-refresh"
export default class extends Controller {
  connect() {
    this.array = document.querySelectorAll('[data-controller="appointment"]')
    this.array.forEach(element => {
      element.setAttribute('data-appointment-fetched-value', Math.floor(Math.random() * 10000000000))
    })
  }
}

# events_controller.rb
  def create
    # @event = current_user.events.create(event_params)
    @event = current_user.events.new(event_params)
    if @event.save
      # Perform Background Jobs
      ActionCable.server.broadcast("appointments", turbo_stream.replace(
        "appointments",
        partial: "events/event"
      ))
    end
    head :ok
  end

  def update
    @event = current_user.events.find(params[:id])
    if @event.update(event_params)
      # Perform Background Jobs
      ActionCable.server.broadcast("appointments", turbo_stream.replace(
        "appointments",
        partial: "events/event"
      ))
    end
    head :ok
  end

# views/events/_event.html.erb
<%= turbo_frame_tag :appointments do %>
  <div data-controller="appointment-refresh"></div>
<% end %>

# views/welcome/index.html.erb
<%= turbo_stream_from :appointments %>

<%= turbo_frame_tag :appointments %>
<div data-controller="appointment"></div>