Skip to content

Commit

Permalink
Merge pull request #5461 from nebulab/rainerd/admin/order/address-form
Browse files Browse the repository at this point in the history
[Admin] Order process: Integrate address form component for billing and shipping
  • Loading branch information
rainerdema authored Nov 8, 2023
2 parents 54712c7 + 3a5daf8 commit 378ed49
Show file tree
Hide file tree
Showing 20 changed files with 278 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<div class="<%= stimulus_id %>">
<%= render component("orders/show").new(order: @order) %>
<%= render component("ui/modal").new(title: t(".title.#{@type}"), close_path: solidus_admin.order_path(@order)) do |modal| %>
<%= form_for @order, url: solidus_admin.send("order_#{@type}_address_path", @order), html: { id: form_id } do |form| %>
<div class="w-full flex flex-col mb-4">
<h2 class="text-sm mb-4 font-semibold"><%= t(".subtitle.#{@type}") %></h2>
<div class="w-full flex gap-4">
<%= form.fields_for :"#{@type}_address" do |address_form| %>
<%= render component('ui/forms/address').new(form: address_form, disabled: false) %>
<% end %>
</div>

<label class="flex gap-2 items-center">
<%= form.hidden_field use_attribute, value: '0', id: false %>

<%= render component("ui/forms/checkbox").new(
name: "#{form.object_name}[#{use_attribute}]",
checked: form.object.send("#{@type}_address").new_record? || form.object.bill_address == form.object.ship_address,
value: '1'
) %>

<span class="body-text-sm">
<%= t(".use_this_address.#{@type}") %>
</span>
</label>
</div>
<% end %>

<% modal.with_actions do %>
<%= render component("ui/button").new(
tag: :a,
scheme: :secondary,
text: t(".cancel"),
href: solidus_admin.order_path(@order)
) %>

<%= render component("ui/button").new(
tag: :button,
text: t(".save"),
form: form_id
) %>
<% end %>
<% end %>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

class SolidusAdmin::Orders::Show::Address::Component < SolidusAdmin::BaseComponent
include SolidusAdmin::Layout::PageHelpers

VALID_TYPES = ['ship', 'bill'].freeze

def initialize(order:, type: 'ship')
@order = order
@type = validate_address_type(type)
end

def form_id
@form_id ||= "#{stimulus_id}--form-#{@type}-#{@order.id}"
end

def use_attribute
case @type
when 'ship'
'use_shipping'
when 'bill'
'use_billing'
end
end

def validate_address_type(type)
VALID_TYPES.include?(type) ? type : raise(ArgumentError, "Invalid address type: #{type}")
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Add your component translations here.
# Use the translation in the example in your template with `t(".hello")`.
en:
save: Save
cancel: Cancel
back: Back
title:
ship: Edit Shipping Address
bill: Edit Billing Address
subtitle:
ship: Shipping Address
bill: Billing Address
use_this_address:
ship: Use this address also for Billing
bill: Use this address also for Shipping
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
<%= page_with_sidebar_aside do %>
<%= render component('ui/panel').new(title: panel_title_with_more_links(t(".customer"), [
link_to(t(".edit_email"), solidus_admin.order_customer_path(@order), class: "p-2 hover:bg-gray-25 rounded-sm text-black"),
link_to(t(".edit_shipping"), "#", class: "p-2 hover:bg-gray-25 rounded-sm text-black"),
link_to(t(".edit_billing"), "#", class: "p-2 hover:bg-gray-25 rounded-sm text-black"),
link_to(t(".edit_shipping"), solidus_admin.new_order_ship_address_path(@order), class: "p-2 hover:bg-gray-25 rounded-sm text-black"),
link_to(t(".edit_billing"), solidus_admin.new_order_bill_address_path(@order), class: "p-2 hover:bg-gray-25 rounded-sm text-black"),
link_to(t(".remove_customer"), solidus_admin.order_customer_path(@order), 'data-turbo-method': :delete, class: "p-2 hover:bg-gray-25 rounded-sm text-red-500"),
])) do %>
<div class="flex flex-col -m-6 p-6 gap-6 border-t border-gray-100 mt-0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
closeMenus() {
this.event.querySelectorAll('details').forEach(details => details.removeAttribute('open'));
this.element.querySelectorAll('details').forEach(details => details.removeAttribute('open'));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def format_address(address)
address.address2,
address.city,
address.zipcode,
address.state.name,
address.state&.name,
tag.br,
address.country.name,
tag.br,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<%= render component("ui/forms/field").text_field(@form, :name) %>
<%= render component("ui/forms/field").text_field(@form, :address1) %>
<%= render component("ui/forms/field").text_field(@form, :address2) %>
<div class="flex w-full space-x-4">
<%= render component("ui/forms/field").text_field(@form, :city, class: "flex-1") %>
<%= render component("ui/forms/field").text_field(@form, :zipcode, class: "flex-1") %>
<div class="flex gap-4 w-full">
<%= render component("ui/forms/field").text_field(@form, :city) %>
<%= render component("ui/forms/field").text_field(@form, :zipcode) %>
</div>

<%= render component("ui/forms/field").select(
Expand All @@ -23,8 +23,9 @@
<%= render component("ui/forms/field").select(
@form,
:state_id,
[],
state_options,
value: @form.object.try(:state_id),
disabled: @form.object.country&.states&.empty?,
"data-#{stimulus_id}-target": "state"
) %>

Expand Down
21 changes: 11 additions & 10 deletions admin/app/components/solidus_admin/ui/forms/address/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import { Controller } from '@hotwired/stimulus'
export default class extends Controller {
static targets = ["country", "state"]

connect() {
this.loadStates()
}

loadStates() {
const countryId = this.countryTarget.value

Expand All @@ -22,12 +18,17 @@ export default class extends Controller {

stateSelect.innerHTML = ""

data.forEach(state => {
const option = document.createElement("option")
if (data.length === 0) {
stateSelect.disabled = true
} else {
stateSelect.disabled = false

option.value = state.id
option.innerText = state.name
stateSelect.appendChild(option)
})
data.forEach((state) => {
const option = document.createElement("option")
option.value = state.id
option.innerText = state.name
stateSelect.appendChild(option)
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ def initialize(form:, disabled: false)
@form = form
@disabled = disabled
end

def state_options
return [] unless @form.object.country
@form.object.country.states.map { |s| [s.name, s.id] }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
) %>
</header>

<div class="p-4 overflow-auto max-h-96">
<div class="p-4 overflow-auto">
<%= content %>
</div>

Expand Down
67 changes: 67 additions & 0 deletions admin/app/controllers/solidus_admin/addresses_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module SolidusAdmin
class AddressesController < BaseController
include Spree::Core::ControllerHelpers::StrongParameters

before_action :load_order
before_action :validate_address_type

def new
address = @order.send("#{address_type}_address")
@order.send("build_#{address_type}_address", country_id: default_country_id) if address.nil?
address ||= @order.send("#{address_type}_address")
address.country_id ||= default_country_id if address.country.nil?

respond_to do |format|
format.html { render component('orders/show/address').new(order: @order, type: address_type) }
end
end

def update
if @order.contents.update_cart(order_params)
redirect_to order_path(@order), status: :see_other, notice: t('.success')
else
flash.now[:error] = @order.errors[:base].join(", ") if @order.errors[:base].any?

respond_to do |format|
format.html { render component('orders/show/address').new(order: @order, type: address_type), status: :unprocessable_entity }
end
end
end

private

def address_type
params[:type].presence_in(%w[bill ship])
end

def validate_address_type
unless address_type
flash[:error] = t('.errors.address_type_invalid')
redirect_to spree.admin_order_url(@order)
end
end

def default_country_id
@default_country_id ||= begin
country = Spree::Country.default
country.id if Spree::Country.available.exists?(id: country.id)
end
end

def load_order
@order = Spree::Order.find_by!(number: params[:order_id])
authorize! action_name, @order
end

def order_params
params.require(:order).permit(
:use_billing,
:use_shipping,
bill_address_attributes: permitted_address_attributes,
ship_address_attributes: permitted_address_attributes
)
end
end
end
6 changes: 6 additions & 0 deletions admin/config/locales/orders.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ en:
title: "Orders"
update:
success: "Order was updated successfully"
addresses:
title: "Addresses"
update:
success: "The address has been successfully updated."
errors:
address_type_invalid: "Invalid address type. Please select either billing or shipping address."
2 changes: 2 additions & 0 deletions admin/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
resources :orders, only: [:index, :show, :edit, :update] do
resources :line_items, only: [:destroy, :create, :update]
resource :customer
resource :ship_address, only: [:new, :update], controller: "addresses", type: "ship"
resource :bill_address, only: [:new, :update], controller: "addresses", type: "bill"

member do
get :variants_for
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

# @component "orders/show/address"
class SolidusAdmin::Orders::Show::Address::ComponentPreview < ViewComponent::Preview
include SolidusAdmin::Preview

def overview
type = "ship"
order = fake_order(type)

render_with_template(
locals: {
order: order,
type: type
}
)
end

# @param type select :type_options
def playground(type: "ship")
order = fake_order(type)
render current_component.new(order: order, type: type)
end

private

def fake_order(type)
order = Spree::Order.new
country = Spree::Country.find_or_initialize_by(iso: Spree::Config.default_country_iso)

order.define_singleton_method(:id) { 1 }
order.define_singleton_method(:persisted?) { true }
order.define_singleton_method(:to_param) { id.to_s }
order.send("build_#{type}_address", { country: country })
order
end

def type_options
current_component::VALID_TYPES
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= render current_component.new(order: order, type: type) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe SolidusAdmin::Orders::Show::Address::Component, type: :component do
it "renders the overview preview" do
render_preview(:overview)
end
end
2 changes: 2 additions & 0 deletions api/openapi/solidus-api.oas.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6754,6 +6754,8 @@ components:
type: string
use_billing:
type: boolean
use_shipping:
type: boolean
bill_address_attributes:
$ref: '#/components/schemas/address-input'
ship_address_attributes:
Expand Down
11 changes: 11 additions & 0 deletions core/app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ def states
before_validation :set_currency
before_validation :generate_order_number, on: :create
before_validation :assign_billing_to_shipping_address, if: :use_billing?
before_validation :assign_shipping_to_billing_address, if: :use_shipping?
attr_accessor :use_billing
attr_accessor :use_shipping

before_create :create_token
before_create :link_by_email
Expand Down Expand Up @@ -271,6 +273,11 @@ def assign_billing_to_shipping_address
true
end

def assign_shipping_to_billing_address
self.bill_address = ship_address if ship_address
true
end

def allow_cancel?
return false unless completed? && state != 'canceled'
shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state)
Expand Down Expand Up @@ -860,6 +867,10 @@ def use_billing?
use_billing.in?([true, 'true', '1'])
end

def use_shipping?
use_shipping.in?([true, 'true', '1'])
end

def set_currency
self.currency = Spree::Config[:currency] if self[:currency].nil?
end
Expand Down
1 change: 1 addition & 0 deletions core/lib/spree/permitted_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ module PermittedAttributes

@@checkout_address_attributes = [
:use_billing,
:use_shipping,
:email,
bill_address_attributes: address_attributes,
ship_address_attributes: address_attributes
Expand Down
Loading

0 comments on commit 378ed49

Please sign in to comment.