Skip to content

Commit

Permalink
Merge pull request #613 from PRX/research/simple-calendar
Browse files Browse the repository at this point in the history
simple calendar
  • Loading branch information
radical-ube committed Apr 26, 2023
2 parents 5239c76 + 38a08de commit e0842d2
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 42 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ gem "kaminari"
gem "sprockets-rails"
gem "stimulus-rails"
gem "turbo-rails"
gem "simple_calendar", "~> 2.4"

# api views
gem "actionpack-action_caching" # for hal_api-rails
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ GEM
aws-sdk-core (>= 2)
concurrent-ruby
thor
simple_calendar (2.4.3)
rails (>= 3.0)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
Expand Down Expand Up @@ -513,6 +515,7 @@ DEPENDENCIES
say_when (~> 2.2.1)
selenium-webdriver
shoryuken
simple_calendar (~> 2.4)
simplecov
spring
sprockets-rails
Expand Down
26 changes: 26 additions & 0 deletions app/assets/stylesheets/app/calendar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@import "shared/bootstrap-variables";

.simple-calendar {
.table {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-auto-rows: 1fr;

:is(thead, tbody, tr) {
display: contents;
}

:is(thead th, td) {
display: grid;
place-content: center;
min-width: 6ch;
border-width: 1px;
line-height: 1;
}

.day.current-month:hover {
background-color: $secondary !important;
color: $light;
}
}
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/app/index.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// App modules

@import "audio-segmenter/index";
@import "calendar";
@import "uploads";
8 changes: 7 additions & 1 deletion app/controllers/podcast_planner_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
class PodcastPlannerController < ApplicationController
before_action :set_podcast
before_action :set_beginning_of_week

# GET /podcasts/1/planner
def show
@planner = PodcastPlanner.new(planner_params)
@planner.generate_dates!
@draft_dates = @podcast.episodes.draft.pluck(:released_at)
end

# POST /podcasts/1/planner
Expand All @@ -28,6 +30,10 @@ def set_podcast
@podcast = Podcast.find(params[:podcast_id])
end

def set_beginning_of_week
Date.beginning_of_week = :sunday
end

# Only allow a list of trusted parameters through.
def planner_params
params.permit(
Expand All @@ -42,7 +48,7 @@ def planner_params
:segment_count,
selected_days: [],
monthly_weeks: [],
generated_dates: []
selected_dates: []
)
end
end
34 changes: 32 additions & 2 deletions app/helpers/podcast_planner_helper.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# frozen_string_literal: true

module PodcastPlannerHelper
PERIODIC_WEEKS = I18n.t([:every_one, :every_two, :every_three, :every_four], scope: [:podcast_planner, :helper, :period_options]).freeze
MONTHLY_WEEKS = I18n.t([:first, :second, :third, :fourth, :fifth], scope: [:podcast_planner, :helper, :monthly_options]).freeze
PERIODIC_WEEKS = I18n.t([:every_one, :every_two, :every_three, :every_four], scope: [:podcast_planner, :helper, :period_options])
MONTHLY_WEEKS = I18n.t([:first, :second, :third, :fourth, :fifth], scope: [:podcast_planner, :helper, :monthly_options])
DATE_CONTROLLER = "date"
TOGGLE_ACTION = "click->date#toggleSelect"

def day_options
DateTime::DAYNAMES.map.with_index { |day, i| [day, i] }
Expand Down Expand Up @@ -36,4 +38,32 @@ def time_options

opts.map { |opt| [I18n.l(opt, format: :time_12_hour), opt] }
end

def days_in_month(month)
month.map { |d| d.day }
end

def date_is_in_dates?(date, dates)
if dates.present?
dates.include?(date)
else
false
end
end

def date_is_in_month?(date, month)
date.month == month
end

def calendar_day_tag(day:, month:, calendar:, &block)
data = {}
if date_is_in_month?(day, month)
data[:controller] = DATE_CONTROLLER
data[:action] = TOGGLE_ACTION
end

content_tag(:td, class: calendar.td_classes_for(day), data: data) do
block.call
end
end
end
44 changes: 44 additions & 0 deletions app/javascript/controllers/date_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["preselected"]

connect() {
if (!this.isDisabled(this.preselectedTarget)) {
if (this.isDraft(this.preselectedTarget)) {
this.element.classList.add("bg-danger", "text-light")
} else {
this.element.classList.add("bg-primary", "text-light")
}
}

if (this.preselectedTarget.getAttribute("draft") === "true") {
this.element.classList.add("bg-warning")
}
}

toggleSelect(event) {
if (!this.isDisabled(event.target.firstElementChild)) {
if (this.isDraft(event.target.firstElementChild)) {
this.element.classList.remove("bg-danger")
}
event.target.classList.remove("bg-primary", "text-light")
event.target.firstElementChild.disabled = true
} else {
if (this.isDraft(event.target.firstElementChild)) {
event.target.classList.add("bg-danger", "text-light")
} else {
event.target.classList.add("bg-primary", "text-light")
}
event.target.firstElementChild.disabled = null
}
}

isDisabled(el) {
return el.disabled === true
}

isDraft(el) {
return el.getAttribute("draft") === "true"
}
}
44 changes: 44 additions & 0 deletions app/javascript/controllers/pager_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["page", "prev", "next"]
static values = {
currentPosition: { type: Number, default: 0 },
range: { type: Number, default: 6 },
step: { type: Number, default: 1 },
}

currentPositionValueChanged() {
this.pageTargets.forEach((el, index) => {
if (this.inRange(index)) {
el.classList.remove("d-none")
} else {
el.classList.add("d-none")
}
})
}

inRange(index) {
return index >= this.currentPositionValue && index < this.currentPositionValue + this.rangeValue
}

pageForward() {
if (this.currentPositionValue < this.pageTargets.length - this.rangeValue) {
this.currentPositionValue += this.stepValue
this.prevTarget.disabled = false
}
if (this.currentPositionValue === this.pageTargets.length - this.rangeValue) {
this.nextTarget.disabled = true
}
}

pageBackward() {
if (this.currentPositionValue > 0) {
this.currentPositionValue -= this.stepValue
this.nextTarget.disabled = false
}
if (this.currentPositionValue === 0) {
this.prevTarget.disabled = true
}
}
}
4 changes: 2 additions & 2 deletions app/models/podcast_planner.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class PodcastPlanner
attr_accessor :podcast_id, :dates, :selected_days, :week_condition, :period, :monthly_weeks, :start_date, :date_range_condition, :number_of_episodes, :end_date, :publish_time, :segment_count, :generated_dates, :drafts
attr_accessor :podcast_id, :dates, :selected_days, :week_condition, :period, :monthly_weeks, :start_date, :date_range_condition, :number_of_episodes, :end_date, :publish_time, :segment_count, :selected_dates, :drafts

def initialize(params = {})
@dates = params[:generated_dates].try { map { |date| date.to_datetime } }
@dates = params[:selected_dates].try { map { |date| date.to_datetime } }
@drafts = []
@podcast_id = params[:podcast_id]
@start_date = params[:start_date].try(:to_datetime)
Expand Down
9 changes: 9 additions & 0 deletions app/views/podcast_planner/_calendar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="col <%= "d-none" if index >= range %>" data-pager-target="page">
<%= month_calendar(start_date: start_date) do |date| %>
<% if date_is_in_month?(date, start_date.month) %>
<%= date.day %>
<%= form.hidden_field :selected_dates, multiple: true, data: {date_target: "preselected"}, value: date.to_datetime, disabled: !date_is_in_dates?(date, @planner.dates), draft: date_is_in_dates?(date, @draft_dates) %>
<% end %>
<% end %>
</div>
78 changes: 43 additions & 35 deletions app/views/podcast_planner/_form_side.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,55 @@
<div class="card-header-primary">
<h5 class="card-title"><%= t(".header.preview") %></h5>
</div>
<%= turbo_frame_tag "preview", target: "_top" do %>
<% if @planner.dates.present? %>
<%= form_with(
url: podcast_planner_path(@podcast),
method: :post
) do |form| %>
<div class="card-body">
<div class="col mb-2">
<h3 class="fw-bold"><%= t(".header.publish_time") %></h3>
<div class="form-floating">
<%= form.select :publish_time, time_options %>
<%= form.label :publish_time %>
</div>
</div>

<div class="col mb-2">
<h3 class="fw-bold"><%= t(".header.audio_segments") %></h3>
<div class="form-floating">
<%= form.number_field :segment_count, {min: 1, max: 10, step: 1, value: 2} %>
<%= form.label :segment_count %>
</div>
<%= form_with(
url: podcast_planner_path(@podcast),
method: :post
) do |form| %>
<div class="card-body">
<div class="row mb-4">
<div class="col-lg-6">
<h3 class="fw-bold"><%= t(".header.publish_time") %></h3>
<div class="form-floating">
<%= form.select :publish_time, time_options %>
<%= form.label :publish_time %>
</div>
</div>

<div class="col">
<p><%= t(".header.number_of_drafts", count: @planner.dates.count) %></p>
<ul class="list-group list-group-numbered">
<% @planner.dates.each do |date| %>
<li class="list-group-item"><%= l(date, format: :day_and_date) %></li>
<%= form.hidden_field :generated_dates, multiple: true, value: date %>
<% end %>
</ul>
<div class="col-lg-6">
<h3 class="fw-bold"><%= t(".header.audio_segments") %></h3>
<div class="form-floating">
<%= form.number_field :segment_count, {min: 1, max: 10, step: 1, value: 2} %>
<%= form.label :segment_count %>
</div>
</div>
<div class="card-footer">
<%= form.submit t(".label.submit"), class: "btn btn-primary" %>
</div>

<div class="col-lg">
<div data-controller="pager" data-pager-range-value="6">
<div class="mb-2">
<%= button_tag type: "button", class: "btn btn-primary btn-sm", data: {action: "click->pager#pageBackward", pager_target: "prev"}, disabled: true do %>
<span class="material-icons">arrow_left</span>
Month
<% end %>
<%= button_tag type: "button", class: "btn btn-primary btn-sm", data: {action: "click->pager#pageForward", pager_target: "next"} do %>
Month
<span class="material-icons">arrow_right</span>
<% end %>
</div>

<%= turbo_frame_tag "preview", target: "_top" do %>
<div class="row">
<% 24.times do |i| %>
<%= render "calendar", form: form, start_date: Date.today.beginning_of_month + i.months, planner: @planner, index: i, range: 6 %>
<% end %>
</div>
<% end %>
</div>
<% end %>
<% else %>
<div class="card-body">
<p><%= t(".help.preview") %></p>
</div>
</div>
<div class="card-footer">
<%= form.submit t(".label.submit"), class: "btn btn-primary" %>
</div>
<% end %>
<% end %>
</div>
4 changes: 2 additions & 2 deletions app/views/podcast_planner/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<%= render "podcasts/tabs" %>

<div class="row my-4 px-2">
<div class="col-lg-8">
<div class="col-lg-4">
<%= render "form_main" %>
</div>
<div class="col-lg-4">
<div class="col-lg-8">
<%= render "form_side" %>
</div>
</div>
33 changes: 33 additions & 0 deletions app/views/simple_calendar/_calendar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<div class="simple-calendar">
<div class="calendar-heading">
<%= link_to t("simple_calendar.previous", default: "Previous"), calendar.url_for_previous_view %>
<span class="calendar-title"><%= t("date.month_names")[start_date.month] %> <%= start_date.year %></span>
<%= link_to t("simple_calendar.next", default: "Next"), calendar.url_for_next_view %>
</div>

<table class="table table-striped">
<thead>
<tr>
<% date_range.slice(0, 7).each do |day| %>
<th><%= t("date.abbr_day_names")[day.wday] %></th>
<% end %>
</tr>
</thead>

<tbody>
<% date_range.each_slice(7) do |week| %>
<%= content_tag :tr, class: calendar.tr_classes_for(week) do %>
<% week.each do |day| %>
<%= content_tag :td, class: calendar.td_classes_for(day) do %>
<% if defined?(Haml) && respond_to?(:block_is_haml?) && block_is_haml?(passed_block) %>
<% capture_haml(day, sorted_events.fetch(day, []), &passed_block) %>
<% else %>
<% passed_block.call day, sorted_events.fetch(day, []) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
</tbody>
</table>
</div>
Loading

0 comments on commit e0842d2

Please sign in to comment.