Merge pull request #76 from CactusPuppy/176894996-vertical-schedule

Vertical schedule
This commit is contained in:
CactusPuppy 2021-05-01 11:00:44 -07:00 committed by GitHub
commit f60b07eb32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 245 additions and 3 deletions

View file

@ -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
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -723,7 +723,7 @@ DEPENDENCIES
leaflet-rails
letter_opener
letter_opener_web (~> 1.0)
mimemagic (~> 0.3.6)
mimemagic
mina
mini_magick
money-rails

View file

@ -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){

View 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();
});

View file

@ -26,4 +26,6 @@
*= require osem-navbar
*= require mastodon
*= require conferences
*= require fullcalendar-scheduler/main.css
*/

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View 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 } }

View file

@ -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

View file

@ -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
View 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
View file

@ -0,0 +1,5 @@
{
"dependencies": {
"fullcalendar-scheduler": "^5.6.0"
}
}

View file

@ -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

View 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