diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml
index b2e7d847..6875c7b8 100644
--- a/.github/workflows/spec.yml
+++ b/.github/workflows/spec.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index c65dc0b1..fcdf9332 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/.travis.yml b/.travis.yml
index b35784c6..00d5b582 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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
diff --git a/Gemfile b/Gemfile
index 7198bdf7..7c1eb7f1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -27,6 +27,7 @@ gem 'paper_trail'
# for upload management
gem 'carrierwave'
gem 'carrierwave-bombshelter'
+gem 'mimemagic'
gem 'mini_magick'
# for internationalizing
diff --git a/Gemfile.lock b/Gemfile.lock
index 38978051..10c6a0bf 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -171,11 +171,15 @@ GEM
railties (>= 3.0.0)
faker (1.9.3)
i18n (>= 0.7)
- faraday (1.3.0)
+ faraday (1.4.1)
+ faraday-excon (~> 1.1)
faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.1)
multipart-post (>= 1.2, < 3)
- ruby2_keywords
+ ruby2_keywords (>= 0.0.4)
+ faraday-excon (1.1.0)
faraday-net_http (1.0.1)
+ faraday-net_http_persistent (1.1.0)
fastimage (2.1.4)
feature (1.4.0)
ffi (1.10.0)
@@ -293,7 +297,7 @@ GEM
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2021.0225)
- mimemagic (0.4.3)
+ mimemagic (0.3.10)
nokogiri (~> 1)
rake
mina (1.2.3)
@@ -301,7 +305,7 @@ GEM
rake
mini_magick (4.9.5)
mini_mime (1.0.3)
- mini_portile2 (2.5.0)
+ mini_portile2 (2.5.1)
minitest (5.14.4)
momentjs-rails (2.20.1)
railties (>= 3.1)
@@ -320,7 +324,7 @@ GEM
nenv (0.3.0)
netrc (0.11.0)
nio4r (2.5.7)
- nokogiri (1.11.2)
+ nokogiri (1.11.3)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
notiffany (0.1.1)
@@ -332,7 +336,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
- octokit (4.20.0)
+ octokit (4.21.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
omniauth (1.9.1)
@@ -497,7 +501,7 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rexml (3.2.5)
- rolify (5.3.0)
+ rolify (5.2.0)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rspec (3.6.0)
@@ -637,7 +641,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
- unicode-display_width (1.6.0)
+ unicode-display_width (1.6.1)
unicode_utils (1.4.0)
unobtrusive_flash (3.3.1)
railties
@@ -719,6 +723,7 @@ DEPENDENCIES
leaflet-rails
letter_opener
letter_opener_web (~> 1.0)
+ mimemagic
mina
mini_magick
money-rails
diff --git a/README.md b/README.md
index 1a549b45..9bb3cff4 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,3 @@
-### Spring 2021 Student Project
-[](https://travis-ci.com/CactusPuppy/snapcon)
-[](https://codeclimate.com/github/CactusPuppy/snapcon/maintainability)
-[](https://codeclimate.com/github/CactusPuppy/snapcon/test_coverage)
-[](https://hakiri.io/github/CactusPuppy/snapcon/master)
-[](https://depfu.com/github/CactusPuppy/snapcon?project_id=22682)
-[](http://dashboard.bluejay.governify.io/dashboard/script/dashboardLoader.js?dashboardURL=https://reporter.bluejay.governify.io/api/v4/dashboards/tpa-CS169L-GH-CactusPuppy_snapcon/main)
-
-
-
-
-
-
-
-
-
----
-
[](https://travis-ci.com/snap-cloud/snapcon)
[](https://codeclimate.com/github/snap-cloud/snapcon/maintainability)
[](https://codeclimate.com/github/snap-cloud/snapcon/test_coverage)
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 6548a3fe..c5b49ae7 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -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){
diff --git a/app/assets/javascripts/fullcalendar.js.erb b/app/assets/javascripts/fullcalendar.js.erb
new file mode 100644
index 00000000..22ee857d
--- /dev/null
+++ b/app/assets/javascripts/fullcalendar.js.erb
@@ -0,0 +1,28 @@
+$( 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();
+});
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index f5c58982..520eedbe 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -26,4 +26,6 @@
*= require osem-navbar
*= require mastodon
*= require conferences
+
+ *= require fullcalendar-scheduler/main.css
*/
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index 35e6b709..cfd7eafc 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -40,7 +40,7 @@ class ConferencesController < ApplicationController
@image_url = "#{request.protocol}#{request.host}#{@conference.picture}"
- if splashpage.include_cfp
+ if splashpage.include_cfp?
cfps = @conference.program.cfps
@call_for_events = cfps.find { |call| call.cfp_type == 'events' }
if @call_for_events.try(:open?)
@@ -50,31 +50,27 @@ class ConferencesController < ApplicationController
@call_for_tracks = cfps.find { |call| call.cfp_type == 'tracks' }
@call_for_booths = cfps.find { |call| call.cfp_type == 'booths' }
end
- if splashpage.include_program
+ if splashpage.include_program?
@highlights = @conference.highlighted_events.eager_load(:speakers)
- if splashpage.include_tracks
+ if splashpage.include_tracks?
@tracks = @conference.confirmed_tracks.eager_load(
:room
).order('tracks.name')
end
- if splashpage.include_booths
+ if splashpage.include_booths?
@booths = @conference.confirmed_booths.order('title')
end
- if splashpage.include_happening_now
- events_schedules_list = get_happening_now_events_schedules(@conference)
- @events_schedules_limit = EVENTS_PER_PAGE
- @events_schedules_length = events_schedules_list.length
- @pagy, @events_schedules = pagy_array(events_schedules_list, items: @events_schedules_limit, link_extra: 'data-remote="true"')
- @happening_now_url = happening_now_conference_schedule_path(conference_id: @conference.short_title, format: :json)
+ if splashpage.include_happening_now?
+ load_happening_now
end
end
- if splashpage.include_registrations || splashpage.include_tickets
+ if splashpage.include_registrations? || splashpage.include_tickets?
@tickets = @conference.tickets.visible.order('price_cents')
end
- if splashpage.include_lodgings
+ if splashpage.include_lodgings?
@lodgings = @conference.lodgings.order('id')
end
- if splashpage.include_sponsors
+ if splashpage.include_sponsors?
@sponsorship_levels = @conference.sponsorship_levels.eager_load(
:sponsors
).order('sponsorship_levels.position ASC', 'sponsors.name')
diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb
index 87636b2c..92625ea9 100644
--- a/app/controllers/proposals_controller.rb
+++ b/app/controllers/proposals_controller.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
+EVENTS_PER_PAGE = Rails.configuration.conference[:events_per_page]
+
class ProposalsController < ApplicationController
+ include ConferenceHelper
before_action :authenticate_user!, except: [:show, :new, :create]
load_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true
@@ -19,6 +22,7 @@ class ProposalsController < ApplicationController
@event_schedule = @event.event_schedules.find_by(schedule_id: @program.selected_schedule_id)
@speakers_ordered = @event.speakers_ordered
@surveys_after_event = @event.surveys.after_event.select(&:active?)
+ load_happening_now
end
def new
diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb
index bdefb939..e0c5f911 100644
--- a/app/controllers/schedules_controller.rb
+++ b/app/controllers/schedules_controller.rb
@@ -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
diff --git a/app/helpers/conference_helper.rb b/app/helpers/conference_helper.rb
index cf2c06ce..4240ad54 100644
--- a/app/helpers/conference_helper.rb
+++ b/app/helpers/conference_helper.rb
@@ -79,10 +79,42 @@ module ConferenceHelper
end
def get_happening_now_events_schedules(conference)
- events_schedules = conference.program.selected_event_schedules(
- includes: [:room, { event: %i[track event_type speakers submitter] }]
- ).select(&:happening_now?)
+ events_schedules = filter_events_schedules(conference, :happening_now?)
events_schedules ||= []
events_schedules
end
+
+ def get_happening_next_events_schedules(conference)
+ events_schedules = filter_events_schedules(conference, :happening_later?)
+
+ if events_schedules.empty?
+ return []
+ end
+
+ # events_schedules have been sorted by start_time in selected_event_schedules
+ happening_next_time = events_schedules[0].start_time
+ events_schedules = events_schedules.select { |s| s.start_time == happening_next_time }
+
+ events_schedules
+ end
+
+ def load_happening_now
+ events_schedules_list = get_happening_now_events_schedules(@conference)
+ @is_happening_next = false
+ if events_schedules_list.empty?
+ events_schedules_list = get_happening_next_events_schedules(@conference)
+ @is_happening_next = true
+ end
+ @events_schedules_limit = EVENTS_PER_PAGE
+ @events_schedules_length = events_schedules_list.length
+ @pagy, @events_schedules = pagy_array(events_schedules_list, items: @events_schedules_limit, link_extra: 'data-remote="true"')
+ end
+
+ private
+
+ def filter_events_schedules(conference, filter)
+ conference.program.selected_event_schedules(
+ includes: [:room, { event: %i[track event_type speakers submitter] }]
+ ).select(&filter)
+ end
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index ec17e14c..68c83066 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -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
diff --git a/app/models/event_schedule.rb b/app/models/event_schedule.rb
index 036a89f2..7be7a7d2 100644
--- a/app/models/event_schedule.rb
+++ b/app/models/event_schedule.rb
@@ -70,6 +70,14 @@ class EventSchedule < ApplicationRecord
event_time_range.overlaps?(now_range)
end
+ def happening_later?
+ # TODO: Save start_time with local timezone info when making an event schedule
+ in_tz_start = start_time.in_time_zone(timezone)
+ in_tz_start -= in_tz_start.utc_offset
+
+ in_tz_start >= Time.now
+ end
+
def self.withdrawn_or_canceled_event_schedules(schedule_ids)
EventSchedule
.unscoped
@@ -125,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
@@ -179,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
diff --git a/app/services/full_calendar_formatter.rb b/app/services/full_calendar_formatter.rb
new file mode 100644
index 00000000..499b8e04
--- /dev/null
+++ b/app/services/full_calendar_formatter.rb
@@ -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
diff --git a/app/views/admin/commercials/index.html.haml b/app/views/admin/commercials/index.html.haml
index 2b91c1a6..d318707c 100644
--- a/app/views/admin/commercials/index.html.haml
+++ b/app/views/admin/commercials/index.html.haml
@@ -1,7 +1,7 @@
.row
.col-md-12
.page-header
- %h1 Session Materials
+ %h1 #{@conference.title} Materials
%p.text-muted
Conference materials will be displayed on the events in the
= link_to 'schedule,', conference_schedule_path(@conference.short_title)
diff --git a/app/views/admin/emails/_help.html.haml b/app/views/admin/emails/_help.html.haml
index d62589b0..1222c1ac 100644
--- a/app/views/admin/emails/_help.html.haml
+++ b/app/views/admin/emails/_help.html.haml
@@ -57,7 +57,7 @@
- if @conference.program.schedule_public
%td {schedule_link}
%td The link to complete schedule of the conference
- - if @conference.splashpage && @conference.splashpage.public
+ - if @conference.splashpage && @conference.splashpage.public?
%tr
%td {conference_splash_link}
%td The link to conference splash page
diff --git a/app/views/admin/splashpages/show.html.haml b/app/views/admin/splashpages/show.html.haml
index 08edb140..e3732b89 100644
--- a/app/views/admin/splashpages/show.html.haml
+++ b/app/views/admin/splashpages/show.html.haml
@@ -49,7 +49,7 @@
%i{ class: "fa-li #{icon_for_todo @splashpage.include_social_media?}" }
Display social media links
%li
- - if @conference.splashpage && @conference.splashpage.public
+ - if @conference.splashpage && @conference.splashpage.public?
%i{ class: "fa-li #{icon_for_todo @splashpage.public?}" }
%text-muted.publicorprivate Public
- else
diff --git a/app/views/conferences/_about_and_happening_now.haml b/app/views/conferences/_about_and_happening_now.haml
index 1bb1adae..3b327c4f 100644
--- a/app/views/conferences/_about_and_happening_now.haml
+++ b/app/views/conferences/_about_and_happening_now.haml
@@ -3,7 +3,8 @@
= render 'happening_now', conference: conference,
events_schedules: events_schedules, pagy: pagy,
events_schedules_length: events_schedules_length,
- events_schedules_limit: events_schedules_limit
+ events_schedules_limit: events_schedules_limit,
+ is_happening_next: is_happening_next
= content_for :about do
#about
@@ -14,9 +15,9 @@
.container
.row
-# happening now events are displayed second in md or lg view
- - if conference.splashpage.include_happening_now && conference.splashpage.include_program
+ - if conference.splashpage.include_happening_now? && conference.splashpage.include_program?
- if conference.description.present?
- .col-md-6.col-md-push-6.col-lg-4.col-lg-push-8
+ .col-md-6.col-md-push-6.col-lg-4.col-lg-push-8{ style: 'margin-top: 60px' }
= yield :happening_now
- else
.col-md-12
diff --git a/app/views/conferences/_happening_now.haml b/app/views/conferences/_happening_now.haml
index 66355a82..ccf0181f 100644
--- a/app/views/conferences/_happening_now.haml
+++ b/app/views/conferences/_happening_now.haml
@@ -1,12 +1,15 @@
-- if conference.splashpage.include_program && conference.splashpage.include_happening_now
- - if events_schedules.any?
- .row
- %h2.text-center{ style: 'margin-bottom:30px' } Happening Now
- - events_schedules.each do |event_schedule|
- = render 'schedules/event', conference: conference, event_schedule: event_schedule, event: event_schedule.event, is_brief: true
- - if events_schedules_length > events_schedules_limit
- .container{ style: 'width:100%; text-align:center' }
- != pagy_bootstrap_nav_js(pagy)
- - else
- .row
- %h3.text-center There are no events happening now.
+- if events_schedules.present? && events_schedules.any?
+ .row
+ %h3.text-left{ style: 'margin-bottom:30px; padding-left:20px' }
+ - if is_happening_next
+ Events Happening Next
+ - else
+ Events Happening Now
+ - events_schedules.each do |event_schedule|
+ = render 'schedules/event', conference: conference, event_schedule: event_schedule, event: event_schedule.event, is_brief: true
+ - if events_schedules_length > events_schedules_limit
+ .container{ style: 'width:100%; text-align:center' }
+ != pagy_bootstrap_nav_js(pagy)
+- else
+ .row
+ %h3.text-center There are no events scheduled yet.
diff --git a/app/views/conferences/_header.haml b/app/views/conferences/_header.haml
index 9a1316f6..4ee7123c 100644
--- a/app/views/conferences/_header.haml
+++ b/app/views/conferences/_header.haml
@@ -1,6 +1,11 @@
- cache [conference, venue, '#splash#header'] do
#banner{ style: ("background-image: url(#{conference.picture_url})" if conference.picture_url) }
.container
+ - if user_signed_in?
+ - if current_user.ticket_purchases.by_conference(@conference).empty?
+ .alert.bg-info{ :role => "alert", :style => "margin-bottom: 100px" }
+ You have not purchased any tickets for this conference yet.
+ = link_to "Book your tickets now!", "#tickets"
.row
.col-md-6.col-md-offset-3{ id: (conference.picture? ? "header-image" : "header-no-image") }
.row
diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml
index 3a73c0b0..ed642f34 100644
--- a/app/views/conferences/show.html.haml
+++ b/app/views/conferences/show.html.haml
@@ -49,21 +49,22 @@
= render 'about_and_happening_now', conference: @conference,
events_schedules: @events_schedules, pagy: @pagy,
events_schedules_length: @events_schedules_length,
- events_schedules_limit: @events_schedules_limit
+ events_schedules_limit: @events_schedules_limit,
+ is_happening_next: @is_happening_next
-# calls for content, or program
- - if @conference.splashpage.include_cfp
+ - if @conference.splashpage.include_cfp?
= render 'call_for_content', conference: @conference,
call_for_events: @call_for_events, call_for_tracks: @call_for_tracks,
call_for_booths: @call_for_booths,
event_types: @event_types, tracks: @track_names
- - if @conference.splashpage.include_program
+ - if @conference.splashpage.include_program?
= render 'program', conference: @conference, tracks: @tracks,
highlights: @highlights, booths: @booths
-# attendance/registration
- - if @conference.splashpage.include_registrations
+ - if @conference.splashpage.include_registrations?
- if @conference.registration_open?
= render 'registration', conference: @conference,
registration_period: @conference.registration_period,
@@ -72,20 +73,20 @@
= render 'tickets', conference: @conference, tickets: @tickets
-# geo
- - if @conference.splashpage.include_venue && @conference.venue
+ - if @conference.splashpage.include_venue? && @conference.venue
= render 'venue', conference: @conference, venue: @conference.venue,
commercial: @conference.venue.commercial
- if @conference.splashpage.include_lodgings && @conference.lodgings.any?
= render 'lodging', venue: @conference.venue, lodgings: @lodgings
-# sponsorship
- - if @conference.splashpage.include_sponsors
+ - if @conference.splashpage.include_sponsors?
= render 'sponsors', conference: @conference,
sponsorship_levels: @sponsorship_levels,
sponsors: @sponsors
-# footer
- - if @conference.splashpage.include_social_media
+ - if @conference.splashpage.include_social_media?
- if @conference.contact.has_social_media?
= render 'social_media', contact: @conference.contact
= render 'footer'
diff --git a/app/views/conferences/show.js.erb b/app/views/conferences/show.js.erb
index fb35b420..61c0a3a3 100644
--- a/app/views/conferences/show.js.erb
+++ b/app/views/conferences/show.js.erb
@@ -1,5 +1,6 @@
$('#happening-now').html("<%= j(render 'happening_now', conference: @conference,
events_schedules: @events_schedules, pagy: @pagy,
events_schedules_length: @events_schedules_length,
- events_schedules_limit: @events_schedules_limit)%>");
+ events_schedules_limit: @events_schedules_limit,
+ is_happening_next: @is_happening_next) %>");
Pagy.init(document.getElementById('happening-now'));
diff --git a/app/views/proposals/show.html.haml b/app/views/proposals/show.html.haml
index 9d5cfe73..432757c2 100644
--- a/app/views/proposals/show.html.haml
+++ b/app/views/proposals/show.html.haml
@@ -25,9 +25,10 @@
.col-md-4
%p{style: "margin-top: 20px"}
- if @event_schedule&.start_time
- = link_to google_calendar_link(@event_schedule), target: '_blank', class: 'btn btn-success btn-mini' do
- %i.fa.fa-calendar
- Add to Google Calendar (beta)
+ - if user_signed_in?
+ = link_to google_calendar_link(@event_schedule), target: '_blank', class: 'btn btn-success btn-mini' do
+ %i.fa.fa-calendar
+ Add to Google Calendar (beta)
- if can?(:update, @event) && @event.require_registration?
= link_to 'Registrations', registrations_conference_program_proposal_path(@conference.short_title, @event), class: 'btn btn-mini btn-success'
- if can?(:edit, @event)
@@ -51,7 +52,7 @@
- @event.volunteers.each do |volunteer|
= render 'volunteer_info', speaker: volunteer, show_bio: false
- .col-md-9
+ .col-md-6
.row
.col-md-12
.lead
@@ -138,6 +139,13 @@
- if @surveys_after_event.any? && @event.ended?
.page-header
= render partial: 'surveys/list', locals: { surveys: @surveys_after_event, conference: @conference }
+ .col-md-3
+ #happening-now
+ = render 'conferences/happening_now', conference: @conference,
+ events_schedules: @events_schedules, pagy: @pagy,
+ events_schedules_length: @events_schedules_length,
+ events_schedules_limit: @events_schedules_limit,
+ is_happening_next: @is_happening_next
- if @event.committee_review.present? || @event.submission_text.present? && can?(:edit, @event)
%hr
diff --git a/app/views/proposals/show.js.erb b/app/views/proposals/show.js.erb
new file mode 100644
index 00000000..53220cb8
--- /dev/null
+++ b/app/views/proposals/show.js.erb
@@ -0,0 +1,6 @@
+$('#happening-now').html("<%= j(render 'conferences/happening_now', conference: @conference,
+ events_schedules: @events_schedules, pagy: @pagy,
+ events_schedules_length: @events_schedules_length,
+ events_schedules_limit: @events_schedules_limit,
+ is_happening_next: @is_happening_next) %>");
+Pagy.init(document.getElementById('happening-now'));
diff --git a/app/views/schedules/vertical_schedule.haml b/app/views/schedules/vertical_schedule.haml
new file mode 100644
index 00000000..9e5ce379
--- /dev/null
+++ b/app/views/schedules/vertical_schedule.haml
@@ -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 } }
diff --git a/config/application.rb b/config/application.rb
index 4e5e9b87..320e4fd3 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -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
diff --git a/config/routes.rb b/config/routes.rb
index 1c31e0d0..a901509e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -202,6 +202,7 @@ Osem::Application.routes.draw do
member do
get :events
get :happening_now
+ get :vertical_schedule
end
end
end
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..88e0e172
--- /dev/null
+++ b/package-lock.json
@@ -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=="
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..d8f5ba1a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "fullcalendar-scheduler": "^5.6.0"
+ }
+}
diff --git a/spec/controllers/schedules_controller_spec.rb b/spec/controllers/schedules_controller_spec.rb
index 22cb7c1a..dbf9e38b 100644
--- a/spec/controllers/schedules_controller_spec.rb
+++ b/spec/controllers/schedules_controller_spec.rb
@@ -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
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 75af7370..557d777f 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -80,25 +80,6 @@ FactoryBot.define do
last_sign_in_at { Date.today }
is_disabled { false }
- after(:build) do |user|
- url_mailbluster = 'https://api.mailbluster.com/api/leads/'
- response_body = "{
- \"message\": \"Lead created\",
- \"lead\": {
- \"id\": 329395,
- \"firstName\": \"#{user.name}\",
- \"lastName\": \"\",
- \"fullName\": \"#{user.name}\",
- \"email\": \"#{user.email}\",
- \"subscribed\": true,
- \"tags\": [
- #{ENV['OSEM_NAME'] || 'snapcon'}
- ],
- }
- }"
- WebMock.stub_request(:post, url_mailbluster)
- .to_return(body: response_body, status: 200)
- end
# Called by every user creation
after(:create) do |user|
diff --git a/spec/features/commercials_spec.rb b/spec/features/commercials_spec.rb
index d6fc7f38..c9b30a6e 100644
--- a/spec/features/commercials_spec.rb
+++ b/spec/features/commercials_spec.rb
@@ -10,11 +10,18 @@ feature Commercial do
let!(:participant) { create(:user) }
context 'in admin area' do
- scenario 'adds, updates, deletes of a conference', feature: true, js: true do
+
+ before do
sign_in organizer
-
visit admin_conference_commercials_path(conference.short_title)
+ end
+ scenario 'contains the conference name in the title', feature: true, js: true do
+ header = conference.title + ' Materials'
+ expect(page).to have_content(header)
+ end
+
+ scenario 'adds, updates, deletes of a conference', feature: true, js: true do
# Create valid commercial
fill_in 'commercial_url', with: 'https://www.youtube.com/watch?v=M9bq_alk-sw'
click_button 'Save Materials'
diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb
index e7377bb2..856d28c9 100644
--- a/spec/features/proposals_spec.rb
+++ b/spec/features/proposals_spec.rb
@@ -212,4 +212,128 @@ feature Event do
expect(page.find('#event_submission_text').value).to eq(event_type.submission_instructions)
end
end
+
+ context 'as a user, looking at a conference with scheduled events' do
+ before(:each) do
+ @program = conference.program
+ @selected_schedule = create(:schedule, program: @program)
+ @program.update_attributes!(selected_schedule: @selected_schedule)
+ @scheduled_event1 = create(:event, program: @program, state: 'confirmed', abstract: '`markdown`')
+ @event_schedule1 = create(:event_schedule, event: @scheduled_event1, schedule: @selected_schedule, start_time: conference.start_hour + 1.hour)
+ end
+
+ scenario 'for a scheduled event, can add an event to google calendar if signed in', feature: true do
+ sign_in participant
+ visit conference_program_proposal_path(conference.short_title, @scheduled_event1.id)
+ expect(page).to have_content('Add to Google Calendar (beta)')
+ end
+
+ scenario 'for a scheduled event, cannot add an event to google calendar if not signed on', feature: true do
+ visit conference_program_proposal_path(conference.short_title, @scheduled_event1.id)
+ expect(page).not_to have_content('Add to Google Calendar (beta)')
+ end
+ end
+
+ context 'happening now or next section' do
+ let!(:conference1) { create(:full_conference, start_date: 1.day.ago, end_date: 7.days.from_now, start_hour: 0, end_hour: 24) }
+ let!(:program) { conference1.program }
+ let!(:selected_schedule) { create(:schedule, program: program) }
+ let!(:splashpage) { create(:full_splashpage, conference: conference1, public: true) }
+
+ let!(:scheduled_event1) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:scheduled_event2) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:scheduled_event3) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:scheduled_event4) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:current_time) { Time.now.in_time_zone(conference1.timezone) }
+
+ let!(:events_list) { [scheduled_event1, scheduled_event2, scheduled_event3, scheduled_event4] }
+
+ before :each do
+ sign_in participant
+ end
+
+ scenario 'No events happening now or next' do
+ events_list.each do |event|
+ visit conference_program_proposal_path(conference1.short_title, event.id)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content('There are no events scheduled yet.')
+ end
+ end
+
+ scenario 'shows all events happening next if nothing is happening now' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+
+ events_list.each do |event|
+ visit conference_program_proposal_path(conference1.short_title, event.id)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule1.event.title)
+ expect(happening_now).to have_content(event_schedule2.event.title)
+ expect(happening_now).not_to have_content(scheduled_event3.title)
+ expect(happening_now).not_to have_content(scheduled_event4.title)
+ end
+ end
+
+ scenario 'only shows all events happening now if something is happening now and next' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule3 = create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ events_list.each do |event|
+ visit conference_program_proposal_path(conference1.short_title, event.id)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).not_to have_content(event_schedule1.event.title)
+ expect(happening_now).not_to have_content(event_schedule2.event.title)
+ expect(happening_now).to have_content(event_schedule3.event.title)
+ expect(happening_now).not_to have_content(scheduled_event4.title)
+ end
+ end
+
+ scenario 'only shows events happening at the earliest time, not at a later time in the future' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule3 = create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: (current_time + 2.hours).strftime('%a, %d %b %Y %H:%M:%S'))
+ events_list.each do |event|
+ visit conference_program_proposal_path(conference1.short_title, event.id)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule1.event.title)
+ expect(happening_now).to have_content(event_schedule2.event.title)
+ expect(happening_now).not_to have_content(event_schedule3.event.title)
+ expect(happening_now).not_to have_content(scheduled_event4.title)
+ end
+ end
+
+ scenario 'only shows 3 events happening now because of pagination' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule3 = create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule4 = create(:event_schedule, event: scheduled_event4, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+
+ events_list.each do |event|
+ visit conference_program_proposal_path(conference1.short_title, event.id)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule1.event.title)
+ expect(happening_now).to have_content(event_schedule2.event.title)
+ expect(happening_now).to have_content(event_schedule3.event.title)
+
+ visit conference_program_proposal_path(conference1.short_title, event.id, page: 2)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).not_to have_content(event_schedule3.event.title)
+ expect(happening_now).not_to have_content(event_schedule1.event.title)
+ expect(happening_now).not_to have_content(event_schedule2.event.title)
+ expect(happening_now).to have_content(event_schedule4.event.title)
+ end
+ end
+ end
end
diff --git a/spec/features/splashpage_spec.rb b/spec/features/splashpage_spec.rb
index 69e1c6ae..d5810ca7 100644
--- a/spec/features/splashpage_spec.rb
+++ b/spec/features/splashpage_spec.rb
@@ -80,4 +80,127 @@ feature Splashpage do
end
end
end
+
+ context 'happening now section', feature: true, js: true do
+ let!(:conference2) { create(:full_conference, start_date: 1.day.ago, end_date: 7.days.from_now, start_hour: 0, end_hour: 24) }
+ let!(:program) { conference2.program }
+ let!(:selected_schedule) { create(:schedule, program: program) }
+ let!(:splashpage) { create(:full_splashpage, conference: conference2, public: true)}
+
+ let!(:scheduled_event1) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed', abstract: '`markdown`')
+ end
+ let!(:scheduled_event2) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:scheduled_event3) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:scheduled_event4) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:current_time) { Time.now.in_time_zone(conference2.timezone) }
+
+ before :each do
+ sign_in participant
+ end
+
+ scenario 'displays \'There are no events scheduled yet.\' if nothing is happening now and next' do
+ visit conference_path(conference2.short_title)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content('There are no events scheduled yet.')
+ end
+
+ scenario 'shows all events happening next if nothing is happening now' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ visit conference_path(conference2.short_title)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule1.event.title)
+ expect(happening_now).to have_content(event_schedule2.event.title)
+ end
+
+ scenario 'only shows all events happening now if something is happening now and next' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule3 = create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ visit conference_path(conference2.short_title)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule3.event.title)
+ expect(happening_now).not_to have_content(event_schedule1.event.title)
+ expect(happening_now).not_to have_content(event_schedule2.event.title)
+ end
+
+ scenario 'only shows events happening at the earliest time, not at a later time in the future' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule3 = create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: (current_time + 2.hours).strftime('%a, %d %b %Y %H:%M:%S'))
+ visit conference_path(conference2.short_title)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule1.event.title)
+ expect(happening_now).to have_content(event_schedule2.event.title)
+ expect(happening_now).not_to have_content(event_schedule3.event.title)
+ end
+
+ scenario 'only shows 3 events happening now because of pagination' do
+ event_schedule1 = create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule2 = create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule3 = create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+ event_schedule4 = create(:event_schedule, event: scheduled_event4, schedule: selected_schedule, start_time: current_time.strftime('%a, %d %b %Y %H:%M:%S'))
+
+ visit conference_path(conference2.short_title)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule1.event.title)
+ expect(happening_now).to have_content(event_schedule2.event.title)
+ expect(happening_now).to have_content(event_schedule3.event.title)
+ expect(happening_now).not_to have_content(event_schedule4.event.title)
+
+ visit conference_path(conference2.short_title, page: 2)
+ happening_now = page.find('#happening-now')
+ expect(happening_now).to have_content(event_schedule4.event.title)
+ end
+ end
+
+ context 'clarify registration status' do
+ let!(:splashpage) { create(:splashpage, conference: conference, public: true)}
+ let!(:ticket_1) { create(:ticket) }
+ let!(:free_ticket) { create(:ticket, price_cents: 0) }
+
+ scenario 'user signed in with no tickets', feature: true do
+ sign_in participant
+ visit conference_path(conference.short_title)
+ expect(page).to have_content 'You have not purchased any tickets for this conference yet.'
+ end
+
+ scenario 'user signed in with 1 free ticket', feature: true do
+ sign_in participant
+ create(:ticket_purchase, conference: conference, user: participant, ticket: free_ticket, quantity: 1)
+ visit conference_path(conference.short_title)
+ expect(page).not_to have_content 'You have not purchased any tickets for this conference yet.'
+ end
+
+ scenario 'user signed in with 1 paid ticket', feature: true do
+ sign_in participant
+ create(:ticket_purchase, conference: conference, user: participant, ticket: ticket_1, quantity: 1)
+ visit conference_path(conference.short_title)
+ expect(page).not_to have_content 'You have not purchased any tickets for this conference yet.'
+ end
+
+ scenario 'user signed in with multiple ticket', feature: true do
+ sign_in participant
+ create(:ticket_purchase, conference: conference, user: participant, ticket: ticket_1, quantity: 1)
+ create(:ticket_purchase, conference: conference, user: participant, ticket: free_ticket, quantity: 1)
+ visit conference_path(conference.short_title)
+ expect(page).not_to have_content 'You have not purchased any tickets for this conference yet.'
+ end
+
+ scenario 'user not signed in', feature: true do
+ visit conference_path(conference.short_title)
+ expect(page).not_to have_content 'You have not purchased any tickets for this conference yet.'
+ end
+ end
end
diff --git a/spec/helpers/conference_helper_spec.rb b/spec/helpers/conference_helper_spec.rb
index 4ea48496..5065411f 100644
--- a/spec/helpers/conference_helper_spec.rb
+++ b/spec/helpers/conference_helper_spec.rb
@@ -99,4 +99,39 @@ describe ConferenceHelper, type: :helper do
expect(conference_color(conference2)).to eq('#0B3559')
end
end
+
+ describe '#get_happening_next_events_schedules' do
+ let!(:conference2) { create(:full_conference, start_date: 1.day.ago, end_date: 7.days.from_now, start_hour: 0, end_hour: 24) }
+ let!(:program) { conference2.program }
+ let!(:selected_schedule) { create(:schedule, program: program) }
+ let!(:scheduled_event1) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed', abstract: '`markdown`')
+ end
+ let!(:current_time) { Time.now.in_time_zone(conference2.timezone) }
+ let!(:event_schedule1) { create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S')) }
+ let!(:scheduled_event2) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule2) { create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (current_time + 1.hour).strftime('%a, %d %b %Y %H:%M:%S')) }
+ let!(:scheduled_event3) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule3) { create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: (current_time - 1.hour).strftime('%a, %d %b %Y %H:%M:%S')) }
+ let!(:scheduled_event4) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule4) { create(:event_schedule, event: scheduled_event4, schedule: selected_schedule, start_time: (current_time + 2.hours).strftime('%a, %d %b %Y %H:%M:%S')) }
+
+ it 'returns all the events happening at the earliest time in the future but not later or in the past' do
+ events_schedules = get_happening_next_events_schedules(conference2)
+ expect(events_schedules).to include(event_schedule1)
+ expect(events_schedules).to include(event_schedule2)
+ expect(events_schedules).to_not include(event_schedule3)
+ expect(events_schedules).to_not include(event_schedule4)
+ end
+ end
end
diff --git a/spec/models/event_schedule_spec.rb b/spec/models/event_schedule_spec.rb
index b160cf15..26cb5dff 100644
--- a/spec/models/event_schedule_spec.rb
+++ b/spec/models/event_schedule_spec.rb
@@ -165,4 +165,31 @@ describe EventSchedule do
end
end
end
+
+ describe 'happening_later' do
+ let!(:conference2) { create(:full_conference, start_date: 1.day.ago, end_date: 7.days.from_now, start_hour: 0, end_hour: 24) }
+ let!(:program) { conference2.program }
+ let!(:selected_schedule) { create(:schedule, program: program) }
+ let!(:scheduled_event1) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed', abstract: '`markdown`')
+ end
+ let!(:event_schedule1) { create(:event_schedule, event: scheduled_event1, schedule: selected_schedule, start_time: (Time.now.in_time_zone(conference2.timezone) + 1.hour).strftime('%a, %d %b %Y %H:%M:%S')) }
+ let!(:scheduled_event2) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule2) { create(:event_schedule, event: scheduled_event2, schedule: selected_schedule, start_time: (Time.now.in_time_zone(conference2.timezone) + 2.hour).strftime('%a, %d %b %Y %H:%M:%S')) }
+ let!(:scheduled_event3) do
+ program.update_attributes!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule3) { create(:event_schedule, event: scheduled_event3, schedule: selected_schedule, start_time: (Time.now.in_time_zone(conference2.timezone) - 1.hour).strftime('%a, %d %b %Y %H:%M:%S')) }
+
+ it 'returns true if the event is happening in the future' do
+ expect(event_schedule1.happening_later?).to be true
+ expect(event_schedule2.happening_later?).to be true
+ expect(event_schedule3.happening_later?).to be false
+ end
+ end
end
diff --git a/spec/services/full_calendar_formatter_spec.rb b/spec/services/full_calendar_formatter_spec.rb
new file mode 100644
index 00000000..63af2d08
--- /dev/null
+++ b/spec/services/full_calendar_formatter_spec.rb
@@ -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