Reworked housing page
This commit is contained in:
		
							parent
							
								
									2f375f7ff2
								
							
						
					
					
						commit
						2c976d40a4
					
				@ -1,4 +1,97 @@
 | 
			
		||||
(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() {
 | 
			
		||||
		document.documentElement.removeAttribute('data-ontop');
 | 
			
		||||
		document.getElementById('guest_id').value = '';
 | 
			
		||||
 | 
			
		||||
@ -1490,6 +1490,43 @@ ul.warnings li,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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 {
 | 
			
		||||
		tr.place-guest {
 | 
			
		||||
			td {
 | 
			
		||||
@ -1498,6 +1535,14 @@ ul.warnings li,
 | 
			
		||||
				&:hover {
 | 
			
		||||
					background-color: $colour-1;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				&.full {
 | 
			
		||||
					background-color: #E8E8E8;
 | 
			
		||||
 | 
			
		||||
					&:hover {
 | 
			
		||||
						background-color: #CCC;
 | 
			
		||||
					}					
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			a {
 | 
			
		||||
@ -1512,6 +1557,7 @@ ul.warnings li,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.remove-guest,
 | 
			
		||||
		button, .button {
 | 
			
		||||
			float: right;
 | 
			
		||||
		}
 | 
			
		||||
@ -1535,13 +1581,15 @@ ul.warnings li,
 | 
			
		||||
				top: 0;
 | 
			
		||||
				background-color: $white;
 | 
			
		||||
				border: 0.1em solid #CCC;
 | 
			
		||||
				padding: 0.25em 0.75em;
 | 
			
		||||
				padding: 0.25em 0.75em 0.25em 1.5em;
 | 
			
		||||
				margin: 0;
 | 
			
		||||
				list-style: none;
 | 
			
		||||
				list-style-type: square;
 | 
			
		||||
				@include default-box-shadow(top, 2);
 | 
			
		||||
				z-index: 10;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			li {
 | 
			
		||||
				white-space: nowrap;
 | 
			
		||||
				margin: 0;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -1554,12 +1602,19 @@ ul.warnings li,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#guest-selector {
 | 
			
		||||
		display: none;
 | 
			
		||||
		position: fixed;
 | 
			
		||||
		top: 0;
 | 
			
		||||
		right: 0;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		left: 0;
 | 
			
		||||
		background-color: rgba($black, 0.5);
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
		z-index: 1000;
 | 
			
		||||
 | 
			
		||||
		&.open {
 | 
			
		||||
			display: block;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.guest-dlg {
 | 
			
		||||
			position: absolute;
 | 
			
		||||
@ -1569,12 +1624,144 @@ ul.warnings li,
 | 
			
		||||
			left: 0;
 | 
			
		||||
			background-color: $white;
 | 
			
		||||
			width: 80%;
 | 
			
		||||
			max-width: 25em;
 | 
			
		||||
			margin: auto;
 | 
			
		||||
			padding: 1em;
 | 
			
		||||
			height: 80%;
 | 
			
		||||
			cursor: default;
 | 
			
		||||
			@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 {
 | 
			
		||||
	#hosts {
 | 
			
		||||
		background-color: $white;
 | 
			
		||||
@ -1862,9 +2049,9 @@ table.schedule {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.event-dlg {
 | 
			
		||||
    @include _-(flex, 0);
 | 
			
		||||
    position: relative;
 | 
			
		||||
    z-index: 1000;
 | 
			
		||||
	@include _-(flex, 0);
 | 
			
		||||
	position: relative;
 | 
			
		||||
	z-index: 1000;
 | 
			
		||||
	background-color: $white;
 | 
			
		||||
	text-align: left;
 | 
			
		||||
	padding: 2em 2em 0.5em;
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ class ApplicationController < LinguaFrancaApplicationController
 | 
			
		||||
 | 
			
		||||
	# Prevent CSRF attacks by raising an exception.
 | 
			
		||||
	# 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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -138,7 +138,7 @@ class ConferencesController < ApplicationController
 | 
			
		||||
					}
 | 
			
		||||
				when :questions
 | 
			
		||||
					# 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.arrival = params[:arrival]
 | 
			
		||||
@ -225,7 +225,18 @@ class ConferencesController < ApplicationController
 | 
			
		||||
		# prepare the form
 | 
			
		||||
		case @register_template
 | 
			
		||||
		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'
 | 
			
		||||
		when :payment
 | 
			
		||||
			@page_title = 'articles.conference_registration.headings.Payment'
 | 
			
		||||
@ -337,7 +348,7 @@ class ConferencesController < ApplicationController
 | 
			
		||||
							],
 | 
			
		||||
						column_types: {
 | 
			
		||||
								name: :bold,
 | 
			
		||||
								#date: :date,
 | 
			
		||||
								date: :datetime,
 | 
			
		||||
								arrival: [:date, :day],
 | 
			
		||||
								departure: [:date, :day],
 | 
			
		||||
								registration_fees_paid: :money
 | 
			
		||||
@ -483,11 +494,15 @@ class ConferencesController < ApplicationController
 | 
			
		||||
				@housing_data[id][:space][s.to_sym] = size
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		
 | 
			
		||||
		@guests_housed = 0
 | 
			
		||||
 | 
			
		||||
		@guests.each do | guest_id, guest |
 | 
			
		||||
			data = guest.housing_data || {}
 | 
			
		||||
			@hosts_affected_by_guests[guest_id] ||= []
 | 
			
		||||
 | 
			
		||||
			if data['host']
 | 
			
		||||
				@guests_housed += 1
 | 
			
		||||
				host_id = (data['host'].present? ? data['host'].to_i : 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 }
 | 
			
		||||
 | 
			
		||||
					# 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][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) ||
 | 
			
		||||
						(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
 | 
			
		||||
 | 
			
		||||
					companions = data['companions'] || []
 | 
			
		||||
@ -534,14 +542,10 @@ class ConferencesController < ApplicationController
 | 
			
		||||
							if reg.present? && @guests[reg.id].present?
 | 
			
		||||
								housing_data = reg.housing_data || {}
 | 
			
		||||
								companion_host = housing_data['host'].present? ? housing_data['host'].to_i : nil
 | 
			
		||||
								if companion_host.blank?
 | 
			
		||||
									@hosts_affected_by_guests[guest_id] << companion_host
 | 
			
		||||
									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
 | 
			
		||||
										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
 | 
			
		||||
								@hosts_affected_by_guests[guest_id] << companion_host
 | 
			
		||||
								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
 | 
			
		||||
									@housing_data[host_id][:guest_data][guest_id][:warnings][:companions] = { name: "<strong>#{reg.user.name}</strong>".html_safe, id: reg.id }
 | 
			
		||||
								end
 | 
			
		||||
							end
 | 
			
		||||
						end
 | 
			
		||||
@ -554,6 +558,24 @@ class ConferencesController < ApplicationController
 | 
			
		||||
				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
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
@ -607,41 +629,41 @@ class ConferencesController < ApplicationController
 | 
			
		||||
				return redirect_to administration_step_path(@this_conference.slug, :payment)
 | 
			
		||||
			end
 | 
			
		||||
		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
 | 
			
		||||
			@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
 | 
			
		||||
 | 
			
		||||
				# get the hosts that need updating
 | 
			
		||||
				affected_hosts = {}
 | 
			
		||||
				affected_hosts[host_id] = @hosts[host_id]
 | 
			
		||||
				if params['affected-hosts'].present?
 | 
			
		||||
					params['affected-hosts'].split(',').each do | id |
 | 
			
		||||
						affected_hosts[id.to_i] = @hosts[id.to_i]
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
				@hosts_affected_by_guests[guest_id].each do | id |
 | 
			
		||||
					affected_hosts[id] ||= @hosts[id]
 | 
			
		||||
				end
 | 
			
		||||
				return render partial: 'conferences/admin/hosts_table'
 | 
			
		||||
			elsif params[:button] == 'remove-guest'
 | 
			
		||||
				guest = ConferenceRegistration.where(
 | 
			
		||||
						id: params[:guest].to_i,
 | 
			
		||||
						conference_id: @this_conference.id
 | 
			
		||||
					).limit(1).first
 | 
			
		||||
 | 
			
		||||
				json = { hosts: {}, affected_hosts: @hosts_affected_by_guests }
 | 
			
		||||
				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
 | 
			
		||||
				guest.housing_data ||= {}
 | 
			
		||||
				guest.housing_data.delete('space')
 | 
			
		||||
				guest.housing_data.delete('host')
 | 
			
		||||
				guest.save!
 | 
			
		||||
				
 | 
			
		||||
				analyze_housing
 | 
			
		||||
 | 
			
		||||
				return render partial: 'conferences/admin/hosts_table'
 | 
			
		||||
			end
 | 
			
		||||
			return redirect_to administration_step_path(@this_conference.slug, :housing)
 | 
			
		||||
		when 'broadcast'
 | 
			
		||||
			@hide_description = true
 | 
			
		||||
			@subject = params[:subject]
 | 
			
		||||
 | 
			
		||||
@ -1003,20 +1003,20 @@ module ApplicationHelper
 | 
			
		||||
				@housing_data[id][:guest_data][guest_id][:errors].each do | error, value |
 | 
			
		||||
					if value.is_a?(Array)
 | 
			
		||||
						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
 | 
			
		||||
					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
 | 
			
		||||
 | 
			
		||||
				@housing_data[id][:guest_data][guest_id][:warnings].each do | error, value |
 | 
			
		||||
					if value.is_a?(Array)
 | 
			
		||||
						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
 | 
			
		||||
					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
 | 
			
		||||
 | 
			
		||||
@ -1028,23 +1028,26 @@ module ApplicationHelper
 | 
			
		||||
					(content_tag :td, guest[:guest].user.name) +
 | 
			
		||||
					(content_tag :td do
 | 
			
		||||
						(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) +
 | 
			
		||||
					(content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
				for i in guests.size..(@housing_data[id][:space][area] || 0)
 | 
			
		||||
					guest_rows += content_tag :tr, class: 'empty-space' do
 | 
			
		||||
						(content_tag :td, '', colspan: 2) +
 | 
			
		||||
						(content_tag :td)
 | 
			
		||||
					end
 | 
			
		||||
			space_size = (@housing_data[id][:space][area] || 0)
 | 
			
		||||
 | 
			
		||||
			# add empty rows to represent empty guest spots
 | 
			
		||||
			for i in guests.size...space_size
 | 
			
		||||
				guest_rows += content_tag :tr, class: 'empty-space' do
 | 
			
		||||
					(content_tag :td, ' '.html_safe, colspan: 2) +
 | 
			
		||||
					(content_tag :td)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			status_html = ''
 | 
			
		||||
			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 |
 | 
			
		||||
					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
 | 
			
		||||
			if status_html.present?
 | 
			
		||||
@ -1057,7 +1060,7 @@ module ApplicationHelper
 | 
			
		||||
			end
 | 
			
		||||
			html += guest_rows
 | 
			
		||||
			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 }
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
@ -1065,6 +1068,43 @@ module ApplicationHelper
 | 
			
		||||
 | 
			
		||||
		content_tag :table, html.html_safe, class: 'host-table'
 | 
			
		||||
	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)
 | 
			
		||||
		html = ''
 | 
			
		||||
@ -1460,6 +1500,22 @@ module ApplicationHelper
 | 
			
		||||
		return html.html_safe
 | 
			
		||||
	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)
 | 
			
		||||
		add_inline_script :time
 | 
			
		||||
		add_js_translation('datetime.distance_in_words')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								app/views/conferences/admin/_hosts_table.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/views/conferences/admin/_hosts_table.html.haml
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
			
		||||
@ -1,47 +1,7 @@
 | 
			
		||||
- add_inline_script :housing
 | 
			
		||||
= form_tag administration_update_path(@this_conference.slug, :housing), id: :hosts, class: 'on-top-target' do
 | 
			
		||||
	= hidden_field_tag :guest_id
 | 
			
		||||
	.on-top-controls
 | 
			
		||||
		= button_tag :close, type: :button, class: ['on-top-close']
 | 
			
		||||
	%ul
 | 
			
		||||
		- @hosts.each do | id, registration |
 | 
			
		||||
			- 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']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
= form_tag administration_update_path(@this_conference.slug, :housing), id: 'housing-table-form' do
 | 
			
		||||
	#housing-table= render partial: 'conferences/admin/hosts_table'
 | 
			
		||||
#guest-selector
 | 
			
		||||
	= form_tag administration_update_path(@this_conference.slug, :housing), class: 'guest-dlg', id: 'guest-list-table' do
 | 
			
		||||
		%h3 Select a Guest
 | 
			
		||||
		#table
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								app/views/conferences/admin/_select_guest_table.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/views/conferences/admin/_select_guest_table.html.haml
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
  - format bg_color: '333333'
 | 
			
		||||
  - format 'td', font_name: 'Calibri', fg_color: '333333'
 | 
			
		||||
  - 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.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333'
 | 
			
		||||
  - format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true
 | 
			
		||||
 | 
			
		||||
@ -167,6 +167,9 @@ en:
 | 
			
		||||
          empty: 'Please enter an address'
 | 
			
		||||
        space:
 | 
			
		||||
          empty: 'Please select a space'
 | 
			
		||||
      housing:
 | 
			
		||||
        space:
 | 
			
		||||
          companions: This host wishes to be housed with %{name}
 | 
			
		||||
    template:
 | 
			
		||||
      body: 'There were problems with the following fields:'
 | 
			
		||||
      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.
 | 
			
		||||
      housing:
 | 
			
		||||
          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:
 | 
			
		||||
    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.
 | 
			
		||||
        no_locations_warning: Before you can schedule workshops, you must first add locations.
 | 
			
		||||
      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:
 | 
			
		||||
          hosts: Hosts
 | 
			
		||||
          guests: Guests
 | 
			
		||||
          email: Email
 | 
			
		||||
          housing: Preference
 | 
			
		||||
          arrival_departure: In City
 | 
			
		||||
      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.
 | 
			
		||||
        headings:
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user