Merge pull request #76 from CactusPuppy/176894996-vertical-schedule
Vertical schedule
This commit is contained in:
commit
f60b07eb32
19 changed files with 245 additions and 3 deletions
6
.github/workflows/spec.yml
vendored
6
.github/workflows/spec.yml
vendored
|
|
@ -38,6 +38,12 @@ jobs:
|
|||
with:
|
||||
ruby-version: 2.6.6
|
||||
bundler-cache: true
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
- name: Install JavaScript libraries via npm
|
||||
run: npm install
|
||||
- name: Prepare spec
|
||||
run: |
|
||||
rm -f osem_test osem_development
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -121,3 +121,6 @@ build-iPhoneSimulator/
|
|||
|
||||
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
||||
.rubocop-https?--*
|
||||
|
||||
# Ignore Javascript package binaries
|
||||
/node_modules
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ jobs:
|
|||
- gem install bundler -v $(tail -n 1 Gemfile.lock)
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > $CCTR
|
||||
- chmod +x $CCTR
|
||||
- npm install
|
||||
before_script:
|
||||
- RAILS_ENV=test bundle exec rake db:bootstrap --trace
|
||||
- RAILS_ENV=test bundle exec bin/rails webdrivers:chromedriver:update
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -27,7 +27,7 @@ gem 'paper_trail'
|
|||
# for upload management
|
||||
gem 'carrierwave'
|
||||
gem 'carrierwave-bombshelter'
|
||||
gem 'mimemagic', '~> 0.3.6'
|
||||
gem 'mimemagic'
|
||||
gem 'mini_magick'
|
||||
|
||||
# for internationalizing
|
||||
|
|
|
|||
|
|
@ -723,7 +723,7 @@ DEPENDENCIES
|
|||
leaflet-rails
|
||||
letter_opener
|
||||
letter_opener_web (~> 1.0)
|
||||
mimemagic (~> 0.3.6)
|
||||
mimemagic
|
||||
mina
|
||||
mini_magick
|
||||
money-rails
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
//= require bootstrap-select
|
||||
//= require osem-survey
|
||||
//= require pagy
|
||||
//= require fullcalendar-scheduler/main.js
|
||||
//= require fullcalendar
|
||||
|
||||
$(document).ready(function() {
|
||||
$('a[disabled=disabled]').click(function(event){
|
||||
|
|
|
|||
30
app/assets/javascripts/fullcalendar.js.erb
Normal file
30
app/assets/javascripts/fullcalendar.js.erb
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
$( document ).ready(function() {
|
||||
let calendarEl = document.getElementById('vert-schedule-full-calendar');
|
||||
if (!calendarEl) return; //check that we need a vertical schedule
|
||||
let fullcalData = $('#fullcalendar');
|
||||
|
||||
let license_key = "<%= Rails.configuration.fullcalendar[:license_key] %>";
|
||||
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
schedulerLicenseKey: license_key,
|
||||
nowIndicator: true,
|
||||
now: fullcalData.data('now'),
|
||||
expandRows: true,
|
||||
allDaySlot: false,
|
||||
slotMinTime: fullcalData.data('startHour') + ':00:00',
|
||||
slotMaxTime: fullcalData.data('endHour') + ':00:00',
|
||||
validRange: {
|
||||
start: fullcalData.data('startDate'),
|
||||
end: fullcalData.data('endDate')
|
||||
},
|
||||
timeZone: fullcalData.data('timezone'),
|
||||
initialDate: fullcalData.data('day'),
|
||||
initialView: 'resourceTimeGridDay',
|
||||
resources: fullcalData.data('rooms'),
|
||||
events: fullcalData.data('events')
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -26,4 +26,6 @@
|
|||
*= require osem-navbar
|
||||
*= require mastodon
|
||||
*= require conferences
|
||||
|
||||
*= require fullcalendar-scheduler/main.css
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -78,6 +78,25 @@ class SchedulesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def vertical_schedule
|
||||
dates = @conference.start_date..@conference.end_date
|
||||
# the schedule takes you to today if it is a date of the schedule
|
||||
current_day = @conference.current_conference_day
|
||||
@day = current_day.present? ? current_day : dates.first
|
||||
event_schedules = @program.selected_event_schedules(
|
||||
includes: [{ event: %i[event_type speakers submitter] }]
|
||||
)
|
||||
|
||||
unless event_schedules
|
||||
redirect_to events_conference_schedule_path(@conference.short_title)
|
||||
return
|
||||
end
|
||||
|
||||
@rooms = FullCalendarFormatter.rooms_to_resources(@conference.venue.rooms) if @conference.venue
|
||||
@event_schedules = FullCalendarFormatter.event_schedules_to_resources(event_schedules)
|
||||
@now = Time.now.in_time_zone(@conference.timezone).strftime('%FT%T%:z')
|
||||
end
|
||||
|
||||
def app
|
||||
@qr_code = RQRCode::QRCode.new(conference_schedule_url).as_svg(offset: 20, color: '000', shape_rendering: 'crispEdges', module_size: 11)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class Ability
|
|||
event.state == 'confirmed'
|
||||
end
|
||||
|
||||
can [:show, :events, :happening_now, :app], Schedule do |schedule|
|
||||
can [:show, :events, :happening_now, :app, :vertical_schedule], Schedule do |schedule|
|
||||
schedule.program.schedule_public
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,14 @@ class EventSchedule < ApplicationRecord
|
|||
other.schedule_id == schedule_id
|
||||
end
|
||||
|
||||
def start_time_in_conference_timezone
|
||||
time_in_conference_timezone(start_time)
|
||||
end
|
||||
|
||||
def end_time_in_conference_timezone
|
||||
time_in_conference_timezone(end_time)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def replaced_event_schedules
|
||||
|
|
@ -187,4 +195,10 @@ class EventSchedule < ApplicationRecord
|
|||
|
||||
errors.add(:schedule, "must be one of #{event.track.name} track's schedules") unless event.track.schedules.include?(schedule)
|
||||
end
|
||||
|
||||
def time_in_conference_timezone(time)
|
||||
time_in_tz = time.in_time_zone(timezone)
|
||||
time_in_tz -= time_in_tz.utc_offset
|
||||
time_in_tz
|
||||
end
|
||||
end
|
||||
|
|
|
|||
42
app/services/full_calendar_formatter.rb
Normal file
42
app/services/full_calendar_formatter.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
class FullCalendarFormatter
|
||||
def self.rooms_to_resources(rooms)
|
||||
rooms.map { |room| room_to_resource(room) }.to_json
|
||||
end
|
||||
|
||||
def self.event_schedules_to_resources(event_schedules)
|
||||
return '[]' if event_schedules.empty?
|
||||
|
||||
conference = event_schedules.first.schedule.program.conference
|
||||
event_schedules.map { |event_schedule| event_schedule_to_resource(conference, event_schedule) }.to_json
|
||||
end
|
||||
|
||||
class << self
|
||||
include FormatHelper
|
||||
|
||||
private
|
||||
|
||||
def room_to_resource(room)
|
||||
{
|
||||
id: room.guid,
|
||||
title: room.name
|
||||
}
|
||||
end
|
||||
|
||||
def event_schedule_to_resource(conference, event_schedule)
|
||||
event_type_color = event_schedule.event.event_type.color
|
||||
url = Rails.application.routes.url_helpers.conference_program_proposal_path(conference.short_title, event_schedule.event.id)
|
||||
|
||||
{
|
||||
id: event_schedule.event.guid,
|
||||
title: event_schedule.event.title,
|
||||
start: event_schedule.start_time_in_conference_timezone,
|
||||
end: event_schedule.end_time_in_conference_timezone,
|
||||
resourceId: event_schedule.room.guid,
|
||||
url: url,
|
||||
borderColor: event_type_color,
|
||||
backgroundColor: event_type_color,
|
||||
textColor: contrast_color(event_type_color)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
12
app/views/schedules/vertical_schedule.haml
Normal file
12
app/views/schedules/vertical_schedule.haml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
.container#program
|
||||
= render 'schedule_tabs', active: 'vertical_schedule'
|
||||
|
||||
%h1.text-center
|
||||
Vertical Schedule for
|
||||
= @conference.title
|
||||
|
||||
#vert-schedule-full-calendar
|
||||
#fullcalendar{ data: { rooms: @rooms, events: @event_schedules, day: @day,
|
||||
'start-hour': @conference.start_hour, 'end-hour': @conference.end_hour,
|
||||
'start-date': @conference.start_date, 'end-date': @conference.end_date + 1.day,
|
||||
'timezone': @conference.timezone, 'now': @now } }
|
||||
|
|
@ -80,5 +80,11 @@ module Osem
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
config.assets.paths << Rails.root.join('node_modules')
|
||||
|
||||
config.fullcalendar = {
|
||||
license_key: ENV['FULL_CALENDAR_LICENSE_KEY']
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ Osem::Application.routes.draw do
|
|||
member do
|
||||
get :events
|
||||
get :happening_now
|
||||
get :vertical_schedule
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
11
package-lock.json
generated
Normal file
11
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"fullcalendar-scheduler": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/fullcalendar-scheduler/-/fullcalendar-scheduler-5.6.0.tgz",
|
||||
"integrity": "sha512-0jx/Q+6QqzvIGWU/52OE27UVjVAiM9E0o/qRwRcEhyuKlL54ypTF/yXP/wo0ctUtui5dDsilGNsSsba1xmARJA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
5
package.json
Normal file
5
package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"fullcalendar-scheduler": "^5.6.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -72,4 +72,20 @@ describe SchedulesController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #vertical_schedule' do
|
||||
let!(:program) { conference.program }
|
||||
|
||||
context 'as a conference participant' do
|
||||
context 'who visits the schedule page' do
|
||||
before(:each) do
|
||||
get :vertical_schedule, params: { conference_id: conference.short_title }
|
||||
end
|
||||
|
||||
it 'returns a successful response' do
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
72
spec/services/full_calendar_formatter_spec.rb
Normal file
72
spec/services/full_calendar_formatter_spec.rb
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe FullCalendarFormatter do
|
||||
let!(:room1) { create(:room) }
|
||||
let!(:room2) { create(:room) }
|
||||
let!(:rooms) { [room1, room2] }
|
||||
let!(:conference) { create(:full_conference) }
|
||||
let!(:program) { conference.program }
|
||||
let!(:selected_schedule) { create(:schedule, program: program) }
|
||||
let!(:event_type1) { create(:event_type, color: '#ffffff') }
|
||||
let!(:event1) do
|
||||
program.update_attributes!(selected_schedule: selected_schedule)
|
||||
create(:event, program: program, event_type: event_type1)
|
||||
end
|
||||
let!(:event_schedule1) { create(:event_schedule, event: event1, schedule: selected_schedule, room: room1) }
|
||||
let!(:event_type2) { create(:event_type, color: '#000000') }
|
||||
let!(:event2) do
|
||||
program.update_attributes!(selected_schedule: selected_schedule)
|
||||
create(:event, program: program, room: room2, event_type: event_type2)
|
||||
end
|
||||
let!(:event_schedule2) { create(:event_schedule, event: event2, schedule: selected_schedule, room: room2) }
|
||||
let!(:event_schedules) { [event_schedule1, event_schedule2] }
|
||||
|
||||
describe 'JSON formatting for FullCalendar' do
|
||||
it 'translates rooms to resources' do
|
||||
resources = described_class.rooms_to_resources(rooms)
|
||||
expected_json = [
|
||||
{
|
||||
id: room1.guid,
|
||||
title: room1.name
|
||||
},
|
||||
{
|
||||
id: room2.guid,
|
||||
title: room2.name
|
||||
}
|
||||
].to_json
|
||||
expect(resources).to eq(expected_json)
|
||||
end
|
||||
|
||||
it 'translates event schedules to resources' do
|
||||
resources = described_class.event_schedules_to_resources(event_schedules)
|
||||
expected_json = [
|
||||
{
|
||||
id: event_schedule1.event.guid,
|
||||
title: event_schedule1.event.title,
|
||||
start: event_schedule1.start_time_in_conference_timezone,
|
||||
end: event_schedule1.end_time_in_conference_timezone,
|
||||
resourceId: room1.guid,
|
||||
url: Rails.application.routes.url_helpers.conference_program_proposal_path(conference.short_title, event1.id),
|
||||
borderColor: event1.event_type.color,
|
||||
backgroundColor: event1.event_type.color,
|
||||
textColor: 'black'
|
||||
},
|
||||
{
|
||||
id: event_schedule2.event.guid,
|
||||
title: event_schedule2.event.title,
|
||||
start: event_schedule2.start_time_in_conference_timezone,
|
||||
end: event_schedule2.end_time_in_conference_timezone,
|
||||
resourceId: room2.guid,
|
||||
url: Rails.application.routes.url_helpers.conference_program_proposal_path(conference.short_title, event2.id),
|
||||
borderColor: event2.event_type.color,
|
||||
backgroundColor: event2.event_type.color,
|
||||
textColor: 'white'
|
||||
}
|
||||
].to_json
|
||||
|
||||
expect(resources).to eq(expected_json)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue