Add address fields

This commit is contained in:
Erik Michaels-Ober 2012-01-02 04:36:34 -08:00
parent d72a93843f
commit ae805e9163
17 changed files with 242 additions and 22 deletions

View File

@ -9,6 +9,7 @@ gem 'haml', '~> 3.2.0.alpha'
gem 'pg'
gem 'rails_admin', :git => 'git://github.com/sferik/rails_admin.git'
gem 'rack-contrib'
gem 'validates_formatting_of'
platforms :jruby do
gem 'jruby-openssl'

View File

@ -164,6 +164,8 @@ GEM
uglifier (1.2.1)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
validates_formatting_of (0.3.7)
rails (~> 3.0)
warden (1.1.0)
rack (>= 1.0)
webmock (1.7.8)
@ -190,4 +192,5 @@ DEPENDENCIES
therubyrhino
thin
uglifier
validates_formatting_of
webmock

View File

@ -154,7 +154,7 @@ $(function() {
});
// Focus on the first non-empty text input or password field
function setComboFormFocus() {
$('#combo-form input[type="text"]:visible, #combo-form input[type="password"]:visible, #combo-form input[type="submit"]:visible, #combo-form button:visible').each(function(index) {
$('#combo-form input[type="email"], #combo-form input[type="text"]:visible, #combo-form input[type="password"]:visible, #combo-form input[type="submit"]:visible, #combo-form input[type="tel"]:visible, #combo-form button:visible').each(function(index) {
if($(this).val() === "" || $(this).attr('type') === 'submit' || this.tagName.toLowerCase() === 'button') {
$(this).focus();
return false;
@ -512,6 +512,14 @@ $(function() {
$('#user_name_label').removeClass('error');
$('#user_name').removeClass('error');
}
if($('#user_zip').val() != '' && !/^\d{5}(-\d{4})?$/.test($('#user_zip').val())) {
errors.push($('#user_zip'));
$('#user_zip_label').addClass('error', 500);
$('#user_zip').addClass('error', 500);
} else {
$('#user_zip_label').removeClass('error');
$('#user_zip').removeClass('error');
}
if($('#user_password').val() && ($('#user_password').val().length < 6 || $('#user_password').val().length > 20)) {
errors.push($('#user_password'));
$('#user_password_label').addClass('error', 500);
@ -547,6 +555,11 @@ $(function() {
'organization': $('#user_organization').val(),
'voice_number': $('#user_voice_number').val(),
'sms_number': $('#user_sms_number').val(),
'address_1': $('#user_address_1').val(),
'address_2': $('#user_address_2').val(),
'city': $('#user_city').val(),
'state': $('#user_state').val(),
'zip': $('#user_zip').val(),
'password': $('#user_password').val(),
'password_confirmation': $('#user_password').val(),
'current_password': $('#user_current_password').val()
@ -580,6 +593,31 @@ $(function() {
$('#user_sms_number_label').addClass('error', 500);
$('#user_sms_number').addClass('error', 500);
}
if(data.errors.address_1) {
errors.push($('#user_address_1'));
$('#user_address_1_label').addClass('error', 500);
$('#user_address_1').addClass('error', 500);
}
if(data.errors.address_2) {
errors.push($('#user_address_2'));
$('#user_address_2_label').addClass('error', 500);
$('#user_address_2').addClass('error', 500);
}
if(data.errors.city) {
errors.push($('#user_city'));
$('#user_city_label').addClass('error', 500);
$('#user_city').addClass('error', 500);
}
if(data.errors.state) {
errors.push($('#user_state'));
$('#user_state_label').addClass('error', 500);
$('#user_state').addClass('error', 500);
}
if(data.errors.zip) {
errors.push($('#user_zip'));
$('#user_zip_label').addClass('error', 500);
$('#user_zip').addClass('error', 500);
}
if(data.errors.password) {
errors.push($('#user_password'));
$('#user_password_label').addClass('error', 500);

View File

@ -1,2 +1,59 @@
module ApplicationHelper
def us_states
[
['Massachusetts', 'MA'],
['Alabama', 'AL'],
['Alaska', 'AK'],
['Arizona', 'AZ'],
['Arkansas', 'AR'],
['California', 'CA'],
['Colorado', 'CO'],
['Connecticut', 'CT'],
['Delaware', 'DE'],
['District of Columbia', 'DC'],
['Florida', 'FL'],
['Georgia', 'GA'],
['Hawaii', 'HI'],
['Idaho', 'ID'],
['Illinois', 'IL'],
['Indiana', 'IN'],
['Iowa', 'IA'],
['Kansas', 'KS'],
['Kentucky', 'KY'],
['Louisiana', 'LA'],
['Maine', 'ME'],
['Maryland', 'MD'],
['Massachusetts', 'MA'],
['Michigan', 'MI'],
['Minnesota', 'MN'],
['Mississippi', 'MS'],
['Missouri', 'MO'],
['Montana', 'MT'],
['Nebraska', 'NE'],
['Nevada', 'NV'],
['New Hampshire', 'NH'],
['New Jersey', 'NJ'],
['New Mexico', 'NM'],
['New York', 'NY'],
['North Carolina', 'NC'],
['North Dakota', 'ND'],
['Ohio', 'OH'],
['Oklahoma', 'OK'],
['Oregon', 'OR'],
['Pennsylvania', 'PA'],
['Puerto Rico', 'PR'],
['Rhode Island', 'RI'],
['South Carolina', 'SC'],
['South Dakota', 'SD'],
['Tennessee', 'TN'],
['Texas', 'TX'],
['Utah', 'UT'],
['Vermont', 'VT'],
['Virginia', 'VA'],
['Washington', 'WA'],
['West Virginia', 'WV'],
['Wisconsin', 'WI'],
['Wyoming', 'WY']
]
end
end

View File

@ -1,8 +1,18 @@
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :name, :organization, :voice_number, :sms_number, :password, :password_confirmation, :remember_me
devise :database_authenticatable, :registerable, :recoverable, :rememberable,
:trackable, :validatable
attr_accessible :address_1, :address_2, :city, :email, :name, :organization,
:password, :password_confirmation, :remember_me, :sms_number, :state,
:voice_number, :zip
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates_formatting_of :zip, :using => :us_zip, :allow_blank => true
validates_presence_of :name
has_many :reminders_to, :class_name => "Reminder", :foreign_key => "to_user_id"
has_many :reminders_from, :class_name => "Reminder", :foreign_key => "from_user_id"
has_many :things
before_validation :remove_non_digits_from_phone_numbers
def remove_non_digits_from_phone_numbers
self.sms_number = self.sms_number.to_s.gsub(/\D/, '').to_i if self.sms_number.present?
self.voice_number = self.voice_number.to_s.gsub(/\D/, '').to_i if self.voice_number.present?
end
end

View File

@ -4,7 +4,7 @@
= t("labels.email")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "email", :value => params[:user] ? params[:user][:email] : nil
= f.email_field "email", :value => params[:user] ? params[:user][:email] : nil
= f.label "new" , radio_button_tag("user", "new", true).html_safe + t("labels.user_new")
= f.label "existing", radio_button_tag("user", "existing").html_safe + t("labels.user_existing")
#user_sign_up_fields
@ -22,12 +22,12 @@
= t("labels.voice_number")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "voice_number", :placeholder => "617-555-5555"
= f.telephone_field "voice_number", :placeholder => t("defaults.voice_number")
%label{:for => "user_sms_number", :id => "user_sms_number_label"}
= t("labels.sms_number")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "sms_number", :placeholder => "857-555-5555"
= f.telephone_field "sms_number", :placeholder => t("defaults.sms_number")
%label{:for => "user_password_confirmation", :id => "user_password_confirmation_label"}
= t("labels.password_choose")
%small

View File

@ -1,8 +1,8 @@
= form_tag "/address", :method => "get", :id => "address_form", :class => "form-stacked" do
= label_tag "city_state", t("labels.city_state"), :id => "city_state_label"
= select_tag "city_state", "<option value=\"#{t("defaults.city")}\" selected=\"selected\">#{t("defaults.city")}</option>".html_safe
= select_tag "city_state", "<option value=\"#{t("defaults.city_state")}\" selected=\"selected\">#{t("defaults.city_state")}</option>".html_safe
= label_tag "address", t("labels.address"), :id => "address_label"
= text_field_tag "address", params[:address], :placeholder => "1 City Hall Plaza, Downtown"
= search_field_tag "address", params[:address], :placeholder => [t("defaults.address_1"), t("defaults.neighborhood")].join(", ")
= hidden_field_tag "limit", params[:limit] || 40
= submit_tag t("buttons.find", :thing => t("defaults.thing").pluralize), :class => "btn primary"
= form_for :user, :url => edit_user_registration_path, :html => {:id => "edit_profile_form", :class => "form-stacked", :method => "get"} do |f|

View File

@ -4,7 +4,7 @@
= t("labels.email")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "email"
= f.email_field "email"
%label{:for => "user_name", :id => "user_name_label"}
= t("labels.name")
%small
@ -19,12 +19,37 @@
= t("labels.voice_number")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "voice_number", :placeholder => "617-555-5555"
= f.telephone_field "voice_number", :placeholder => t("defaults.voice_number"), :value => number_to_phone(f.object.voice_number)
%label{:for => "user_sms_number", :id => "user_sms_number_label"}
= t("labels.sms_number")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "sms_number", :placeholder => "857-555-5555"
= f.telephone_field "sms_number", :placeholder => t("defaults.sms_number"), :value => number_to_phone(f.object.sms_number)
%label{:for => "user_address_1", :id => "user_address_1_label"}
= t("labels.address_1")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "address_1", :placeholder => t("defaults.address_1")
%label{:for => "user_address_2", :id => "user_address_2_label"}
= t("labels.address_2")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "address_2", :placeholder => t("defaults.address_2")
%label{:for => "user_city", :id => "user_city_label"}
= t("labels.city")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "city", :placeholder => t("defaults.city")
%label{:for => "user_state", :id => "user_state_label"}
= t("labels.state")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.select "state", us_states, :include_blank => true
%label{:for => "user_zip", :id => "user_zip_label"}
= t("labels.zip")
%small
= image_tag "lock.png", :class => "lock", :alt => t("captions.private"), :title => t("captions.private")
= f.text_field "zip", :placeholder => t("defaults.zip")
%label{:for => "user_password", :id => "user_password_label"}
= t("labels.password_new")
%small
@ -32,6 +57,8 @@
= f.password_field "password"
%label{:for => "user_current_password", :id => "user_current_password_label"}
= t("labels.current_password")
%small
= t("captions.required")
= f.password_field "current_password"
= f.submit t("buttons.update"), :class => "btn primary"
= form_for :things, :url => root_path, :html => {:id => "back_form", :class => "form-stacked", :method => "get"} do |f|

View File

@ -18,19 +18,31 @@ de:
optional: "(fakultativ)"
private: "(privat)"
public: "(für andere sichtbar)"
required: "(erforderlich)"
defaults:
address: "Adresse"
city: "Boston, MA"
address_1: "1 City Hall Plaza"
address_2: "Suite 500"
city: "Boston"
city_state: "Boston, Massachusetts"
neighborhood: "Downtown"
sms_number: "857-555-5555"
state: "MA"
thing: "Hydranten"
this_thing: "Dieser %{thing}"
tagline: "Claim Verantwortung für schaufeln ein Feuer Hydrant, nachdem es schneit."
tos: "Mit der Anmeldung erklären Sie sich mit den %{tos}."
voice_number: "617-555-5555"
zip: "02201-2013"
errors:
password: "Sie müssen sich einloggen oder registrieren, bevor Sie fortfahren."
not_found: "Konnte nicht gefunden werden %{thing}."
labels:
address: "Adresse, Stadtviertel"
city_state: "City"
address_1: "Adresszeile 1"
address_2: "Adresszeile 2"
city: "Stadt"
city_state: "Stadt"
email: "E-Mail-Adresse"
name: "Name"
name_thing: "Geben Sie diese %{thing} einen Namen"
@ -41,9 +53,11 @@ de:
password_new: "Neues Passwort"
remember_me: "Eingeloggt bleiben"
sms_number: "Handy-Nummer"
state: "Zustand"
user_existing: "Ich habe schon unterschrieben"
user_new: "Ich habe noch nicht registriert"
voice_number: "Startseite Telefonnummer"
zip: "Postleitzahl"
links:
feedback: "Feedback senden"
forgot_password: "Passwort vergessen?"

View File

@ -18,18 +18,30 @@ en:
optional: "(optional)"
private: "(private)"
public: "(visible to others)"
required: "(required)"
defaults:
address: "address"
city: "Boston, MA"
address_1: "1 City Hall Plaza"
address_2: "Suite 500"
city: "Boston"
city_state: "Boston, Massachusetts"
neighborhood: "Downtown"
sms_number: "857-555-5555"
state: "MA"
thing: "hydrant"
this_thing: "This %{thing}"
tagline: "Claim responsibility for shoveling out a fire hydrant after it snows."
tos: "By signing up, you agree to the %{tos}."
voice_number: "617-555-5555"
zip: "02201-2013"
errors:
password: "You need to sign in or sign up before continuing."
not_found: "Could not find %{thing}."
labels:
address: "Address, Neighborhood"
address_1: "Address Line 1"
address_2: "Address Line 2"
city: "City"
city_state: "City"
email: "Email address"
name: "Name"
@ -41,9 +53,11 @@ en:
password_new: "New password"
remember_me: "Stay signed in"
sms_number: "Mobile phone number"
state: "State"
user_existing: "I've already signed up"
user_new: "I haven't signed up yet"
voice_number: "Home phone number"
zip: "ZIP"
links:
feedback: "Send feedback"
forgot_password: "Forgot your password?"

View File

@ -18,18 +18,30 @@ es:
optional: "(opcional)"
private: "(privado)"
public: "(visible para los demás)"
required: "(necesario)"
defaults:
address: "dirección"
city: "Boston, MA"
address_1: "1 City Hall Plaza"
address_2: "Suite 500"
city: "Boston"
city_state: "Boston, Massachusetts"
neighborhood: "Downtown"
sms_number: "857-555-5555"
state: "MA"
thing: "boca de incendio"
this_thing: "Esta %{thing}"
tagline: "Reclamar la responsabilidad para palear un hidrante de incendios después de que las nieves."
tos: "Al registrarse, usted está de acuerdo con los %{tos}."
voice_number: "617-555-5555"
zip: "02201-2013"
errors:
password: "Es necesario iniciar sesión o registrarse antes de continuar."
not_found: "No se pudo encontrar %{thing}."
labels:
address: "Dirección, Barrio"
address_1: "Dirección línea 1"
address_2: "Dirección línea 2"
city: "Ciudad"
city_state: "Ciudad"
email: "Dirección de correo electrónico"
name: "Nombre"
@ -41,9 +53,11 @@ es:
password_new: "Una nueva contraseña"
remember_me: "Mantener el"
sms_number: "Número de teléfono móvil"
state: "Estado"
user_existing: "Ya he firmado"
user_new: "No se ha inscrito"
voice_number: "Número de teléfono"
zip: "Código postal"
links:
feedback: "Envíenos sus comentarios"
forgot_password: "¿Olvidaste tu contraseña?"

View File

@ -18,18 +18,30 @@ fr:
optional: "(optionnelle)"
private: "(privé)"
public: "(visible pour les autres)"
required: "(nécessaire)"
defaults:
address: "Adresse"
city: "Boston, MA"
address_1: "1 City Hall Plaza"
address_2: "Suite 500"
city: "Boston"
city_state: "Boston, Massachusetts"
neighborhood: "Downtown"
sms_number: "857-555-5555"
state: "MA"
thing: "bouche d'incendie"
this_thing: "Cette %{thing}"
tagline: "La responsabilité Réclamation pour pelleter une %{thing} le feu après qu'il neige."
tos: "En vous inscrivant, vous acceptez les %{tos}."
voice_number: "617-555-5555"
zip: "02201-2013"
errors:
password: "Vous devez vous identifier ou vous inscrire avant de continuer."
not_found: "Impossible de trouver %{thing}."
labels:
address: "Adresse, Quartier"
address_1: "Adresse ligne 1"
address_2: "Adresse ligne 2"
city: "Ville"
city_state: "Ville"
email: "Adresse e-mail"
name: "Nom"
@ -41,9 +53,11 @@ fr:
password_new: "Nouveau mot de passe"
remember_me: "Rester connecté"
sms_number: "Numéro de téléphone portable"
state: "Etat"
user_existing: "J'ai déjà signé"
user_new: "Je n'ai pas encore inscrits"
voice_number: "Le numéro de téléphone Accueil"
zip: "Code postal"
links:
feedback: "Envoyer des commentaires"
forgot_password: "Mot de passe oublié?"

View File

@ -18,18 +18,30 @@ pt:
optional: "(opcional)"
private: "(privado)"
public: "(visível para os outros)"
required: "(exigido)"
defaults:
address: "endereço"
city: "Boston, MA"
address_1: "1 City Hall Plaza"
address_2: "Suite 500"
city: "Boston"
city_state: "Boston, Massachusetts"
neighborhood: "Downtown"
sms_number: "857-555-5555"
state: "MA"
thing: "hidrante"
this_thing: "Este %{thing}"
tagline: "Responsabilidade pedido de pá para fora um %{thing} de incêndio depois que neva."
tos: "Ao inscrever-se, você concorda com os %{tos}."
voice_number: "617-555-5555"
zip: "02201-2013"
errors:
password: "Você precisa fazer login ou inscreva-se antes de continuar."
not_found: "Não foi possível encontrar %{thing}."
labels:
address: "Bairro endereço,"
address: "Endereço, Bairro"
address_1: "Endereço linha 1"
address_2: "Endereço linha 2"
city: "Cidade"
city_state: "Cidade"
email: "Endereço de email"
name: "Nome"
@ -41,9 +53,11 @@ pt:
password_new: "Nova senha"
remember_me: "Fique assinado em"
sms_number: "Número de telemóvel"
state: "Estado"
user_existing: "Eu já se inscreveram"
user_new: "Eu não se inscreveram ainda"
voice_number: "Número de telefone residencial"
zip: "Código postal"
links:
feedback: "Envie seu comentário"
forgot_password: "Esqueceu sua senha?"

View File

@ -0,0 +1,9 @@
class AddAddressToUsers < ActiveRecord::Migration
def change
add_column :users, :address_1, :string
add_column :users, :address_2, :string
add_column :users, :city, :string
add_column :users, :state, :string
add_column :users, :zip, :string
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 4) do
ActiveRecord::Schema.define(:version => 5) do
create_table "rails_admin_histories", :force => true do |t|
t.string "message"
@ -70,6 +70,11 @@ ActiveRecord::Schema.define(:version => 4) do
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "address_1"
t.string "address_2"
t.string "city"
t.string "state"
t.string "zip"
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true

View File

@ -25,7 +25,7 @@ class MainControllerTest < ActionController::TestCase
end
assert_select 'label#city_state_label', 'City'
assert_select 'select#city_state' do
assert_select 'option', 'Boston, MA'
assert_select 'option', 'Boston, Massachusetts'
end
assert_select 'label#address_label', 'Address, Neighborhood'
assert_select 'input#address', true

View File

@ -15,8 +15,8 @@ class UsersControllerTest < ActionController::TestCase
assert_select '[action=?]', '/users'
assert_select '[method=?]', 'post'
end
assert_select 'input', :count => 14
assert_select 'label', :count => 7
assert_select 'input', :count => 18
assert_select 'label', :count => 12
assert_select 'input[name="commit"]' do
assert_select '[type=?]', 'submit'
assert_select '[value=?]', 'Update'