Skip to content

Recover from referral invite send failures - #132

Closed
absalonCRC wants to merge 1 commit into
profullstack:masterfrom
absalonCRC:fix-referral-invite-error-recovery
Closed

Recover from referral invite send failures#132
absalonCRC wants to merge 1 commit into
profullstack:masterfrom
absalonCRC:fix-referral-invite-error-recovery

Conversation

@absalonCRC

@absalonCRC absalonCRC commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • wrap referral invite submission in try/catch/finally so the form recovers from failed requests
  • tolerate non-JSON error responses from POST /api/referrals
  • keep the Send Invites button from staying stuck in "Sending..." after failures
  • add regression coverage for network failures and invalid JSON error responses

Fixes #131

Validation

  • pnpm test:run src/app/dashboard/referrals/page.invites.test.tsx
  • pnpm type-check
  • pnpm exec eslint src/app/dashboard/referrals/page.tsx src/app/dashboard/referrals/page.invites.test.tsx
  • git diff --check

Bounty / payment

Submitted for the active uGig affiliate-program testing task. SOL receive address: 27sdMYXofqoM9qR13bZhccRNYeEgYn5EoHXTSJn4QWKP.

Payment fallback: PayPal cultofrozen@gmail.com

@greptile-apps

greptile-apps Bot commented May 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a UX bug where the "Send Invites" button could get stuck in a "Sending..." state after a failed network request or a non-JSON error response from POST /api/referrals. The fix wraps the fetch call in try/catch/finally, uses .catch(() => null) to tolerate non-JSON responses, and moves setSending(false) into the finally block.

  • page.tsx: sendInvites now resets the loading state unconditionally via finally, and uses optional chaining on the parsed body so a null parse result falls back to a generic error message.
  • page.invites.test.tsx (new): Adds two regression tests covering a thrown network error and an invalid-JSON error response, asserting both that the error message appears and that the button re-enables.

Confidence Score: 4/5

Safe to merge — the change is narrow and well-tested, fixing a real user-facing stuck-button issue.

The production fix in page.tsx is correct: try/catch/finally guarantees setSending resets on every path, and the .catch(() => null) guard cleanly handles non-JSON bodies without masking the HTTP error. The one thing holding the score slightly below a clean pass is in the test file — cleanup() from @testing-library/react is never explicitly called in afterEach (only vi.unstubAllGlobals), so test isolation depends entirely on the auto-cleanup behaviour that @testing-library/react wires up via the global afterEach. If the project's vitest config sets globals: false, auto-cleanup would not fire and DOM state from one test would leak into the next, potentially causing false passes.

src/app/dashboard/referrals/page.invites.test.tsx — verify that @testing-library/react auto-cleanup is active in this project's vitest configuration.

Important Files Changed

Filename Overview
src/app/dashboard/referrals/page.tsx Wraps the fetch call in try/catch/finally, adds JSON-parse error tolerance, and ensures the sending state always resets — correct and minimal change.
src/app/dashboard/referrals/page.invites.test.tsx New regression test file covering network failures and invalid-JSON responses; test isolation relies on auto-cleanup from @testing-library/react rather than an explicit cleanup call.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[sendInvites called] --> B[setSending=true, clear error/success]
    B --> C{emailList empty?}
    C -- Yes --> D[setError + setSending=false + early return]
    C -- No --> E[try: POST /api/referrals]
    E --> F[parse JSON, fallback to null on failure]
    F --> G{res.ok?}
    G -- No --> H[setError with fallback message]
    G -- Yes --> I[setSuccess + clear emails + reload referrals]
    E -- throws --> J[catch: setError network fallback]
    H --> K[finally: setSending=false]
    I --> K
    J --> K
Loading

Reviews (1): Last reviewed commit: "Recover from referral invite send failur..." | Re-trigger Greptile

Comment on lines +63 to +67
afterEach(() => {
vi.unstubAllGlobals();
});

it("recovers from invite network errors", async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing explicit cleanup between tests

afterEach only calls vi.unstubAllGlobals() — it never calls cleanup() from @testing-library/react. Auto-cleanup relies on @testing-library/react detecting a globally-available afterEach, which only happens when vitest's globals: true option is set. If the project runs vitest with globals disabled, the rendered <ReferralsPage /> from the first test remains mounted in the JSDOM document when the second test runs, and screen queries in the second test will see both instances, which could produce false positives. Adding import { cleanup } from "@testing-library/react" and cleanup() inside afterEach makes the teardown explicit and independent of the globals configuration.

@ralyodio ralyodio closed this May 23, 2026
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.

bug: invite form stays stuck when referral invite request fails

2 participants