From fe9dd2b49376f4269a1aa8cf2d0b7a32639f45c3 Mon Sep 17 00:00:00 2001 From: Godwin Date: Wed, 24 Aug 2016 22:52:10 -0700 Subject: [PATCH] Editable registrations --- app/assets/javascripts/registrations.js | 109 ++++-- app/assets/stylesheets/_application.scss | 50 ++- app/controllers/conferences_controller.rb | 344 +++++++++++++------ app/helpers/application_helper.rb | 53 ++- app/models/conference_registration.rb | 8 + app/models/user.rb | 5 +- app/views/conferences/_hosting.html.haml | 4 +- app/views/conferences/admin/_stats.html.haml | 2 +- config/locales/en.yml | 1 + 9 files changed, 419 insertions(+), 157 deletions(-) diff --git a/app/assets/javascripts/registrations.js b/app/assets/javascripts/registrations.js index 6715a46..563ceab 100644 --- a/app/assets/javascripts/registrations.js +++ b/app/assets/javascripts/registrations.js @@ -3,15 +3,17 @@ function filterTable() { forEach(document.getElementById('search-table').getElementsByTagName('TBODY')[0].getElementsByTagName('TR'), function(tr) { - tr.classList.remove('hidden'); + if (tr.classList.contains('editable')) { + tr.classList.remove('hidden'); - var value = searchControl.value; - if (value) { - var words = value.split(/\s+/); - for (var i = 0; i < words.length; i++) { - var word = new RegExp(words[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), "i"); - if (tr.innerHTML.search(word) == -1) { - tr.classList.add('hidden'); + var value = searchControl.value; + if (value) { + var words = value.split(/\s+/); + for (var i = 0; i < words.length; i++) { + var word = new RegExp(words[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), "i"); + if (tr.innerHTML.search(word) == -1) { + tr.classList.add('hidden'); + } } } } @@ -30,39 +32,50 @@ function saveRow(row) { if (row) { row.classList.remove('editing'); - /*row.removeAttribute('data-editing'); - forEach(row.getElementsByTagName('TD'), function(cell) { - var input = cell.getElementsByClassName('cell-editor')[0]; - if (input) { - cell.removeChild(input); + var table = row.parentElement.parentElement; + var editRow = row.nextSibling; + var url = table.getAttribute('data-update-url'); + var data = new FormData(); + var request = new XMLHttpRequest(); + request.onreadystatechange = function() { + if (request.readyState == 4) { + row.classList.remove('requesting'); + if (request.status == 200 && request.responseText) { + var tempTable = document.createElement('table'); + tempTable.innerHTML = request.responseText; + var rows = tempTable.getElementsByTagName('tr'); + row.innerHTML = rows[0].innerHTML; + editRow.innerHTML = rows[1].innerHTML; + } } - - });*/ + } + request.open('POST', url, true); + cells = editRow.getElementsByClassName('cell-editor'); + data.append('key', row.getAttribute('data-key')); + var changed = false; + for (var i = 0; i < cells.length; i++) { + if (cells[i].value !== cells[i].getAttribute('data-value')) { + data.append(cells[i].getAttribute('name'), cells[i].value); + changed = true; + } + } + if (changed) { + row.classList.add('requesting'); + request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + request.send(data); + } } } function editTableCell(cell) { if (selectorMatches(cell, 'tr[data-key].editable td')) { editTableRow(cell.parentElement, cell); - } - /*var currentRow = document.querySelector('[data-key][data-editing="1"]'); - if (currentRow && !currentRow.contains(cell)) { - saveRow(currentRow); - } - - if (selectorMatches(cell, 'tr[data-key] td[name]')) { - if (!cell.getElementsByClassName('cell-editor').length) { - //var tr = cell.parentElement; - - //saveRow(document.querySelector('[data-key][data-editing="1"]'), tr); - cell.parentElement.setAttribute('data-editing', "1"); - - var value = cell.innerHTML; - cell.innerHTML += ''; - cell.parentElement.classList.add(); - setTimeout(function() { cell.getElementsByClassName('cell-editor')[0].focus(); }, 100); + } else if (!selectorMatches(cell, 'tr[data-key].editable + tr, tr[data-key].editable + tr *')) { + var currentRow = document.querySelector('tr[data-key].editable.editing'); + if (currentRow) { + saveRow(currentRow); } - }*/ + } } function editTableRow(row, cell) { if (selectorMatches(row, 'tr[data-key].editable')) { @@ -78,11 +91,39 @@ if (cell) { focusElement = editor.querySelector('td[data-column-id="' + cell.getAttribute('data-column-id') + '"] .cell-editor'); } - (focusElement || editor.getElementsByClassName('cell-editor')[0]).focus(); + focusElement = focusElement || editor.getElementsByClassName('cell-editor')[0]; + focusElement.focus(); + if (focusElement.tagName === 'TEXTAREA' || (focusElement.tagName === 'INPUT' && focusElement.type != 'number' && focusElement.type != 'email')) { + focusElement.setSelectionRange(0, focusElement.value.length); + } } } } document.addEventListener('click', function (event) { editTableCell(event.target); }); + document.addEventListener('keyup', function (event) { + if (event.code === "Enter") { + var currentRow = document.querySelector('tr[data-key].editable.editing'); + if (currentRow) { + event.stopPropagation(); + event.preventDefault(); + var next = event.shiftKey ? 'previousSibling' : 'nextSibling'; + var cell = document.activeElement.parentElement.getAttribute('data-column-id'); + var row = currentRow[next] ? currentRow[next][next] : null; + if (!row) { + rows = currentRow.parentElement.children; + row = event.shiftKey ? rows[rows.length - 2] : rows[0]; + } + editTableRow(row, row.querySelector('[data-column-id="' + cell + '"]')); + } + } else if (event.code === "Escape") { + var currentRow = document.querySelector('tr[data-key].editable.editing'); + if (currentRow) { + event.stopPropagation(); + event.preventDefault(); + saveRow(currentRow); + } + } + }); if (document.observe) { document.observe("focusin", function (event) { editTableCell(event.target); }); } else { diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss index 3e4236a..58b17f9 100644 --- a/app/assets/stylesheets/_application.scss +++ b/app/assets/stylesheets/_application.scss @@ -185,7 +185,7 @@ table, .table { } tr[data-key] { - cursor: default; + cursor: cell; &:hover { background-color: lighten($colour-2, 33%); @@ -205,6 +205,16 @@ table, .table { &.has-editor { opacity: 1; + + @include after { + content: ''; + position: absolute; + top: 100%; + right: 0; + left: 0; + height: 0.25em; + background-color: rgba($black, 0.125); + } } .cell-editor { @@ -224,8 +234,13 @@ table, .table { overflow: hidden; box-shadow: none; text-align: inherit; - //border-bottom: 0.25em solid rgba(0,0,0,0.25); + + &[type=number]::-webkit-inner-spin-button, + &[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + } } + select.cell-editor { -webkit-appearance: none; -moz-appearance: none; @@ -233,6 +248,10 @@ table, .table { appearance: none; cursor: pointer; } + + &.date .cell-editor { + text-align-last: right; + } } } @@ -269,15 +288,32 @@ table, .table { td { white-space: nowrap; - &.text { - white-space: normal; - } - - &.date, &.datetime, &.money { + &.date, &.datetime, &.money, &.number { font-family: monospace; font-size: 1.25em; text-align: right; } + + &.text { + max-width: 20em; + } + } + } + + tr.editable td.text, + tr.editor td.text .value { + overflow: hidden; + text-overflow: ellipsis; + } + + tr.editor { + td.text .cell-editor { + white-space: normal; + bottom: auto; + height: 10em; + z-index: 1; + background: inherit; + overflow: auto !important; } } } diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index e731042..fad8780 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -336,94 +336,7 @@ class ConferencesController < ApplicationController end end when :stats - @registrations = ConferenceRegistration.where(:conference_id => @this_conference.id).sort { |a,b| (a.user.present? ? (a.user.firstname || '') : '').downcase <=> (b.user.present? ? (b.user.firstname || '') : '').downcase } - @excel_data = { - columns: [ - :name, - :email, - :status, - :registration_fees_paid, - :date, - :city, - :preferred_language, - :languages, - :arrival, - :departure, - :housing, - :bike, - :food, - :companion, - :companion_email, - :allergies - ], - column_types: { - name: :bold, - date: :datetime, - arrival: [:date, :day], - departure: [:date, :day], - registration_fees_paid: :money, - allergies: :text - }, - keys: { - name: 'forms.labels.generic.name', - email: 'forms.labels.generic.email', - status: 'forms.labels.generic.registration_status', - city: 'forms.labels.generic.location', - date: 'articles.conference_registration.terms.Date', - languages: 'articles.conference_registration.terms.Languages', - preferred_language: 'articles.conference_registration.terms.Preferred_Languages', - arrival: 'forms.labels.generic.arrival', - departure: 'forms.labels.generic.departure', - housing: 'forms.labels.generic.housing', - bike: 'forms.labels.generic.bike', - food: 'forms.labels.generic.food', - companion: 'articles.conference_registration.terms.companion', - companion_email: 'forms.labels.generic.email', - allergies: 'forms.labels.generic.allergies', - registration_fees_paid: 'articles.conference_registration.headings.fees_paid' - }, - data: [] - } - @registrations.each do | r | - user = r.user_id ? User.where(id: r.user_id).first : nil - if user.present? - companion = view_context.companion(r) - companion = companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}") if companion.present? - steps = r.steps_completed || [] - - @excel_data[:data] << { - id: r.id, - name: user.firstname || '', - email: user.email || '', - status: (view_context._"articles.conference_registration.terms.registration_status.#{(steps.include? 'questions') ? 'registered' : ((steps.include? 'contact_info') ? 'preregistered' : 'unregistered')}"), - date: r.created_at ? r.created_at.strftime("%F %T") : '', - city: r.city || '', - preferred_language: user.locale.present? ? (view_context.language_name user.locale) : '', - languages: ((r.languages || []).map { |x| view_context.language_name x }).join(', ').to_s, - arrival: r.arrival ? r.arrival.strftime("%F %T") : '', - departure: r.departure ? r.departure.strftime("%F %T") : '', - housing: r.housing.present? ? (view_context._"articles.conference_registration.questions.housing.#{r.housing}") : '', - bike: r.bike.present? ? (view_context._"articles.conference_registration.questions.bike.#{r.bike}") : '', - food: r.food.present? ? (view_context._"articles.conference_registration.questions.food.#{r.food}") : '', - companion: companion, - companion_email: ((r.housing_data || {})['companions'] || ['']).first, - allergies: r.allergies, - registration_fees_paid: r.registration_fees_paid, - raw_values: { - housing: r.housing, - bike: r.bike, - food: r.food, - arrival: r.arrival, - departure: r.departure - }, - html_values: { - date: r.created_at.present? ? r.created_at.strftime("%F %T") : '', - arrival: r.arrival.present? ? view_context.date(r.arrival.to_date, :span_same_year_date_1) : '', - departure: r.departure.present? ? view_context.date(r.departure.to_date, :span_same_year_date_1) : '' - } - } - end - end + get_stats(!request.format.xlsx?) if request.format.xlsx? logger.info "Generating stats.xls" @@ -437,22 +350,6 @@ class ConferencesController < ApplicationController @donation_count = 0 @donations = 0 @food = { meat: 0, vegan: 0, vegetarian: 0, all: 0 } - @column_options = { - housing: ConferenceRegistration.all_housing_options.map { |h| [ - (view_context._"articles.conference_registration.questions.housing.#{h}"), - h] }, - bike: ConferenceRegistration.all_bike_options.map { |b| [ - (view_context._"articles.conference_registration.questions.bike.#{b}"), - b] }, - food: ConferenceRegistration.all_food_options.map { |f| [ - (view_context._"articles.conference_registration.questions.food.#{f}"), - f] }, - arrival: view_context.conference_days_options_list(:before_plus_one), - departure: view_context.conference_days_options_list(:after_minus_one), - preferred_language: I18n.backend.enabled_locales.map { |l| [ - (view_context.language_name l), l - ] } - } @registrations.each do | r | if r.steps_completed.include? 'questions' @completed_registrations += 1 @@ -576,6 +473,191 @@ class ConferencesController < ApplicationController end + def get_stats(html_format = false, id = nil) + @registrations = ConferenceRegistration.where(:conference_id => @this_conference.id).sort { |a,b| (a.user.present? ? (a.user.firstname || '') : '').downcase <=> (b.user.present? ? (b.user.firstname || '') : '').downcase } + @excel_data = { + columns: [ + :name, + :email, + :status, + :is_attending, + :registration_fees_paid, + :date, + :city, + :preferred_language + ] + + User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } + + [ + :arrival, + :departure, + :housing, + :bike, + :food, + :companion, + :companion_email, + :allergies, + :other, + :can_provide_housing, + :first_day, + :last_day, + :address, + :phone + ] + ConferenceRegistration.all_spaces + [ + :notes + ], + column_types: { + name: :bold, + date: :datetime, + companion_email: :email, + arrival: [:date, :day], + departure: [:date, :day], + registration_fees_paid: :money, + allergies: :text, + other: :text, + first_day: [:date, :day], + last_day: [:date, :day], + notes: :text + }, + keys: { + name: 'forms.labels.generic.name', + email: 'forms.labels.generic.email', + status: 'forms.labels.generic.registration_status', + is_attending: 'articles.conference_registration.terms.is_attending', + city: 'forms.labels.generic.location', + date: 'articles.conference_registration.terms.Date', + preferred_language: 'articles.conference_registration.terms.Preferred_Languages', + arrival: 'forms.labels.generic.arrival', + departure: 'forms.labels.generic.departure', + housing: 'forms.labels.generic.housing', + bike: 'forms.labels.generic.bike', + food: 'forms.labels.generic.food', + companion: 'articles.conference_registration.terms.companion', + companion_email: 'forms.labels.generic.email', + allergies: 'forms.labels.generic.allergies', + registration_fees_paid: 'articles.conference_registration.headings.fees_paid', + other: 'articles.conference_registration.headings.other', + can_provide_housing: 'articles.conference_registration.can_provide_housing', + first_day: 'forms.labels.generic.first_day', + last_day: 'forms.labels.generic.last_day', + notes: 'forms.labels.generic.notes', + phone: 'forms.labels.generic.phone', + address: 'forms.labels.generic.address' + }, + data: [] + } + User.AVAILABLE_LANGUAGES.each do | l | + @excel_data[:keys]["language_#{l}".to_sym] = "languages.#{l.to_s}" + end + ConferenceRegistration.all_spaces.each do |s| + @excel_data[:column_types][s] = :number + @excel_data[:keys][s] = "forms.labels.generic.#{s.to_s}" + end + @registrations.each do | r | + user = r.user_id ? User.where(id: r.user_id).first : nil + if user.present? + companion = view_context.companion(r) + companion = companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}") if companion.present? + steps = r.steps_completed || [] + + if id.nil? || id == r.id + housing_data = r.housing_data || {} + availability = housing_data['availability'] || [] + availability[0] = Date.parse(availability[0]) if availability[0].present? + availability[1] = Date.parse(availability[1]) if availability[1].present? + + data = { + id: r.id, + name: user.firstname || '', + email: user.email || '', + status: (view_context._"articles.conference_registration.terms.registration_status.#{(steps.include? 'questions') ? 'registered' : ((steps.include? 'contact_info') ? 'preregistered' : 'unregistered')}"), + is_attending: (view_context._"articles.conference_registration.questions.bike.#{r.is_attending == 'n' ? 'no' : 'yes'}"), + date: r.created_at ? r.created_at.strftime("%F %T") : '', + city: r.city || '', + preferred_language: user.locale.present? ? (view_context.language_name user.locale) : '', + #languages: ((r.languages || []).map { |x| view_context.language_name x }).join(', ').to_s, + arrival: r.arrival ? r.arrival.strftime("%F %T") : '', + departure: r.departure ? r.departure.strftime("%F %T") : '', + housing: r.housing.present? ? (view_context._"articles.conference_registration.questions.housing.#{r.housing}") : '', + bike: r.bike.present? ? (view_context._"articles.conference_registration.questions.bike.#{r.bike}") : '', + food: r.food.present? ? (view_context._"articles.conference_registration.questions.food.#{r.food}") : '', + companion: companion, + companion_email: (housing_data['companions'] || ['']).first, + allergies: r.allergies, + registration_fees_paid: r.registration_fees_paid, + other: r.other, + can_provide_housing: r.can_provide_housing ? (view_context._'articles.conference_registration.questions.bike.yes') : '', + first_day: availability[0].present? ? availability[0].strftime("%F %T") : '', + last_day: availability[1].present? ? availability[1].strftime("%F %T") : '', + notes: housing_data['notes'], + address: housing_data['address'], + phone: housing_data['phone'], + raw_values: { + housing: r.housing, + bike: r.bike, + food: r.food, + arrival: r.arrival.present? ? r.arrival.to_date : nil, + departure: r.departure.present? ? r.departure.to_date : nil, + preferred_language: user.locale, + is_attending: r.is_attending != 'n', + can_provide_housing: r.can_provide_housing, + first_day: availability[0].present? ? availability[0].to_date : nil, + last_day: availability[1].present? ? availability[1].to_date : nil + }, + html_values: { + date: r.created_at.present? ? r.created_at.strftime("%F %T") : '', + arrival: r.arrival.present? ? view_context.date(r.arrival.to_date, :span_same_year_date_1) : '', + departure: r.departure.present? ? view_context.date(r.departure.to_date, :span_same_year_date_1) : '', + first_day: availability[0].present? ? view_context.date(availability[0].to_date, :span_same_year_date_1) : '', + last_day: availability[1].present? ? view_context.date(availability[1].to_date, :span_same_year_date_1) : '' + } + } + User.AVAILABLE_LANGUAGES.each do | l | + can_speak = ((user.languages || []).include? l.to_s) + data["language_#{l}".to_sym] = (can_speak ? (view_context._'articles.conference_registration.questions.bike.yes') : '') + data[:raw_values]["language_#{l}".to_sym] = can_speak + end + ConferenceRegistration.all_spaces.each do |s| + space = (housing_data['space'] || {})[s.to_s] + data[s] = space.present? ? space.to_i : nil + end + @excel_data[:data] << data + end + end + end + + if html_format + yes_no = [ + [(view_context._"articles.conference_registration.questions.bike.yes"), true], + [(view_context._"articles.conference_registration.questions.bike.no"), false] + ] + @column_options = { + housing: ConferenceRegistration.all_housing_options.map { |h| [ + (view_context._"articles.conference_registration.questions.housing.#{h}"), + h] }, + bike: ConferenceRegistration.all_bike_options.map { |b| [ + (view_context._"articles.conference_registration.questions.bike.#{b}"), + b] }, + food: ConferenceRegistration.all_food_options.map { |f| [ + (view_context._"articles.conference_registration.questions.food.#{f}"), + f] }, + arrival: view_context.conference_days_options_list(:before_plus_one), + departure: view_context.conference_days_options_list(:after_minus_one), + preferred_language: I18n.backend.enabled_locales.map { |l| [ + (view_context.language_name l), l + ] }, + is_attending: yes_no, + can_provide_housing: yes_no, + first_day: view_context.conference_days_options_list(:before), + last_day: view_context.conference_days_options_list(:after) + } + User.AVAILABLE_LANGUAGES.each do | l | + @column_options["language_#{l}".to_sym] = [ + [(view_context._"articles.conference_registration.questions.bike.yes"), true] + ] + end + end + end + def get_housing_data @hosts = {} @guests = {} @@ -699,6 +781,60 @@ class ConferencesController < ApplicationController @admin_step = params[:admin_step] case params[:admin_step] + when 'stats' + registration = ConferenceRegistration.where( + id: params[:key].to_i, + conference_id: @this_conference.id + ).limit(1).first + user_changed = false + params.each do | key, value | + case key.to_sym + when :city + registration.city = value.present? ? view_context.location(Geocoder.search(value, language: @this_conference.locale).first, @this_conference.locale) : nil + when :housing, :bike, :food, :allergies, :other + registration.send("#{key.to_s}=", value) + when :registration_fees_paid + registration.registration_fees_paid = value.to_i + when :can_provide_housing + registration.send("#{key.to_s}=", value.present?) + when :arrival, :departure + registration.send("#{key.to_s}=", value.present? ? Date.parse(value) : nil) + when :companion_email + registration.housing_data ||= {} + registration.housing_data['companions'] = [value] + when :preferred_language + registration.user.locale = value + user_changed = true + when :is_attending + registration.is_attending = value.present? ? 'y' : 'n' + when :address, :phone, :first_day, :last_day, :notes + registration.housing_data ||= {} + registration.housing_data[key.to_s] = value + else + if key.start_with?('language_') + l = key.split('_').last + if User.AVAILABLE_LANGUAGES.include? l.to_sym + registration.user.languages ||= [] + if value.present? + registration.user.languages |= [l] + else + registration.user.languages -= [l] + end + user_changed = true + end + elsif ConferenceRegistration.all_spaces.include? key.to_sym + registration.housing_data ||= {} + registration.housing_data['space'] ||= {} + registration.housing_data['space'][key.to_s] = value + end + end + end + registration.user.save! if user_changed + registration.save! + get_stats(true, params[:key].to_i) + options = view_context.registrations_table_options + options[:html] = true + return render html: view_context.excel_rows(@excel_data, {}, options) when 'edit' case params[:button] when 'save' diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4302517..a1091c7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -756,7 +756,7 @@ module ApplicationHelper belongs_to_periods << :before_plus_one if day <= (conference.start_date + 1.day) belongs_to_periods << :after_minus_one if day >= (conference.end_date - 1.day) belongs_to_periods << :during if day >= conference.start_date && day <= conference.end_date - days << [date(day.to_date, format || :span_same_year_date_1), day] if belongs_to_periods.include?(period) + days << [date(day.to_date, format || :span_same_year_date_1), day.to_date] if belongs_to_periods.include?(period) end return days end @@ -1558,6 +1558,7 @@ module ApplicationHelper format 'td.datetime', num_fmt: 22, font_name: 'Courier New', sz: 10, fg_color: '333333' format 'td.date.day', num_fmt: 14, font_name: 'Courier New', sz: 10, fg_color: '333333' format 'td.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333' + format 'td.number', font_name: 'Courier New', sz: 10, fg_color: '333333' format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true end @@ -1670,16 +1671,27 @@ module ApplicationHelper if (options[:column_names] || []).include? column attributes[:class] << 'has-editor' - raw_value = value + raw_value = row[:raw_values][column] || value if options[:html] && row[:html_values].present? && row[:html_values][column].present? value = row[:html_values][column] end + + editor_attributes = { class: 'cell-editor', data: { value: raw_value.to_s } } + # create the control but add the original value to set the width and height + editor_value = content_tag(:span, CGI::escapeHTML(value), class: 'value') if (options[:column_options] || {})[column].present? - value = (value + select_tag(column, options_for_select(options[:column_options][column], row[:raw_values][column] || raw_value), class: 'cell-editor')).html_safe + value = (editor_value.html_safe + select_tag(column, options_for_select([['', '']] + options[:column_options][column], raw_value), editor_attributes)).html_safe + elsif data[:column_types][column] == :text + editor_attributes[:name] = column + value = (editor_value.html_safe + content_tag(:textarea, raw_value, editor_attributes)).html_safe else - value = (value + content_tag(:textarea, raw_value, name: column, class: 'cell-editor')).html_safe + editor_attributes[:name] = column + editor_attributes[:value] = raw_value + type = data[:column_types][column] || :unknown + editor_attributes[:type] = { money: :number, number: :number, email: :email }[type] || :text + value = (editor_value.html_safe + content_tag(:input, nil, editor_attributes)).html_safe end end @@ -1724,7 +1736,6 @@ module ApplicationHelper if options[:editable] attributes[:class] << :editable - # attributes[:tabindex] = 0 end rows += content_tag(:tr, excel_columns(row, data, padding, options), attributes) + @@ -1734,6 +1745,38 @@ module ApplicationHelper rows.html_safe end + def registrations_table_options + { + id: 'search-table', + class: ['registrations', 'admin-edit'], + primary_key: :id, + column_names: [ + :registration_fees_paid, + :is_attending, + :city, + :preferred_language, + :arrival, + :departure, + :housing, + :bike, + :food, + :companion_email, + :allergies, + :other, + :can_provide_housing, + :address, + :phone, + :first_day, + :last_day, + :notes + ] + + User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } + + ConferenceRegistration.all_spaces, + editable: administration_update_path(@this_conference.slug, :stats), + column_options: @column_options + } + end + private def _original_content(value, lang) content_tag(:div, ( diff --git a/app/models/conference_registration.rb b/app/models/conference_registration.rb index af1df98..573e798 100644 --- a/app/models/conference_registration.rb +++ b/app/models/conference_registration.rb @@ -13,6 +13,10 @@ class ConferenceRegistration < ActiveRecord::Base [:none, :tent, :house] end + def self.all_spaces + [:bed_space, :floor_space, :tent_space] + end + def self.all_bike_options [:yes, :no] end @@ -20,4 +24,8 @@ class ConferenceRegistration < ActiveRecord::Base def self.all_food_options [:meat, :vegetarian, :vegan] end + + def self.all_considerations + [:vegan, :smoking, :pets, :quiet] + end end diff --git a/app/models/user.rb b/app/models/user.rb index 6d3a6dd..b6c633f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,7 +13,7 @@ class User < ActiveRecord::Base accepts_nested_attributes_for :authentications before_save do |user| - user.locale = I18n.locale + user.locale ||= I18n.locale end def can_translate?(to_locale = nil, from_locale = nil) @@ -48,9 +48,6 @@ class User < ActiveRecord::Base user = where(email: email).first unless user - # not really a good UX so we should fix this later - #do_404 - #return user = new(email: email) user.save! end diff --git a/app/views/conferences/_hosting.html.haml b/app/views/conferences/_hosting.html.haml index d879af0..f8927eb 100644 --- a/app/views/conferences/_hosting.html.haml +++ b/app/views/conferences/_hosting.html.haml @@ -6,14 +6,14 @@ = textfield :address, @hosting_data['address'], required: true, heading: 'articles.conference_registration.headings.host.address', help: 'articles.conference_registration.paragraphs.host.address' = telephonefield :phone, @hosting_data['phone'], required: true = fieldset :space, heading: 'articles.conference_registration.headings.host.space', help: 'articles.conference_registration.paragraphs.host.space' do - - [:bed_space, :floor_space, :tent_space].each do | space | + - ConferenceRegistration.all_spaces.each do | space | = numberfield space, @hosting_data['space'][space.to_s] || 0, min: 0, required: true = fieldset :hosting_dates, heading: 'articles.conference_registration.headings.host.availability', help: 'articles.conference_registration.paragraphs.host.availability' do - first_day_options = conference_days_options_list(:before) - last_day_options = conference_days_options_list(:after) = selectfield :first_day, @hosting_data['availability'][0] || first_day_options.first.last, first_day_options = selectfield :last_day, @hosting_data['availability'][1] || last_day_options.last.last, last_day_options - = checkboxes :considerations, [:vegan, :smoking, :pets, :quiet], @hosting_data['considerations'], 'articles.conference_registration.host.considerations', heading: 'articles.conference_registration.headings.host.considerations', help: 'articles.conference_registration.paragraphs.host.considerations', vertical: true + = checkboxes :considerations, ConferenceRegistration.all_considerations, @hosting_data['considerations'], 'articles.conference_registration.host.considerations', heading: 'articles.conference_registration.headings.host.considerations', help: 'articles.conference_registration.paragraphs.host.considerations', vertical: true = textarea :notes, @hosting_data['notes'], help: 'articles.conference_registration.paragraphs.host.notes', edit_on: :focus .actions.next-prev = button_tag (params[:step] == :save ? :save : :next), value: :hosting diff --git a/app/views/conferences/admin/_stats.html.haml b/app/views/conferences/admin/_stats.html.haml index 8e77d68..7643dbe 100644 --- a/app/views/conferences/admin/_stats.html.haml +++ b/app/views/conferences/admin/_stats.html.haml @@ -22,4 +22,4 @@ %h4 Registrations = searchfield :search, nil, big: true .table-scroller - = html_table @excel_data, id: 'search-table', class: ['registrations', 'admin-edit'], primary_key: :id, column_names: [:registration_fees_paid, :city, :preferred_language, :arrival, :departure, :housing, :bike, :food, :companion_email, :allergies], editable: administration_update_path(@this_conference.slug, :stats), column_options: @column_options + = html_table @excel_data, registrations_table_options diff --git a/config/locales/en.yml b/config/locales/en.yml index 34b44d7..fb0553c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1117,6 +1117,7 @@ en: registered: Registered companion: Companion Preferred_Languages: Preferred Language + is_attending: Attending? about_bikebike: paragraphs: bicycle_project_paragraph: 'From collectives that use the bicycle as an excuse to change society, economy and the environment. Non-profit groups that have a community bike shops, cooperatives and other projects that promote the use of the bicycle and that come together to turn their communities into a place where riding is easier, more inclusive, safer and more fun. The list below uses the criteria found in the old Bicycle Organization Organization Project for what constitutes a community bike shop. The bike project need not meet all these criteria. Rather, it is a general list of qualities which are common among many bicycle projects.'