Browse Source

Schedule divisions and workshops table

development
Godwin 7 years ago
parent
commit
ad3136a5cb
  1. 1
      app/assets/images/admin/workshops.svg
  2. 218
      app/assets/javascripts/schedule.js
  3. 5
      app/assets/stylesheets/_admin.scss
  4. 134
      app/controllers/application_controller.rb
  5. 321
      app/controllers/conference_administration_controller.rb
  6. 7
      app/helpers/admin_helper.rb
  7. 6
      app/helpers/form_helper.rb
  8. 33
      app/helpers/table_helper.rb
  9. 2
      app/views/conference_administration/_events.html.haml
  10. 147
      app/views/conference_administration/_schedule.html.haml
  11. 4
      app/views/conference_administration/_select_workshop_table.html.haml
  12. 12
      app/views/conference_administration/_workshops.html.haml
  13. 4
      config/locales/en.yml

1
app/assets/images/admin/workshops.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 26.25"><path d="M0 21h24V0H0v21zM19 1h4v3h-4V1zm0 4h4v3h-4V5zm0 4h4v3h-4V9zm0 4h4v3h-4v-3zm0 4h4v3h-4v-3zM14 1h4v3h-4V1zm0 4h4v3h-4V5zm0 4h4v3h-4V9zm0 4h4v3h-4v-3zm0 4h4v3h-4v-3zM9 1h4v3H9V1zm0 4h4v3H9V5zm0 4h4v3H9V9zm0 4h4v3H9v-3zm0 4h4v3H9v-3zM1 1h7v3H1V1zm0 4h7v3H1V5zm0 4h7v3H1V9zm0 4h7v3H1v-3zm0 4h7v3H1v-3z"/></svg>

After

Width:  |  Height:  |  Size: 377 B

218
app/assets/javascripts/schedule.js

@ -1,114 +1,114 @@
(function() {
function closeWorkshopSelector() {
document.getElementById('workshop-selector').classList.remove('open');
document.body.classList.remove('modal-open');
}
document.getElementById('workshop-selector').addEventListener('click', function(event) {
if (event.target.id == 'workshop-selector') {
closeWorkshopSelector();
}
});
function _post(form, params, f) {
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
f(request.responseText);
}
}
}
request.open('POST', form.getAttribute('action'), true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
params['authenticity_token'] = form.querySelector('[name="authenticity_token"]').value;
var data = [];
for (var key in params) {
data.push(key + '=' + params[key]);
}
request.send(data.join('&'));
}
function selectorMatches(el, selector) {
var p = Element.prototype;
var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
};
return f.call(el, selector);
}
function updateSchedule(html) {
var schedule = document.getElementById('schedule-preview');
var s = document.createElement('div');
s.innerHTML = html;
schedule.innerHTML = s.children[0].innerHTML;
schedule.classList.remove('requesting');
}
function closeWorkshopSelector() {
document.getElementById('workshop-selector').classList.remove('open');
document.body.classList.remove('modal-open');
}
document.getElementById('workshop-selector').addEventListener('click', function(event) {
if (event.target.id == 'workshop-selector') {
closeWorkshopSelector();
}
});
function _post(form, params, f) {
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
f(request.responseText);
}
}
}
request.open('POST', form.getAttribute('action'), true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
params['authenticity_token'] = form.querySelector('[name="authenticity_token"]').value;
var data = [];
for (var key in params) {
data.push(key + '=' + params[key]);
}
request.send(data.join('&'));
}
function selectorMatches(el, selector) {
var p = Element.prototype;
var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
};
return f.call(el, selector);
}
function updateSchedule(html) {
var schedule = document.getElementById('schedule-preview');
var s = document.createElement('div');
s.innerHTML = html;
schedule.innerHTML = s.children[0].innerHTML;
schedule.classList.remove('requesting');
}
document.body.addEventListener('submit', function (event) {
if (event.target.classList.contains('deschedule-workshop')) {
event.preventDefault();
var schedule = document.getElementById('schedule-preview');
var form = event.target;
schedule.classList.add('requesting');
_post(
form,
{
id: form.querySelector('[name="id"]').value,
button: 'deschedule_workshop'
},
updateSchedule
);
}
});
document.body.addEventListener('click', function (event) {
//console.log(event.target);
if (selectorMatches(event.target, 'td.workshop.open, td.workshop.open *')) {
//event.stopPropagation();
var button = event.target;
while (button && button.tagName && button.tagName !== 'TD') {
button = button.parentElement;
}
document.body.addEventListener('submit', function (event) {
if (event.target.classList.contains('deschedule-workshop')) {
event.preventDefault();
var schedule = document.getElementById('schedule-preview');
var form = event.target;
schedule.classList.add('requesting');
_post(
form,
{
id: form.querySelector('[name="id"]').value,
button: 'deschedule_workshop'
},
updateSchedule
);
}
});
document.body.addEventListener('click', function (event) {
if (selectorMatches(event.target, 'td.workshop.open, td.workshop.open *')) {
var button = event.target;
while (button && button.tagName && button.tagName !== 'TD') {
button = button.parentElement;
}
document.getElementById('workshop-selector').classList.add('open');
var table = document.getElementById('table');
table.classList.add('loading');
document.body.classList.add('modal-open');
document.getElementById('workshop-selector').classList.add('open');
var table = document.getElementById('table');
table.classList.add('loading');
document.body.classList.add('modal-open');
var block = button.getAttribute('data-block');
var day = button.getAttribute('data-day');
var location = button.getAttribute('data-location');
_post(
document.getElementById('workshop-table-form'),
{
block: block,
day: day,
location: location,
button: 'get-workshop-list'
},
function (response) {
var table = document.getElementById('table');
table.innerHTML = response;
table.classList.remove('loading');
forEachElement('tr.selectable', function(row) {
row.addEventListener('click', function(event) {
var schedule = document.getElementById('schedule-preview');
schedule.classList.add('requesting');
closeWorkshopSelector();
var form = document.getElementById('workshop-table-form');
_post(
form,
{
workshop: row.getAttribute('data-workshop'),
block: block,
day: day,
location: form.querySelector('#event_location').value,
button: 'set-workshop'
},
updateSchedule
);
});
}, table);
}
);
}
}, true);
var block = button.getAttribute('data-block');
var day = button.getAttribute('data-day');
var location = button.getAttribute('data-location');
var division = button.getAttribute('data-division');
_post(
document.getElementById('workshop-table-form'),
{
block: block,
day: day,
location: location,
division: division,
button: 'get-workshop-list'
},
function (response) {
var table = document.getElementById('table');
table.innerHTML = response;
table.classList.remove('loading');
forEachElement('tr.selectable', function(row) {
row.addEventListener('click', function(event) {
var schedule = document.getElementById('schedule-preview');
schedule.classList.add('requesting');
closeWorkshopSelector();
var form = document.getElementById('workshop-table-form');
_post(
form,
{
workshop: row.getAttribute('data-workshop'),
block: block,
day: day,
location: form.querySelector('#event_location').value,
button: 'set-workshop'
},
updateSchedule
);
});
}, table);
}
);
}
}, true);
})();

5
app/assets/stylesheets/_admin.scss

@ -1370,7 +1370,6 @@ nav.sub-menu {
.event-detail-link {
width: auto;
font-size: 1.25em;
}
.event-detail-link, .details, .title {
display: inline;
@ -1403,6 +1402,10 @@ nav.sub-menu {
width: 15em;
}
li {
white-space: normal;
}
+ .event-detail-link {
padding-right: 1em;
}

134
app/controllers/application_controller.rb

@ -1,5 +1,5 @@
class ApplicationController < BaseController
protect_from_forgery with: :exception, :except => [:do_confirm, :js_error, :admin_update]
protect_from_forgery with: :exception, except: [:do_confirm, :js_error, :admin_update]
before_filter :application_setup
after_filter :capture_page_info
@ -392,13 +392,13 @@ class ApplicationController < BaseController
def get_scheule_data(do_analyze = true)
conference = @this_conference || @conference
@meals = Hash[(conference.meals || {}).map{ |k, v| [k.to_i, v] }].sort.to_h
@events = Event.where(:conference_id => conference.id).order(start_time: :asc)
@workshops = Workshop.where(:conference_id => conference.id).order(start_time: :asc)
@events = Event.where(conference_id: conference.id).order(start_time: :asc)
@workshops = Workshop.where(conference_id: conference.id).order(start_time: :asc)
@locations = {}
get_block_data
@schedule = {}
schedule = {}
day_1 = conference.start_date.wday
@workshop_blocks.each_with_index do |info, block|
@ -407,11 +407,11 @@ class ApplicationController < BaseController
day_diff += 7 if day_diff < 0
day = (conference.start_date + day_diff.days).to_date
time = info['time'].to_f
@schedule[day] ||= { times: {}, locations: {} }
@schedule[day][:times][time] ||= {}
@schedule[day][:times][time][:type] = :workshop
@schedule[day][:times][time][:length] = info['length'].to_f
@schedule[day][:times][time][:item] = { block: block, workshops: {} }
schedule[day] ||= { times: {}, locations: {} }
schedule[day][:times][time] ||= {}
schedule[day][:times][time][:type] = :workshop
schedule[day][:times][time][:length] = info['length'].to_f
schedule[day][:times][time][:item] = { block: block, workshops: {} }
end
end
@ -423,9 +423,9 @@ class ApplicationController < BaseController
day_diff += 7 if day_diff < 0
day = (conference.start_date + day_diff.days).to_date
if block.present? && @schedule[day].present? && @schedule[day][:times].present? && @schedule[day][:times][block['time'].to_f].present?
@schedule[day][:times][block['time'].to_f][:item][:workshops][workshop.event_location_id] = { workshop: workshop, status: { errors: [], warnings: [], conflict_score: nil } }
@schedule[day][:locations][workshop.event_location_id] ||= workshop.event_location if workshop.event_location.present?
if block.present? && schedule[day].present? && schedule[day][:times].present? && schedule[day][:times][block['time'].to_f].present?
schedule[day][:times][block['time'].to_f][:item][:workshops][workshop.event_location_id] = { workshop: workshop, status: { errors: [], warnings: [], conflict_score: nil } }
schedule[day][:locations][workshop.event_location_id] ||= workshop.event_location if workshop.event_location.present?
end
end
end
@ -433,50 +433,50 @@ class ApplicationController < BaseController
@meals.each do |time, meal|
day = meal['day'].to_date
time = meal['time'].to_f
@schedule[day] ||= {}
@schedule[day][:times] ||= {}
@schedule[day][:times][time] ||= {}
@schedule[day][:times][time][:type] = :meal
@schedule[day][:times][time][:length] = (meal['length'] || 1.0).to_f
@schedule[day][:times][time][:item] = meal
schedule[day] ||= {}
schedule[day][:times] ||= {}
schedule[day][:times][time] ||= {}
schedule[day][:times][time][:type] = :meal
schedule[day][:times][time][:length] = (meal['length'] || 1.0).to_f
schedule[day][:times][time][:item] = meal
end
@events.each do |event|
if event.present? && event.start_time.present? && event.end_time.present?
day = event.start_time.midnight.to_date
time = event.start_time.hour.to_f + (event.start_time.min / 60.0)
@schedule[day] ||= {}
@schedule[day][:times] ||= {}
@schedule[day][:times][time] ||= {}
@schedule[day][:times][time][:type] = :event
@schedule[day][:times][time][:length] = (event.end_time - event.start_time) / 3600.0
@schedule[day][:times][time][:item] = event
schedule[day] ||= {}
schedule[day][:times] ||= {}
schedule[day][:times][time] ||= {}
schedule[day][:times][time][:type] = :event
schedule[day][:times][time][:length] = (event.end_time - event.start_time) / 3600.0
schedule[day][:times][time][:item] = event
end
end
@schedule = @schedule.sort.to_h
@schedule.each do |day, data|
@schedule[day][:times] = data[:times].sort.to_h
schedule = schedule.sort.to_h
schedule.each do |day, data|
schedule[day][:times] = data[:times].sort.to_h
end
@schedule.each do |day, data|
schedule.each do |day, data|
last_event = nil
data[:times].each do |time, time_data|
if last_event.present?
@schedule[day][:times][last_event][:next_event] = time
schedule[day][:times][last_event][:next_event] = time
end
last_event = time
end
@schedule[day][:num_locations] = (data[:locations] || []).size
schedule[day][:num_locations] = (data[:locations] || []).size
end
@schedule.deep_dup.each do |day, data|
schedule.deep_dup.each do |day, data|
data[:times].each do |time, time_data|
if time_data[:next_event].present? || time_data[:length] > @this_conference.schedule_interval
span = @this_conference.schedule_interval
length = time_data[:next_event].present? ? time_data[:next_event] - time : time_data[:length]
while span < length
@schedule[day][:times][time + span] ||= {
schedule[day][:times][time + span] ||= {
type: (span >= time_data[:length] ? :empty : :nil),
length: @this_conference.schedule_interval
}
@ -486,16 +486,20 @@ class ApplicationController < BaseController
end
end
@schedule = @schedule.sort.to_h
# schedule = schedule.sort.to_h
@schedule.each do |day, data|
@schedule[day][:times] = data[:times].sort.to_h
@schedule[day][:locations] ||= {}
schedule.each do |day, data|
# @schedule[day] = [{}]
# division = 0
# schedule[day][:num_locations] = schedule[day][:num_locations]
# schedule[day][:times] = data[:times].sort.to_h
schedule[day][:times] = data[:times].sort.to_h
schedule[day][:locations] ||= {}
# # sort the locations by name
schedule[day][:locations] = schedule[day][:locations].sort_by { |event_id, event| event.present? ? event.title.downcase : '' }.to_h
# sort the locations by name
@schedule[day][:locations] = @schedule[day][:locations].sort_by { |event_id, event| event.present? ? event.title.downcase : '' }.to_h
# add an empty block if no workshops are scheduled on this day yet
@schedule[day][:locations][0] = :add if do_analyze || @schedule[day][:locations].empty?
# # add an empty block if no workshops are scheduled on this day yet
# schedule[day][:locations][0] = :add if do_analyze || schedule[day][:locations].empty?
if do_analyze
data[:times].each do |time, time_data|
@ -512,8 +516,8 @@ class ApplicationController < BaseController
workshop_i.active_facilitators.each do |facilitator_i|
workshop_j.active_facilitators.each do |facilitator_j|
if facilitator_i.id == facilitator_j.id
@schedule[day][:times][time][:status] ||= {}
@schedule[day][:times][time][:item][:workshops][ids[j]][:status][:errors] << {
schedule[day][:times][time][:status] ||= {}
schedule[day][:times][time][:item][:workshops][ids[j]][:status][:errors] << {
name: :common_facilitator,
facilitator: facilitator_i,
workshop: workshop_i,
@ -533,7 +537,7 @@ class ApplicationController < BaseController
amenities = JSON.parse(location.amenities || '[]').map &:to_sym
needs.each do |need|
@schedule[day][:times][time][:item][:workshops][ids[i]][:status][:errors] << {
schedule[day][:times][time][:item][:workshops][ids[i]][:status][:errors] << {
name: :need_not_available,
need: need,
location: location,
@ -555,13 +559,55 @@ class ApplicationController < BaseController
end
end
@schedule[day][:times][time][:item][:workshops][ids[i]][:status][:conflict_score] = (interests & (workshop_i.interested.map { | u | u.user_id })).length
schedule[day][:times][time][:item][:workshops][ids[i]][:status][:conflict_score] = (interests & (workshop_i.interested.map { | u | u.user_id })).length
end
end
end
end
end
end
@schedule = {}
schedule.sort.to_h.each do |day, data|
@schedule[day] = []
division = 0
# @schedule[day][division] = data
locations = nil
@schedule[day][division] = {}
@schedule[day][division][:times] = {}
# @schedule[day][division][:times] = data[:times]
# # sort the locations by name
# @schedule[day][:locations] = schedule[day][:locations].sort_by { |event_id, event| event.present? ? event.title.downcase : '' }.to_h
# # add an empty block if no workshops are scheduled on this day yet
# schedule[day][:locations][0] = :add if do_analyze || schedule[day][:locations].empty?
# last_time_data = nil
data[:times].each do |time, time_data|
if time_data[:type] == :workshop && time_data[:item].present? && time_data[:item][:workshops].present?
if !locations.nil? && ((locations.keys - time_data[:item][:workshops].keys) | (time_data[:item][:workshops].keys - locations.keys)).length > 0
# data[:locations]
# xxx
@schedule[day][division][:locations] = locations.deep_dup
@schedule[day][division][:locations][0] = :add if do_analyze || locations.empty?
locations = data[:locations].select { |id, l| time_data[:item][:workshops][id].present? }
division += 1
@schedule[day][division] = {}
@schedule[day][division][:times] = {}
else
locations = data[:locations].select { |id, l| time_data[:item][:workshops][id].present? }
end
end
# last_time_data = time_data
@schedule[day][division][:times][time] = time_data
end
locations ||= data[:locations]
@schedule[day][division][:locations] = locations
@schedule[day][division][:locations][0] = :add if do_analyze || locations.empty?
@schedule[day][division][:num_locations] = locations.length
end
end
protected

321
app/controllers/conference_administration_controller.rb

@ -253,46 +253,7 @@ class ConferenceAdministrationController < ApplicationController
format.xlsx { render xlsx: '../conferences/stats', filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" }
end
else
if params[:sort_column]
col = params[:sort_column].to_sym
@excel_data[:data].sort_by! do |row|
value = row[col]
if row[:raw_values].key?(col)
value = if row[:raw_values][col].is_a?(TrueClass)
't'
elsif row[:raw_values][col].is_a?(FalseClass)
''
else
row[:raw_values][col]
end
elsif value.is_a?(City)
value = value.sortable_string
end
if value.nil?
case @excel_data[:column_types][col]
when :datetime, [:date, :day]
value = Date.new
when :money
value = 0
else
value = ''
end
end
value
end
if params[:sort_dir] == 'up'
@sort_dir = :up
@excel_data[:data].reverse!
end
@sort_column = col
else
@sort_column = :name
end
sort_data(params[:sort_column], params[:sort_dir], :name)
end
@registration_count = @registrations.size
@ -324,6 +285,22 @@ class ConferenceAdministrationController < ApplicationController
end
end
def administrate_workshops
get_workshops(true)
if request.format.xlsx?
logger.info "Generating stats.xls"
return respond_to do |format|
format.xlsx { render xlsx: '../conferences/stats', filename: "workshops-#{DateTime.now.strftime('%Y-%m-%d')}" }
end
else
sort_data(params[:sort_column], params[:sort_dir], :name)
end
if request.xhr?
render html: view_context.html_table(@excel_data, view_context.registrations_table_options)
end
end
def administrate_check_in
sort_weight = {
checked_in: 5,
@ -769,6 +746,179 @@ class ConferenceAdministrationController < ApplicationController
end
end
def get_workshops(html_format = false, id = nil, conference = @this_conference)
@workshops = conference.workshops.sort_by { |w| w.title.downcase }
@excel_data = {
columns: [
:title,
:owner,
:locale,
:date,
:info,
:notes,
:facilitators
] + User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
Workshop.all_needs.map { |n| "need_#{n}".to_sym } + [
:theme,
:space
] + (User.AVAILABLE_LANGUAGES - [I18n.locale]).map { |l| "title_#{l}".to_sym } +
(User.AVAILABLE_LANGUAGES - [I18n.locale]).map { |l| "info_#{l}".to_sym },
column_types: {
title: :bold,
date: :datetime,
info: :text,
notes: :text,
owner: :email
},
keys: {
title: 'forms.labels.generic.title',
owner: 'roles.workshops.facilitator.creator',
locale: 'articles.conference_registration.terms.Preferred_Languages',
info: 'forms.labels.generic.info',
date: 'workshop.created_at',
notes: 'forms.labels.generic.notes',
facilitators: 'roles.workshops.facilitator.facilitator',
theme: 'articles.workshops.headings.theme',
space: 'articles.workshops.headings.space'
},
data: []
}
@excel_data[:key_vars] = {}
User.AVAILABLE_LANGUAGES.each do |l|
@excel_data[:keys]["language_#{l}".to_sym] = "languages.#{l}"
if l != I18n.locale
@excel_data[:keys]["title_#{l}".to_sym] = 'translate.content.item_translation'
@excel_data[:key_vars]["title_#{l}".to_sym] = { language: view_context.language_name(l), item: I18n.t('forms.labels.generic.title') }
@excel_data[:keys]["info_#{l}".to_sym] = 'translate.content.item_translation'
@excel_data[:key_vars]["info_#{l}".to_sym] = { language: view_context.language_name(l), item: I18n.t('forms.labels.generic.info') }
@excel_data[:column_types]["info_#{l}".to_sym] = :text
end
end
Workshop.all_needs.each do |n|
@excel_data[:keys]["need_#{n}".to_sym] = "workshop.options.needs.#{n}"
end
@workshops.each do |w|
if w.present?
if id.nil? || id == w.id
owner = User.find(w.creator)
facilitators = w.collaborators.map { |f| User.find(f) }
data = {
id: w.id,
title: w.title,
info: view_context.strip_tags(w.info),
notes: view_context.strip_tags(w.notes),
owner: owner.name,
locale: w.locale.present? ? (view_context.language_name w.locale) : '',
date: w.created_at ? w.created_at.strftime("%F %T") : '',
facilitators: facilitators.map { |f| f.name }.join(', '),
theme: w.theme && Workshop.all_themes.include?(w.theme.to_sym) ? I18n.t("workshop.options.theme.#{w.theme}") : w.theme,
space: w.space && Workshop.all_spaces.include?(w.space.to_sym) ? I18n.t("workshop.options.space.#{w.space}") : '',
raw_values: {
info: w.info,
owner: owner.email,
notes: w.notes,
locale: w.locale,
facilitators: facilitators.map { |f| f.email }.join(', '),
theme: w.theme,
space: w.space
},
html_values: {
}
}
languages = JSON.parse(w.languages || '[]').map &:to_sym
User.AVAILABLE_LANGUAGES.each do |l|
in_language = ((languages || []).include? l.to_sym)
data["language_#{l}".to_sym] = (in_language ? I18n.t('articles.conference_registration.questions.bike.yes') : '')
data[:raw_values]["language_#{l}".to_sym] = in_language
if l != I18n.locale
data["title_#{l}".to_sym] = w.get_column_for_locale!(:title, l, false)
data["info_#{l}".to_sym] = view_context.strip_tags(w.get_column_for_locale!(:info, l, false))
data[:raw_values]["info_#{l}".to_sym] = w.get_column_for_locale!(:info, l, false)
end
end
needs = JSON.parse(w.needs || '[]').map &:to_sym
Workshop.all_needs.each do |n|
in_need = ((needs || []).include? n.to_sym)
data["need_#{n}".to_sym] = (in_need ? I18n.t('articles.conference_registration.questions.bike.yes') : '')
data[:raw_values]["need_#{n}".to_sym] = in_need
end
@excel_data[:data] << data
end
end
end
if html_format
@column_options = {
locale: I18n.backend.enabled_locales.map { |l| [(view_context.language_name l), l] },
theme: Workshop.all_themes.map { |t| [I18n.t("workshop.options.theme.#{t}"), t] },
space: Workshop.all_spaces.map { |s| [I18n.t("workshop.options.space.#{s}"), s] }
}
@column_options[:theme] += ((conference.workshops.map { |w| w.theme }) - Workshop.all_themes.map(&:to_s)).uniq.map { |t| [t, t] }
User.AVAILABLE_LANGUAGES.each do |l|
@column_options["language_#{l}".to_sym] = [
[I18n.t("articles.conference_registration.questions.bike.yes"), true]
]
end
Workshop.all_needs.each do |n|
@column_options["need_#{n}".to_sym] = [
[I18n.t("articles.conference_registration.questions.bike.yes"), true]
]
end
end
end
def sort_data(col, sort_dir, default_col)
if col
col = col.to_sym
@excel_data[:data].sort_by! do |row|
value = row[col]
if row[:raw_values].key?(col)
value = if row[:raw_values][col].is_a?(TrueClass)
't'
elsif row[:raw_values][col].is_a?(FalseClass)
''
elsif @excel_data[:column_types][col] == :text
view_context.strip_tags(row[:raw_values][col] || '').downcase
else
row[:raw_values][col]
end
elsif value.is_a?(City)
value = value.sortable_string
end
if value.nil?
case @excel_data[:column_types][col]
when :datetime, [:date, :day]
value = Date.new
when :money
value = 0
else
value = ''
end
end
value
end
if sort_dir == 'up'
@sort_dir = :up
@excel_data[:data].reverse!
end
@sort_column = col
else
@sort_column = default_col
end
end
def get_housing_data
@hosts = {}
@guests = {}
@ -1255,6 +1405,85 @@ class ConferenceAdministrationController < ApplicationController
return nil
end
def admin_update_workshops
if params[:button] == 'update'
workshop = Workshop.where(
id: params[:key].to_i,
conference_id: @this_conference.id
).limit(1).first
params.each do |key, value|
case key.to_sym
when :owner
user = User.get(value.strip)
user_role = WorkshopFacilitator.where(user_id: user.id, workshop_id: workshop.id).first || WorkshopFacilitator.new(user_id: user.id, workshop_id: workshop.id)
owner_role = WorkshopFacilitator.where(role: :creator, workshop_id: workshop.id).first
if !owner_role || owner_role.user_id != user.id
owner_role.role = :collaborator
user_role.role = :creator
owner_role.save!
user_role.save!
end
when :facilitators
ids = []
value.split(/[\s,;]+/).each do |email|
user = User.get(email)
ids << user.id
user_role = WorkshopFacilitator.where(user_id: user.id, workshop_id: workshop.id).first || WorkshopFacilitator.new(user_id: user.id, workshop_id: workshop.id)
unless user_role.role == 'creator' || user_role.role == 'collaborator'
user_role.role = 'collaborator'
user_role.save
end
end
WorkshopFacilitator.where("workshop_id = ? AND role = ? AND user_id NOT IN (?)", workshop.id, 'collaborator', ids).destroy_all
when :title, :locale, :date, :info, :notes, :theme, :space
workshop.send("#{key}=", value.present? ? value : nil)
else
if key.start_with?('language_')
l = key.split('_').last.to_sym
languages = JSON.parse(workshop.languages || '[]').map &:to_sym
if User.AVAILABLE_LANGUAGES.include? l
if value.present?
languages |= [l]
else
languages -= [l]
end
workshop.languages = languages.to_json
end
elsif key.start_with?('need_')
n = key.split('_').last.to_sym
needs = JSON.parse(workshop.needs || '[]').map &:to_sym
if Workshop.all_needs.include? n
if value.present?
needs |= [n]
else
needs -= [n]
end
workshop.needs = needs.to_json
end
elsif key.start_with?('title_')
l = key.split('_').last.to_sym
workshop.set_column_for_locale(:title, l, value)
elsif key.start_with?('info_')
l = key.split('_').last.to_sym
workshop.set_column_for_locale(:info, l, value)
end
end
end
workshop.save!
get_workshops(true, params[:key].to_i)
options = view_context.workshops_table_options
options[:html] = true
render html: view_context.excel_rows(@excel_data, {}, options)
else
do_404
end
return nil
end
def admin_update_check_in
unless params[:button] == 'cancel'
user_id = params[:user_id]
@ -1478,7 +1707,7 @@ class ConferenceAdministrationController < ApplicationController
end
(params[:title] || {}).each do |locale, value|
event.set_column_for_locale(:title, locale, html_value(value), current_user.id) if value != event._title(locale) && view_context.strip_tags(value).strip.present?
event.set_column_for_locale(:title, locale, html_value(value), current_user.id) if value != event._title(locale) && view_context.strip.present?
end
event.save
@ -1555,8 +1784,18 @@ class ConferenceAdministrationController < ApplicationController
@time = @workshop_blocks[@block]['time'].to_f
@day = (Date.parse params[:day])
@location = params[:location]
@division = params[:division].to_i
@event_location = @location.present? && @location.to_i > 0 ? EventLocation.find(@location.to_i) : nil
@schedule ||= {}
@schedule[@day] ||= {}
@schedule[@day][@division] ||= []
@schedule[@day][@division][:times] ||= {}
@schedule[@day][@division][:times][@time] ||= {}
@schedule[@day][@division][:times][@time][:item] ||= {}
@schedule[@day][@division][:times][@time][:item][:workshops] || {}
@invalid_locations = @schedule[@day][@division.to_i][:times][@time][:item][:workshops].keys
@workshops.sort { |a, b| a.title.downcase <=> b.title.downcase }.each do |workshop|
@ordered_workshops[workshop.id] = workshop
end

7
app/helpers/admin_helper.rb

@ -38,7 +38,8 @@ module AdminHelper
events: [
:locations,
:meals,
:events
:events,
:workshops
],
schedule: [
:workshop_times,
@ -177,8 +178,8 @@ module AdminHelper
def available_dates_match?(host, guest)
return false unless host.housing_data['availability'].present? && host.housing_data['availability'][1].present?
return false unless guest.arrival.present? && guest.departure.present?
if host.housing_data['availability'][0] <= guest.arrival &&
host.housing_data['availability'][1] >= guest.departure
if host.housing_data['availability'][0].to_date <= guest.arrival.to_date &&
host.housing_data['availability'][1].to_date >= guest.departure.to_date
return true
end

6
app/helpers/form_helper.rb

@ -11,13 +11,15 @@ module FormHelper
# set the selected locale
selected_locale = (options[:locale] || object.locale || I18n.locale).to_sym
I18n.backend.enabled_locales.each do |locale|
locales = I18n.backend.enabled_locales.map { |l| [l, _("languages.#{l}")] }.sort_by { |l| l.last.downcase }.to_h
locales.each do |locale, locale_name|
# ses if this should b the selected field
class_name = selected_locale == locale.to_sym ? 'selected' : nil
# add the locale to the nav
nav += content_tag(:li,
content_tag(:a, _("languages.#{locale}"), href: 'javascript:void(0)'),
content_tag(:a, locale_name, href: 'javascript:void(0)'),
class: class_name, data: { locale: locale }).html_safe
fields = ''

33
app/helpers/table_helper.rb

@ -16,7 +16,7 @@ module TableHelper
headers = ''
options[:column_names].each do |header_name, columns|
column_names[header_name] ||= []
headers += content_tag(:th, excel_data[:keys][header_name].present? ? _(excel_data[:keys][header_name]) : '', colspan: 2)
headers += content_tag(:th, excel_data[:keys][header_name].present? ? I18n.t(excel_data[:keys][header_name], (excel_data[:key_vars] || {})[header_name]) : '', colspan: 2)
row_count = columns.size
columns.each do |column|
column_names[header_name] << column
@ -43,7 +43,7 @@ module TableHelper
attributes[:rowspan] = options[:row_spans][column]
end
column_text = excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : ''
column_text = excel_data[:keys][column].present? ? I18n.t(excel_data[:keys][column], (excel_data[:key_vars] || {})[header_name]) : ''
columns_html += content_tag(:th, column_text.html_safe, rowspan: attributes[:rowspan]) +
edit_column(nil, column, nil, attributes, excel_data, options)
@ -64,7 +64,7 @@ module TableHelper
if (excel_data[:column_types] || {})[column] != :table && ((options[:column_names] || []).include? column)
rows += content_tag(:tr, { class: 'always-edit', data: { key: '' } }) do
attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
column_text = excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : ''
column_text = excel_data[:keys][column].present? ? I18n.t(excel_data[:keys][column], (excel_data[:key_vars] || {})[header_name]) : ''
columns = content_tag(:th, column_text.html_safe) + edit_column(nil, column, nil, attributes, excel_data, options)
end
@ -124,7 +124,7 @@ module TableHelper
data[:columns].each do |column|
unless data[:column_types].present? && data[:column_types][column] == :table
column_text = data[:keys][column].present? ? _(data[:keys][column]) : ''
column_text = data[:keys][column].present? ? I18n.t(data[:keys][column], (data[:key_vars] || {})[column]) : ''
attrs = { class: class_name }
unless @sort_column.nil?
@ -400,4 +400,29 @@ module TableHelper
column_options: @column_options
}
end
def workshops_table_options
{
id: 'search-table',
class: ['registrations', 'admin-edit'],
primary_key: :id,
column_names: [
:title,
:owner,
:info,
:notes,
:locale,
:facilitators
] +
User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
(User.AVAILABLE_LANGUAGES - [I18n.locale]).map { |l| "title_#{l}".to_sym } +
Workshop.all_needs.map { |n| "need_#{n}".to_sym } + [
:theme,
:space
],
editable: administration_update_path(@this_conference.slug, @admin_step),
sortable: administration_step_path(@this_conference.slug, @admin_step),
column_options: @column_options
}
end
end

2
app/views/conference_administration/_events.html.haml

@ -35,7 +35,7 @@
= day_select @day, small: true, format: :weekday
= hour_select @time, small: true
= length_select @length, small: true
= translate_fields @event, { title: { type: :textfield, big: true, label: 'forms.labels.generic.title' }, info: { type: :textarea, label: 'forms.labels.generic.info', edit_on: :focus } }
= translate_fields @event, { title: { type: :textfield, big: true }, info: { type: :textarea, label: 'forms.labels.generic.info', edit_on: :focus } }
.actions.next-prev
= button :save, value: :save
= button :cancel, value: :cancel, class: :subdued, formnovalidate: true if @event.id.present?

147
app/views/conference_administration/_schedule.html.haml

@ -6,81 +6,82 @@
- else
- add_inline_script :schedule if @entire_page
#schedule-preview
- @schedule.each do |day, data|
- @schedule.each do |day, data_array|
%h4=date(day, :weekday).html_safe
%table.schedule{class: [data[:locations].present? ? 'has-locations' : 'no-locations', "locations-#{data[:num_locations]}"]}
- if data[:locations].present? && data[:locations].values.first != :add
%thead
%tr
%th.corner
- data[:locations].each do |id, location|
%th=location.is_a?(Symbol) ? '' : _!(location.title)
%tbody
- data[:times].each do |time, time_data|
%tr{class: "row-type-#{time_data[:type] || 'nil'}"}
- rowspan = (time_data[:length] * (1 / @this_conference.schedule_interval)).to_i
%th=time(time).html_safe
- if time_data[:type] == :workshop
- data_array.each_with_index do |data, division|
%table.schedule{class: [data[:locations].present? ? 'has-locations' : 'no-locations', "locations-#{data[:num_locations]}"]}
- if data[:locations].present? && data[:locations].values.first != :add
%thead
%tr
%th.corner
- data[:locations].each do |id, location|
- if time_data[:item][:workshops][id].present?
- workshop = time_data[:item][:workshops][id][:workshop]
- status = time_data[:item][:workshops][id][:status]
- else
- workshop = status = nil
%td{class: [time_data[:type], workshop.present? ? :filled : :open], rowspan: rowspan, data: workshop.present? ? nil : { block: time_data[:item][:block], day: day, location: id }}
- if workshop.present? && workshop.event_location.present?
.workshop-container
- if @can_edit
-if strip_tags(workshop.notes).strip.present?
= admin_notes(workshop.notes)
- if status[:errors].present?
= admin_status content_tag(:ul, (status[:errors].collect { |error| (_"errors.messages.schedule.#{error[:name].to_s}", vars: error[:i18nVars])}.join).html_safe).html_safe
= link_to view_workshop_path(@conference.slug, workshop.id), class: 'event-detail-link' do
.details
.title=_!workshop.title
%template.event-details{data: { href: view_workshop_path(@conference.slug, workshop.id) }}
%h1.title=_!workshop.title
%p.address
= _!("#{workshop.event_location.title}:")
= location_link workshop.event_location
.workshop-description= richtext workshop.info, 1
- if @can_edit
= form_tag administration_update_path(conference.slug, @admin_step), class: 'deschedule-workshop' do
.status
.conflict-score
%span.title Conflicts:
%span.value="#{status[:conflict_score]} / #{workshop.interested.size}"
= hidden_field_tag :id, workshop.id
= button :deschedule, value: :deschedule_workshop, class: [:delete, :small]
- elsif @can_edit
.title="Block #{time_data[:item][:block] + 1}"
- elsif time_data[:type] != :nil
%td{class: time_data[:type], rowspan: rowspan, colspan: data[:locations].present? ? data[:locations].size : 1}
- case time_data[:type]
- when :meal
- location = EventLocation.where(id: time_data[:item]['location'].to_i).first
- if location.present?
%a.event-detail-link
.details
.title=_!(time_data[:item]['title'])
.location=_!location.title
%template.event-details
%h1.title=_!(time_data[:item]['title'])
%p.address
= _!("#{location.title}:")
= location_link location
- when :event
- if time_data[:item].event_location.present?
%a.event-detail-link
.details
.title=_!(time_data[:item][:title]) if time_data[:item][:title]
.location=_!(time_data[:item].event_location.title)
%template.event-details
%h1.title=_!(time_data[:item][:title]) if time_data[:item][:title]
%p.address
= _!("#{time_data[:item].event_location.title}:")
= location_link time_data[:item].event_location
= richtext time_data[:item][:info], 1
%th=location.is_a?(Symbol) ? '' : _!(location.title)
%tbody
- data[:times].each do |time, time_data|
%tr{class: "row-type-#{time_data[:type] || 'nil'}"}
- rowspan = (time_data[:length] * (1 / @this_conference.schedule_interval)).to_i
%th=time(time).html_safe
- if time_data[:type] == :workshop
- data[:locations].each do |id, location|
- if time_data[:item][:workshops][id].present?
- workshop = time_data[:item][:workshops][id][:workshop]
- status = time_data[:item][:workshops][id][:status]
- else
- workshop = status = nil
%td{class: [time_data[:type], workshop.present? ? :filled : :open], rowspan: rowspan, data: workshop.present? ? nil : { block: time_data[:item][:block], day: day, location: id, division: division }}
- if workshop.present? && workshop.event_location.present?
.workshop-container
- if @can_edit
-if strip_tags(workshop.notes).strip.present?
= admin_notes(workshop.notes)
- if status[:errors].present?
= admin_status content_tag(:ul, (status[:errors].collect { |error| "<li>#{(_"errors.messages.schedule.#{error[:name].to_s}", vars: error[:i18nVars])}</li>"}.join).html_safe).html_safe
= link_to view_workshop_path(@conference.slug, workshop.id), class: 'event-detail-link' do
.details
.title=_!workshop.title
%template.event-details{data: { href: view_workshop_path(@conference.slug, workshop.id) }}
%h1.title=_!workshop.title
%p.address
= _!("#{workshop.event_location.title}:")
= location_link workshop.event_location
.workshop-description= richtext workshop.info, 1
- if @can_edit
= form_tag administration_update_path(conference.slug, @admin_step), class: 'deschedule-workshop' do
.status
.conflict-score
%span.title Conflicts:
%span.value="#{status[:conflict_score]} / #{workshop.interested.size}"
= hidden_field_tag :id, workshop.id
= button :deschedule, value: :deschedule_workshop, class: [:delete, :small]
- elsif @can_edit
.title="Block #{time_data[:item][:block] + 1}"
- elsif time_data[:type] != :nil
%td{class: time_data[:type], rowspan: rowspan, colspan: data[:locations].present? ? data[:locations].size : 1}
- case time_data[:type]
- when :meal
- location = EventLocation.where(id: time_data[:item]['location'].to_i).first
- if location.present?
%a.event-detail-link
.details
.title=_!(time_data[:item]['title'])
.location=_!location.title
%template.event-details
%h1.title=_!(time_data[:item]['title'])
%p.address
= _!("#{location.title}:")
= location_link location
- when :event
- if time_data[:item].event_location.present?
%a.event-detail-link
.details
.title=_!(time_data[:item][:title]) if time_data[:item][:title]
.location=_!(time_data[:item].event_location.title)
%template.event-details
%h1.title=_!(time_data[:item][:title]) if time_data[:item][:title]
%p.address
= _!("#{time_data[:item].event_location.title}:")
= location_link time_data[:item].event_location
= richtext time_data[:item][:info], 1
- if @entire_page
#workshop-selector
= form_tag administration_update_path(@this_conference.slug, @admin_step), class: 'workshop-dlg', id: 'workshop-table-form' do

4
app/views/conference_administration/_select_workshop_table.html.haml

@ -5,7 +5,7 @@
= @event_location.title
= hidden_field_tag :event_location, @location
- else
= location_select(nil, inline_label: true, small: true, invalid_locations: (((((@schedule[@day] || {})[:times] || {})[@time] || {})[:item] || {})[:workshops] || {}).keys, label: false)
= location_select(nil, inline_label: true, small: true, invalid_locations: @invalid_locations, label: false)
- if @event_location.present?
.host-field
%h4.inline=_'articles.admin.locations.headings.amenities'
@ -38,7 +38,7 @@
%td=(workshop.active_facilitators.map { |x| x.named_email }).join(', ')
%td=workshop.interested_count
%td
.text=workshop.notes
.text=strip_tags(workshop.notes)
.legend
%h4 Legend

12
app/views/conference_administration/_workshops.html.haml

@ -0,0 +1,12 @@
- add_inline_script :registrations
= columns(medium: 12) do
.goes-fullscreen#registrations-table
.flex-column
= searchfield :search, nil, big: true, stretch: true
%a.button{data: { expands: 'registrations-table' }}='expand'
%a.button.delete{data: { contracts: 'registrations-table' }}='close'
.table-scroller#registrations
= html_table(@excel_data, workshops_table_options)
= columns(medium: 12) do
.actions.center
= link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, :workshops, format: :xlsx), class: [:button, :download]

4
config/locales/en.yml

@ -1427,6 +1427,7 @@ en:
locations: Locations
meals: Meals
events: Events
workshops: Workshops
descriptions:
locations: Create the list of locations that you will be using for events,
meals, and workshops.
@ -1435,6 +1436,7 @@ en:
events: Create event details. These events should be any type of event other
than meals and workshops such as meeting, rides, and parties. The events
will be added to your schedule.
workshops: View and modify all workshops created by registered users.
schedule:
description: On this page you can schedule workshops and publish your schedule
to the front page.
@ -2625,6 +2627,7 @@ en:
unregistered: Unregistered
facilitator: Facilitator
workshop:
created_at: Creation Date
options:
needs:
projector: Projector
@ -2718,6 +2721,7 @@ en:
content:
change_locale: Read in %{language}
Translation_of: Translation of
item_translation: "%{item} in %{language}"
number:
currency:
format:

Loading…
Cancel
Save