Skip to content

Fix saving personal info on the Admin User page - #1469

Merged
ardalis merged 4 commits into
mainfrom
fix/admin-user-personal-info-save
Sep 21, 2026
Merged

ardalis merged 4 commits into
mainfrom
fix/admin-user-personal-info-save

Conversation

@ardalis

@ardalis ardalis commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Why

Editing a new member's name and email on /Admin/User?userId=... failed with a raw JSON 400 instead of saving:

{"UserId":["The userId field is required."],"InvalidUserId":["Bad Data"],
 "UserPersonalUpdateModel.City":["The City field is required."], ... ,
 "UserPersonalUpdateModel.LastName":["The LastName field is required."]}

Root causes

  1. Blank userId. The Personal and Links forms posted UserPersonalUpdateModel.UserId / UserLinksUpdateModel.UserId. Those are only set after OnGetAsync loads the member. That happens after the Stripe invoice search, inside a catch-all that swallows exceptions. If anything earlier fails, the page still renders with an empty hidden userId and every save fails.
  2. Address fields are [Required]. New members have no shipping address, so an admin couldn't save a name or email change without inventing an address.
  3. BadRequest(ModelState) turned any validation error into raw JSON.

Changes (Admin User page only)

  • Adds a UserId page property set from the route at the start of OnGetAsync, and uses it for the Personal and Links hidden fields.
  • Wraps the Stripe invoice lookup in its own try/catch with logging, so a Stripe failure doesn't stop the rest of the page from loading.
  • OnPostUpdatePersonalInfoAsync:
    • If every shipping address field is blank, drops the address validation errors and leaves the member's shipping address unchanged. Once any address field is filled in, the existing required rules still apply.
    • On a validation failure, reloads the page, keeps the admin's submitted values, and shows inline errors instead of returning a 400 with JSON.
    • A missing member record is shown as an error on the page instead of throwing MemberNotFoundException.
  • Adds a validation summary to the Personal card for errors that aren't tied to a field.

UserPersonalUpdateModel itself is unchanged, so validation on members' own My Profile → Personal page is unaffected. That's the difference from draft #1423, which this supersedes.

LastName is still required. Members with a single name will need something entered there. That's now shown as an inline error rather than a 400.

Testing

  • dotnet build DevBetterWeb.slnx succeeds.
  • DevBetterWeb.Tests: 104/104 pass. 4 new tests for OnPostUpdatePersonalInfoAsync:
    • saves name and email with no shipping address
    • redisplays the form with submitted values when the address is partial
    • redisplays the form instead of returning a 400 when LastName is missing
    • shows an error when there's no member record
  • Codex review was not run. The change was self-reviewed.

🤖 Generated with Claude Code

Saving name/email on /Admin/User failed with a raw JSON 400:
- The form's hidden userId came from UserPersonalUpdateModel.UserId,
  which is only set once OnGetAsync reaches the member load. Anything
  failing earlier (e.g. the Stripe invoice search, swallowed by the
  catch-all) left it blank, so every save failed "userId is required".
  The page now captures UserId from the route first, and invoice lookup
  failures are isolated and logged.
- Address/City/Country/PostalCode are [Required], so members without a
  shipping address could not be edited. On the admin page they are now
  only required once any address field is filled in, and the shipping
  address is left untouched when none is.
- Validation failures redisplay the form with the submitted values and
  inline errors instead of returning BadRequest(ModelState). A missing
  member record is reported on the page instead of throwing.

Supersedes draft #1423, which changed the shared model and would also
have relaxed validation on members' own profile page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread src/DevBetterWeb.Web/Pages/Admin/User.cshtml.cs Fixed
Comment on lines +108 to +111
catch (Exception exception)
{
_logger.LogError(exception, "Unable to load Stripe invoices for userId {UserId}", userId);
}
ardalis and others added 3 commits September 21, 2026 15:14
…from user input'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@ardalis
ardalis merged commit ab90d75 into main Sep 21, 2026
0 of 2 checks passed
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.

2 participants