identity-vault/app/views/addresses/_form.html.erb
nora 3e456b98aa
Address autocomplete! (#115)
* first shot

* it works!

* fix addr portal start action

* fix add address button

* pass in country

* that should do it!

* wew!

* lint
2025-12-19 12:20:18 -05:00

98 lines
4.3 KiB
Text

<%
country_calling_codes = Address.countries.keys.each_with_object({}) do |alpha2, hash|
hash[alpha2] = Address.country_calling_code(alpha2)
end
initial_country = address.country || current_identity&.country || "US"
initial_calling_code = Address.country_calling_code(initial_country) || "1"
# Strip country code prefix for display (it's shown separately)
raw_phone = address.phone_number.presence || current_identity&.phone_number
display_phone = if raw_phone.present? && raw_phone.start_with?("+")
raw_phone.sub(/^\+#{Regexp.escape(initial_calling_code)}/, "")
else
raw_phone
end
%>
<% content_for :head do %>
<%= vite_javascript_tag 'address-autocomplete' %>
<%= google_places_api_script_tag %>
<% end %>
<%
if local_assigns[:htmx_target]
htmx_method_key = address.persisted? ? "hx-patch" : "hx-post"
form_html_attrs = { htmx_method_key => local_assigns[:url], "hx-target" => local_assigns[:htmx_target], "hx-swap" => "innerHTML", "data-turbo" => "false" }
else
form_html_attrs = {}
end
%>
<%= form_with model: address, url: local_assigns[:url], local: true, html: form_html_attrs.merge("x-data": "addressAutocomplete", "@alpine:init": "Object.assign($data, { callingCodes: #{country_calling_codes.to_json}, callingCode: '#{initial_calling_code}', selectedCountry: '#{initial_country}' })") do |f| %>
<% if local_assigns[:from_program] %>
<%= f.hidden_field :from_program, value: true %>
<% end %>
<% if address.errors.any? %>
<div class="banner danger" style="margin-bottom: 1.5rem;">
<h4 style="color: var(--error-fg); margin: 0 0 0.5rem;"><%= pluralize(address.errors.count, "issue") %> prevented this from being saved:</h4>
<ul style="margin: 0; padding-left: 1.5rem;">
<% address.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="grid">
<%= f.text_field :first_name, placeholder: t("addresses.first_name"), required: true %>
<%= f.text_field :last_name, placeholder: t("addresses.last_name"), required: true %>
</div>
<div>
<gmp-place-autocomplete placeholder="Address line 1" x-ref="autocomplete" style="width: 100%;"></gmp-place-autocomplete>
</div>
<%= f.hidden_field :line_1, "x-ref": "line1" %>
<%= f.text_field :line_2, placeholder: "#{t("addresses.line_2")} (optional)", "x-ref": "line2" %>
<div class="grid">
<%= f.text_field :city, placeholder: t("addresses.city"), required: true, "x-ref": "city" %>
<%= f.text_field :state, placeholder: t("addresses.state"), required: true, "x-ref": "state" %>
</div>
<div>
<div class="grid">
<%= f.text_field :postal_code, placeholder: t("addresses.postal_code"), required: true, "x-ref": "postalCode" %>
<%= f.collection_select :country, Address.countries_for_select,
:first, :last,
{ include_blank: "Country" },
{ required: true, "@change": "updateCallingCode($el)", "x-ref": "country" } %>
</div>
<div class="grid" style="grid-template-columns: auto 1fr; margin-top: 1rem;">
<span style="padding: 0.5rem 0.75rem; background: var(--input-bg); border: 1px solid var(--input-border); border-radius: var(--radius); display: flex; align-items: center; font-size: 0.95rem; color: var(--text-muted);">
+<span x-text="callingCode"></span>
</span>
<%= f.telephone_field :phone_number,
placeholder: t("addresses.phone_number"),
value: display_phone,
required: true,
style: "margin: 0;" %>
</div>
</div>
<div class="form-actions">
<%= f.submit local_assigns[:submit] || t("addresses.save") %>
<% unless local_assigns[:from_program] %>
<% if local_assigns[:htmx_target] %>
<button type="button" class="secondary"
hx-get="<%= addresses_path(portal: local_assigns[:portal], refresh_list: true) %>"
hx-target="<%= local_assigns[:htmx_target] %>"
hx-swap="innerHTML">
<%= t("addresses.cancel") %>
</button>
<% else %>
<%= link_to t("addresses.cancel"), addresses_path, role: "button", class: "secondary" %>
<% end %>
<% end %>
</div>
<% end %>