feat(website): claim the pending company on a register confirm link - #36
Merged
Conversation
Self-serve registration parks a pending_company marker on the unconfirmed user, and a shared database trigger mints the company at confirmation. The trigger cannot tell a register link from an invite link, so it carries an affiliation guard. The fix is for the website to ask for the company explicitly once the reader is authenticated. Register confirm links now arrive tagged `&flow=register`. When the confirm tap spends a tagged link's token and gets an access token back, /welcome calls the claim_pending_company RPC before asking for a password. Invite and recovery links are untagged and take the old path unchanged. A failed claim gets its own phase rather than an error: the one-time confirm token is already spent, so the reader must never be sent back to the confirm tap or to a fresh link. The claim phase holds the access token and retries the RPC alone. The RPC is idempotent, so a retry after a timeout that actually succeeded is harmless. 401/403 takes the existing expired path, 429 the existing rate-limit copy. The page's existing invariants hold: nothing fires on mount or in an effect, the flow tag is read once where the fragment is first read, the double-submit ref guard still covers every call, and no server text is ever rendered.
Give the failed-claim card an escape hatch (skip to password setup, matching PunchLog's shipped shape), key its copy on formError instead of saving so a failure doesn't repeat the in-flight message, and fix the module comment: the claim step already shipped in PunchLog (kubiknyc/PunchLog#158), and the recovery/legacy paths that never claim are now documented as depending on the confirm-time trigger.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WorkLog website half of jobsight-backend #34 (phase 1), matching kubiknyc/PunchLog#158. After the tap-driven confirm on a register link,
/welcomecalls theclaim_pending_companyRPC before showing the password form.lib/welcomeLink.ts:readRegisterFlow(hash)— true only forflow=register, whichworklog-register-company(jobsight-backend #39, deployed v6) now appends to its token_hash landing links. Invite and recovery links never carry it.app/welcome/page.tsx: newclaimphase between the confirm tap and set-password. Success → set-password. 401/403 → existing expired path. 429 → existing rate-limit copy. Any other failure keeps "Try again" (the one-time token is already spent, so never re-verify) plus "Skip for now and choose your password", because the confirm-time DB trigger still mints the company today. Nothing fires on mount or in an effect; no server text is rendered. Recovery copy still points at the app.Order and dependencies
Backend side is already live: migration
20260909000001_claim_pending_company(#36) andworklog-register-companyv6 (#39). Until this merges, the flag in WorkLog's mail is inert and tenants come from the trigger. The stale-register-mail residual phase 1 accepts, and the consent step that closes it, are kubiknyc/jobsight-backend#35.Verification
npm run typecheck,npm test(34),npm run build— greenwebsite/**; those are the complete checksecc:code-reviewer(Opus; the repo'sworklog-revieweris not registered in this session) + Reviewer B Gemini, both PASS in round 2. Round-1 HIGH (no way past a failed claim) fixed. Two round-2 MEDIUMs (button class, stale error on skip) fixed in the third commit.Not covered: no component test for the claim phase (no jsdom in
website/); live round trip owed after deploy.