Integrate the schedule in the admin interface

Integrate the schedule in the admin interface while adapting it to the new associations and models.
This commit is contained in:
Ana 2016-07-13 01:33:56 +02:00
parent e7a30302ff
commit dfd64a4510
32 changed files with 291 additions and 10074 deletions

View file

@ -13,6 +13,7 @@
//= require jquery
//= require jquery_ujs
//= require jquery.mobile.custom.min
//= require jquery-ui.min
//= require waypoints/jquery.waypoints
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap

13
app/assets/javascripts/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -14,4 +14,5 @@
*= require bootstrap-datetimepicker
*= require leaflet
*= require bootstrap3-switch
*= require jquery-ui.min
*/

File diff suppressed because one or more lines are too long

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

@ -7,10 +7,11 @@ module Admin
load_resource :venue, through: :conference, singleton: true
skip_before_filter :verify_authenticity_token, only: [:update]
layout 'schedule'
def show
authorize! :update, @program.events.new
event = @program.events.new
authorize! :update, event
event.destroy
if @conference.nil?
redirect_to admin_conference_index_path
return
@ -21,6 +22,13 @@ module Admin
else
@rooms = [ Room.new(name: 'No Rooms!', size: 0) ]
end
# if there is not selected schedule we create it
unless @program.selected_schedule.present?
schedule = @program.schedules.create
@program.selected_schedule = schedule.id
@program.save!
end
@schedule_id = @program.selected_schedule
end
def update
@ -31,14 +39,17 @@ module Admin
error_message = "Could not find event GUID: #{params[:event]}"
end
event_schedule = event.event_schedule(params[:schedule])
if params[:date] == 'none'
event.start_time = nil
event.room = nil
event.save!
event_schedule.destroy if event_schedule.present?
render json: { 'status' => 'ok' }
return
end
event_schedule = event.event_schedules.create(schedule_id: params[:schedule]) unless event_schedule.present?
room = Room.where(guid: room_params).first
if room.nil?
error_message = "Could not find room GUID: #{params[:room]}"
end
@ -48,7 +59,7 @@ module Admin
return
end
event.room = room
event_schedule.room = room
time = "#{params[:date]} #{params[:time]}"
Rails.logger.debug("Loading #{time}")
@ -57,8 +68,8 @@ module Admin
# 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!
event_schedule.start_time = start_time
event_schedule.save!
render json: { 'status' => 'ok' }
end

View file

@ -79,11 +79,12 @@ class Event < ActiveRecord::Base
end
##
# Checkes if the event has a start_time and a room for the selected one if there is any.
# Checkes if the event has a start_time and a room for the given schedule
# (given by its id) or for the selected one if there is any.
# ====Returns
# * +true+ or +false+
def unscheduled?
state == 'confirmed' && (!selected_event_schedule.try(:start_time).present? || !selected_event_schedule.try(:room).present?)
def unscheduled?(schedule_id)
state == 'confirmed' && (!event_schedule(schedule_id).try(:start_time).present? || !event_schedule(schedule_id).try(:room).present?)
end
def registration_possible?
@ -137,11 +138,10 @@ class Event < ActiveRecord::Base
end
end
def as_json(options)
json = super(options)
json[:room_guid] = scheduled_room.try(:guid)
def as_json(options={})
json = super({ include: { event_schedules: { methods: [:room_guid] } } }.merge(options))
json[:track_color] = track.try(:color) || '#FFFFFF'
json[:track_text_color] = ApplicationController.helpers.contrast_color(json[:track_color])
json[:length] = event_type.try(:length) || EventType::LENGTH_STEP
json
@ -277,6 +277,11 @@ class Event < ActiveRecord::Base
room.events.where(start_time: start_time).where.not(id: id)
end
# returns the event_schedule for this event and the schedule given in case that it exists
def event_schedule(schedule_id)
event_schedules.find_by(schedule_id: schedule_id)
end
private
# returns the event_schedule for this event and for the selected_schedule

View file

@ -2,4 +2,8 @@ class EventSchedule < ActiveRecord::Base
belongs_to :schedule
belongs_to :event
belongs_to :room
def room_guid
room.try(:guid)
end
end

View file

@ -31,8 +31,8 @@ class Program < ActiveRecord::Base
joins(:event_schedules).where('event_schedules.schedule_id = ? AND event_schedules.start_time IS NOT NULL AND event_schedules.room_id IS NOT NULL', schedule_id)
end
def unscheduled
select(&:unscheduled?)
def unscheduled(schedule_id=nil)
select{ |e| e.unscheduled?(schedule_id) }
end
def highlights

View file

@ -1,23 +1,13 @@
.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"}
.row
- @rooms.each do |room|
.col-md-2.col-xs-6
.room-name
= room.name
- (9*4..18*4).each do |slot|
- hour = slot / 4
- time = (15 * (slot % 4) == 0) ? '00' : 15 * (slot % 4)
.schedule-room-slot{ id: "schedule-room-#{room.guid}-#{hour}-#{time}", |
"room-guid" => room.guid, |
hour: "#{hour}:#{time}", |
date: date}
= "#{hour}:#{time}"

View file

@ -1,48 +1,166 @@
.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'
.row
.col-md-12
.page-header
%h1 Schedule
%p.text-muted
Create the schedules for the conference
.row
.col-md-2
.h4
Unscheduled events
.unscheduled-events
.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");
}
});
});
var conference = "#{@conference.short_title}"
var schedule_id = "#{@schedule_id}";
var Schedule = {
loadEvents: function(conference_id, start_date) {
var url = '/admin/conference/' + conference_id + '/program/events';
var callback = function(data) {
$.each(data, function(key, val) {
Schedule.newEvent(val, conference_id);
});
};
$.getJSON(url, callback);
},
newEvent: function(vars, conference_id) {
var height = (vars["length"] / 15 * 58) - 23; // this height fits the room cells
var lines = Math.floor((height - 7) / 23); // subtracting the padding before calculate the number of lines
var newEvent = $('<div class="schedule-event">'
+ '<div class="schedule-event-text" style="-webkit-line-clamp: '+ lines + '; height: ' + (lines * 23) + 'px;">'
+ '<span onclick="Schedule.remove(\'event-' + vars["guid"] + '\', \'' + conference_id +'\');" class="schedule-event-delete-button">X</span>'
+ vars["title"] + '</div></div>');
newEvent.attr("id", "event-" + vars["guid"]);
newEvent.attr("guid", vars["guid"]);
newEvent.css("height", height);
newEvent.css('background-color',vars["track_color"]);
newEvent.css('color',vars["track_text_color"]);
var date = "none";
var room = "none"
var time = "none";
for (i = 0; i < vars["event_schedules"].length; i++) {
event_schedule = vars["event_schedules"][i];
if(event_schedule["schedule_id"] == schedule_id){
room = event_schedule["room_guid"];
var d = new Date(event_schedule["start_time"]);
date = d.getUTCFullYear() + "-"
+ ('0' + (d.getUTCMonth() +1)).slice(-2) + '-'
+ ('0' + d.getUTCDate()).slice(-2);
minutes = d.getUTCMinutes();
time = d.getUTCHours() + ':' + (minutes == 0 ? '00' : minutes);
console.log("date: " + d);
newEvent.attr("room", room);
newEvent.attr("date", date);
newEvent.attr("hour", time);
}
}
newEvent.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
});
if (date == "none" || room == "none") {
newEvent.find(".schedule-event-delete-button").hide();
$('.unscheduled-events').append(newEvent);
} else {
var element = "[date='" + date +"'][hour='" + time + "'][room-guid='" + room + "']";
$(element).append(newEvent);
}
},
remove: function(element, conference_id) {
var e = $("#" + element);
var unscheduled = $(".unscheduled-events");
var url = '/admin/conference/' + conference_id + '/schedule';
var params = {
event: e.attr("guid"),
room: "none",
date: "none",
time: "none",
schedule: schedule_id
};
var callback = function(data) {
console.log(data);
e.appendTo(unscheduled);
e.find(".schedule-event-delete-button").hide();
}
$.ajax({
url: url,
type: 'PUT',
data: params,
success: callback,
dataType : 'json'
});
},
save: function (conference_id, event_id, room_id, date, time) {
var url = '/admin/conference/' + conference_id + '/schedule';
var params = {
event: event_id,
room: room_id,
date: date,
time: time,
schedule: schedule_id
};
var callback = function(data) {
console.log(data);
$("#event-" + event_id).find(".schedule-event-delete-button").show();
}
$.ajax({
url: url,
type: 'PUT',
data: params,
success: callback,
dataType : 'json'
});
}
};
Schedule.loadEvents("#{@conference.short_title}", "#{@dates.first}");
$(document).ready( function() {
$('.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");
$(ui.draggable).css("left", 0);
$(ui.draggable).css("top", 0);
$(this).css("background-color", "#ffffff");
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");
}
});
});

View file

@ -83,7 +83,7 @@
= 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'
= link_to 'Schedule', admin_conference_schedule_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))}