Calendar

Simple UTC based calendar with events

August 2020

Sunday Monday Tuesday Wednesday Thursday Friday Saturday
26
27
28
29
30
31
1
2
3
4
5
6
7
7 AM
Hi! Edit...
9 PM
Hello Edit...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
12 AM
Another test. Edit...
27
12 AM
27 Edit...
28
29
30
12 AM
wild Edit...
12 AM
jhfkh Edit...
12 AM
test Edit...
12 AM
jhfkh Edit...
12 AM
wild Edit...
12 AM
wild event Edit...
12 AM
sa,SD<`/,LM<MSDVLM Edit...
9 PM
klk Edit...
31
12 AM
Test Edit...
12 AM
Let us go make money Edit...
1
2
3
4
5

Code (176 LOC)

JavaScript (12 LOC)
app/javascript/controllers/button_group_controller.js (12 LOC)
import ApplicationController from './application_controller'
export default class extends ApplicationController {
static targets = ['input']
select (event) {
this.buttons.forEach(el => el.classList.remove('active'))
event.target.classList.add('active')
this.inputTarget.value = event.target.innerText
}
get buttons () {
return this.element.querySelectorAll('.btn')
}
}
ERB (96 LOC)
app/views/calendars/_demo.html.erb (56 LOC)
<div class="d-flex justify-content-between align-items-center">
<h2><%= @start_date.strftime("%B %Y") %></h2>
<div class="text-success" hidden data-activity-indicator>
<div class="spinner-border spinner-border-sm"></div>
<span>Loading...</span>
</div>
<div>
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<%= link_to calendar_path(@start_date.advance(months: -1).iso8601), title: "Previous", class: "btn btn-light" do %>
<i class="fas fa-chevron-left"></i>
<% end %>
<%= link_to calendar_path(@start_date.advance(months: 1).iso8601), title: "Next", class: "btn btn-light" do %>
<i class="fas fa-chevron-right"></i>
<% end %>
</div>
<%= link_to "Today", calendar_path(Date.current.beginning_of_month.iso8601), class: "btn btn-light" %>
</div>
</div>
<table id="calendar-<%= @start_date.iso8601 %>" class="table table-bordered mt-2">
<thead class="thead-dark">
<tr class="text-center">
<% Date::DAYNAMES.each do |name| %>
<th style="width:14%" ><%= name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% @dates.each_slice(7) do |week| %>
<tr>
<% week.each do |date| %>
<%= tag.td class: css("", "bg-warning-lighter": date.today?, "bg-secondary-lightest": @start_date.month != date.month),
data: { reflex: "click->CalendarReflex#new_calendar_event", date: date.iso8601 } do %>
<%= tag.span date.day, class: "lead" %>
<div class="mt-2" style="height:65px; overflow-y:scroll">
<% (@calendar_events[date] || []).each do |calendar_event| %>
<div class="bg-white py-1 px-2 border mb-1" data-controller="application" data-action="click->application#default_reflex">
<%= link_to "#", class: "float-right text-danger", data: { reflex: "click->CalendarReflex#destroy_calendar_event", id: calendar_event.id} do %>
<i class="fas fa-window-close"></i>
<% end %>
<small class="d-block text-secondary">
<%= calendar_event.occurs_at.strftime "%l %p" %>
</small>
<div>
<%= truncate calendar_event.description, length: 24 %>
<small><%= link_to "Edit...", "#", class: "ml-2", data: { reflex: "click->CalendarReflex#edit_calendar_event", id: calendar_event.id } %></small>
</div>
</div>
<% end %>
</div>
<% end %>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= render "/calendars/calendar_event_modal" if @calendar_event %>
app/views/calendars/_calendar_event_modal.html.erb (40 LOC)
<div class="modal" tabindex="-1" style="display:block">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><%= @calendar_event.occurs_at.strftime "%A %B, %e %Y" %></h5>
<button type="button" class="close" data-reflex="click->CalendarReflex#default_reflex">
<span>&times;</span>
</button>
</div>
<%= form_with model: @calendar_event, html: { data: { controller: "application", action: "ajax:success->application#reload" } } do |f| %>
<%= f.hidden_field :occurs_at %>
<div class="modal-body">
<div class="btn-group mt-3" data-controller="button-group" data-reflex-permanent>
<%= f.hidden_field :hour, data: { target: "button-group.input" } %>
<% (1..12).each do |i| %>
<%= tag.button i, type: "button",
class: css("btn btn-light", "active": @calendar_event.occurs_at.strftime("%I").to_i == i),
data: { action: "click->button-group#select" } %>
<% end %>
</div>
<div class="btn-group mt-2" data-controller="button-group" data-reflex-permanent>
<%= f.hidden_field :meridian, data: { target: "button-group.input" } %>
<%= tag.button "AM", type: "button",
class: css("btn btn-light", "active": @calendar_event.occurs_at.strftime("%p") == "AM"),
data: { action: "click->button-group#select" } %>
<%= tag.button "PM", type: "button",
class: css("btn btn-light", "active": @calendar_event.occurs_at.strftime("%p") == "PM"),
data: { action: "click->button-group#select" } %>
</div>
<div class="form-group my-3">
<%= f.text_area :description, placeholder: "Description for this event...", class: "form-control", rows: 3,
data: { reflex: "input->CalendarReflex#validate_calendar_event", id: @calendar_event.id, date: @calendar_event.occurs_at.to_date.iso8601 } %>
</div>
<%= f.submit "Save", class: "btn btn-primary", disabled: @calendar_event.invalid? %>
<button type="button" class="btn btn-link" data-reflex="click->CalendarReflex#default_reflex">Cancel</button>
</div>
<% end %>
</div>
</div>
</div>
Ruby (68 LOC)
app/controllers/calendars_controller.rb (11 LOC)
class CalendarsController < ApplicationController
def show
@start_date ||= Date.parse(params[:date])
@start_date = @start_date.to_date.beginning_of_month
date_range = (@start_date..@start_date.end_of_month)
@dates = date_range.to_a
@dates.prepend(@dates.first - 1) until @dates.first.sunday?
@dates.append(@dates.last + 1) until @dates.last.saturday?
@calendar_events = CalendarEvent.where(occurs_at: date_range).order(:occurs_at).group_by(&:occurs_at_date)
end
end
app/controllers/calendar_events_controller.rb (26 LOC)
class CalendarEventsController < ApplicationController
after_action :cleanup
def create
calendar_event = CalendarEvent.new(calendar_event_params)
calendar_event.assign_hour hour_params
calendar_event.save
redirect_to calendar_path(calendar_event.occurs_at.beginning_of_month.iso8601)
end
def update
calendar_event = CalendarEvent.find(params[:id])
calendar_event.assign_attributes calendar_event_params
calendar_event.assign_hour hour_params
calendar_event.save
redirect_to calendar_path(calendar_event.occurs_at.beginning_of_month.iso8601)
end
private
def calendar_event_params
params.require(:calendar_event).permit(:occurs_at, :description).merge(session_id: session.id)
end
def hour_params
params.require(:calendar_event).permit(:hour, :meridian)
end
def cleanup
CalendarEvent.old.delete_all
end
end
app/models/calendar_event.rb (14 LOC)
class CalendarEvent < ApplicationRecord
validates :description, presence: true
validates :description, length: {maximum: 240}
validates :occurs_at, presence: true
scope :old, -> { where arel_table[:created_at].lt(1.month.ago) }
def occurs_at_date
occurs_at.to_date
end
def assign_hour(params)
hour = params[:hour].to_i
hour += 12 if params[:meridian] == "PM"
self.occurs_at = occurs_at.change(hour: hour)
end
end
app/reflexes/calendar_reflex.rb (17 LOC)
class CalendarReflex < ApplicationReflex
def new_calendar_event
@calendar_event = CalendarEvent.new(session_id: session.id, occurs_at: Date.parse(element.dataset["date"]))
session[:calendar_event_attributes] = @calendar_event.attributes
end
def edit_calendar_event
@calendar_event = CalendarEvent.find(element.dataset[:id])
end
def destroy_calendar_event
CalendarEvent.find(element.dataset[:id]).destroy
end
def validate_calendar_event
@calendar_event = CalendarEvent.where(id: element.dataset[:id]).first_or_initialize(session_id: session.id, occurs_at: Date.parse(element.dataset["date"]))
@calendar_event.description = element[:value]
@calendar_event.validate
end
end