fix(webapp): hard-navigate after creating an organization - #4530
Conversation
|
Observability mapAs of 18/100 over 414 measured of 430 entry points (base 18, no change) What this PR changed
FIX FIRST
AUDIT 3 of 50 sensitive mutations record an actor. 47 without one. What the score is made ofThe score and findings here are report-only and never gate the merge. Separately, a required test suite keeps this tool's symbol and route lists in sync with the code they name, and can fail a pull request that renames or removes a symbol they reference, or that adds the first route with a segment they anticipate. Each failure names the list to edit. The rules and their reasons: internal-packages/observability-map/README.md. |
2c4ee48 to
3157d9a
Compare
3157d9a to
708d4d0
Compare
@trigger.dev/build
trigger.dev
@trigger.dev/core
@trigger.dev/python
@trigger.dev/react-hooks
@trigger.dev/redis-worker
@trigger.dev/rsc
@trigger.dev/schema-to-json
@trigger.dev/sdk
commit: |
708d4d0 to
ba81b12
Compare
Once the organization row was committed, the action returned a client-side redirect and the browser navigated on through a chain of redirects to the new organization's first page. That navigation could be aborted while the destination route's code was still loading, and the browser then went back to /orgs/new — leaving the user sitting on the creation form with what they typed still in place, even though the organization had in fact been created. Clicking Create again made a duplicate. The success redirects now use redirectDocument(), so the browser performs a hard navigation to the destination instead of client-side routing into its chunks, which is the window that broke. Remix still handles the submission itself, so the zod validation and the pending/disabled state on the Create button behave exactly as before. The failure path also returns a conform-shaped result and logs the underlying error, so a genuine failure renders its message with the submitted values preserved instead of resetting silently. Co-Authored-By: Claude <noreply@anthropic.com>
ba81b12 to
52feba2
Compare
Requested by Chris Arderne · Slack thread
Before / After
Before — submitting the new-organization form could drop you back on the creation form, with no error message anywhere and nothing to say the submit had succeeded, while the organization had in fact already been created. The natural next step was to click Create again — which created a second organization.
After — submitting the form completes and takes you to your new organization. If creating it genuinely fails, you get an error message on the form with what you typed still there.
How
Once the organization row is committed, the action redirects to the new organization, which redirects on again to its first page. That whole chain was being followed client-side: the router received the action's redirect, fetched the destination's loaders, followed the next redirect, and then had to load the destination route's code chunk before it could render anything.
That last step is where it broke. A capture of a real reproduction shows the navigation being aborted while the destination route's chunk was still loading. Remix's recovery for a chunk that fails to load is
window.location.reload(), and because the router had not yet committed the new URL, the reload re-requested the URL still in the address bar — the creation form. The user landed back on the form with the organization already created, because the action had run to completion on the server. Clicking Create a second time then made a duplicate.The two success redirects now use
redirectDocument()instead ofredirect(). That returns the same redirect response with an added reload-document marker, which the router acts on by handing the destination straight to the browser as a full document navigation. It does this before it starts the destination's loaders, so neither those loaders nor the destination route's code chunk are ever fetched client-side — the step the navigation was being lost on no longer happens.The
<Form>is unchanged frommain. Remix still handles the submission itself, so the client-side zod validation and the pending/disabled state on the Create button behave exactly as before.Deliberately not done: turning the submit itself into a native document POST via
reloadDocumenton the<Form>. An earlier revision of this PR did that, and it was removed. It takes the submission out of Remix's hands, and with it the pending navigation state that disables the Create button while the form is submitting — weakening the very double-click guard that stops the duplicate. It also re-initialises the page's React state on the error path, dropping the URL and company-size selections. Changing only the action's redirects leaves the submission, the validation and the disabled-button state exactly where they were, and hardens just the navigation after success.The action's failure path is also tightened up: it returns a conform-shaped result and logs the underlying error, so a genuine failure is rendered with its message and the submitted values preserved instead of resetting silently.
Testing
Tested locally against a dev webapp, driving the real form in a browser:
302to the organization, then200on its first page), with no client-side loader or route-chunk request for it — this is the behaviour change that closes the window.Also ran
pnpm run format,pnpm run lint:fix, andpnpm run typecheck --filter webapp— all clean.Follow-ups
Not addressed here, to keep this change small:
seedDefaultBillingAlertsis awaited inline after the organization row is committed, which adds latency to a request the user is waiting on. It cannot fail the request — it is wrapped intryCatchand only logs — but it is still worth moving off the critical path. The inlineawaitis deliberate, though: it stops the seed landing after the user's first alert edit, so any move off the request path has to preserve that ordering.Changelog
Fixed the new-organization form so submitting it completes and takes you to your new organization, instead of sometimes landing you back on the creation form after the organization had already been created.
Screenshots
No visual changes — the form and the page it lands on are unchanged. The fix is in how the navigation after a successful submit is performed.
💯