diff --git a/admin/app/components/solidus_admin/orders/show/address/component.html.erb b/admin/app/components/solidus_admin/orders/show/address/component.html.erb index f8a547ac963..f3e1600f2ab 100644 --- a/admin/app/components/solidus_admin/orders/show/address/component.html.erb +++ b/admin/app/components/solidus_admin/orders/show/address/component.html.erb @@ -1,12 +1,43 @@ -
+
<%= 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| %>
-

<%= t(".subtitle.#{@type}") %>

+
+

+ <%= t(".subtitle.#{@type}") %> +

+ + <% if @user&.addresses&.any? %> +
-target="addresses"> + + <%= t(".select_address") %> + <%= render component("ui/icon").new(name: 'arrow-down-s-fill', class: 'w-5 h-5') %> + + +
+ <% @user.addresses.each do |address| %> + <%= tag.a( + href: solidus_admin.send("order_#{@type}_address_path", @order, address_id: address.id), + class: 'block text-black text-sm hover:bg-gray-50 p-2 mx-2 w-auto rounded-lg', + 'data-action': "#{stimulus_id}#close", + 'data-turbo-frame': address_frame_id + ) do %> + <%= format_address(address) %> + <% end %> + <% end %> +
+
+ <% end %> +
+
- <%= form.fields_for :"#{@type}_address" do |address_form| %> - <%= render component('ui/forms/address').new(form: address_form, disabled: false) %> + <%= turbo_frame_tag address_frame_id do %> + <%= render component('ui/forms/address').new(address: @address, name: "order[#{@type}_address_attributes]") %> <% end %>
diff --git a/admin/app/components/solidus_admin/orders/show/address/component.js b/admin/app/components/solidus_admin/orders/show/address/component.js new file mode 100644 index 00000000000..ebb6df4ab34 --- /dev/null +++ b/admin/app/components/solidus_admin/orders/show/address/component.js @@ -0,0 +1,9 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + static targets = ["addresses"] + + close() { + this.addressesTarget.removeAttribute('open') + } +} diff --git a/admin/app/components/solidus_admin/orders/show/address/component.rb b/admin/app/components/solidus_admin/orders/show/address/component.rb index 6bb5970074b..4ab9208ebf5 100644 --- a/admin/app/components/solidus_admin/orders/show/address/component.rb +++ b/admin/app/components/solidus_admin/orders/show/address/component.rb @@ -5,8 +5,10 @@ class SolidusAdmin::Orders::Show::Address::Component < SolidusAdmin::BaseCompone VALID_TYPES = ['ship', 'bill'].freeze - def initialize(order:, type: 'ship') + def initialize(order:, address:, user: nil, type: 'ship') @order = order + @user = user + @address = address @type = validate_address_type(type) end @@ -14,6 +16,10 @@ def form_id @form_id ||= "#{stimulus_id}--form-#{@type}-#{@order.id}" end + def address_frame_id + @table_frame_id ||= "#{stimulus_id}--#{@type}-address-frame-#{@order.id}" + end + def use_attribute case @type when 'ship' @@ -23,6 +29,23 @@ def use_attribute end end + def format_address(address) + safe_join([ + address.name, + tag.br, + address.address1, + tag.br, + address.address2, + address.city, + address.zipcode, + address.state&.name, + tag.br, + address.country.name, + tag.br, + address.phone, + ], " ") + end + def validate_address_type(type) VALID_TYPES.include?(type) ? type : raise(ArgumentError, "Invalid address type: #{type}") end diff --git a/admin/app/components/solidus_admin/orders/show/address/component.yml b/admin/app/components/solidus_admin/orders/show/address/component.yml index e406088f45f..d7c82068537 100644 --- a/admin/app/components/solidus_admin/orders/show/address/component.yml +++ b/admin/app/components/solidus_admin/orders/show/address/component.yml @@ -4,6 +4,7 @@ en: save: Save cancel: Cancel back: Back + select_address: Select address title: ship: Edit Shipping Address bill: Edit Billing Address diff --git a/admin/app/components/solidus_admin/orders/show/component.html.erb b/admin/app/components/solidus_admin/orders/show/component.html.erb index a92fa6ea60c..cb585e8196d 100644 --- a/admin/app/components/solidus_admin/orders/show/component.html.erb +++ b/admin/app/components/solidus_admin/orders/show/component.html.erb @@ -16,8 +16,8 @@ <%= page_with_sidebar_aside do %> <%= render component('ui/panel').new do |panel| %> <% panel.with_menu t(".edit_email"), solidus_admin.order_customer_path(@order) %> - <% panel.with_menu t(".edit_shipping"), solidus_admin.new_order_ship_address_path(@order) %> - <% panel.with_menu t(".edit_billing"), solidus_admin.new_order_bill_address_path(@order) %> + <% panel.with_menu t(".edit_shipping"), solidus_admin.edit_order_ship_address_path(@order) %> + <% panel.with_menu t(".edit_billing"), solidus_admin.edit_order_bill_address_path(@order) %> <% panel.with_menu t(".remove_customer"), solidus_admin.order_customer_path(@order), method: :delete, class: "text-red-500" if @order.user %> <% panel.with_section(class: 'flex flex-col gap-6') do %> @@ -49,7 +49,7 @@ <% if @order.ship_address %> <%= format_address @order.ship_address %> <% else %> - <%= link_to t(".add_shipping"), solidus_admin.new_order_ship_address_path(@order), class: 'body-link' %> + <%= link_to t(".add_shipping"), solidus_admin.edit_order_ship_address_path(@order), class: 'body-link' %> <% end %>
@@ -58,7 +58,7 @@ <%= @order.class.human_attribute_name(:bill_address) %>
<% if @order.bill_address.blank? %> - <%= link_to t(".add_billing"), solidus_admin.new_order_bill_address_path(@order), class: 'body-link' %> + <%= link_to t(".add_billing"), solidus_admin.edit_order_bill_address_path(@order), class: 'body-link' %> <% elsif @order.bill_address == @order.ship_address %> <%= t('.same_as_shipping') %> <% else %> diff --git a/admin/app/components/solidus_admin/ui/forms/address/component.html.erb b/admin/app/components/solidus_admin/ui/forms/address/component.html.erb index e4c66ad6057..3567418ffb1 100644 --- a/admin/app/components/solidus_admin/ui/forms/address/component.html.erb +++ b/admin/app/components/solidus_admin/ui/forms/address/component.html.erb @@ -3,32 +3,34 @@ <%= :disabled if @disabled %> >
- <%= 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) %> + <%= render component("ui/forms/field").text_field(@name, :name, object: @address) %> + <%= render component("ui/forms/field").text_field(@name, :address1, object: @address) %> + <%= render component("ui/forms/field").text_field(@name, :address2, object: @address) %>
- <%= render component("ui/forms/field").text_field(@form, :city) %> - <%= render component("ui/forms/field").text_field(@form, :zipcode) %> + <%= render component("ui/forms/field").text_field(@name, :city, object: @address) %> + <%= render component("ui/forms/field").text_field(@name, :zipcode, object: @address) %>
<%= render component("ui/forms/field").select( - @form, + @name, :country_id, Spree::Country.all.map { |c| [c.name, c.id] }, - value: @form.object.try(:country_id), + object: @address, + value: @address.try(:country_id), "data-#{stimulus_id}-target": "country", "data-action": "change->#{stimulus_id}#loadStates" ) %> <%= render component("ui/forms/field").select( - @form, + @name, :state_id, state_options, - value: @form.object.try(:state_id), - disabled: @form.object.country&.states&.empty?, + object: @address, + value: @address.try(:state_id), + disabled: @address.country&.states&.empty?, "data-#{stimulus_id}-target": "state" ) %> - <%= render component("ui/forms/field").text_field(@form, :phone) %> + <%= render component("ui/forms/field").text_field(@name, :phone, object: @address) %>
diff --git a/admin/app/components/solidus_admin/ui/forms/address/component.rb b/admin/app/components/solidus_admin/ui/forms/address/component.rb index 5aa67048967..fffe19e6d3a 100644 --- a/admin/app/components/solidus_admin/ui/forms/address/component.rb +++ b/admin/app/components/solidus_admin/ui/forms/address/component.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true class SolidusAdmin::UI::Forms::Address::Component < SolidusAdmin::BaseComponent - def initialize(form:, disabled: false) - @form = form + def initialize(address:, name:, disabled: false) + @address = address + @name = name @disabled = disabled end def state_options - return [] unless @form.object.country - @form.object.country.states.map { |s| [s.name, s.id] } + return [] unless @address.country + @address.country.states.map { |s| [s.name, s.id] } end end diff --git a/admin/app/components/solidus_admin/ui/forms/field/component.rb b/admin/app/components/solidus_admin/ui/forms/field/component.rb index b66c7cc1a97..a2bb0b300a1 100644 --- a/admin/app/components/solidus_admin/ui/forms/field/component.rb +++ b/admin/app/components/solidus_admin/ui/forms/field/component.rb @@ -12,61 +12,78 @@ def initialize(label:, hint: nil, tip: nil, error: nil, input_attributes: nil, * raise ArgumentError, "provide either a block or input_attributes" if content? && input_attributes end - def self.text_field(form, method, hint: nil, tip: nil, size: :m, **attributes) - errors = form.object.errors.messages_for(method).presence + def self.text_field(form, method, object: nil, hint: nil, tip: nil, size: :m, **attributes) + object_name, object, label, errors = extract_form_details(form, object, method) new( - label: form.object.class.human_attribute_name(method), + label: label, hint: hint, tip: tip, error: errors, input_attributes: { - name: "#{form.object_name}[#{method}]", + name: "#{object_name}[#{method}]", tag: :input, size: size, - value: form.object.public_send(method), + value: object.public_send(method), error: (errors.to_sentence.capitalize if errors), **attributes, } ) end - def self.select(form, method, choices, hint: nil, tip: nil, size: :m, **attributes) - errors = form.object.errors.messages_for(method).presence + def self.select(form, method, choices, object: nil, hint: nil, tip: nil, size: :m, **attributes) + object_name, object, label, errors = extract_form_details(form, object, method) new( - label: form.object.class.human_attribute_name(method), + label: label, hint: hint, tip: tip, error: errors, input_attributes: { - name: "#{form.object_name}[#{method}]", + name: "#{object_name}[#{method}]", tag: :select, choices: choices, size: size, - value: form.object.public_send(method), + value: object.public_send(method), error: (errors.to_sentence.capitalize if errors), **attributes, } ) end - def self.text_area(form, method, hint: nil, tip: nil, size: :m, **attributes) - errors = form.object.errors.messages_for(method).presence + def self.text_area(form, method, object: nil, hint: nil, tip: nil, size: :m, **attributes) + object_name, object, label, errors = extract_form_details(form, object, method) new( - label: form.object.class.human_attribute_name(method), + label: label, hint: hint, tip: tip, error: errors, input_attributes: { - name: "#{form.object_name}[#{method}]", + name: "#{object_name}[#{method}]", size: size, tag: :textarea, - value: form.object.public_send(method), + value: object.public_send(method), error: (errors.to_sentence.capitalize if errors), **attributes, } ) end + + def self.extract_form_details(form, object, method) + if form.is_a?(String) + object_name = form + raise ArgumentError, "Object must be provided when form name is a string" unless object + elsif form.respond_to?(:object) + object_name = form.object_name + object = form.object + else + raise ArgumentError, "Invalid arguments: expected a form object or form.object_name and form.object" + end + + errors = object.errors.messages_for(method).presence if object.respond_to?(:errors) + label = object.class.human_attribute_name(method) + + [object_name, object, label, errors] + end end diff --git a/admin/app/controllers/solidus_admin/addresses_controller.rb b/admin/app/controllers/solidus_admin/addresses_controller.rb index 8cbb7bfd3e2..83a8da3a91e 100644 --- a/admin/app/controllers/solidus_admin/addresses_controller.rb +++ b/admin/app/controllers/solidus_admin/addresses_controller.rb @@ -7,17 +7,25 @@ class AddressesController < BaseController 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? + def show + address = find_address || build_new_address respond_to do |format| - format.html { render component('orders/show/address').new(order: @order, type: address_type) } + format.html do + render component('orders/show/address').new( + order: @order, + user: @order.user, + address: address, + type: address_type, + ) + end end end + def edit + redirect_to action: :show + end + def update if @order.contents.update_cart(order_params) redirect_to order_path(@order), status: :see_other, notice: t('.success') @@ -25,13 +33,36 @@ def update 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 } + format.html do + render component('orders/show/address').new( + order: @order, + user: @order.user, + address: @order.send("#{address_type}_address"), + type: address_type, + status: :unprocessable_entity, + ) + end end end end private + def find_address + if params[:address_id].present? && @order.user + address = @order.user.addresses.find_by(id: params[:address_id]) + @order.send("#{address_type}_address=", address) if address + else + @order.send("#{address_type}_address") + end + end + + def build_new_address + @order.send("build_#{address_type}_address", country_id: default_country_id).tap do |address| + address.country_id ||= default_country_id if address.country.nil? + end + end + def address_type params[:type].presence_in(%w[bill ship]) end diff --git a/admin/config/routes.rb b/admin/config/routes.rb index d0c3a997fa9..6b45f676195 100644 --- a/admin/config/routes.rb +++ b/admin/config/routes.rb @@ -21,8 +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" + resource :ship_address, only: [:show, :edit, :update], controller: "addresses", type: "ship" + resource :bill_address, only: [:show, :edit, :update], controller: "addresses", type: "bill" member do get :variants_for diff --git a/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview.rb b/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview.rb index 2120f317a95..4279afad087 100644 --- a/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview.rb +++ b/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview.rb @@ -11,6 +11,7 @@ def overview render_with_template( locals: { order: order, + address: order.send("#{type}_address"), type: type } ) @@ -19,7 +20,7 @@ def overview # @param type select :type_options def playground(type: "ship") order = fake_order(type) - render current_component.new(order: order, type: type) + render current_component.new(order: order, address: order.send("#{type}_address"), type: type) end private diff --git a/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview/overview.html.erb b/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview/overview.html.erb index 1f2f4e68235..a5b1b6ac203 100644 --- a/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview/overview.html.erb +++ b/admin/spec/components/previews/solidus_admin/orders/show/address/component_preview/overview.html.erb @@ -1 +1 @@ -<%= render current_component.new(order: order, type: type) %> +<%= render current_component.new(order: order, address: address, type: type) %> diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview.rb b/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview.rb index e3bd6a00526..cba6252cd1b 100644 --- a/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview.rb +++ b/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview.rb @@ -5,15 +5,22 @@ class SolidusAdmin::UI::Forms::Address::ComponentPreview < ViewComponent::Previe include SolidusAdmin::Preview def overview - render_with_template + render_with_template(locals: { address: fake_address }) end # @param disabled toggle def playground(disabled: false) - view = ActionView::Base.new(ActionView::LookupContext.new([]), {}, nil) render component("ui/forms/address").new( - form: ActionView::Helpers::FormBuilder.new(:address, Spree::Address.new, view, {}), + name: "", + address: fake_address, disabled: disabled ) end + + private + + def fake_address + country = Spree::Country.find_or_initialize_by(iso: Spree::Config.default_country_iso) + Spree::Address.new(country: country) + end end diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview/overview.html.erb b/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview/overview.html.erb index 8068d4ec9b7..057e6ed9a3f 100644 --- a/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview/overview.html.erb +++ b/admin/spec/components/previews/solidus_admin/ui/forms/address/component_preview/overview.html.erb @@ -1,3 +1 @@ -<%= form_for Spree::Address.new, url: '#' do |f| %> - <%= render current_component.new(form: f, disabled: false) %> -<% end %> +<%= render current_component.new(name: "", address: address) %>