From 15483c13df92c28622f6b2219873977dc139079b Mon Sep 17 00:00:00 2001 From: Godwin Date: Mon, 22 Aug 2016 11:17:55 -0700 Subject: [PATCH] Fixed error deleting a workshop --- app/assets/javascripts/registrations.js | 74 +++++++++++++- app/assets/stylesheets/_application.scss | 98 +++++++++++++++++++ app/controllers/conferences_controller.rb | 57 +++++++---- app/helpers/application_helper.rb | 98 +++++++++++++++++-- .../admin/_select_guest_table.html.haml | 2 +- app/views/conferences/admin/_stats.html.haml | 5 +- 6 files changed, 303 insertions(+), 31 deletions(-) diff --git a/app/assets/javascripts/registrations.js b/app/assets/javascripts/registrations.js index e950adb..6715a46 100644 --- a/app/assets/javascripts/registrations.js +++ b/app/assets/javascripts/registrations.js @@ -2,7 +2,7 @@ var searchControl = document.getElementById('search'); function filterTable() { - forEach(document.getElementById('search-rows').getElementsByTagName('tr'), function(tr) { + forEach(document.getElementById('search-table').getElementsByTagName('TBODY')[0].getElementsByTagName('TR'), function(tr) { tr.classList.remove('hidden'); var value = searchControl.value; @@ -18,6 +18,78 @@ }); } + // ref = https://davidwalsh.name/element-matches-selector + 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 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); + } + + });*/ + } + } + + 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); + } + }*/ + } + function editTableRow(row, cell) { + if (selectorMatches(row, 'tr[data-key].editable')) { + var key = row.getAttribute('data-key'); + var currentRow = document.querySelector('tr[data-key].editable.editing'); + if (currentRow && currentRow.getAttribute('data-key') !== key) { + saveRow(currentRow); + } + var editor = row.nextSibling; + if (!row.classList.contains('editing')) { + row.classList.add('editing'); + var focusElement = null; + if (cell) { + focusElement = editor.querySelector('td[data-column-id="' + cell.getAttribute('data-column-id') + '"] .cell-editor'); + } + (focusElement || editor.getElementsByClassName('cell-editor')[0]).focus(); + } + } + } + document.addEventListener('click', function (event) { editTableCell(event.target); }); + if (document.observe) { + document.observe("focusin", function (event) { editTableCell(event.target); }); + } else { + document.addEventListener("focus", function (event) { editTableCell(event.target); }, true); + // document.addEventListener("focus", function (event) { editTableCell(event.target); }, true); + } + searchControl.addEventListener('keyup', filterTable); searchControl.addEventListener('search', filterTable); })(); diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss index 8da841b..3e4236a 100644 --- a/app/assets/stylesheets/_application.scss +++ b/app/assets/stylesheets/_application.scss @@ -183,10 +183,108 @@ table, .table { tr.hidden { display: none; } + + tr[data-key] { + cursor: default; + + &:hover { + background-color: lighten($colour-2, 33%); + } + + &.editable { + + .editor { + display: none; + background-color: lighten($colour-1, 50%); + + td { + position: relative; + vertical-align: top; + background: inherit; + cursor: default; + opacity: 0.5; + + &.has-editor { + opacity: 1; + } + + .cell-editor { + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: inherit; + font: inherit; + margin: inherit; + background: inherit; + border: 0; + min-height: 0; + width: 100% !important; + border-radius: 0; + line-height: inherit; + overflow: hidden; + box-shadow: none; + text-align: inherit; + //border-bottom: 0.25em solid rgba(0,0,0,0.25); + } + select.cell-editor { + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + appearance: none; + cursor: pointer; + } + } + } + + &.editing { + display: none; + + + .editor { + display: table-row; + + .cell-editor { + position: absolute; + } + } + } + } + + /*td[name] { + position: relative; + cursor: text; + + &:hover { + background-color: lighten($colour-2, 25%); + } + + input, textarea, select { + } + } + + &[data-editing="1"] { + }*/ + } + + tr.editable, tr.editor { + td { + white-space: nowrap; + + &.text { + white-space: normal; + } + + &.date, &.datetime, &.money { + font-family: monospace; + font-size: 1.25em; + text-align: right; + } + } + } } .table-scroller { overflow: auto; + background-color: $white; } .table { diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 0f86c0e..cbe2c39 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -353,6 +353,7 @@ class ConferencesController < ApplicationController :bike, :food, :companion, + :companion_email, :allergies ], column_types: { @@ -360,7 +361,8 @@ class ConferencesController < ApplicationController date: :datetime, arrival: [:date, :day], departure: [:date, :day], - registration_fees_paid: :money + registration_fees_paid: :money, + allergies: :text }, keys: { name: 'forms.labels.generic.name', @@ -376,6 +378,7 @@ class ConferencesController < ApplicationController 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' }, @@ -384,22 +387,12 @@ class ConferencesController < ApplicationController @registrations.each do | r | user = r.user_id ? User.where(id: r.user_id).first : nil if user.present? - companion = '' - if r.housing_data.present? && r.housing_data['companions'].present? - companion_user = User.find_by_email(r.housing_data['companions'].first) - companion = view_context._'articles.conference_registration.terms.registration_status.unregistered' - - if companion_user.present? - cr = ConferenceRegistration.where(user_id: companion_user.id).order(created_at: :desc).limit(1).first - - if cr.present? && ((cr.steps_completed || []).include? 'questions') - companion = companion_user.named_email - end - end - end + 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')}"), @@ -409,12 +402,25 @@ class ConferencesController < ApplicationController 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 || '', + 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 + 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 @@ -425,13 +431,28 @@ class ConferencesController < ApplicationController format.xlsx { render xlsx: :stats, filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" } end else - # @registrations = ConferenceRegistration.where(:conference_id => @this_conference.id) @registration_count = @registrations.size @completed_registrations = 0 @bikes = 0 @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 @@ -1057,7 +1078,7 @@ class ConferencesController < ApplicationController @workshop.destroy end - return redirect_to workshops_url + return redirect_to register_step_path(@this_conference.slug, 'workshops') end return redirect_to view_workshop_url(@this_conference.slug, @workshop.id) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4839d64..4302517 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1536,6 +1536,18 @@ module ApplicationHelper end end + def html_table(excel_data, options = {}) + options[:html] = true + attributes = { class: options[:class], id: options[:id] } + attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present? + content_tag(:table, attributes) do + (content_tag(:thead) do + content_tag(:tr, excel_header_columns(excel_data)) + end) + + content_tag(:tbody, excel_rows(excel_data, {}, options)) + end + end + def excel_table(excel_data) format_xls 'table' do workbook use_autowidth: true @@ -1562,7 +1574,6 @@ module ApplicationHelper data[:columns].each do |column| unless data[:column_types].present? && data[:column_types][column] == :table - # columns += content_tag(:th, _(data[:keys][column].present? ? data[:keys][column] : "#{key}.#{column.to_s}"), class: class_name) columns += content_tag(:th, data[:keys][column].present? ? _(data[:keys][column]) : '', class: class_name) end end @@ -1597,7 +1608,7 @@ module ApplicationHelper (left + columns + right).html_safe end - def excel_columns(row, data, padding = {}) + def excel_columns(row, data, padding = {}, options = {}) columns = '' data[:columns].each do |column| @@ -1613,13 +1624,73 @@ module ApplicationHelper end end - columns += content_tag(:td, value, { class: class_name }) unless is_sub_table + unless is_sub_table + attributes = { class: [class_name] } + if options[:html] && row[:html_values].present? && row[:html_values][column].present? + value = row[:html_values][column] + end + + if options[:editable] + attributes[:data] = { 'column-id' => column } + end + + if (options[:column_names] || []).include? column + attributes[:tabindex] = 0 + end + + columns += content_tag(:td, value, attributes) + end end pad_columns(columns, padding) end - def excel_sub_tables(row, data, padding = {}) + def editor_columns(row, data, padding = {}, options = {}) + columns = '' + + data[:columns].each do |column| + value = row[column].present? ? (_!row[column].to_s) : '' + class_name = nil + is_sub_table = false + + if data[:column_types].present? && data[:column_types][column].present? + if data[:column_types][column] == :table + is_sub_table = true + else + class_name = data[:column_types][column] + end + end + + unless is_sub_table + attributes = { class: [class_name] } + + if options[:editable] + attributes[:data] = { 'column-id' => column } + end + + if (options[:column_names] || []).include? column + attributes[:class] << 'has-editor' + raw_value = value + + if options[:html] && row[:html_values].present? && row[:html_values][column].present? + value = row[:html_values][column] + end + # create the control but add the original value to set the width and height + 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 + else + value = (value + content_tag(:textarea, raw_value, name: column, class: 'cell-editor')).html_safe + end + end + + columns += content_tag(:td, value, attributes) + end + end + + pad_columns(columns, padding) + end + + def excel_sub_tables(row, data, padding = {}, options = {}) rows = '' # shift the table right @@ -1630,7 +1701,6 @@ module ApplicationHelper data[:columns].each do |column| if data[:column_types].present? && data[:column_types][column] == :table - puts row[column].to_json.to_s rows += content_tag(:tr, excel_header_columns(row[column], new_padding, 'sub-table')) rows += excel_rows(row[column], new_padding) rows += excel_empty_row(row[column], new_padding) @@ -1641,11 +1711,25 @@ module ApplicationHelper rows.html_safe end - def excel_rows(data, padding = {}) + def excel_rows(data, padding = {}, options = {}) rows = '' data[:data].each do |row| - rows += content_tag(:tr, excel_columns(row, data, padding)) + + attributes = {} + + if options[:primary_key].present? + attributes[:data] = { key: row[options[:primary_key]] } + end + + attributes[:class] = [] + + if options[:editable] + attributes[:class] << :editable + # attributes[:tabindex] = 0 + end + + rows += content_tag(:tr, excel_columns(row, data, padding, options), attributes) + excel_sub_tables(row, data, padding) + rows += content_tag(:tr, editor_columns(row, data, padding, options), class: :editor) if options[:editable] end rows.html_safe end diff --git a/app/views/conferences/admin/_select_guest_table.html.haml b/app/views/conferences/admin/_select_guest_table.html.haml index 45dd1b7..139ef82 100644 --- a/app/views/conferences/admin/_select_guest_table.html.haml +++ b/app/views/conferences/admin/_select_guest_table.html.haml @@ -30,7 +30,7 @@ %td=_"articles.conference_registration.questions.housing.#{registration.housing}" %td=date_span(registration.arrival.to_date, registration.departure.to_date) - companion = companion(registration) - %td=companion.present? ? (companion.is_a?(User) ? companion.name : (_"articles.conference_registration.terms.registration_status.#{companion}")) : '' + %td=companion.present? ? (companion.is_a?(User) ? companion.named_email : (_"articles.conference_registration.terms.registration_status.#{companion}")) : '' %td=_"articles.conference_registration.questions.food.#{registration.food}" %td=registration.allergies %td diff --git a/app/views/conferences/admin/_stats.html.haml b/app/views/conferences/admin/_stats.html.haml index 15ef688..8e77d68 100644 --- a/app/views/conferences/admin/_stats.html.haml +++ b/app/views/conferences/admin/_stats.html.haml @@ -22,7 +22,4 @@ %h4 Registrations = searchfield :search, nil, big: true .table-scroller - %table.registrations.admin-edit - %thead - %tr=excel_header_columns(@excel_data) - %tbody#search-rows=excel_rows(@excel_data) + = 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