Skip to content

Commit 56fe8ee

Browse files
committed
[wip] Include "Select address" feature
1 parent 6bbb872 commit 56fe8ee

File tree

11 files changed

+160
-45
lines changed

11 files changed

+160
-45
lines changed

admin/app/components/solidus_admin/orders/show/address/component.html.erb

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,36 @@
1-
<div class="<%= stimulus_id %>">
1+
<div class="<%= stimulus_id %>" data-controller="<%= stimulus_id %>">
22
<%= render component("orders/show").new(order: @order) %>
33
<%= render component("ui/modal").new(title: t(".title.#{@type}"), close_path: solidus_admin.order_path(@order)) do |modal| %>
44
<%= form_for @order, url: solidus_admin.send("order_#{@type}_address_path", @order), html: { id: form_id } do |form| %>
55
<div class="w-full flex flex-col mb-4">
6-
<h2 class="text-sm mb-4 font-semibold"><%= t(".subtitle.#{@type}") %></h2>
6+
<div class="flex justify-between items-center mb-4">
7+
<h2 class="text-sm font-semibold"><%= t(".subtitle.#{@type}") %></h2>
8+
9+
<div class="flex justify-between items-center">
10+
<div class="relative">
11+
<button type="button" data-action="<%= stimulus_id %>#toggle" class="text-sm w-full text-left flex items-center justify-between">
12+
<%= t(".select_address") %>
13+
<%= render component("ui/icon").new(name: 'arrow-down-s-fill', class: 'w-5 h-5') %>
14+
</button>
15+
16+
<div
17+
data-<%= stimulus_id %>-target="menu"
18+
class="absolute hidden right-0 my-1 bg-white border border-gray-300 rounded py-2 mt-1 shadow-lg z-10 w-full min-w-[16rem] max-h-[26rem] overflow-y-auto"
19+
>
20+
<% @order.user&.addresses&.each do |address| %>
21+
<%= tag.a class: 'block text-sm hover:bg-gray-50 p-2 mx-2 w-auto rounded',
22+
'data-turbo-frame': address_frame_id,
23+
href: solidus_admin.send("order_#{@type}_address_path", @order, address_id: address.id) do %>
24+
<%= format_address(address) %>
25+
<% end %>
26+
<% end %>
27+
</div>
28+
</div>
29+
</div>
30+
</div>
731
<div class="w-full flex gap-4">
8-
<%= form.fields_for :"#{@type}_address" do |address_form| %>
9-
<%= render component('ui/forms/address').new(form: address_form, disabled: false) %>
32+
<%= turbo_frame_tag address_frame_id, target: "_top" do %>
33+
<%= render component('ui/forms/address').new(form: "order[#{@type}_address_attributes]", object: @address, disabled: false) %>
1034
<% end %>
1135
</div>
1236

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Controller } from '@hotwired/stimulus'
2+
3+
export default class extends Controller {
4+
static targets = ["menu"]
5+
6+
toggle() {
7+
this.menuTarget.classList.toggle('hidden')
8+
}
9+
10+
close() {
11+
this.menuTarget.classList.add('hidden')
12+
}
13+
}

admin/app/components/solidus_admin/orders/show/address/component.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,20 @@ class SolidusAdmin::Orders::Show::Address::Component < SolidusAdmin::BaseCompone
55

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

8-
def initialize(order:, type: 'ship')
8+
def initialize(order:, address:, type: 'ship')
99
@order = order
1010
@type = validate_address_type(type)
11+
@address = address
1112
end
1213

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

18+
def address_frame_id
19+
@table_frame_id ||= "#{stimulus_id}--address-frame-#{@order.id}"
20+
end
21+
1722
def use_attribute
1823
case @type
1924
when 'ship'
@@ -23,6 +28,24 @@ def use_attribute
2328
end
2429
end
2530

31+
def format_address(address)
32+
return unless address
33+
safe_join([
34+
address.name,
35+
tag.br,
36+
address.address1,
37+
tag.br,
38+
address.address2,
39+
address.city,
40+
address.zipcode,
41+
address.state&.name,
42+
tag.br,
43+
address.country.name,
44+
tag.br,
45+
address.phone,
46+
], " ")
47+
end
48+
2649
def validate_address_type(type)
2750
VALID_TYPES.include?(type) ? type : raise(ArgumentError, "Invalid address type: #{type}")
2851
end

admin/app/components/solidus_admin/orders/show/address/component.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ en:
44
save: Save
55
cancel: Cancel
66
back: Back
7+
select_address: Select address
78
title:
89
ship: Edit Shipping Address
910
bill: Edit Billing Address

admin/app/components/solidus_admin/orders/show/component.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
<%= page_with_sidebar_aside do %>
1717
<%= render component('ui/panel').new do |panel| %>
1818
<% panel.with_menu t(".edit_email"), solidus_admin.order_customer_path(@order) %>
19-
<% panel.with_menu t(".edit_shipping"), solidus_admin.new_order_ship_address_path(@order) %>
20-
<% panel.with_menu t(".edit_billing"), solidus_admin.new_order_bill_address_path(@order) %>
19+
<% panel.with_menu t(".edit_shipping"), solidus_admin.edit_order_ship_address_path(@order) %>
20+
<% panel.with_menu t(".edit_billing"), solidus_admin.edit_order_bill_address_path(@order) %>
2121
<% panel.with_menu t(".remove_customer"), solidus_admin.order_customer_path(@order), method: :delete, class: "text-red-500" if @order.user %>
2222

2323
<% panel.with_section(class: 'flex flex-col gap-6') do %>
Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
<fieldset class="<%= stimulus_id %>"
1+
<fieldset class="<%= stimulus_id %> address-form-selector"
22
data-controller="<%= stimulus_id %>"
33
<%= :disabled if @disabled %>
44
>
55
<div class="<%= stimulus_id %>--address-form flex flex-wrap gap-4 pb-4">
6-
<%= render component("ui/forms/field").text_field(@form, :name) %>
7-
<%= render component("ui/forms/field").text_field(@form, :address1) %>
8-
<%= render component("ui/forms/field").text_field(@form, :address2) %>
6+
<%= render component("ui/forms/field").text_field(@form, :name, object: @object) %>
7+
<%= render component("ui/forms/field").text_field(@form, :address1, object: @object) %>
8+
<%= render component("ui/forms/field").text_field(@form, :address2, object: @object) %>
99
<div class="flex gap-4 w-full">
10-
<%= render component("ui/forms/field").text_field(@form, :city) %>
11-
<%= render component("ui/forms/field").text_field(@form, :zipcode) %>
10+
<%= render component("ui/forms/field").text_field(@form, :city, object: @object) %>
11+
<%= render component("ui/forms/field").text_field(@form, :zipcode, object: @object) %>
1212
</div>
1313

1414
<%= render component("ui/forms/field").select(
1515
@form,
1616
:country_id,
1717
Spree::Country.all.map { |c| [c.name, c.id] },
18-
value: @form.object.try(:country_id),
18+
object: @object,
19+
value: @object.try(:country_id),
1920
"data-#{stimulus_id}-target": "country",
2021
"data-action": "change->#{stimulus_id}#loadStates"
2122
) %>
@@ -24,11 +25,12 @@
2425
@form,
2526
:state_id,
2627
state_options,
27-
value: @form.object.try(:state_id),
28-
disabled: @form.object.country&.states&.empty?,
28+
object: @object,
29+
value: @object.try(:state_id),
30+
disabled: @object.country&.states&.empty?,
2931
"data-#{stimulus_id}-target": "state"
3032
) %>
3133

32-
<%= render component("ui/forms/field").text_field(@form, :phone) %>
34+
<%= render component("ui/forms/field").text_field(@form, :phone, object: @object) %>
3335
</div>
3436
</fieldset>

admin/app/components/solidus_admin/ui/forms/address/component.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { Controller } from '@hotwired/stimulus'
22

33
export default class extends Controller {
4-
static targets = ["country", "state"]
4+
static targets = ["state", "country"]
5+
6+
countryValueChanged() {
7+
this.loadStates()
8+
}
59

610
loadStates() {
711
const countryId = this.countryTarget.value
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# frozen_string_literal: true
22

33
class SolidusAdmin::UI::Forms::Address::Component < SolidusAdmin::BaseComponent
4-
def initialize(form:, disabled: false)
4+
def initialize(form:, object:, disabled: false, html_attributes: {})
55
@form = form
6+
@object = object
67
@disabled = disabled
8+
@html_attributes = html_attributes
79
end
810

911
def state_options
10-
return [] unless @form.object.country
11-
@form.object.country.states.map { |s| [s.name, s.id] }
12+
return [] unless @object.country
13+
@object.country.states.map { |s| [s.name, s.id] }
1214
end
1315
end

admin/app/components/solidus_admin/ui/forms/field/component.rb

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,61 +12,78 @@ def initialize(label:, hint: nil, tip: nil, error: nil, input_attributes: nil, *
1212
raise ArgumentError, "provide either a block or input_attributes" if content? && input_attributes
1313
end
1414

15-
def self.text_field(form, method, hint: nil, tip: nil, size: :m, **attributes)
16-
errors = form.object.errors.messages_for(method).presence
15+
def self.text_field(form, method, object: nil, hint: nil, tip: nil, size: :m, **attributes)
16+
object_name, object, label, errors = extract_form_details(form, object, method)
1717

1818
new(
19-
label: form.object.class.human_attribute_name(method),
19+
label: label,
2020
hint: hint,
2121
tip: tip,
2222
error: errors,
2323
input_attributes: {
24-
name: "#{form.object_name}[#{method}]",
24+
name: "#{object_name}[#{method}]",
2525
tag: :input,
2626
size: size,
27-
value: form.object.public_send(method),
27+
value: object.public_send(method),
2828
error: (errors.to_sentence.capitalize if errors),
2929
**attributes,
3030
}
3131
)
3232
end
3333

34-
def self.select(form, method, choices, hint: nil, tip: nil, size: :m, **attributes)
35-
errors = form.object.errors.messages_for(method).presence
34+
def self.select(form, method, choices, object: nil, hint: nil, tip: nil, size: :m, **attributes)
35+
object_name, object, label, errors = extract_form_details(form, object, method)
3636

3737
new(
38-
label: form.object.class.human_attribute_name(method),
38+
label: label,
3939
hint: hint,
4040
tip: tip,
4141
error: errors,
4242
input_attributes: {
43-
name: "#{form.object_name}[#{method}]",
43+
name: "#{object_name}[#{method}]",
4444
tag: :select,
4545
choices: choices,
4646
size: size,
47-
value: form.object.public_send(method),
47+
value: object.public_send(method),
4848
error: (errors.to_sentence.capitalize if errors),
4949
**attributes,
5050
}
5151
)
5252
end
5353

54-
def self.text_area(form, method, hint: nil, tip: nil, size: :m, **attributes)
55-
errors = form.object.errors.messages_for(method).presence
54+
def self.text_area(form, method, object: nil, hint: nil, tip: nil, size: :m, **attributes)
55+
object_name, object, label, errors = extract_form_details(form, object, method)
5656

5757
new(
58-
label: form.object.class.human_attribute_name(method),
58+
label: label,
5959
hint: hint,
6060
tip: tip,
6161
error: errors,
6262
input_attributes: {
63-
name: "#{form.object_name}[#{method}]",
63+
name: "#{object_name}[#{method}]",
6464
size: size,
6565
tag: :textarea,
66-
value: form.object.public_send(method),
66+
value: object.public_send(method),
6767
error: (errors.to_sentence.capitalize if errors),
6868
**attributes,
6969
}
7070
)
7171
end
72+
73+
def self.extract_form_details(form, object, method)
74+
if form.is_a?(String)
75+
object_name = form
76+
raise ArgumentError, "Object must be provided when form name is a string" unless object
77+
elsif form.respond_to?(:object)
78+
object_name = form.object_name
79+
object = form.object
80+
else
81+
raise ArgumentError, "Invalid arguments: expected a form object or form.object_name and form.object"
82+
end
83+
84+
errors = object.errors.messages_for(method).presence if object.respond_to?(:errors)
85+
label = object.class.human_attribute_name(method)
86+
87+
[object_name, object, label, errors]
88+
end
7289
end

admin/app/controllers/solidus_admin/addresses_controller.rb

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,60 @@ class AddressesController < BaseController
77
before_action :load_order
88
before_action :validate_address_type
99

10-
def new
11-
address = @order.send("#{address_type}_address")
12-
@order.send("build_#{address_type}_address", country_id: default_country_id) if address.nil?
13-
address ||= @order.send("#{address_type}_address")
14-
address.country_id ||= default_country_id if address.country.nil?
10+
def show
11+
address = find_address || build_new_address
1512

1613
respond_to do |format|
17-
format.html { render component('orders/show/address').new(order: @order, type: address_type) }
14+
format.html do
15+
render component('orders/show/address').new(
16+
order: @order,
17+
address: address,
18+
type: address_type,
19+
)
20+
end
1821
end
1922
end
2023

24+
def edit
25+
redirect_to action: :show
26+
end
27+
2128
def update
2229
if @order.contents.update_cart(order_params)
2330
redirect_to order_path(@order), status: :see_other, notice: t('.success')
2431
else
2532
flash.now[:error] = @order.errors[:base].join(", ") if @order.errors[:base].any?
2633

2734
respond_to do |format|
28-
format.html { render component('orders/show/address').new(order: @order, type: address_type), status: :unprocessable_entity }
35+
format.html do
36+
render component('orders/show/address').new(
37+
order: @order,
38+
address: @order.send("#{address_type}_address"),
39+
type: address_type,
40+
status: :unprocessable_entity,
41+
)
42+
end
2943
end
3044
end
3145
end
3246

3347
private
3448

49+
def find_address
50+
if params[:address_id].present? && @order.user
51+
#@order.user.addresses.find_by(id: params[:address_id])
52+
@order.user.addresses.find(params[:address_id])
53+
else
54+
@order.send("#{address_type}_address")
55+
end
56+
end
57+
58+
def build_new_address
59+
@order.send("build_#{address_type}_address", country_id: default_country_id).tap do |address|
60+
address.country_id ||= default_country_id if address.country.nil?
61+
end
62+
end
63+
3564
def address_type
3665
params[:type].presence_in(%w[bill ship])
3766
end

0 commit comments

Comments
 (0)