Skip to content

WAIT: Support international addresses with country-driven state field#1855

Draft
maebeale wants to merge 1 commit into
mainfrom
maebeale/international-addresses
Draft

WAIT: Support international addresses with country-driven state field#1855
maebeale wants to merge 1 commit into
mainfrom
maebeale/international-addresses

Conversation

@maebeale

Copy link
Copy Markdown
Collaborator

What is the goal of this PR and why is this important?

  • AWBW now works with people and organizations outside the US, but every address forced a US-state dropdown and required a US state — so international addresses couldn't be entered (admin forms) or submitted (public registration).
  • This makes the address forms adapt to the country: US addresses stay validated against real US states; international addresses can capture any region.

How did you approach the change?

  • Country drives the state field. On the org/person address forms, country is now a real dropdown (via the countries gem, defaulting to United States). A new address-region Stimulus controller shows a validated US-state dropdown when the country is the US, and swaps to a required free-text region input otherwise. Only the active input is enabled, so exactly one value submits.
  • Model validation. Address requires a state, but only enforces a recognized US abbreviation (case-insensitive, to tolerate legacy lowercase) when the country is the US; international addresses just need a value. Address#name drops a blank region without leaving a stray separator.
  • Shared reference data. US states live in one UsState module used by both the dropdown and the validation, so options and accepted values can't drift.
  • Public registration. The registration service now persists the submitted mailing_country/agency_country, and the public state field is free-text (US states offered as datalist suggestions) so non-US registrants can enter their region. The same model rules reject an invalid US state.

UI Testing Checklist

  • New org/person address: country defaults to United States and the state dropdown validates against US states.
  • Switching country to a non-US value swaps the state dropdown for an empty, required text field; switching back restores the dropdown.
  • Editing an existing international address shows the free-text region field pre-filled.
  • Public registration: a non-US registrant can submit a region; a US registrant entering an invalid state sees an error.

Anything else to add?

  • Reporting (EventDashboard) is unchanged: it still buckets recognized US abbreviations into the States map, leaving international regions for the Countries map.
  • The address-region controller is scoped per address block, so cocoon-added addresses work too.

🤖 Generated with Claude Code

AWBW now serves people and orgs outside the US, but every address forced a
US-state dropdown and required a US state, so international addresses couldn't
be entered or registered. Drive the state field off the country instead:

- Address forms swap the validated US-state dropdown for a required free-text
  region input whenever the country isn't the United States (new address-region
  Stimulus controller; country is a real dropdown via the countries gem).
- Address validates state is a recognized US abbreviation only for US addresses
  (case-insensitive); international addresses just require a value.
- Public registration persists the submitted country and keeps the state field
  free-text so non-US registrants can enter their region; the same model rules
  reject an invalid US state.
- Address#name drops a blank/again-optional region without a stray separator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread app/models/address.rb
# US addresses must use a recognized state abbreviation; international addresses
# store a free-form region, so they only need a value (presence above). The check
# is case-insensitive to tolerate legacy lowercase abbreviations (e.g. "tx").
validate :state_is_a_us_state, if: :united_states?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: The US-state check only runs when united_states? (blank/US country). International addresses just need a value, so a region like "Ontario" passes. The check itself is case-insensitive (see state_is_a_us_state) to tolerate legacy lowercase abbreviations the dashboard already normalizes.

def create_mailing_address(person)
new_city = field_value("mailing_city")&.strip
new_state = field_value("mailing_state")&.strip
new_country = field_value("mailing_country")&.strip.presence

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: The form already collected mailing_country but the service never saved it. That gap is load-bearing now: without a country, an international address defaults to domestic and the new US-state validation would reject the region. Persisting it (also for agency_country) is what lets non-US registrations through.

class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500" %>
</div>
<div data-address-region-wrapper class="<%= "hidden" if domestic %>">
<%= f.label :state, "State / region", for: f.field_id(:state, :region),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Both state inputs bind to the same state attribute, so the inactive one is disabled (not just hidden) — disabled fields do not submit, so exactly one value posts. Initial visibility is server-rendered from the stored country so there is no flash before the Stimulus controller connects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant