Merge pull request #1094 from Ana06/schedule-versioning

Schedule versioning
This commit is contained in:
Christian Bruckmayer 2016-08-12 09:56:31 +02:00 committed by GitHub
commit 3b1a474ee0
85 changed files with 971 additions and 10343 deletions

View file

@ -13,6 +13,8 @@
//= require jquery
//= require jquery_ujs
//= require jquery.mobile.custom.min
//= require jquery.ui.draggable
//= require jquery.ui.droppable
//= require waypoints/jquery.waypoints
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
@ -36,6 +38,7 @@
//= require osem-datatables
//= require osem-tickets
//= require bootstrap-switch
//= require osem-schedule
//= require osem-switch
//= require osem-bootstrap
//= require osem-commercials

View file

@ -0,0 +1,118 @@
var url; // Should be initialize in Schedule.initialize
var schedule_id; // Should be initialize in Schedule.initialize
function showError(error){
// Delete other error messages before showing the new one
$('.unobtrusive-flash-container').empty();
UnobtrusiveFlash.showFlashMessage(error, {type: 'error'});
}
var Schedule = {
initialize: function(url_param, schedule_id_param) {
url = url_param;
schedule_id = schedule_id_param;
},
remove: function(element) {
var e = $("#" + element);
var event_schedule_id = e.attr("event_schedule_id");
if(event_schedule_id != null){
var my_url = url + '/' + event_schedule_id;
var success_callback = function(data) {
console.log(data);
e.attr("event_schedule_id", null);
e.appendTo($(".unscheduled-events"));
e.find(".schedule-event-delete-button").hide();
}
var error_callback = function(data) {
console.log(data);
showError($.parseJSON(data.responseText).errors);
}
$.ajax({
url: my_url,
type: 'DELETE',
success: success_callback,
error: error_callback,
dataType : 'json'
});
}
else{
showError("The event couldn't be unscheduled");
}
},
add: function (previous_parent, new_parent, event) {
event.appendTo(new_parent);
var event_schedule_id = event.attr("event_schedule_id");
var my_url = url;
var type = 'POST';
var params = { event_schedule: {
room_id: new_parent.attr("room_id"),
start_time: (new_parent.attr("date") + ' ' + new_parent.attr("hour"))
}};
if(event_schedule_id != null){
type = 'PUT';
my_url += ('/' + event_schedule_id);
}
else{
params['event_schedule']['event_id'] = event.attr("event_id");
params['event_schedule']['schedule_id'] = schedule_id;
}
var success_callback = function(data) {
console.log(data);
event.attr("event_schedule_id", data.event_schedule_id);
event.find(".schedule-event-delete-button").show();
}
var error_callback = function(data) {
console.log(data);
showError($.parseJSON(data.responseText).errors);
event.appendTo(previous_parent);
}
$.ajax({
url: my_url,
type: type,
data: params,
success: success_callback,
error: error_callback,
dataType : 'json'
});
}
};
$(document).ready( function() {
// hide the remove button for unscheduled events
$('.unscheduled-events .schedule-event-delete-button').hide();
// set events as draggable
$('.schedule-event').draggable({
snap: '.schedule-room-slot',
revertDuration: 200,
revert: function (event, ui) {
console.log(event.attr);
return !event;
},
stop: function(event, ui) {
this._originalPosition = this._originalPosition || ui.originalPosition;
ui.helper.animate( this._originalPosition );
},
opacity: 0.7,
snapMode: "inner",
zIndex: 2
});
// set room cells as droppable
$('.schedule-room-slot').droppable({
accept: '.schedule-event',
tolerance: "pointer",
drop: function(event, ui) {
$(ui.draggable).css("left", 0);
$(ui.draggable).css("top", 0);
$(this).css("background-color", "#ffffff");
Schedule.add($(ui.draggable).parent(), $(this), $(ui.draggable));
},
over: function(event, ui) {
$(this).css("background-color", "#009ED8");
},
out: function(event, ui) {
$(this).css("background-color", "#ffffff");
}
});
});

View file

@ -11,4 +11,25 @@ $(function () {
dataType: 'script'
});
});
$("[class='switch-checkbox-schedule']").bootstrapSwitch();
$('input[class="switch-checkbox-schedule"]').on('switchChange.bootstrapSwitch', function(event, state) {
var url = $(this).attr('url');
var method = $(this).attr('method');
if(state){
url += $(this).attr('value');
}
var callback = function(data) {
showError($.parseJSON(data.responseText).errors);
}
$.ajax({
url: url,
type: method,
error: callback,
dataType: 'json'
});
});
});

View file

@ -1,3 +1,52 @@
.room-name{
font-weight: bold;
padding:10px;
margin-top: 30px;
border: 1px solid #848484;
background-color: #E6E6E6;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
height: 40px;
line-height: 25px;
overflow: hidden;
}
.schedule-room-slot{
padding: 2px 5px;
border: 1px solid #848484;
height: 58px;
line-height: 20px;
font-size: 13px;
}
.schedule-event{
padding: 7px;
border: 2px solid #151515;
position:relative;
z-index:1;
cursor:move;
}
.schedule-event-text{
display: -webkit-box;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
line-height: 23px;
overflow: hidden;
}
.schedule-event-delete-button {
font-weight:bold;
cursor: pointer;
padding: 0px 3px;
border: 1px solid grey;
margin-right: 5px;
}
#schedule td.event {
background-image: linear-gradient(bottom, rgb(247,250,242) 24%, rgb(194,232,190) 97%, rgb(194,232,190) 100%);
background-image: -o-linear-gradient(bottom, rgb(247,250,242) 24%, rgb(194,232,190) 97%, rgb(194,232,190) 100%);

View file

@ -0,0 +1,35 @@
module Admin
class EventSchedulesController < Admin::BaseController
load_and_authorize_resource :event_schedule
def create
if @event_schedule.save
render json: { event_schedule_id: @event_schedule.id }
else
render json: { errors: "The event couldn't be scheduled. #{@event_schedule.errors.full_messages.join('. ')}" }, status: 422
end
end
def update
if @event_schedule.update(event_schedule_params)
render json: { event_schedule_id: @event_schedule.id }
else
render json: { errors: "The event couldn't be scheduled. #{@event_schedule.errors.full_messages.join('. ')}" }, status: 422
end
end
def destroy
if @event_schedule.destroy
render json: {}
else
render json: { errors: "The event couldn't be unscheduled. #{@event_schedule.errors.full_messages.join('. ')}" }, status: 422
end
end
private
def event_schedule_params
params.require(:event_schedule).permit(:schedule_id, :event_id, :room_id, :start_time)
end
end
end

View file

@ -191,7 +191,7 @@ module Admin
# Set also in proposals controller
:title, :subtitle, :event_type_id, :abstract, :description, :require_registration, :difficulty_level_id,
# Set only in admin/events controller
:track_id, :state, :language, :start_time, :is_highlight, :max_attendees,
:track_id, :state, :language, :is_highlight, :max_attendees,
# Not used anymore?
:proposal_additional_speakers, :user, :users_attributes)
end

View file

@ -15,18 +15,28 @@ module Admin
if @program.update_attributes(program_params)
ConferenceScheduleUpdateMailJob.perform_later(@conference) if send_mail_on_schedule_public
redirect_to admin_conference_program_path(@conference.short_title),
notice: 'The program was successfully updated.'
respond_to do |format|
format.html do
redirect_to admin_conference_program_path(@conference.short_title),
notice: 'The program was successfully updated.'
end
format.js { render json: {} }
end
else
flash[:error] = "Updating program failed. #{@program.errors.to_a.join('. ')}."
render :new
respond_to do |format|
format.html do
flash[:error] = "Updating program failed. #{@program.errors.to_a.join('. ')}."
render :new
end
format.js { render json: { errors: "The selected schedule couldn't been updated #{@program.errors.to_a.join('. ')}" }, status: 422 }
end
end
end
private
def program_params
params.require(:program).permit(:rating, :schedule_public, :schedule_fluid, :languages, :blind_voting, :voting_start_date, :voting_end_date)
params.require(:program).permit(:rating, :schedule_public, :schedule_fluid, :languages, :blind_voting, :voting_start_date, :voting_end_date, :selected_schedule_id)
end
end
end

View file

@ -4,72 +4,38 @@ module Admin
# the schedule of a conference, which should not be accessed in the first place
load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :program, through: :conference, singleton: true
load_and_authorize_resource :schedule, through: :program
load_resource :event_schedules, through: :schedule
load_resource :selected_schedule, through: :program, singleton: true
load_resource :venue, through: :conference, singleton: true
skip_before_filter :verify_authenticity_token, only: [:update]
layout 'schedule'
def index; end
def create
if @schedule.save
redirect_to admin_conference_schedule_path(@conference.short_title, @schedule.id),
notice: 'Schedule was successfully created.'
else
redirect_to admin_conference_schedules_path(conference_id: @conference.short_title),
error: "Could not create schedule. #{@schedule.errors.full_messages.join('. ')}."
end
end
def show
authorize! :update, @program.events.new
if @conference.nil?
redirect_to admin_conference_index_path
return
end
@event_schedules = @schedule.event_schedules
@unscheduled_events = @program.events.confirmed - @schedule.events
@dates = @conference.start_date..@conference.end_date
if @venue && @venue.rooms.any?
@rooms = @venue.rooms
@rooms = (@venue && @venue.rooms.any?) ? @venue.rooms : [Room.new(name: 'No Rooms!', size: 0)]
end
def destroy
if @schedule.destroy
redirect_to admin_conference_schedules_path(conference_id: @conference.short_title),
notice: 'Schedule successfully deleted.'
else
@rooms = [ Room.new(name: 'No Rooms!', size: 0) ]
redirect_to admin_conference_schedules_path(conference_id: @conference.short_title),
error: "Schedule couldn't be deleted. #{@schedule.errors.full_messages.join('. ')}."
end
end
def update
authorize! :update, @program.events.new
event = Event.where(guid: params[:event]).first
error_message = nil
if event.nil?
error_message = "Could not find event GUID: #{params[:event]}"
end
if params[:date] == 'none'
event.start_time = nil
event.room = nil
event.save!
render json: { 'status' => 'ok' }
return
end
room = Room.where(guid: room_params).first
if room.nil?
error_message = "Could not find room GUID: #{params[:room]}"
end
unless error_message.nil?
render json: { 'status' => 'error', 'message' => error_message }, status: 500
return
end
event.room = room
time = "#{params[:date]} #{params[:time]}"
Rails.logger.debug("Loading #{time}")
# FIXME: Same here as in events_controller.rb. Event timezone should be applied
# only on output
# zone = ActiveSupport::TimeZone::new(@conference.timezone)
# start_time = DateTime.strptime(time + zone.formatted_offset, "%Y-%m-%d %k:%M %Z")
start_time = DateTime.strptime(time, '%Y-%m-%d %k:%M')
event.start_time = start_time
event.save!
render json: { 'status' => 'ok' }
end
private
def event_params
params.require(:event).permit(:guid)
end
def room_params
params.require(:room)
end
end
end

View file

@ -5,7 +5,7 @@ module Api
respond_to :json
def index
events = Event.includes(:track, :room, :event_type, event_users: :user)
events = Event.includes(:track, :event_type, event_users: :user)
if @conference
events = events.where(program: @conference.program)

View file

@ -11,38 +11,6 @@ class ConferenceController < ApplicationController
def show; end
def schedule
@rooms = @conference.venue.rooms if @conference.venue
unless @conference.program.events.scheduled.any?
redirect_to events_conference_path(@conference.short_title)
end
@events = @conference.program.events
@events_xml = @events.scheduled.order(start_time: :asc).group_by{ |event| event.start_time.to_date }
@dates = @conference.start_date..@conference.end_date
@step_minutes = EventType::LENGTH_STEP.minutes
@conf_start = 9
conf_end = 20
@conf_period = conf_end - @conf_start
# 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
return unless @current_day
# the schedule takes you to the current time if it is beetween the start and the end time.
@hour_column = @conference.hours_from_start_time(@conf_start, conf_end)
end
def events
@dates = @conference.start_date..@conference.end_date
@scheduled_events = @conference.program.events.scheduled
@unscheduled_events = @conference.program.events.unscheduled
day = @conference.current_conference_day
@tag = day.strftime('%Y-%m-%d') if day
end
private
def respond_to_options

View file

@ -15,6 +15,7 @@ class ProposalController < ApplicationController
def show
# FIXME: We should show more than the first speaker
@speaker = @event.speakers.first || @event.submitter
@event_schedule = @event.event_schedules.find_by(schedule_id: @program.selected_schedule_id)
end
def new

View file

@ -0,0 +1,52 @@
class SchedulesController < ApplicationController
protect_from_forgery with: :null_session
before_action :respond_to_options
load_and_authorize_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true, except: :index
def show
@rooms = @conference.venue.rooms if @conference.venue
schedules = @program.selected_event_schedules
unless schedules
redirect_to events_conference_schedule_path(@conference.short_title)
end
@events_xml = schedules.map(&:event).group_by{ |event| event.time.to_date } if schedules
@dates = @conference.start_date..@conference.end_date
@step_minutes = EventType::LENGTH_STEP.minutes
@conf_start = 9
conf_end = 20
@conf_period = conf_end - @conf_start
# 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
return unless @current_day
# the schedule takes you to the current time if it is beetween the start and the end time.
@hour_column = @conference.hours_from_start_time(@conf_start, conf_end)
end
def events
@dates = @conference.start_date..@conference.end_date
@events_schedules = @program.selected_event_schedules
@events_schedules = [] unless @events_schedules
@unscheduled_events = if @program.selected_schedule
@program.events.confirmed - @program.selected_schedule.events
else
@program.events.confirmed
end
day = @conference.current_conference_day
@tag = day.strftime('%Y-%m-%d') if day
end
private
def respond_to_options
respond_to do |format|
format.html { head :ok }
end if request.options?
end
end

View file

@ -366,4 +366,8 @@ module ApplicationHelper
end
item_class
end
def selected_scheduled?(schedule)
(schedule == @selected_schedule) ? 'Yes' : 'No'
end
end

View file

@ -143,6 +143,8 @@ class Ability
can :manage, Vposition, conference_id: conf_ids_for_organizer
can :manage, Vday, conference_id: conf_ids_for_organizer
can :manage, Program, conference_id: conf_ids_for_organizer
can :manage, Schedule, program: { conference_id: conf_ids_for_organizer }
can :manage, EventSchedule, schedule: { program: { conference_id: conf_ids_for_organizer } }
can :manage, Cfp, program: { conference_id: conf_ids_for_organizer}
can :manage, Event, program: { conference_id: conf_ids_for_organizer}
can :manage, EventType, program: { conference_id: conf_ids_for_organizer}

View file

@ -13,7 +13,7 @@ class EmailSettings < ActiveRecord::Base
'conference_splash_link' => Rails.application.routes.url_helpers.conference_url(
conference.short_title, host: (ENV['OSEM_HOSTNAME'] || 'localhost:3000')),
'schedule_link' => Rails.application.routes.url_helpers.schedule_conference_url(
'schedule_link' => Rails.application.routes.url_helpers.conference_schedule_url(
conference.short_title, host: (ENV['OSEM_HOSTNAME'] || 'localhost:3000'))
}

View file

@ -16,9 +16,9 @@ class Event < ActiveRecord::Base
has_many :events_registrations
has_many :registrations, through: :events_registrations
has_many :event_schedules, dependent: :destroy
belongs_to :track
belongs_to :room
belongs_to :difficulty_level
belongs_to :program
@ -71,11 +71,11 @@ class Event < ActiveRecord::Base
end
##
# Checkes if the event has a start_time and a room
# Checkes if the event has a start_time and a room for the selected schedule if there is any
# ====Returns
# * +true+ or +false+
def scheduled?
room.present? && start_time.present?
event_schedules.find_by(schedule_id: program.selected_schedule_id).present?
end
def registration_possible?
@ -129,16 +129,6 @@ class Event < ActiveRecord::Base
end
end
def as_json(options)
json = super(options)
json[:room_guid] = room.try(:guid)
json[:track_color] = track.try(:color) || '#FFFFFF'
json[:length] = event_type.try(:length) || EventType::LENGTH_STEP
json
end
def transition_possible?(transition)
self.class.state_machine.events_for(current_state).include?(transition)
end
@ -242,17 +232,19 @@ class Event < ActiveRecord::Base
end
##
# Returns end of the event
# Returns the room in which the event is scheduled
#
def end_time
self.start_time + self.event_type.length.minutes
def room
# We use try(:selected_schedule_id) because this function is used for
# validations so program could not be present there
event_schedules.find_by(schedule_id: program.try(:selected_schedule_id)).try(:room)
end
##
# Returns events that are scheduled in the same room and start_time as event
# Returns the start time at which this event is scheduled
#
def intersecting_events
room.events.where(start_time: start_time).where.not(id: id)
def time
event_schedules.find_by(schedule_id: program.selected_schedule_id).try(:start_time)
end
private

View file

@ -0,0 +1,30 @@
class EventSchedule < ActiveRecord::Base
belongs_to :schedule
belongs_to :event
belongs_to :room
validates :schedule, presence: true
validates :event, presence: true
validates :room, presence: true
validates :start_time, presence: true
scope :confirmed, -> { joins(:event).where('state = ?', 'confirmed') }
scope :canceled, -> { joins(:event).where('state = ?', 'canceled') }
scope :withdrawn, -> { joins(:event).where('state = ?', 'withdrawn') }
delegate :guid, to: :room, prefix: true
##
# Returns end of the event
#
def end_time
start_time + event.event_type.length.minutes
end
##
# Returns events that are scheduled in the same room and start_time as event
#
def intersecting_events
room.event_schedules.where(start_time: start_time, schedule: schedule).where.not(id: id)
end
end

View file

@ -7,6 +7,8 @@ class Program < ActiveRecord::Base
has_many :event_types, dependent: :destroy
has_many :tracks, dependent: :destroy
has_many :difficulty_levels, dependent: :destroy
has_many :schedules, dependent: :destroy
belongs_to :selected_schedule, class_name: 'Schedule'
has_many :events, dependent: :destroy do
def require_registration
where(require_registration: true, state: :confirmed)
@ -26,12 +28,8 @@ class Program < ActiveRecord::Base
where(state: :confirmed)
end
def scheduled
where.not(start_time: nil).where.not(room: nil).order(start_time: :asc)
end
def unscheduled
confirmed.where('start_time IS NULL OR room_id IS NULL')
def scheduled(schedule_id)
joins(:event_schedules).where('event_schedules.schedule_id = ?', schedule_id)
end
def highlights
@ -59,6 +57,11 @@ class Program < ActiveRecord::Base
before_create :create_difficulty_levels
validate :check_languages_format
# Returns all event_schedules for the selected schedule ordered by start_time
def selected_event_schedules
selected_schedule.event_schedules.order(start_time: :asc) if selected_schedule
end
##
# Checks if blind_voting is enabled and if voting period is over
# ====Returns

View file

@ -1,6 +1,6 @@
class Room < ActiveRecord::Base
belongs_to :venue
has_many :events, dependent: :nullify
has_many :event_schedules, dependent: :nullify
before_create :generate_guid

5
app/models/schedule.rb Normal file
View file

@ -0,0 +1,5 @@
class Schedule < ActiveRecord::Base
belongs_to :program
has_many :event_schedules, dependent: :destroy
has_many :events, through: :event_schedules
end

View file

@ -22,23 +22,23 @@ class ConferenceSerializer < ActiveModel::Serializer
def rooms
if object.venue
object.venue.rooms.includes(:events).map do |room| { id: room.id,
size: room.size,
events: room.events.map do |event| { guid: event.title,
title: event.title,
subtitle: event.subtitle,
abstract: event.abstract,
description: event.description,
is_highlight: event.is_highlight,
require_registration: event.require_registration,
start_time: event.start_time,
event_type_id: event.event_type.id,
difficulty_level_id: event.difficulty_level_id,
track_id: event.track_id,
speaker_names: event.speaker_names
object.venue.rooms.map do |room| { id: room.id,
size: room.size,
events: room.event_schedules.map do |event_schedule| { guid: event_schedule.event.title,
title: event_schedule.event.title,
subtitle: event_schedule.event.subtitle,
abstract: event_schedule.event.abstract,
description: event_schedule.event.description,
is_highlight: event_schedule.event.is_highlight,
require_registration: event_schedule.event.require_registration,
start_time: event_schedule.start_time,
event_type_id: event_schedule.event.event_type.id,
difficulty_level_id: event_schedule.event.difficulty_level_id,
track_id: event_schedule.event.track_id,
speaker_names: event_schedule.event.speaker_names
}
end
}
end
}
end
else
[]

View file

@ -0,0 +1,14 @@
class EventScheduleSerializer < ActiveModel::Serializer
include ActionView::Helpers::TextHelper
attributes :date, :room
def date
t = object.start_time
t.blank? ? '' : %( #{I18n.l t, format: :short}#{t.formatted_offset(false)} )
end
def room
object.room.guid
end
end

View file

@ -1,11 +1,11 @@
class EventSerializer < ActiveModel::Serializer
include ActionView::Helpers::TextHelper
attributes :guid, :title, :length, :date, :language, :abstract, :speaker_ids, :type, :room, :track
attributes :guid, :title, :length, :scheduled_date, :language, :abstract, :speaker_ids, :type, :room, :track
def date
t = object.start_time
t.blank? ? '' : %{ #{I18n.l t, format: :short}#{t.formatted_offset(false)} }
def scheduled_date
t = object.time
t.blank? ? '' : %( #{I18n.l t, format: :short}#{t.formatted_offset(false)} )
end
def speaker_ids
@ -34,7 +34,6 @@ class EventSerializer < ActiveModel::Serializer
end
end
# FIXME: duplicated logic from Event#as_json
def length
object.event_type.try(:length) || EventType::LENGTH_STEP
end

View file

@ -4,7 +4,7 @@
%h1 Commercials
%p.text-muted
Conference commercials will be displayed on the events in the
= link_to "schedule,", schedule_conference_path(@conference.short_title)
= link_to "schedule,", conference_schedule_path(@conference.short_title)
if the event speaker didn't add an event commercial.
- if can? :create, @conference.commercials.new
.row

View file

@ -112,12 +112,12 @@
%b Room
%td
= @event.room.name
- unless @event.start_time.nil?
- unless @event.time.nil?
%tr
%td
%b Scheduled time
%td
= @event.start_time
= @event.time
%tr
%td
%b Submitter

View file

@ -1,23 +1,21 @@
.schedule-time-column
.schedule-time-column-header
= Time
- (9..18).each do |hour|
.schedule-time-slot
= "#{hour}:00"
.schedule-time-slot
= "#{hour}:15"
.schedule-time-slot
= "#{hour}:30"
.schedule-time-slot
= "#{hour}:45"
- @rooms.each do |room|
.schedule-room-column{:id => "schedule-room-#{room.guid}", "room-guid" => room.guid}
.schedule-room-column-header
= room.name
= "(#{room.size} people)"
- (9..18).each do |hour|
.schedule-room-slot{:id => "schedule-room-#{room.guid}-#{hour}-0", "room-guid" => room.guid, "hour" => "#{hour}:00"}
.schedule-room-slot{:id => "schedule-room-#{room.guid}-#{hour}-15", "room-guid" => room.guid, "hour" => "#{hour}:15"}
.schedule-room-slot{:id => "schedule-room-#{room.guid}-#{hour}-30", "room-guid" => room.guid, "hour" => "#{hour}:30"}
.schedule-room-slot{:id => "schedule-room-#{room.guid}-#{hour}-45", "room-guid" => room.guid, "hour" => "#{hour}:45"}
- date_event_schedules = @event_schedules.select{ |e| e.start_time.to_date.eql? date }
.row
- @rooms.each do |room|
.col-md-2.col-xs-6
.room-name
- room_date_event_schedules = date_event_schedules.select{ |e| e.room == room }
= room.name
- (9*4..18*4).each do |slot|
- hour = slot / 4
- minutes = (15 * (slot % 4) == 0) ? '00' : 15 * (slot % 4)
- time = "#{hour}:#{minutes}"
.schedule-room-slot{ id: "schedule-room-#{room.guid}-#{hour}-#{minutes}", |
room_id: room.id, |
hour: time, |
date: date}
.div
= time
- event_schedules = room_date_event_schedules.select{ |e| (e.start_time.hour.to_s + e.start_time.strftime(':%M')).eql? time }
- if event_schedules.any?
- event_schedule = event_schedules.first
= render partial: 'event', locals: { event: event_schedule.event, event_schedule_id: event_schedule.id}

View file

@ -0,0 +1,14 @@
- cells_length = event.event_type.length / EventType::LENGTH_STEP
/ this height fits the room cells
- height = (cells_length * 58) - 23
/ subtracting the padding before calculate the number of lines
- lines = (height - 7) / 23
- color = event.track.try(:color).present? ? event.track.try(:color) : 'FFFFFF'
.schedule-event{ style: "height: #{height}px; background-color: #{color}; color: #{contrast_color(color)}", |
id: "event-#{event.id}", |
event_id: event.id, |
length: cells_length, |
event_schedule_id: event_schedule_id }
.schedule-event-text{ style: "-webkit-line-clamp: #{lines}; height: #{lines * 23}px;"}
%span.schedule-event-delete-button{ onclick: "Schedule.remove(\'event-#{event.id}\');" } X
= event.title

View file

@ -0,0 +1,32 @@
.row
.col-md-12
.page-header
%h1 Schedules
%p.text-muted
The schedules for your conference
.row
.col-md-12
%table.table.table-hover#event_types
%thead
%th Schedule
%th Selected
%th Actions
%tbody
- @schedules.each do |schedule|
%tr
%td
Schedule
= schedule.id
%td
= selected_scheduled?(schedule)
%td
.btn-group{role: "group"}
= link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule.id),
method: :get, class: 'btn btn-primary'
= link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule.id),
method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" }
.row
.col-md-12.text-right
= link_to 'Add Schedule', admin_conference_schedules_path(@conference.short_title),
method: :post, class: 'btn btn-primary'

View file

@ -1,48 +1,38 @@
.schedule-content
%h2
= "Schedule for #{@conference.title}"
#unscheduled.unscheduled
.unscheduled-header
Unscheduled Events
#schedule.schedule
.schedule-dates-header
%ul#date-tabs
- @dates.each do |date|
%li.date-selector
= link_to "#{date}", "#", id: "#{date}-selector", onclick: "Schedule.changeDay('#{date}')"
.schedule-rooms-container
= render 'day_tab'
.unobtrusive-flash-container
.row
.col-md-12
.page-header
%h1 Schedule
%p.text-muted
Create the schedules for the conference
.row
.col-md-2
Selected schedule
= check_box_tag @conference.short_title, @schedule.id, (@schedule.id == @selected_schedule.try(:id)),
method: :patch, url: (admin_conference_program_path(@conference.short_title) + '?[program][selected_schedule_id]='),
class: 'switch-checkbox-schedule', data: { size: 'small',
off_color: 'warning',
on_text: 'Yes',
off_text: 'No' }
.h4
Unscheduled events
.unscheduled-events
- @unscheduled_events.each do |e|
= render partial: 'event', locals: { event: e, event_schedule_id: nil }
.col-md-10
%ul.nav.nav-tabs
- @dates.each do |date|
%li{ class: "#{ (@dates.first == date) ? 'active' : '' }"}
%a{ href: "##{date}" }
= date
.tab-content
- @dates.each do |date|
.tab-pane{ class: "#{ (@dates.first == date) ? 'active' : '' }", id: "#{date}" }
= render partial: 'day_tab', locals: { date: date }
:javascript
$(document).ready( function() {
//Schedule.loadDaysAndTracks("#{@conference.short_title}");
var conference = "#{@conference.short_title}"
Schedule.loadEvents("#{@conference.short_title}", "#{@dates.first}");
$('.schedule-room-slot').droppable({
accept: '.schedule-event',
tolerance: "pointer",
drop: function(event, ui) {
$(ui.draggable).appendTo(this);
var myId = $(ui.draggable).attr("guid");
var myRoom = $(this).attr("room-guid")
var myDate = $(this).attr("date");
var myTime = $(this).attr("hour");
var length = $(ui.draggable).attr("length");
$(ui.draggable).css("left", 0);
$(ui.draggable).css("top", 0);
$(this).css("background-color", "#ffffff");
// 15 minute slots on the schedule are 30px,
// thus the magic multiplier here
$(ui.draggable).height(length * 2 - 7);
$(ui.draggable).width($(this).width() - 10);
Schedule.save(conference, myId, myRoom, myDate, myTime);
},
over: function(event, ui) {
$(this).css("background-color", "#009ED8");
},
out: function(event, ui) {
$(this).css("background-color", "#ffffff");
}
});
});
Schedule.initialize("#{admin_conference_event_schedules_path(@conference)}", "#{@schedule.id}");
});

View file

@ -27,5 +27,4 @@
%td
= event.state
%td
= event.start_time
= event.time

View file

@ -22,7 +22,7 @@
- if conference.splashpage && conference.splashpage.public
= link_to "View Conference", conference_path(conference.short_title), :class =>"btn btn-default"
- if conference.program and conference.program.schedule_public
= link_to "Schedule", schedule_conference_path(conference.short_title), :class =>"btn btn-default"
= link_to "Schedule", conference_schedule_path(conference.short_title), :class =>"btn btn-default"
- if conference.registration_open?
- if conference.user_registered?(current_user)
= link_to "My Registration", conference_conference_registration_path(conference.short_title), :class =>"btn btn-default"

View file

@ -22,7 +22,7 @@
.row
.col-md-12
%p.cta-button.text-center
= link_to(schedule_conference_path(@conference.short_title), class: 'btn btn-default btn-lg') do
= link_to(conference_schedule_path(@conference.short_title), class: 'btn btn-default btn-lg') do
Full Schedule

View file

@ -11,7 +11,7 @@
.text-muted
= registered_text(event)
- if event.scheduled?
(Scheduled on: #{event.start_time.to_date})
(Scheduled on: #{event.time.to_date})
%br

View file

@ -81,9 +81,9 @@
- if can? :update, @conference.program.difficulty_levels.build, conference_id: @conference.id
%li{:class=> active_nav_li(admin_conference_program_difficulty_levels_path(@conference.short_title))}
= link_to 'Difficulty Levels', admin_conference_program_difficulty_levels_path(@conference.short_title)
- if can? :update, @conference.program.events.build
%li{class: active_nav_li(admin_conference_schedule_path(@conference.short_title))}
= link_to 'Schedule', admin_conference_schedule_path(@conference.short_title), target: '_blank'
- if can? :update, @conference.program.schedules.build
%li{class: active_nav_li(admin_conference_schedules_path(@conference.short_title))}
= link_to 'Schedules', admin_conference_schedules_path(@conference.short_title)
- if can? :update, Registration.new(conference_id: @conference.id)
%li{:class=> active_nav_li(admin_conference_registrations_path(@conference.short_title))}

View file

@ -1,20 +0,0 @@
!!!
%html
%head
%meta{:charset => "utf-8"}
%meta{:name => "viewport", :content => "width=device-width, initial-scale=1, maximum-scale=1"}
%title= content_for?(:title) ? yield(:title) : (ENV['OSEM_NAME'] || 'OSEM')
%meta{:content => "", :name => "description"}
%meta{:content => "", :name => "author"}
= stylesheet_link_tag "/stylesheets/schedule/jquery-ui-1.9.2.custom.min"
= stylesheet_link_tag "/stylesheets/schedule/schedule"
= javascript_include_tag "/javascripts/schedule/jquery-1.8.3"
= javascript_include_tag "/javascripts/schedule/jquery-ui-1.9.2.custom.min"
= javascript_include_tag "/javascripts/schedule/date.format"
= javascript_include_tag "/javascripts/schedule/schedule"
= csrf_meta_tags
= yield(:head)
%body
.content
= yield

View file

@ -13,11 +13,11 @@
- if can? :edit, @event
= link_to "Edit", edit_conference_program_proposal_path(@conference.short_title, @event), :class => "btn btn-mini btn-primary"
- if can? :schedule, @conference
= link_to "Schedule", schedule_conference_path(@conference.short_title), :class =>"btn btn-success"
= link_to "Schedule", conference_schedule_path(@conference.short_title), :class =>"btn btn-success"
- if @event.state == 'canceled' || @event.state == 'withdrawn'
%span.label.label-danger CANCELED
- elsif @event.state == 'confirmed' && (!@event.intersecting_events.canceled.empty? || !@event.intersecting_events.withdrawn.empty?)
- elsif @event.state == 'confirmed' && @event_schedule.present? && (!@event_schedule.intersecting_events.canceled.empty? || !@event_schedule.intersecting_events.withdrawn.empty?)
%span.label.label-info REPLACEMENT
.row
@ -40,15 +40,16 @@
.col-md-9
.row
.col-md-12
.lead
- if @event.state == 'confirmed' && !@event.intersecting_events.withdrawn.empty?
= "Please note that this talk replaces"
= link_to @event.intersecting_events.withdrawn.first.title,
conference_program_proposal_path(@conference.short_title, @event.intersecting_events.withdrawn.first.id)
- elsif @event.state == 'confirmed' && !@event.intersecting_events.canceled.empty?
= "Please note that this talk replaces"
= link_to @event.intersecting_events.canceled.first.title,
conference_program_proposal_path(@conference.short_title, @event.intersecting_events.canceled.first.id)
-if @event_schedule.present?
.lead
- if @event.state == 'confirmed' && !@event_schedule.intersecting_events.withdrawn.empty?
= "Please note that this talk replaces"
= link_to @event_schedule.intersecting_events.withdrawn.first.event.title,
conference_program_proposal_path(@conference.short_title, @event_schedule.intersecting_events.withdrawn.first.event.id)
- elsif @event.state == 'confirmed' && !@event_schedule.intersecting_events.canceled.empty?
= "Please note that this talk replaces"
= link_to @event_schedule.intersecting_events.canceled.first.title,
conference_program_proposal_path(@conference.short_title, @event_schedule.intersecting_events.canceled.first.event.id)
- if @event.commercials.empty?
%h5.text-warning
@ -67,7 +68,7 @@
%dl#proposal-info
.col-md-12
%dt Date:
%dd= @event.start_time.strftime("%Y %B %e %H:%M") if @event.start_time
%dd= @event_schedule.start_time.strftime("%Y %B %e %H:%M") if @event_schedule
.col-md-12
%dt Duration:
%dd= show_time(@event.event_type.length)

View file

@ -27,22 +27,21 @@
%td.room{ style: "height: #{ td_height(@rooms) }px;" }
.room.elipsis.break-words{ style: "-webkit-line-clamp: #{ room_lines(@rooms) }; height: #{ room_height(@rooms) }px;" }
= room.name
- events = room.events{ |e| e.start_time >= start_time and e.start_time < (start_time + number_columns.hour)}
- event_schedules = room.event_schedules.select{ |e| (e.schedule_id == @conference.program.selected_schedule.id) && (e.start_time >= start_time) && (e.start_time < (start_time + number_columns.hour)) }
- (1..intervals).each do |i|
- if span > 1
- span -= 1
- else
- event = events.find{|e| e.start_time <= start_room_time and e.end_time > start_room_time}
- event_schedule = event_schedules.find{ |e| e.start_time <= start_room_time and e.end_time > start_room_time }
- if event_schedule && (event_schedule.event.state == 'canceled' || event_schedule.event.state == 'withdrawn') && !event_schedule.intersecting_events.confirmed.empty?
- replacement_event = event_schedule.intersecting_events.confirmed.first
- event_schedule = (replacement_event.start_time <= start_room_time && replacement_event.end_time > start_room_time) ? replacement_event : nil
- if event && (event.state == 'canceled' || event.state == 'withdrawn') && !event.intersecting_events.confirmed.empty?
- replacement_event = event.intersecting_events.confirmed.first
- event = (replacement_event.start_time <= start_room_time && replacement_event.end_time > start_room_time) ? replacement_event : nil
- if event
- if event_schedule
/ There is an event, calculate the span and show it
- event_span = (event.end_time.to_i - start_room_time.to_i) / 60 / EventType::LENGTH_STEP
- event_span = (event_schedule.end_time.to_i - start_room_time.to_i) / 60 / EventType::LENGTH_STEP
- span = ((event_span + i - 1 ) > intervals ? intervals + 1 - i : event_span)
= render partial: 'schedule_item', locals: {event: event, span: span, width: width}
= render partial: 'schedule_item', locals: {event: event_schedule.event, event_schedule: event_schedule, span: span, width: width}
- else
/ if span equals 1 show an empty td
%td.no-padding{ width: "#{ width }%"}

View file

@ -14,17 +14,17 @@
%p
= truncate(event.abstract, :length => 400)
= link_to 'more', conference_program_proposal_path(@conference.short_title, event.id) if event.abstract.length > 400
- if event.scheduled?
- if event_schedule.present?
%span.track
%span.fa.fa-clock-o
%span.label{ style: "background-color: grey" }
= event.start_time.strftime('%H:%M')
= event_schedule.start_time.strftime('%H:%M')
\-
= event.end_time.strftime('%H:%M')
= event_schedule.end_time.strftime('%H:%M')
%span.track
%span.fa.fa-map-marker
%span.label{ style: "background-color: grey" }
= event.room.name
= event_schedule.room.name
- if event.track
%span.track
%span.fa.fa-road
@ -35,7 +35,7 @@
$("#link-#{event.id}").click(function(e) {
var url = "#{url_for(conference_program_proposal_path(@conference.short_title, event.id))}";
if(e.ctrlKey)
window.open(url,'_blank');
else

View file

@ -7,7 +7,7 @@
- if event.state == 'canceled' || event.state == 'withdrawn'
%span.label.label-danger.schedule-label CANCELED
- elsif event.state == 'confirmed' && (!event.intersecting_events.canceled.empty? || !event.intersecting_events.withdrawn.empty?)
- elsif event.state == 'confirmed' && (!event_schedule.intersecting_events.canceled.empty? || !event_schedule.intersecting_events.withdrawn.empty?)
%span.label.label-info.schedule-label REPLACEMENT
= event.title

View file

@ -2,6 +2,6 @@
/ Nav tabs
%ul.nav.nav-tabs{ role: "tablist" }
%li{ class: "schedule #{ 'active' if active == 'schedule' }", role: "presentation" }
= link_to('Schedule', schedule_conference_path(@conference.short_title))
= link_to('Schedule', conference_schedule_path(@conference.short_title))
%li{ class: "program #{ 'active' if active == 'program' }", role: "presentation" }
= link_to('All events', events_conference_path(@conference.short_title))
= link_to('All events', events_conference_schedule_path(@conference.short_title))

View file

@ -1,5 +1,5 @@
.container#program
-if @scheduled_events.any?
-if @events_schedules.any?
= render partial: 'schedule_tabs', locals: { active: 'program' }
%h1.text-center
@ -22,24 +22,24 @@
/ scheduled events
- date = nil
- time = nil
- @scheduled_events.each do |event|
- unless event.start_time.strftime('%Y-%m-%d').eql?(date)
- @events_schedules.each do |event_schedule|
- unless event_schedule.start_time.strftime('%Y-%m-%d').eql?(date)
.col-xs-12.col-md-12
.date-content
%span{ class: 'date-title', id: "#{ event.start_time.strftime('%Y-%m-%d') }" }
= date = event.start_time.strftime('%Y-%m-%d')
%span{ class: 'date-title', id: "#{ event_schedule.start_time.strftime('%Y-%m-%d') }" }
= date = event_schedule.start_time.strftime('%Y-%m-%d')
%a{ title: "Go up", class: "pull-right", href: "#program" }
%i{ class: "fa fa-angle-double-up fa-lg", 'aria-hidden' => true }
- unless event.start_time.strftime('%H:%M').eql?(time)
- unless event_schedule.start_time.strftime('%H:%M').eql?(time)
.col-xs-12.col-md-1
.start-time
= time = event.start_time.strftime('%H:%M')
= time = event_schedule.start_time.strftime('%H:%M')
.col-xs-12.col-md-11
.new-time-event
= render partial: 'event', locals: {event: event}
= render partial: 'event', locals: { event: event_schedule.event, event_schedule: event_schedule }
- else
.col-xs-12.col-md-11.col-md-offset-1
= render partial: 'event', locals: {event: event}
= render partial: 'event', locals: { event: event_schedule.event, event_schedule: event_schedule }
/ confirmed events that are not scheduled
- if @unscheduled_events.any?
@ -52,7 +52,7 @@
- @unscheduled_events.each do |event|
.col-xs-12.col-md-12
.unscheduled-event
= render partial: 'event', locals: {event: event}
= render partial: 'event', locals: { event: event, event_schedule: nil }
:javascript
$('.program-selector').on('click', function(e) {

View file

@ -16,8 +16,8 @@
%room{ name: room.name }
- events_in_rooms[room].each do |event|
%event{ guid: event.guid, id: event.id }
%date= event.start_time.iso8601
%start= event.start_time.strftime('%H:%M')
%date= event.time.iso8601
%start= event.time.strftime('%H:%M')
%duration= length_timestamp(event.event_type.length)
%room= event.room.name
%type= event.event_type.name