Skip to content

fix(spa): smart-route action redirect URLs (#620) + release 1.4.13#643

Merged
MartinCastroAlvarez merged 1 commit into
mainfrom
fix/action-redirect-routing
May 31, 2026
Merged

fix(spa): smart-route action redirect URLs (#620) + release 1.4.13#643
MartinCastroAlvarez merged 1 commit into
mainfrom
fix/action-redirect-routing

Conversation

@MartinCastroAlvarez
Copy link
Copy Markdown
Owner

Closes #620. Also tags v1.4.13 to ship every change accumulated on main since v1.4.12.

#620 — what actually broke

A ModelAdmin action that returns HttpResponseRedirect(some_url) was silently no-op'd from the operator's POV. The issue blamed the API for swallowing the response, but the API correctly extracts response["Location"] into the JSON envelope's redirect field (api/views/actions.py:256). The real bug was SPA-side: DetailPage piped redirect straight into React Router's navigate — scoped to the SPA's BrowserRouter basename, so any URL outside the SPA mount silently failed:

  • Legacy admin paths (/admin/<app>/<model>/<pk>/change/)
  • Hijack / impersonate URLs (/hijack/release-user/?next=…)
  • Cross-origin downloads (signed S3 URLs)

Fix

New followActionRedirect helper picks the right primitive per URL:

  • Same origin AND under the SPA mount → navigate (no full reload, basename-relative path)
  • Anything else → window.location.assign (full browser navigation)

Helper is dependency-injected (currentOrigin, assignLocation) so tests can lock the routing logic without touching jsdom's non-configurable window.location.

What else lands in 1.4.13

All already merged on main since 1.4.12:

Verification

  • 6 new vitests in action-redirect.test.ts cover SPA-internal path, search + hash preservation, legacy-admin path, cross-origin URL, hijack pattern, malformed URL.
  • pnpm test — 187 / 187 ✓ (up from 181 / 181)
  • pnpm -r typecheck
  • pnpm lint
  • pnpm -w build

🤖 Generated with Claude Code

….4.13

A ModelAdmin action that returns ``HttpResponseRedirect(some_url)``
was looking silently no-op'd to the operator: the click ran, the
toast didn't appear, and nothing visible happened. The diagnosis in
the issue blamed the API for swallowing the response, but the API
correctly extracts ``response["Location"]`` into the JSON envelope's
``redirect`` field (``api/views/actions.py:256``). The actual bug was
on the SPA: ``DetailPage`` piped the redirect URL straight into
React Router's ``navigate`` — which is scoped to the SPA's
``BrowserRouter`` ``basename``, so any URL outside the SPA mount
silently no-op'd:

  - legacy admin paths (``/admin/<app>/<model>/<pk>/change/``)
  - hijack / impersonate URLs (``/hijack/release-user/?next=…``)
  - cross-origin downloads (signed S3 URLs)

New ``followActionRedirect`` helper (`apps/web/src/action-redirect.ts`)
picks the right primitive per URL: ``navigate`` for same-origin paths
inside the SPA mount (no full reload), ``window.location.assign``
for everything else. Returns a stripped basename-relative path to
the navigate call so BrowserRouter doesn't double-prefix.

The helper is dependency-injected (``currentOrigin``,
``assignLocation``) so the test suite can lock the routing logic
without touching jsdom's non-configurable ``window.location``.

Locks: 6 new vitests in `action-redirect.test.ts` cover the SPA-
internal path, search + hash preservation, the legacy-admin path,
cross-origin URLs, the hijack pattern, and a malformed-URL fallback.

Release 1.4.13 bundles this with the unreleased changes since
1.4.12 (all already merged on main):

  - #631 / PR #641 — ``PRIMARY_COLOR`` reads ``site_primary_color``
    off the configured ``AdminSite`` before falling back to the
    setting + default.
  - #626 / PR #642 — ``raw_id_fields`` and ``radio_fields`` now
    render their intended widgets (plain-pk text input + lookup
    link, inline radio bank) instead of falling through to
    autocomplete / ``<select>``.
  - #623 / #624 / #633 / #634 / #635 — README "Stock-Django hooks
    that do NOT carry through" / "Writing safe ``list_display``
    callables" / "Hardening" / "Mounting the API on a different
    origin" sections (PR #640).
  - PR #638 — ``release.yml`` → ``publish.yml`` rename so PyPI's
    Trusted Publisher config matches the workflow filename.

Closes #620.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MartinCastroAlvarez MartinCastroAlvarez merged commit 91e8ed1 into main May 31, 2026
5 checks passed
@MartinCastroAlvarez MartinCastroAlvarez deleted the fix/action-redirect-routing branch May 31, 2026 10:40
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.

SPA action runner: HttpResponseRedirect from a ModelAdmin action is silently swallowed — "Download <thing>", "View HTML", "Hijack" all break

2 participants