fix: webhook/email avatar fallback + inline settings form errors (LC-356, LC-360) #370

Merged
longjacksonle merged 2 commits from fix/lc-356-360-audit-ux into main 2026-06-19 06:11:35 +02:00

Two UX/correctness MED findings from the UI audit. just test and just test-saas green (one unrelated flake in email_notification_post_path confirmed by isolated + full re-run).

LC-360 - webhook/email avatars degrade gracefully

The webhook (LC-74) and email-ingress (LC-77) message avatars rendered an external URL as a bare <img> with no error handling, so a broken/blocked URL showed a broken-image icon (unlike the bridge path, which falls back to initials). Add the same onerror initials fallback + loading="lazy". The LC-77 render fixtures are updated to the new markup.

LC-356 - inline form errors

post_profile and post_delete_account returned AppError::BadRequest on validation failure (too-long bio/name/email, invalid email, wrong avatar type/size, wrong confirm phrase, sole-admin / sole-owner blocks), rendering a full-page error with a "back home" link and losing the user's place on the highest-stakes forms. Add a shared settings_error_redirect helper that PRG-redirects to /settings?error=<msg> (mirroring the existing ?saved=1 flash); get_settings reads it and the template renders a danger banner. Messages are fixed server strings (never raw user input), Askama-escaped on render. Tests: an over-long profile field and each delete-account blocker now redirect to /settings?error= and persist nothing.

Closes LC-356, LC-360.

Two UX/correctness MED findings from the UI audit. `just test` and `just test-saas` green (one unrelated flake in `email_notification_post_path` confirmed by isolated + full re-run). ## LC-360 - webhook/email avatars degrade gracefully The webhook (LC-74) and email-ingress (LC-77) message avatars rendered an external URL as a bare `<img>` with no error handling, so a broken/blocked URL showed a broken-image icon (unlike the bridge path, which falls back to initials). Add the same `onerror` initials fallback + `loading="lazy"`. The LC-77 render fixtures are updated to the new markup. ## LC-356 - inline form errors `post_profile` and `post_delete_account` returned `AppError::BadRequest` on validation failure (too-long bio/name/email, invalid email, wrong avatar type/size, wrong confirm phrase, sole-admin / sole-owner blocks), rendering a full-page error with a "back home" link and losing the user's place on the highest-stakes forms. Add a shared `settings_error_redirect` helper that PRG-redirects to `/settings?error=<msg>` (mirroring the existing `?saved=1` flash); `get_settings` reads it and the template renders a danger banner. Messages are fixed server strings (never raw user input), Askama-escaped on render. Tests: an over-long profile field and each delete-account blocker now redirect to `/settings?error=` and persist nothing. Closes LC-356, LC-360.
The webhook (LC-74) and email-ingress (LC-77) message avatars rendered an operator/daemon-supplied external URL as a bare <img> with no error handling, so a broken or blocked URL showed a broken-image icon - unlike the bridge avatar, which falls back to initials via onerror. Add the same onerror initials fallback (and loading="lazy") to both, so a failed external avatar degrades gracefully. The LC-77 render fixtures are updated to the new img markup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(settings): flash profile/delete-account errors inline instead of a full-page error (LC-356)
All checks were successful
check-secrets / Nosey parker (pull_request) Successful in 3s
check-secrets / Kingfisher (push) Successful in 4s
check-secrets / Nosey parker (push) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 7s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / TruffleHog (push) Successful in 6s
Check / clippy + fmt + tests (pull_request) Successful in 2m55s
Create release / Create release from merged PR (pull_request) Has been skipped
f95c68d4eb
post_profile and post_delete_account returned AppError::BadRequest on validation failure (too-long bio/name/email, invalid email, wrong avatar type/size, wrong confirm phrase, sole-admin / sole-owner blocks), which rendered a full-page error with a "back home" link - losing the user's place on the highest-stakes forms on the page. Other settings sub-forms (api-tokens, blocked) already re-render inline.

Add a shared settings_error_redirect helper that PRG-redirects to /settings?error=<msg> (mirroring the existing ?saved=1 / ?session_revoked=1 flashes); get_settings reads the param and the template renders a danger banner. The messages are fixed server-side strings (never raw user input) and Askama-escaped on render. Tests: an over-long profile field and each delete-account blocker now redirect to /settings?error= and persist nothing; the blocking enclave/admin detail rides the query param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch fix/lc-356-360-audit-ux 2026-06-19 06:11:35 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!370
No description provided.