Browse Source

Added hosting page for confrence city residents

development
Godwin 8 years ago
parent
commit
2003028a15
  1. 21
      app/assets/javascripts/editor.js
  2. 117
      app/assets/stylesheets/_application.scss
  3. 52
      app/controllers/conferences_controller.rb
  4. 132
      app/helpers/application_helper.rb
  5. 11
      app/views/conferences/_contact_info.html.haml
  6. 24
      app/views/conferences/_hosting.html.haml
  7. 2
      app/views/conferences/register.html.haml
  8. 28
      config/locales/en.yml
  9. 5
      db/migrate/20160604221121_add_can_provide_housing_to_conference_registrations.rb
  10. 5
      db/migrate/20160604221248_add_housing_data_to_conference_registrations.rb
  11. 5
      db/migrate/20160604221432_add_locale_to_users.rb
  12. 5
      db/schema.rb

21
app/assets/javascripts/editor.js

@ -76,4 +76,25 @@
_input.addEventListener('change', function(event) { setRequired(); });
});
});
Array.prototype.forEach.call(document.querySelectorAll('[data-toggles]'), function(checkbox) {
var toggles = document.getElementById(checkbox.dataset.toggles);
toggles.classList.add('toggleable');
var form = checkbox.parentNode;
while (form && form.nodeName != 'FORM') {
form = form.parentNode;
}
var toggle = function() {
toggles.classList[checkbox.checked ? 'add' : 'remove']('open');
if (form) {
if (checkbox.checked) {
form.removeAttribute('novalidate');
} else {
form.setAttribute('novalidate', 'novalidate');
}
}
};
toggle();
checkbox.addEventListener('change', function(event) { toggle(); });
});
})();

117
app/assets/stylesheets/_application.scss

@ -239,7 +239,7 @@ textarea, .textarea {
}
input {
&[type="text"], &[type="password"], &[type="telephone"], &[type="search"], &[type="email"], &[type="url"] {
&[type="text"], &[type="password"], &[type="tel"], &[type="search"], &[type="email"], &[type="url"], &[type="number"] {
display: block;
font-size: 1.25em;
outline: 0;
@ -249,10 +249,15 @@ input {
padding: 0.25em 0.5em;
border-bottom: 0.15em solid transparent;
}
&[type="number"], &[type="tel"] {
@include font-family(secondary);
}
}
.number-field,
.email-field,
.telephone-field,
.text-field {
position: relative;
margin-bottom: 2em;
@ -321,6 +326,10 @@ input {
}
}
.telephone-field {
max-width: 15em;
}
@include keyframes(bend) {
to {
@include _(transform, skewX(-5deg));
@ -530,6 +539,10 @@ input {
font-size: 0.75em;
}
.check-box-field.inline {
display: inline-block;
}
.radio-button-field {
label {
width: 7em;
@ -582,11 +595,11 @@ form {
}
&.mini-flex-form {
display: flex;
align-items: flex-start;
@include _-(display, flex);
@include _(align-items, flex-start);
.input-field {
flex: 1;
@include _(flex, 1);
}
button, .button {
@ -597,8 +610,27 @@ form {
}
fieldset {
margin: 0;
padding: 0;
border: 0;
&.centered {
margin-top: 3em;
text-align: center;
}
}
.fieldgroup {
@include _-(display, flex);
@include _-(display, inline-flex);
@include _(align-items, flex-end);
@include _(flex-wrap, wrap);
margin: 0 0 3em 1em;
> .input-field {
margin-right: 1em;
@include _(flex, 1);
}
}
.view-object {
@ -645,9 +677,9 @@ fieldset {
}
&.next-prev {
display: flex;
flex-wrap: wrap;
flex-direction: row-reverse;
@include _-(display, flex);
@include _(flex-wrap, wrap);
@include _(flex-direction, row-reverse);
margin: 0;
button, .button {
@ -893,6 +925,7 @@ ul.warnings li,
a {
color: inherit;
position: static;
@include before {
content: '';
@ -1152,15 +1185,15 @@ $header-tilt: 8deg;
padding: 1em;
footer {
display: flex;
flex-flow: row wrap;
@include _-(display, flex);
@include _(flex-flow, row wrap);
font-size: 4.1vw;
}
.github,
ul.locales {
flex: 1;
flex-basis: 50%;
@include _(flex, 1);
@include _(flex-basis, 50%);
}
.copy {
@ -1174,7 +1207,7 @@ $header-tilt: 8deg;
}
.facebook {
flex: none;
@include _(flex, none);
margin: 0.5em;
a {
@ -1255,16 +1288,17 @@ input[type="submit"]:focus {
}
.check-box-field input:focus + label,
.radio-button-field input:focus + label {
@include before {
background-color: $colour-2;
@include _(animation, colour-out ease-in-out 500ms infinite alternate both);
}
.radio-button-field input:focus + label,
.select-field select:focus {
outline: 0.25rem solid $colour-2;
outline-offset: 0.2rem;
z-index: 1;
}
body {
input:focus,
textarea:focus,
select:focus,
.textarea {
@include _(animation, none);
}
@ -2177,15 +2211,31 @@ html[data-lingua-franca-example="html"] {
select {
display: block;
width: 100%;
height: 2.1em;
height: 1.75em;
font-family: inherit;
font-size: 1.5em;
padding: 0.25em;
padding: 0 0.5em;
border: 0.1em solid #333;
cursor: pointer;
@include default-box-shadow(top, 1.5, false);
}
}
.toggleable {
@include _(transition, 'transform 250ms ease-in-out, max-height 250ms ease-in-out, visibility 0s linear 250ms');
@include _(transform, scaleY(0));
@include _(transform-origin, 0 0);
max-height: 0;
visibility: hidden;
&.open {
@include _(transition, 'transform 250ms ease-in-out, max-height 250ms ease-in-out');
max-height: 100em;
visibility: visible;
@include _(transform, scaleX(1));
}
}
@include breakpoint(medium) {
body {
padding-bottom: 0;
@ -2201,11 +2251,11 @@ html[data-lingua-franca-example="html"] {
form {
&.flex-form {
display: flex;
align-items: flex-start;
@include _-(display, flex);
@include _(align-items, flex-start);
.input-field {
flex: 1;
@include _(flex, 1);
}
button, .button {
@ -2217,6 +2267,7 @@ html[data-lingua-franca-example="html"] {
.number-field,
.email-field,
.telephone-field,
.text-field {
&.big input {
font-size: 2em;
@ -2224,8 +2275,8 @@ html[data-lingua-franca-example="html"] {
}
.flex-column {
display: flex;
align-items: flex-start;
@include _-(display, flex);
@include _(align-items, flex-start);
margin-top: 1em;
p:first-child {
@ -2233,7 +2284,7 @@ html[data-lingua-franca-example="html"] {
}
.stretch-item {
flex: 1;
@include _(flex, 1);
margin-right: 1em;
}
}
@ -2537,8 +2588,8 @@ html[data-lingua-franca-example="html"] {
}
.github {
flex: none;
flex-basis: auto;
@include _(flex, none);
@include _(flex-basis, auto);
bottom: 0.5em;
float: left;
margin-right: 1em;
@ -2548,18 +2599,18 @@ html[data-lingua-franca-example="html"] {
margin: 0;
}
ul.locales {
flex: none;
flex-basis: auto;
flex-grow: 1;
@include _(flex, none);
@include _(flex-basis, auto);
@include _(flex-grow, 1);
}
}
.check-box-field,
.radio-button-field {
display: flex;
@include _-(display, flex);
label {
flex: 1;
@include _(flex, 1);
border: 0.1em solid;
border-left: 0;
text-align: left;

52
app/controllers/conferences_controller.rb

@ -578,6 +578,20 @@ class ConferencesController < ApplicationController
end
current_user.save! unless @errors.present?
when :hosting
@registration.can_provide_housing = params[:can_provide_housing].present?
@registration.housing_data = {
address: params[:address],
phone: params[:phone],
space: {
bed_space: params[:bed_space],
floor_space: params[:floor_space],
tent_space: params[:tent_space],
},
considerations: params[:considerations].keys,
availability: [ params[:first_day], params[:last_day] ],
notes: params[:notes]
}
end
if @errors.present?
@ -743,11 +757,13 @@ class ConferencesController < ApplicationController
:bike => nil,
:other => ''
);
@languages = [I18n.locale.to_sym]
@languages = current_user.languages
if @registration.languages
@languages = JSON.parse(@registration.languages).map &:to_sym
if @languages.blank? && @registration.languages.present?
@languages = (@registration.languages.is_a?(String) ? JSON.parse(@registration.languages) : @registration.languages).map &:to_sym
end
@languages ||= [I18n.locale.to_sym]
when :workshops
@page_title = 'articles.conference_registration.headings.Workshops'
@workshops = Workshop.where(conference_id: @this_conference.id)
@ -758,6 +774,12 @@ class ConferencesController < ApplicationController
@workshops_in_need = Workshop.where(conference_id: @this_conference.id, needs_facilitators: true)
when :contact_info
@page_title = 'articles.conference_registration.headings.Contact_Info'
when :hosting
@page_title = 'articles.conference_registration.headings.Hosting'
@hosting_data = @registration.housing_data || {}
@hosting_data['space'] ||= Hash.new
@hosting_data['availability'] ||= Array.new
@hosting_data['considerations'] ||= Array.new
when :policy
@page_title = 'articles.conference_registration.headings.Policy_Agreement'
when :done
@ -1447,10 +1469,14 @@ class ConferencesController < ApplicationController
def registration_steps(conference = nil)
conference ||= @this_conference || @conference
{
pre: [:policy, :contact_info, :workshops],
open: [:policy, :contact_info, :questions, :payment, :workshops]
}[conference.registration_status]
status = conference.registration_status
return [] unless status == :pre || status == :open
steps = [:policy, :contact_info, :questions, :hosting, :payment, :workshops]
steps -= [:questions, :payment] unless status == :open
steps -= [:hosting] unless @registration.present? && view_context.same_city?(@registration.city, @conference.location)
return steps
end
def required_steps(conference = nil)
@ -1508,11 +1534,23 @@ class ConferencesController < ApplicationController
def set_or_create_conference_registration
set_conference_registration
return @registration if @registration.present?
@registration ||= ConferenceRegistration.new(
conference: @this_conference,
user_id: current_user.id,
steps_completed: []
)
last_registration_data = ConferenceRegistration.where(user_id: current_user.id).order(created_at: :desc).limit(1).first
if last_registration_data.present?
if last_registration_data['languages'].present? && current_user.languages.blank?
current_user.languages = JSON.parse(last_registration_data['languages'])
current_user.save!
end
@registration.city = last_registration_data.city if last_registration_data.city.present?
end
end
# Only allow a trusted parameter "white list" through.

132
app/helpers/application_helper.rb

@ -611,6 +611,13 @@ module ApplicationHelper
return hash.length > 1 ? _("geography.formats.#{hash.keys.join('_')}", vars: hash) : hash.values.first
end
def same_city?(location1, location2)
location1 = location(location1) unless location1.is_a?(String)
location2 = location(location2) unless location2.is_a?(String)
location1.eql? location2
end
def show_errors(field)
return '' unless @errors && @errors[field].present?
@ -665,6 +672,38 @@ module ApplicationHelper
_!((p * 10000).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2%'))
end
def conference_housing_options(conference = nil)
conference ||= @this_conference || @conference
return [] unless conference
dates = []
day = conference.start_date - 7.days
last_day = conference.end_date + 7.days
while day <= last_day
dates << day
day += 1.day
end
return dates
end
def conference_housing_options_list(period, conference = nil)
conference ||= @this_conference || @conference
return [] unless conference
days = []
conference_housing_options(conference).each do |day|
belongs_to_periods = []
belongs_to_periods << :before if day <= conference.start_date
belongs_to_periods << :after if day >= conference.end_date
belongs_to_periods << :during if day >= conference.start_date && day <= conference.end_date
days << [date(day.to_date, :span_same_year_date_1), day] if belongs_to_periods.include?(period)
end
return days
end
def richtext(text, reduce_headings = 2)
return '' unless text.present?
return _!(text).
@ -704,26 +743,75 @@ module ApplicationHelper
return html.html_safe
end
def fieldset(name, options = {}, &block)
html = ''
label_id = nil
description_id = nil
if options[:heading].present?
label_id ||= "#{name.to_s}-label"
html += content_tag(:h3, _(options[:heading], :t), id: label_id)
end
if options[:help].present?
description_id ||= "#{name.to_s}-desc"
html += content_tag(:div, _(options[:help], :s, 2), class: 'input-field-help', id: description_id)
end
(html + content_tag(:fieldset, content_tag(:div, class: :fieldgroup, &block).html_safe,
aria: {
labeledby: label_id,
describedby: description_id
}
)
).html_safe
end
def selectfield(name, value, select_options, options = {})
textfield(name, value, options.merge({type: :select, options: select_options}))
end
def telephonefield(name, value, options = {})
textfield(name, value, options.merge({type: :telephone}))
end
def numberfield(name, value, options = {})
textfield(name, value, options.merge({type: :number}))
end
def textfield(name, value, options = {})
html = ''
description_id = nil
if options[:heading].present?
description_id ||= "#{name.to_s}-desc"
html += content_tag(:h3, _(options[:heading]), id: description_id)
html += content_tag(:h3, _(options[:heading], :t), id: description_id)
end
if options[:help].present?
description_id ||= "#{name.to_s}-desc"
html += content_tag(:div, _(options[:help], :s, 2), class: 'input-field-help', id: description_id)
end
html += show_errors name
html += label_tag name
html += text_field_tag(name, value,
input_options = {
required: options[:required],
lang: options[:lang],
min: options[:min],
max: options[:max],
aria: { describedby: description_id }
)
}
case options[:type]
when :select
html += select_tag(name, options_for_select(options[:options], value), input_options)
else
html += send("#{(options[:type] || :text).to_s}_field_tag", name, value, input_options)
end
html = content_tag(:div, html.html_safe,
class: [
'text-field',
"#{(options[:type] || :text).to_s}-field",
'input-field',
options[:big] ? 'big' : nil,
(@errors || {})[name].present? ? 'has-error' : nil
@ -738,6 +826,10 @@ module ApplicationHelper
checkboxes(name, boxes, [value], label_key, options.merge({radiobuttons: true}))
end
def checkbox(name, value, label_key, options = {})
checkboxes(name, [true], value, label_key, options)
end
def checkboxes(name, boxes, values, label_key, options = {})
html = ''
@ -746,7 +838,7 @@ module ApplicationHelper
if options[:heading].present?
label_id ||= "#{name.to_s}-label"
html += content_tag(:h3, _(options[:heading]), id: label_id)
html += content_tag(:h3, _(options[:heading], :t), id: label_id)
end
if options[:help].present?
@ -756,23 +848,24 @@ module ApplicationHelper
boxes_html = ''
values = values.present? ? values.map(&:to_s) : []
is_single = !values.is_a?(Array)
values = values.present? ? values.map(&:to_s) : [] unless is_single
boxes = boxes.map(&:to_s)
boxes.each do | box |
checked = values.include?(box)
values -= [box] if checked
checked = (is_single ? values.present? : values.include?(box))
values -= [box] if checked && !is_single
id = nil
if options[:radiobuttons].present?
id = "#{name.to_s}_#{box}"
boxes_html += radio_button_tag(name, box, checked)
else
id = "#{name.to_s}[#{box}]"
boxes_html += check_box_tag(id, 1, checked)
id = (is_single ? name : "#{name.to_s}[#{box}]")
boxes_html += check_box_tag(id, 1, checked, data: { toggles: options[:toggles] }.compact)
end
boxes_html += label_tag(id, _("#{label_key.to_s}.#{box}"))
boxes_html += label_tag(id, _(is_single ? label_key.to_s : "#{label_key.to_s}.#{box}"))
end
if options[:other].present?
if options[:other].present? && !is_single
id = nil
if options[:radiobuttons].present?
id = "#{name.to_s}_other"
@ -787,16 +880,19 @@ module ApplicationHelper
class: 'other')
end
html += content_tag(:fieldset, boxes_html.html_safe,
html += content_tag(:fieldset, content_tag(:div, boxes_html.html_safe,
class: [
'check-box-field',
'input-field',
options[:vertical] ? 'vertical' : nil,
options[:inline] ? 'inline' : nil
]).html_safe,
aria: {
labeledby: label_id,
describedby: description_id
},
class: [
'check-box-field',
'input-field',
options[:vertical] ? 'vertical' : nil
])
class: options[:centered] ? 'centered' : nil
)
return html.html_safe
end

11
app/views/conferences/_contact_info.html.haml

@ -3,16 +3,9 @@
%p=_'articles.conference_registration.paragraphs.Contact_Info', :s, 2
= columns(medium: 12) do
= form_tag register_path(@this_conference.slug) do
= textfield :name, @name, required: true, heading: 'articles.conference_registration.headings.name'
= textfield :name, @name, required: true, heading: 'articles.conference_registration.headings.name', big: true
= textfield :location, (@registration.city || location(lookup_ip_location)), required: true, heading: 'articles.conference_registration.headings.location'
%h3=_'articles.conference_registration.headings.languages','Which languages do you speak?'
- puts @errors.to_json.to_s
.check-box-field.input-field{class: @errors[:languages].present? ? 'has-error' : nil}
- [:en, :es, :fr].each do |language|
= check_box_tag "languages[#{language}]", 1, (@registration.languages || [I18n.locale.to_s]).include?(language.to_s)
= label_tag "languages_#{language}" do
= _"languages.#{language}"
= show_errors :languages
= checkboxes :languages, [:en, :es, :fr], current_user.languages, 'languages', heading: 'articles.conference_registration.headings.languages'
.actions.next-prev
= button_tag (params[:step] == :save ? :save : :next), value: :contact_info
= button_tag :previous, value: :prev_contact_info, class: :subdued, formnovalidate: true

24
app/views/conferences/_hosting.html.haml

@ -0,0 +1,24 @@
- add_stylesheet :editor
- add_inline_script :pen
- add_inline_script :markdown
- add_inline_script :editor
= columns(medium: 12) do
%h2=_(@page_title)
= columns(medium: 12) do
= form_tag register_path(@this_conference.slug) do
= checkbox :can_provide_housing, @registration.can_provide_housing, 'articles.conference_registration.can_provide_housing', heading: 'articles.conference_registration.headings.can_provide_housing', help: 'articles.conference_registration.paragraphs.can_provide_housing', inline: true, toggles: 'hosting-options', centered: true
#hosting-options
= 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 |
= 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
= selectfield :first_day, @hosting_data['availability'][0] || @this_conference.start_date, conference_housing_options_list(:before)
= selectfield :last_day, @hosting_data['availability'][1] || @this_conference.start_date, conference_housing_options_list(:after)
= checkboxes :considerations, [:vegan, :smoking, :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
= textarea :notes, @hosting_data['notes'], heading: 'articles.conference_registration.headings.host.notes', help: 'articles.conference_registration.paragraphs.host.notes'
.actions.next-prev
= button_tag (params[:step] == :save ? :save : :next), value: :hosting
= button_tag :previous, value: :prev_contact_info, class: :subdued, formnovalidate: true

2
app/views/conferences/register.html.haml

@ -1,5 +1,5 @@
= render :partial => 'page_header', :locals => {:page_key => 'Conference_Registration'}
- if (steps = current_registration_steps)
- if (steps = current_registration_steps(@registration))
= row id: 'registration-steps', class: 'flow-steps' do
= columns do
%ul

28
config/locales/en.yml

@ -5328,11 +5328,19 @@ en:
fees_paid: Fees Paid
Contact_Info: Contact Info
contact_info: Contact Info
hosting: Hosting
Add_Workshop: Propose a Workshop
Workshops_Looking_For_Facilitators: Workshops Looking for Facilitators
All_Workshops: All Workshops
Pre_Registration_Details: Pre-Registration is now open!
Verify_Account: Verify your account
Hosting: Hosting
can_provide_housing: Can you provide housing to attendees visiting the city?
host:
considerations: Special Considerations
space: Available Space
availability: Availability
address: Address
paragraphs:
Policy_Agreement: Ensuring that all attendees feel welcome, safe, and respected at all times is especially important to us all. Please ensure that you have fully read and understand our safer spaces policy below, if you have any questions or concerns you can reach out to the organizers at any time.
Confirm_Agreement: By clicking the "I Agree" button, you are pledging to do
@ -5378,6 +5386,19 @@ en:
Pre_Registration_Details: By completing the pre-registration process you are letting us know that you are interested in coming, it allows us to contact you when we have news, and lets us better plan by knowing who might be coming. Once registration is fully open, we will need to ask a few more questions and you can confirm whether or not you will actually be coming.
Verify_Account: In order to confirm that you are a real person and that we will be able to contact you later, please verify your email address. Before continuing with pre-registration, we will send you an email that will allow you to continue with the process.
facebook_sign_in: Alternatively you can sign in using your Facebook account and skip waiting for us to send you an email.
can_provide_housing: Hosting visiting conference attendees is an important part of Bike!Bike!, it allows many people who would not normally be able to afford the cost of accommodations the chance to come to the conference and lets attendees really get to know our city and its residents. You will need to grant access to your home to guests overnight keeping in mind that events can sometimes go late into the evening. If your home will not be accessible during the day you should make sure to clearly communicate this with your guests.
host:
address: Your address and phone number will be shared with your guests and conference organizers.
space: How much space do you have to share?
availability: The most common arrival and departure dates for attendees are the first and last day of the conference but people often arrive earlier or later. When will your space be available?
considerations: Organizers will do their best to match up hosts and guests, what considerations should organizers keep in mind when selecting guests to stay in your home?
notes: Leave a message for organizers should take into consideration when selecting guests for your household.
host:
considerations:
vegan: Vegan or vegetarian only
smoking: Smoking is permitted
quiet: Quiet household
can_provide_housing: I can provide housing
questions:
bike:
large: Large
@ -5546,6 +5567,13 @@ en:
content: Content
notes: Notes
message: 'Your Message:'
address: Street Address
phone: Phone number
bed_space: Bed/Couch Spaec
floor_space: Floor Space
tent_space: Tent Space
first_day: From
last_day: To
actions:
generic:
login: Sign In

5
db/migrate/20160604221121_add_can_provide_housing_to_conference_registrations.rb

@ -0,0 +1,5 @@
class AddCanProvideHousingToConferenceRegistrations < ActiveRecord::Migration
def change
add_column :conference_registrations, :can_provide_housing, :boolean
end
end

5
db/migrate/20160604221248_add_housing_data_to_conference_registrations.rb

@ -0,0 +1,5 @@
class AddHousingDataToConferenceRegistrations < ActiveRecord::Migration
def change
add_column :conference_registrations, :housing_data, :json
end
end

5
db/migrate/20160604221432_add_locale_to_users.rb

@ -0,0 +1,5 @@
class AddLocaleToUsers < ActiveRecord::Migration
def change
add_column :users, :locale, :string
end
end

5
db/schema.rb

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160530175805) do
ActiveRecord::Schema.define(version: 20160604221432) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -83,6 +83,8 @@ ActiveRecord::Schema.define(version: 20160530175805) do
t.string "food"
t.string "highest_step"
t.json "steps_completed"
t.boolean "can_provide_housing"
t.json "housing_data"
end
create_table "conference_types", force: :cascade do |t|
@ -321,6 +323,7 @@ ActiveRecord::Schema.define(version: 20160530175805) do
t.string "lastname"
t.boolean "is_translator"
t.json "languages"
t.string "locale"
end
add_index "users", ["activation_token"], name: "index_users_on_activation_token", using: :btree

Loading…
Cancel
Save