shoc-backend/SeaHaven.Services/Helpers/UsStateCodes.cs
Alexandre Brandizzi 22308f21f6 fix(dashboard): resolve region zones from full state names (SH-348)
Locations store State as either a two-letter postal code or a full state
name (the location list filter expands codes to both forms via
UsStateCodes.ExpandStorageValues, and dev fixtures carry State = "indiana").
DashboardRegions.Resolve only matched the two-letter key, so every location
stored as a full name fell into Unmapped/Other and the East/Central/West/
California bars undercounted.

Normalise the stored value through a new UsStateCodes.ToCode, which accepts a
code or a full name and returns the canonical postal code, before the zone
lookup. Extend the region test with full-name storage forms and add an
invariant asserting every US state and its full-name form resolves to a
canonical bucket.
2026-09-16 20:15:20 -03:00

89 lines
3.1 KiB
C#

namespace SeaHaven.Services.Helpers
{
public static class UsStateCodes
{
private static readonly IReadOnlyDictionary<string, string> Names = new Dictionary<string, string>(StringComparer.Ordinal)
{
["AL"] = "ALABAMA",
["AK"] = "ALASKA",
["AZ"] = "ARIZONA",
["AR"] = "ARKANSAS",
["CA"] = "CALIFORNIA",
["CO"] = "COLORADO",
["CT"] = "CONNECTICUT",
["DE"] = "DELAWARE",
["FL"] = "FLORIDA",
["GA"] = "GEORGIA",
["HI"] = "HAWAII",
["ID"] = "IDAHO",
["IL"] = "ILLINOIS",
["IN"] = "INDIANA",
["IA"] = "IOWA",
["KS"] = "KANSAS",
["KY"] = "KENTUCKY",
["LA"] = "LOUISIANA",
["ME"] = "MAINE",
["MD"] = "MARYLAND",
["MA"] = "MASSACHUSETTS",
["MI"] = "MICHIGAN",
["MN"] = "MINNESOTA",
["MS"] = "MISSISSIPPI",
["MO"] = "MISSOURI",
["MT"] = "MONTANA",
["NE"] = "NEBRASKA",
["NV"] = "NEVADA",
["NH"] = "NEW HAMPSHIRE",
["NJ"] = "NEW JERSEY",
["NM"] = "NEW MEXICO",
["NY"] = "NEW YORK",
["NC"] = "NORTH CAROLINA",
["ND"] = "NORTH DAKOTA",
["OH"] = "OHIO",
["OK"] = "OKLAHOMA",
["OR"] = "OREGON",
["PA"] = "PENNSYLVANIA",
["RI"] = "RHODE ISLAND",
["SC"] = "SOUTH CAROLINA",
["SD"] = "SOUTH DAKOTA",
["TN"] = "TENNESSEE",
["TX"] = "TEXAS",
["UT"] = "UTAH",
["VT"] = "VERMONT",
["VA"] = "VIRGINIA",
["WA"] = "WASHINGTON",
["WV"] = "WEST VIRGINIA",
["WI"] = "WISCONSIN",
["WY"] = "WYOMING"
};
private static readonly IReadOnlyDictionary<string, string> CodesByName =
Names.ToDictionary(pair => pair.Value, pair => pair.Key, StringComparer.Ordinal);
public static IReadOnlySet<string> All { get; } = new HashSet<string>(Names.Keys, StringComparer.Ordinal);
/// <summary>
/// Normalises a stored state value to its two-letter postal code. Accepts either a
/// postal code ("IN") or a full state name ("Indiana"), case-insensitively, matching
/// the storage forms produced by <see cref="ExpandStorageValues"/>. Returns null when
/// the value is blank or is not a recognised US state.
/// </summary>
public static string? ToCode(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
var normalized = value.Trim().ToUpperInvariant();
if (All.Contains(normalized))
return normalized;
return CodesByName.TryGetValue(normalized, out var code) ? code : null;
}
public static IReadOnlyCollection<string> ExpandStorageValues(IEnumerable<string> codes)
{
return codes
.SelectMany(code => new[] { code, Names[code] })
.ToArray();
}
}
}