diff --git a/app/assets/javascripts/admin/spree/orders/address_states.js b/app/assets/javascripts/admin/spree/orders/address_states.js index 5542e5656a..ef1cf6b367 100644 --- a/app/assets/javascripts/admin/spree/orders/address_states.js +++ b/app/assets/javascripts/admin/spree/orders/address_states.js @@ -1,27 +1,17 @@ var update_state = function(region) { var country = $('span#' + region + 'country .select2').select2('val'); - var state_select = $('span#' + region + 'state select.select2'); - var state_input = $('span#' + region + 'state input.state_name'); - $.get(Spree.routes.states_search + "?country_id=" + country, function(data) { - var states = data["states"] - if (states.length > 0) { - state_select.html(''); - var states_with_blank = [{name: '', id: ''}].concat(states); - $.each(states_with_blank, function(pos,state) { - var opt = $(document.createElement('option')) - .attr('value', state.id) - .html(state.name); - state_select.append(opt); - }); - state_select.prop("disabled", false).show(); - state_select.select2(); - state_input.hide().prop("disabled", true); - - } else { - state_input.prop("disabled", false).show(); - state_select.select2('destroy').hide(); - } + $.get(Spree.routes.states_search + "?country_id=" + country, function(states) { + state_select.html(''); + var states_with_blank = [{name: '', id: ''}].concat(states); + $.each(states_with_blank, function(pos,state) { + var opt = $(document.createElement('option')) + .attr('value', state.id) + .html(state.name); + state_select.append(opt); + }); + state_select.prop("disabled", false).show(); + state_select.select2(); }) }; diff --git a/app/assets/javascripts/admin/spree/orders/use_billing.js b/app/assets/javascripts/admin/spree/orders/use_billing.js new file mode 100644 index 0000000000..20d1d96668 --- /dev/null +++ b/app/assets/javascripts/admin/spree/orders/use_billing.js @@ -0,0 +1,17 @@ +$(document).ready(function() { + var order_use_billing_input = $('input#order_use_billing'); + + var order_use_billing = function () { + if (!order_use_billing_input.is(':checked')) { + $('#shipping').show(); + } else { + $('#shipping').hide(); + } + }; + + order_use_billing_input.click(function() { + order_use_billing(); + }); + + order_use_billing(); +}); diff --git a/app/controllers/api/states_controller.rb b/app/controllers/api/states_controller.rb new file mode 100644 index 0000000000..0ad0b991d4 --- /dev/null +++ b/app/controllers/api/states_controller.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Api + class StatesController < Api::BaseController + respond_to :json + + skip_authorization_check + + def index + render json: states, each_serializer: Api::StateSerializer, status: :ok + end + + def show + @state = scope.find(params[:id]) + render json: @state, serializer: Api::StateSerializer, status: :ok + end + + private + + def scope + if params[:country_id] + @country = Spree::Country.find(params[:country_id]) + @country.states + else + Spree::State.all + end + end + + def states + states = scope.ransack(params[:q]).result. + includes(:country).order('name ASC') + + if pagination? + states = states.page(params[:page]).per(params[:per_page]) + end + + states + end + + def pagination? + params[:page] || params[:per_page] + end + end +end diff --git a/app/serializers/api/state_serializer.rb b/app/serializers/api/state_serializer.rb index 38ad444701..2862c24468 100644 --- a/app/serializers/api/state_serializer.rb +++ b/app/serializers/api/state_serializer.rb @@ -1,3 +1,3 @@ class Api::StateSerializer < ActiveModel::Serializer - attributes :id, :name, :abbr + attributes :id, :name, :abbr, :country_id end diff --git a/app/views/spree/admin/orders/customer_details/_address_form.html.haml b/app/views/spree/admin/orders/customer_details/_address_form.html.haml new file mode 100644 index 0000000000..18bd0970b6 --- /dev/null +++ b/app/views/spree/admin/orders/customer_details/_address_form.html.haml @@ -0,0 +1,45 @@ +- if use_billing + .field{style: "position: absolute;margin-top: -15px;right: 0;"} + %span + = check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address)) + = label_tag 'order[use_billing]', Spree.t(:use_billing_address) + +- is_shipping_address = name == Spree.t(:shipping_address) +- s_or_b = is_shipping_address ? 's' : 'b' + +- display_style = (use_billing && (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address == @order.ship_address)) ? 'none' : 'block' + +%div{id: "#{is_shipping_address ? 'shipping' : 'billing'}", style: "display: #{display_style}"} + %div{class: "field"} + = f.label :firstname, Spree.t(:first_name) + ':' + = f.text_field :firstname, class: 'fullwidth' + %div{class: "field"} + = f.label :lastname, Spree.t(:last_name) + ':' + = f.text_field :lastname, class: 'fullwidth' + - if Spree::Config[:company] + %div{class: "field"} + = f.label :company, Spree.t(:company) + ':' + = f.text_field :company, class: 'fullwidth' + %div{class: "field"} + = f.label :address1, Spree.t(:street_address) + ':' + = f.text_field :address1, class: 'fullwidth' + %div{class: "field"} + = f.label :address2, Spree.t(:street_address_2) + ':' + = f.text_field :address2, class: 'fullwidth' + %div{class: "field"} + = f.label :city, Spree.t(:city) + ':' + = f.text_field :city, class: 'fullwidth' + %div{class: "field"} + = f.label :zipcode, Spree.t(:zip) + ':' + = f.text_field :zipcode, class: 'fullwidth' + %div{class: "field"} + = f.label :country_id, Spree.t(:country) + ':' + %span{id: "#{s_or_b}country"} + = f.collection_select :country_id, available_countries, :id, :name, {}, {class: 'select2 fullwidth', onchange: "update_state('#{s_or_b}')"} + %div{class: "field"} + = f.label :state_id, Spree.t(:state) + ':' + %span{id: "#{s_or_b}state"} + = f.collection_select :state_id, f.object.country.states.sort, :id, :name, {include_blank: true}, {class: 'select2 fullwidth', disabled: f.object.country.states.empty?} + %div{class: "field"} + = f.label :phone, Spree.t(:phone) + ':' + = f.phone_field :phone, class: 'fullwidth' diff --git a/app/views/spree/admin/orders/customer_details/_form.html.haml b/app/views/spree/admin/orders/customer_details/_form.html.haml index 3cfb029474..5ac4e15a52 100644 --- a/app/views/spree/admin/orders/customer_details/_form.html.haml +++ b/app/views/spree/admin/orders/customer_details/_form.html.haml @@ -11,13 +11,13 @@ %fieldset.no-border-bottom %legend{:align => "center"}= Spree.t(:billing_address) = f.fields_for :bill_address do |ba_form| - = render :partial => 'spree/admin/shared/address_form', :locals => { :f => ba_form, :name => Spree.t(:billing_address), :use_billing => false } + = render :partial => 'address_form', :locals => { :f => ba_form, :name => Spree.t(:billing_address), :use_billing => false } .omega.six.columns{"data-hook" => "ship_address_wrapper"} %fieldset.no-border-bottom %legend{:align => "center"}= Spree.t(:shipping_address) = f.fields_for :ship_address do |sa_form| - = render :partial => 'spree/admin/shared/address_form', :locals => { :f => sa_form, :name => Spree.t(:shipping_address), :use_billing => true } + = render :partial => 'address_form', :locals => { :f => sa_form, :name => Spree.t(:shipping_address), :use_billing => true } .clear @@ -26,3 +26,4 @@ - content_for :head do = javascript_include_tag 'admin/spree/orders/address_states.js' + = javascript_include_tag 'admin/spree/orders/use_billing.js' diff --git a/app/views/spree/admin/shared/_address_form.html.erb b/app/views/spree/admin/shared/_address_form.html.erb deleted file mode 100644 index 012c40d9e8..0000000000 --- a/app/views/spree/admin/shared/_address_form.html.erb +++ /dev/null @@ -1,72 +0,0 @@ -<% if use_billing %> -
- - <%= check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address)) %> - <%= label_tag 'order[use_billing]', Spree.t(:use_billing_address) %> - -
-<% end %> - -<% is_shipping_address = name == Spree.t(:shipping_address) %> -<% shipping_or_billing = is_shipping_address ? 'shipping' : 'billing' %> -<% s_or_b = is_shipping_address ? 's' : 'b' %> - -
-
"> - <%= f.label :firstname, Spree.t(:first_name) + ':' %> - <%= f.text_field :firstname, :class => 'fullwidth' %> -
-
"> - <%= f.label :lastname, Spree.t(:last_name) + ':' %> - <%= f.text_field :lastname, :class => 'fullwidth' %> -
- <% if Spree::Config[:company] %> -
"> - <%= f.label :company, Spree.t(:company) + ':' %> - <%= f.text_field :company, :class => 'fullwidth' %> -
- <% end %> -
"> - <%= f.label :address1, Spree.t(:street_address) + ':' %> - <%= f.text_field :address1, :class => 'fullwidth' %> -
-
"> - <%= f.label :address2, Spree.t(:street_address_2) + ':' %> - <%= f.text_field :address2, :class => 'fullwidth' %> -
-
"> - <%= f.label :city, Spree.t(:city) + ':' %> - <%= f.text_field :city, :class => 'fullwidth' %> -
-
"> - <%= f.label :zipcode, Spree.t(:zip) + ':' %> - <%= f.text_field :zipcode, :class => 'fullwidth' %> -
-
"> - <%= f.label :country_id, Spree.t(:country) + ':' %> - - <%= f.collection_select :country_id, available_countries, :id, :name, {}, {:class => 'select2 fullwidth'} %> - -
-
"> - <%= f.label :state_id, Spree.t(:state) + ':' %> - - <%= f.text_field :state_name, - :style => "display: #{f.object.country.states.empty? ? 'block' : 'none' };", - :disabled => !f.object.country.states.empty?, :class => 'fullwidth state_name' %> - <%= f.collection_select :state_id, f.object.country.states.sort, :id, :name, {:include_blank => true}, {:class => 'select2 fullwidth', :style => "display: #{f.object.country.states.empty? ? 'none' : 'block' };", :disabled => f.object.country.states.empty?} %> - -
-
"> - <%= f.label :phone, Spree.t(:phone) + ':' %> - <%= f.phone_field :phone, :class => 'fullwidth' %> -
-
- -<% content_for :head do %> - <%= javascript_tag do -%> - $(document).ready(function(){ - $('span#<%= s_or_b %>country .select2').on('change', function() { update_state('<%= s_or_b %>'); }); - }); - <% end -%> -<% end %> diff --git a/app/views/spree/admin/shared/_routes.html.erb b/app/views/spree/admin/shared/_routes.html.erb index ba3bbda3fe..15d562e4ef 100644 --- a/app/views/spree/admin/shared/_routes.html.erb +++ b/app/views/spree/admin/shared/_routes.html.erb @@ -2,6 +2,7 @@ Spree.routes = <%== { :variants_search => spree.admin_search_variants_path(:format => 'json'), :taxons_search => main_app.api_taxons_path(:format => 'json'), - :orders_api => main_app.api_orders_path + :orders_api => main_app.api_orders_path, + :states_search => main_app.api_states_path(:format => 'json') }.to_json %>; diff --git a/config/routes/api.rb b/config/routes/api.rb index 7bc81c03fe..6bdfd5dcb3 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -62,6 +62,8 @@ Openfoodnetwork::Application.routes.draw do post '/product_images/:product_id', to: 'product_images#update_product_image' + resources :states, :only => [:index, :show] + resources :taxons, :only => [:index] resources :taxonomies do diff --git a/spec/controllers/api/states_controller_spec.rb b/spec/controllers/api/states_controller_spec.rb new file mode 100644 index 0000000000..81313c3153 --- /dev/null +++ b/spec/controllers/api/states_controller_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Api + describe StatesController do + render_views + + let!(:state) { create(:state, name: "Victoria") } + let(:attributes) { [:id, :name, :abbr, :country_id] } + let(:current_user) { create(:user) } + + before do + allow(controller).to receive(:spree_current_user) { current_user } + end + + it "gets all states" do + api_get :index + expect(json_response.first.symbolize_keys.keys).to include(*attributes) + expect(json_response.map { |state| state[:name] }).to include(state.name) + end + + context "pagination" do + before do + expect(Spree::State).to receive(:all).and_return(@scope = double) + allow(@scope).to receive_message_chain(:ransack, :result, :includes, :order).and_return(@scope) + end + + it "does not paginate states results when asked not to do so" do + expect(@scope).not_to receive(:page) + expect(@scope).not_to receive(:per) + api_get :index + end + + it "paginates when page parameter is passed through" do + expect(@scope).to receive(:page).with(1).and_return(@scope) + expect(@scope).to receive(:per).with(nil) + api_get :index, page: 1 + end + + it "paginates when per_page parameter is passed through" do + expect(@scope).to receive(:page).with(nil).and_return(@scope) + expect(@scope).to receive(:per).with(25) + api_get :index, per_page: 25 + end + end + + context "with two states" do + before { create(:state, name: "New South Wales") } + + it "gets all states for a country" do + country = create(:country, states_required: true) + state.country = country + state.save + + api_get :index, country_id: country.id + expect(json_response.first.symbolize_keys.keys).to include(*attributes) + expect(json_response.count).to eq 1 + end + + it "can view all states" do + api_get :index + expect(json_response.first.symbolize_keys.keys).to include(*attributes) + end + + it 'can query the results through a paramter' do + api_get :index, q: { name_cont: 'Vic' } + expect(json_response.first['name']).to eq("Victoria") + end + end + + it "can view a state" do + api_get :show, id: state.id + expect(json_response.symbolize_keys.keys).to include(*attributes) + end + end +end