Browse Source

Reworked housing page

development
Godwin 8 years ago
parent
commit
2c976d40a4
  1. 93
      app/assets/javascripts/housing.js
  2. 199
      app/assets/stylesheets/_application.scss
  3. 2
      app/controllers/application_controller.rb
  4. 122
      app/controllers/conferences_controller.rb
  5. 80
      app/helpers/application_helper.rb
  6. 11
      app/views/conferences/admin/_hosts_table.html.haml
  7. 52
      app/views/conferences/admin/_housing.html.haml
  8. 46
      app/views/conferences/admin/_select_guest_table.html.haml
  9. 2
      app/views/conferences/stats.xlsx.haml
  10. 11
      config/locales/en.yml

93
app/assets/javascripts/housing.js

@ -1,4 +1,97 @@
(function() { (function() {
function closeGuestSelector() {
document.getElementById('guest-selector').classList.remove('open');
}
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 + '=' + encodeURI(params[key]));
data.push(key + '=' + params[key]);
}
request.send(data.join('&'));
}
function initHostTable(table) {
forEachElement('.select-guest', function(button) {
button.addEventListener('click', function(event) {
event.preventDefault();
document.getElementById('guest-selector').classList.add('open');
var table = document.getElementById('table');
table.classList.add('loading');
_post(
document.getElementById('housing-table-form'),
{
host: button.getAttribute('data-host'),
space: button.getAttribute('data-space'),
button: 'get-guest-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 table = document.getElementById('housing-table');
table.classList.add('loading');
closeGuestSelector();
_post(
document.getElementById('guest-list-table'),
{
host: row.getAttribute('data-host'),
guest: row.getAttribute('data-guest'),
space: row.getAttribute('data-space'),
button: 'set-guest'
},
function(response) {
table.innerHTML = response;
table.classList.remove('loading');
initHostTable(table);
}
)
});
}, table);
}
);
});
});
forEachElement('.remove-guest', function(button) {
button.addEventListener('click', function(event) {
event.preventDefault();
var table = document.getElementById('housing-table');
table.classList.add('loading');
_post(
document.getElementById('housing-table-form'),
{
guest: button.getAttribute('data-guest'),
button: 'remove-guest'
},
function (response) {
table.innerHTML = response;
table.classList.remove('loading');
initHostTable(table);
}
);
});
});
}
initHostTable(document.getElementById('housing-table'));
document.getElementById('guest-selector').addEventListener('click', function(event) {
if (event.target.id == 'guest-selector') {
closeGuestSelector();
}
});
function closeOnTop() { function closeOnTop() {
document.documentElement.removeAttribute('data-ontop'); document.documentElement.removeAttribute('data-ontop');
document.getElementById('guest_id').value = ''; document.getElementById('guest_id').value = '';

199
app/assets/stylesheets/_application.scss

@ -1490,6 +1490,43 @@ ul.warnings li,
} }
#admin-housing { #admin-housing {
.guests-housed {
margin-bottom: 1em;
text-align: right;
h5 {
display: inline-block;
margin: 0;
padding-right: 0.5em;
}
.data {
display: inline-block;
font-size: 1.125em;
}
}
#housing-table {
@include _(transition, opacity 1s ease-in-out);
&.loading {
@include _(opacity, 0.5);
pointer-events: none;
}
tr.host {
th {
vertical-align: top;
}
.address {
margin-top: 1em;
text-align: right;
@include font-family(primary);
}
}
}
.host-table { .host-table {
tr.place-guest { tr.place-guest {
td { td {
@ -1498,6 +1535,14 @@ ul.warnings li,
&:hover { &:hover {
background-color: $colour-1; background-color: $colour-1;
} }
&.full {
background-color: #E8E8E8;
&:hover {
background-color: #CCC;
}
}
} }
a { a {
@ -1512,6 +1557,7 @@ ul.warnings li,
} }
} }
.remove-guest,
button, .button { button, .button {
float: right; float: right;
} }
@ -1535,13 +1581,15 @@ ul.warnings li,
top: 0; top: 0;
background-color: $white; background-color: $white;
border: 0.1em solid #CCC; border: 0.1em solid #CCC;
padding: 0.25em 0.75em; padding: 0.25em 0.75em 0.25em 1.5em;
margin: 0; margin: 0;
list-style: none; list-style-type: square;
@include default-box-shadow(top, 2); @include default-box-shadow(top, 2);
z-index: 10;
} }
li { li {
white-space: nowrap;
margin: 0; margin: 0;
} }
@ -1554,12 +1602,19 @@ ul.warnings li,
} }
#guest-selector { #guest-selector {
display: none;
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
background-color: rgba($black, 0.5); background-color: rgba($black, 0.5);
cursor: pointer;
z-index: 1000;
&.open {
display: block;
}
.guest-dlg { .guest-dlg {
position: absolute; position: absolute;
@ -1569,12 +1624,144 @@ ul.warnings li,
left: 0; left: 0;
background-color: $white; background-color: $white;
width: 80%; width: 80%;
max-width: 25em; margin: auto;
padding: 1em;
height: 80%;
cursor: default;
@include default-box-shadow(top, 2); @include default-box-shadow(top, 2);
h3 {
margin: 0 0 1em;
}
} }
} }
} }
@include keyframes(whiten) {
to { background-color: $white; }
}
#table {
position: relative;
overflow: auto;
height: 80%;
height: calc(100% - 4em);
background-color: $white;
@include _(transition, background-color 250ms ease-in-out);
&.loading {
background-color: #CCC;
@include _(animation, whiten ease-in-out 1s infinite alternate both);
.host-field, table, .legend {
display: none;
}
}
table {
min-width: 100em;
}
h4 {
margin: 0;
&.inline {
display: inline-block;
padding-right: 0.5em;
}
}
.plain-value {
font-size: 1.2em;
}
blockquote {
margin-top: 0;
font-size: 0.85em;
> :first-child {
margin-top: 0;
}
}
tr.selectable {
cursor: pointer;
&:hover {
th {
background-color: $colour-2;
}
}
&.other-host, &.other-space, &.bad-match {
td, th {
opacity: 0.5;
}
}
&.selected-space, &.other-space {
td {
background-color: lighten($colour-5, 35);
}
th {
background-color: $colour-5;
}
}
&.other-host {
td {
background-color: lighten($colour-1, 40%);
}
th {
background-color: $colour-1;
}
}
&:hover {
td {
opacity: 1;
background-color: lighten($colour-2, 25%);
}
}
}
.legend ul {
@include _-(display, flex);
list-style: none;
padding: 0;
li {
@include _(flex, 1);
text-align: center;
margin-bottom: 0.5em;
padding: 0.125em 0.5em;
margin: 0.1em;
border: 0.1em solid #EEE;
background-color: #F8F8F8;
@include font-family(secondary);
&.other-host, &.other-space, &.bad-match {
opacity: 0.5;
}
&.selected-space, &.other-space {
background-color: $colour-5;
}
&.other-host {
background-color: $colour-1;
}
}
}
.p {
max-height: 4em;
overflow: auto;
}
}
#admin-housing { #admin-housing {
#hosts { #hosts {
background-color: $white; background-color: $white;
@ -1862,9 +2049,9 @@ table.schedule {
} }
.event-dlg { .event-dlg {
@include _-(flex, 0); @include _-(flex, 0);
position: relative; position: relative;
z-index: 1000; z-index: 1000;
background-color: $white; background-color: $white;
text-align: left; text-align: left;
padding: 2em 2em 0.5em; padding: 2em 2em 0.5em;

2
app/controllers/application_controller.rb

@ -8,7 +8,7 @@ class ApplicationController < LinguaFrancaApplicationController
# Prevent CSRF attacks by raising an exception. # Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead. # For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception, :except => [:do_confirm, :js_error] protect_from_forgery with: :exception, :except => [:do_confirm, :js_error, :admin_update]
before_filter :capture_page_info before_filter :capture_page_info

122
app/controllers/conferences_controller.rb

@ -138,7 +138,7 @@ class ConferencesController < ApplicationController
} }
when :questions when :questions
# create the companion's user account and send a registration link unless they have already registered # create the companion's user account and send a registration link unless they have already registered
generate_confirmation(User.create(email: params[:companion]), register_path(@this_conference.slug)) unless User.find_by_email(params[:companion]) generate_confirmation(User.create(email: params[:companion]), register_path(@this_conference.slug)) if params[:companion].present? && User.find_by_email(params[:companion]).nil?
@registration.housing = params[:housing] @registration.housing = params[:housing]
@registration.arrival = params[:arrival] @registration.arrival = params[:arrival]
@ -225,7 +225,18 @@ class ConferencesController < ApplicationController
# prepare the form # prepare the form
case @register_template case @register_template
when :questions when :questions
@registration.housing_data ||= { } # see if someone else has asked to be your companion
if @registration.housing_data.blank?
ConferenceRegistration.where(
conference_id: @this_conference.id, can_provide_housing: [nil, false]
).where.not(housing_data: nil).each do | r |
@registration.housing_data = {
companions: [ r.user.email ]
} if r.housing_data['companions'].present? && r.housing_data['companions'].include?(current_user.email)
end
@registration.housing_data ||= { }
end
@page_title = 'articles.conference_registration.headings.Registration_Info' @page_title = 'articles.conference_registration.headings.Registration_Info'
when :payment when :payment
@page_title = 'articles.conference_registration.headings.Payment' @page_title = 'articles.conference_registration.headings.Payment'
@ -337,7 +348,7 @@ class ConferencesController < ApplicationController
], ],
column_types: { column_types: {
name: :bold, name: :bold,
#date: :date, date: :datetime,
arrival: [:date, :day], arrival: [:date, :day],
departure: [:date, :day], departure: [:date, :day],
registration_fees_paid: :money registration_fees_paid: :money
@ -483,11 +494,15 @@ class ConferencesController < ApplicationController
@housing_data[id][:space][s.to_sym] = size @housing_data[id][:space][s.to_sym] = size
end end
end end
@guests_housed = 0
@guests.each do | guest_id, guest | @guests.each do | guest_id, guest |
data = guest.housing_data || {} data = guest.housing_data || {}
@hosts_affected_by_guests[guest_id] ||= [] @hosts_affected_by_guests[guest_id] ||= []
if data['host'] if data['host']
@guests_housed += 1
host_id = (data['host'].present? ? data['host'].to_i : nil) host_id = (data['host'].present? ? data['host'].to_i : nil)
host = host_id.present? ? @hosts[host_id] : nil host = host_id.present? ? @hosts[host_id] : nil
@ -506,21 +521,14 @@ class ConferencesController < ApplicationController
@housing_data[host_id][:guests][space][guest_id] = { guest: guest } @housing_data[host_id][:guests][space][guest_id] = { guest: guest }
# make sure the host isn't overbooked
space_available = ((host_data['space'] || {})[space.to_s] || 0).to_i
if @housing_data[host_id][:guests][space].size > space_available
@housing_data[host_id][:warnings] ||= {}
@housing_data[host_id][:warnings][:space] ||= {}
@housing_data[host_id][:warnings][:space][space] ||= []
@housing_data[host_id][:warnings][:space][space] << :overbooked
end
@housing_data[host_id][:guest_data] ||= {} @housing_data[host_id][:guest_data] ||= {}
@housing_data[host_id][:guest_data][guest_id] = { warnings: {}, errors: {} } @housing_data[host_id][:guest_data][guest_id] = { warnings: {}, errors: {} }
@housing_data[host_id][:guest_data][guest_id][:warnings][:dates] = {} unless view_context.available_dates_match?(host, guest)
if (guest.housing == 'house' && space == :tent) || if (guest.housing == 'house' && space == :tent) ||
(guest.housing == 'tent' && (space == :bed_space || space == :floor_space)) (guest.housing == 'tent' && (space == :bed_space || space == :floor_space))
@housing_data[host_id][:guest_data][guest_id][:warnings][:space] = { actual: space.to_s, expected: guest.housing} @housing_data[host_id][:guest_data][guest_id][:warnings][:space] = { actual: (view_context._"forms.labels.generic.#{space.to_s}"), expected: (view_context._"articles.conference_registration.questions.housing.#{guest.housing}")}
end end
companions = data['companions'] || [] companions = data['companions'] || []
@ -534,14 +542,10 @@ class ConferencesController < ApplicationController
if reg.present? && @guests[reg.id].present? if reg.present? && @guests[reg.id].present?
housing_data = reg.housing_data || {} housing_data = reg.housing_data || {}
companion_host = housing_data['host'].present? ? housing_data['host'].to_i : nil companion_host = housing_data['host'].present? ? housing_data['host'].to_i : nil
if companion_host.blank? @hosts_affected_by_guests[guest_id] << companion_host
@hosts_affected_by_guests[guest_id] << companion_host if companion_host != host_id && reg.housing.present? && reg.housing != 'none'
if companion_host != host_id && reg.housing.present? && reg.housing != 'none' # set this as an error if the guest has selected only one other to stay with, but if they have requested to stay with more, make this only a warning
# set this as an error if the guest has selected only one other to stay with, but if they have requested to stay with more, make this only a warning @housing_data[host_id][:guest_data][guest_id][:warnings][:companions] = { name: "<strong>#{reg.user.name}</strong>".html_safe, id: reg.id }
status = companions.size > 1 ? :warnings : :errors
@housing_data[host_id][:guest_data][guest_id][status][:companions] ||= []
@housing_data[host_id][:guest_data][guest_id][status][:companions] << { name: reg.user.name, id: reg.id }
end
end end
end end
end end
@ -554,6 +558,24 @@ class ConferencesController < ApplicationController
end end
end end
end end
@hosts.each do | id, host |
host_data = host.housing_data
@hosts[id].housing_data['space'].each do | space, size |
# make sure the host isn't overbooked
space = space.to_sym
space_available = (size || 0).to_i
@housing_data[id][:warnings] ||= {}
@housing_data[id][:warnings][:space] ||= {}
@housing_data[id][:warnings][:space][space] ||= []
if @housing_data[id][:guests][space].size > space_available
@housing_data[id][:warnings][:space][space] << :overbooked
end
end
end
return @hosts_affected_by_guests return @hosts_affected_by_guests
end end
@ -607,41 +629,41 @@ class ConferencesController < ApplicationController
return redirect_to administration_step_path(@this_conference.slug, :payment) return redirect_to administration_step_path(@this_conference.slug, :payment)
end end
when 'housing' when 'housing'
space = params[:button].split(':')[0]
host_id = params[:button].split(':')[1].to_i
guest_id = params[:guest_id].to_i
get_housing_data
# modify the guest data # modify the guest data
@guests[guest_id].housing_data ||= {}
@guests[guest_id].housing_data['space'] = space
@guests[guest_id].housing_data['host'] = host_id
@guests[guest_id].save!
if request.xhr? if params[:button] == 'get-guest-list'
# get_housing_data
analyze_housing
return render partial: 'conferences/admin/select_guest_table', locals: { host: @hosts[params['host'].to_i], space: params['space'] }
elsif params[:button] == 'set-guest'
guest = ConferenceRegistration.where(
id: params[:guest].to_i,
conference_id: @this_conference.id
).limit(1).first
guest.housing_data ||= {}
guest.housing_data['space'] = params[:space]
guest.housing_data['host'] = params[:host].to_i
guest.save!
analyze_housing analyze_housing
# get the hosts that need updating return render partial: 'conferences/admin/hosts_table'
affected_hosts = {} elsif params[:button] == 'remove-guest'
affected_hosts[host_id] = @hosts[host_id] guest = ConferenceRegistration.where(
if params['affected-hosts'].present? id: params[:guest].to_i,
params['affected-hosts'].split(',').each do | id | conference_id: @this_conference.id
affected_hosts[id.to_i] = @hosts[id.to_i] ).limit(1).first
end
end guest.housing_data ||= {}
@hosts_affected_by_guests[guest_id].each do | id | guest.housing_data.delete('space')
affected_hosts[id] ||= @hosts[id] guest.housing_data.delete('host')
end guest.save!
analyze_housing
json = { hosts: {}, affected_hosts: @hosts_affected_by_guests } return render partial: 'conferences/admin/hosts_table'
puts @hosts_affected_by_guests[guest_id].to_json.to_s
affected_hosts.each do | id, host |
json[:hosts][id] = view_context.host_guests_widget(host)
end
return render json: json
end end
return redirect_to administration_step_path(@this_conference.slug, :housing)
when 'broadcast' when 'broadcast'
@hide_description = true @hide_description = true
@subject = params[:subject] @subject = params[:subject]

80
app/helpers/application_helper.rb

@ -1003,20 +1003,20 @@ module ApplicationHelper
@housing_data[id][:guest_data][guest_id][:errors].each do | error, value | @housing_data[id][:guest_data][guest_id][:errors].each do | error, value |
if value.is_a?(Array) if value.is_a?(Array)
value.each do | v | value.each do | v |
status_html += _("errors.housing.space.#{error.to_s}", vars: {value: v}) status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: v))
end end
else else
status_html += _("errors.housing.space.#{error.to_s}", vars: {value: value }) status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: value))
end end
end end
@housing_data[id][:guest_data][guest_id][:warnings].each do | error, value | @housing_data[id][:guest_data][guest_id][:warnings].each do | error, value |
if value.is_a?(Array) if value.is_a?(Array)
value.each do | v | value.each do | v |
status_html += _("warnings.housing.space.#{error.to_s}", v) status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", v))
end end
else else
status_html += _("warnings.housing.space.#{error.to_s}", vars: value) status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", vars: value))
end end
end end
@ -1028,23 +1028,26 @@ module ApplicationHelper
(content_tag :td, guest[:guest].user.name) + (content_tag :td, guest[:guest].user.name) +
(content_tag :td do (content_tag :td do
(guest[:guest].city + (guest[:guest].city +
(button_tag :remove, class: [:small, :delete])).html_safe (content_tag :a, (_'actions.workshops.Remove'), href: '#', class: 'remove-guest', data: { guest: guest_id })).html_safe
end) + end) +
(content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy]) (content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
end end
end
for i in guests.size..(@housing_data[id][:space][area] || 0) space_size = (@housing_data[id][:space][area] || 0)
guest_rows += content_tag :tr, class: 'empty-space' do
(content_tag :td, '', colspan: 2) + # add empty rows to represent empty guest spots
(content_tag :td) for i in guests.size...space_size
end guest_rows += content_tag :tr, class: 'empty-space' do
(content_tag :td, '&nbsp'.html_safe, colspan: 2) +
(content_tag :td)
end end
end end
status_html = '' status_html = ''
if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present? if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
@housing_data[id][:warnings][:space][area].each do | w | @housing_data[id][:warnings][:space][area].each do | w |
status_html += content_tag(:li, _("warnings.housing.space.#{w.to_s}")) status_html += content_tag(:li, _("warnings.messages.housing.space.#{w.to_s}"))
end end
end end
if status_html.present? if status_html.present?
@ -1057,7 +1060,7 @@ module ApplicationHelper
end end
html += guest_rows html += guest_rows
html += content_tag :tr, class: 'place-guest' do html += content_tag :tr, class: 'place-guest' do
content_tag :td, colspan: 3 do content_tag :td, class: guests.size >= space_size ? 'full' : nil, colspan: 3 do
content_tag :a, (_'forms.actions.generic.place_guest'), class: 'select-guest', href: '#', data: { host: id, space: area } content_tag :a, (_'forms.actions.generic.place_guest'), class: 'select-guest', href: '#', data: { host: id, space: area }
end end
end end
@ -1065,6 +1068,43 @@ module ApplicationHelper
content_tag :table, html.html_safe, class: 'host-table' content_tag :table, html.html_safe, class: 'host-table'
end end
def get_housing_match(host, guest, space)
housing_data = guest.housing_data || []
if housing_data['host'].present?
if housing_data['host'] == host.id
return space == housing_data['space'] ? :selected_space : :other_space
end
return :other_host
end
if space_matches?(space, guest.housing) && available_dates_match?(host, guest)
return :good_match
end
return :bad_match
end
def space_matches?(host_space, guest_space)
return false unless host_space.present? && guest_space.present?
if host_space.to_s == 'bed_space' || host_space.to_s == 'floor_space'
return guest_space.to_s == 'house'
end
return host_space.to_s == 'tent_space' && guest_space.to_s == 'tent'
end
def available_dates_match?(host, guest)
if host.housing_data['availability'][0] <= guest.arrival &&
host.housing_data['availability'][1] >= guest.departure
return true
end
return false
end
def host_guests_widget(registration) def host_guests_widget(registration)
html = '' html = ''
@ -1460,6 +1500,22 @@ module ApplicationHelper
return html.html_safe return html.html_safe
end end
def companion(registration)
if registration.housing_data.present? && registration.housing_data['companions'].present? && registration.housing_data['companions'].first.present?
companion_user = User.find_by_email(registration.housing_data['companions'].first)
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')
return companion_user
end
end
return :unregistered
end
return nil
end
def comment(comment) def comment(comment)
add_inline_script :time add_inline_script :time
add_js_translation('datetime.distance_in_words') add_js_translation('datetime.distance_in_words')

11
app/views/conferences/admin/_hosts_table.html.haml

@ -0,0 +1,11 @@
.guests-housed
%h5 Guests Housed:
.data="#{@guests_housed} / #{@guests.size}"
%table.hosts.admin-edit
- @hosts.each do | id, registration |
%tr.host
%th
.name=registration.user.name
.address=registration.housing_data['address']
%td.inner-table{colspan: 2}=host_guests_table(registration)

52
app/views/conferences/admin/_housing.html.haml

@ -1,47 +1,7 @@
- add_inline_script :housing - add_inline_script :housing
= form_tag administration_update_path(@this_conference.slug, :housing), id: :hosts, class: 'on-top-target' do = form_tag administration_update_path(@this_conference.slug, :housing), id: 'housing-table-form' do
= hidden_field_tag :guest_id #housing-table= render partial: 'conferences/admin/hosts_table'
.on-top-controls #guest-selector
= button_tag :close, type: :button, class: ['on-top-close'] = form_tag administration_update_path(@this_conference.slug, :housing), class: 'guest-dlg', id: 'guest-list-table' do
%ul %h3 Select a Guest
- @hosts.each do | id, registration | #table
- if registration.user.present?
- widget_data = host_guests_widget(registration)
%li{id: "host-#{id}", class: widget_data[:class]}
%h4=registration.user.name
.email=registration.user.email
.address=registration.housing_data['address']
.guests=widget_data[:html]
#guests
%h4=_'articles.admin.housing.headings.guests', :t
%ul.guests
- @guests.each do | id, registration |
- if registration.user.present?
%li.guest{id: "guest-#{id}", data: { id: id, 'affected-hosts': @hosts_affected_by_guests[id].join(',') }}
%h4= registration.user.name
.city=registration.city
.on-top-only.details
= data_set(:h4, 'articles.admin.housing.headings.email') do
= registration.user.email
= data_set(:h4, 'articles.admin.housing.headings.housing') do
= registration.housing
- if registration.arrival.present?
= data_set(:h4, 'articles.admin.housing.headings.arrival_departure') do
= date_span(registration.arrival.to_date, registration.departure.to_date)
- if (registration.housing_data || {})['companions'].present?
= data_set(:h4, 'articles.admin.housing.headings.companion') do
- companion = User.find_by_email(registration.housing_data['companions'].first)
- if companion
= "#{companion.firstname} (#{companion.email})"
- else
= registration.housing_data['companions'].first
= _'articles.admin.housing.headings.unregistered'
- if registration.allergies.present?
= data_set(:h4, 'articles.admin.housing.headings.allergies') do
= registration.allergies
- if registration.other.present?
= data_set(:h4, 'articles.admin.housing.headings.other') do
= registration.other
= button_tag :set_host, type: :button, class: [:small, 'set-host', 'not-on-top']

46
app/views/conferences/admin/_select_guest_table.html.haml

@ -0,0 +1,46 @@
= hidden_field_tag :host, host.id
.host-field
%h4.inline=_'forms.labels.generic.name'
%span.plain-value= host.user.name
.host-field
%h4.inline=_'articles.conference_registration.headings.host.availability'
%span.plain-value= date_span(host.housing_data['availability'][0].to_date, host.housing_data['availability'][1].to_date)
- if host.housing_data['considerations'].present?
.host-field
%h4.inline=_'articles.conference_registration.headings.host.considerations'
%span.plain-value= (host.housing_data['considerations'].map { | consideration | _"articles.conference_registration.host.considerations.#{consideration}" }).join(', ')
- if sanitize(host.housing_data['notes'], tags: []).present?
.host-field
%h4=_'articles.conference_registration.headings.host.notes'
%blockquote= host.housing_data['notes'].html_safe
%table.guests.admin-edit
%tr
%th.corner
%th=_'forms.labels.generic.city'
%th=_'forms.labels.generic.housing'
%th=_'articles.admin.housing.headings.arrival_departure'
%th=_'forms.labels.generic.companion'
%th=_'forms.labels.generic.food'
%th=_'forms.labels.generic.allergies'
%th=_'forms.labels.generic.other'
- @guests.each do | id, registration |
%tr.selectable{class: get_housing_match(host, registration, space).to_s.gsub('_', '-'), data: {host: host.id, guest: id, space: space}}
%th=registration.user.name
%td=registration.city
%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=_"articles.conference_registration.questions.food.#{registration.food}"
%td=registration.allergies
%td
.p=registration.other
.legend
%h4 Legend
%ul
%li.good-match Good Match
%li.bad-match Poor Match
%li.selected-space Also in this space
%li.other-space Also with this host
%li.other-host Already hosted

2
app/views/conferences/stats.xlsx.haml

@ -15,7 +15,7 @@
- format bg_color: '333333' - format bg_color: '333333'
- format 'td', font_name: 'Calibri', fg_color: '333333' - format 'td', font_name: 'Calibri', fg_color: '333333'
- format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff' - format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff'
- format 'td.date', num_fmt: 22, font_name: 'Courier New', sz: 10, fg_color: '333333' - 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.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.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333'
- format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true - format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true

11
config/locales/en.yml

@ -167,6 +167,9 @@ en:
empty: 'Please enter an address' empty: 'Please enter an address'
space: space:
empty: 'Please select a space' empty: 'Please select a space'
housing:
space:
companions: This host wishes to be housed with %{name}
template: template:
body: 'There were problems with the following fields:' body: 'There were problems with the following fields:'
header: header:
@ -177,7 +180,10 @@ en:
location_corrected: Your location was corrected from "%{original}" to "%{corrected}". If this doesn't reflect your intended location, you can change this again in the contact info step. location_corrected: Your location was corrected from "%{original}" to "%{corrected}". If this doesn't reflect your intended location, you can change this again in the contact info step.
housing: housing:
space: space:
overbooked: Overbooked overbooked: This space is overbooked
companions: This guest wishes to be housed with %{name}
space: This guest wishes to be housed in a %{expected}
dates: This guest's arrival or departure dates conflict with the host's availability dates
helpers: helpers:
select: select:
prompt: Please select prompt: Please select
@ -887,12 +893,13 @@ en:
description: On this page you can schedule workshops and publish your schedule to the front page. description: On this page you can schedule workshops and publish your schedule to the front page.
no_locations_warning: Before you can schedule workshops, you must first add locations. no_locations_warning: Before you can schedule workshops, you must first add locations.
housing: housing:
description: The housing tool can be used to help you set up visitors that have requested to be housed with a host. description: The housing tool can be used to help you set up visitors that have requested to be housed with a host. When you see an error icon beside a host's space or a guest's details, hover over it to see warnings. Ideally you should only check marks which will indicate that all hosts and guests are happy.
headings: headings:
hosts: Hosts hosts: Hosts
guests: Guests guests: Guests
email: Email email: Email
housing: Preference housing: Preference
arrival_departure: In City
locations: locations:
description: Locations are used to schedule workshops, events, and meals. Once your schedule is published, users will be able to see the name and address and be given a link to a map so that they can find their way. description: Locations are used to schedule workshops, events, and meals. Once your schedule is published, users will be able to see the name and address and be given a link to a map so that they can find their way.
headings: headings:

Loading…
Cancel
Save