-
Notifications
You must be signed in to change notification settings - Fork 3
feat: make the 404 page nicer and implement 404 handling #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds two server-side helper functions to centralize invoice fetching with not-found handling, updates two page routes to use those helpers, and replaces the app-wide 404 page with a redesigned NotFound component. Changes
Sequence Diagram(s)sequenceDiagram
participant Page as Page Component
participant Helper as Server Helper
participant API as TRPC API
participant Util as isNotFoundError
Page->>Helper: getInvoice(id) / getInvoiceMeLink(id)
Helper->>API: api.invoice.getById.query(id) / api.invoiceMe.getById.query(id)
API-->>Helper: data or error
alt success
Helper-->>Page: return data
else error
Helper->>Util: isNotFoundError(error)?
alt not found
Helper-->>Page: return null
else other error
Helper-->>Page: throw error
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changesNo out-of-scope changes detected. Possibly related PRs
Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/app/i/[id]/helpers.ts (1)
4-13
: Add explicit return type and type the catch variableSmall typing improvements increase clarity and safety.
-export async function getInvoiceMeLink(id: string) { +export async function getInvoiceMeLink( + id: string, +): Promise<Awaited<ReturnType<typeof api.invoiceMe.getById.query>> | null> { try { return await api.invoiceMe.getById.query(id); - } catch (error) { + } catch (error: unknown) { if (isNotFoundError(error)) { return null; } throw error; } }src/app/invoices/[ID]/helpers.ts (1)
4-13
: Mirror the small typing improvements from the other helperAdd explicit return type and type the catch variable for consistency.
-export async function getInvoice(id: string) { +export async function getInvoice( + id: string, +): Promise<Awaited<ReturnType<typeof api.invoice.getById.query>> | null> { try { return await api.invoice.getById.query(id); - } catch (error) { + } catch (error: unknown) { if (isNotFoundError(error)) { return null; } throw error; } }src/app/invoices/[ID]/page.tsx (1)
29-29
: Nit: consider normalizing the param name toid
(lowercase) across routesHere we use params.ID while other routes use params.id. Not a blocker, but standardizing casing reduces cognitive overhead. Fine to handle in a separate PR.
src/app/not-found.tsx (2)
17-21
: Wording: make the heading generic to all 404s“Transaction Not Found” may confuse users who hit unrelated routes. Suggest a more general label.
- <span className="text-lg font-medium text-gray-700"> - Transaction Not Found - </span> + <span className="text-lg font-medium text-gray-700"> + Page Not Found + </span>
69-75
: Verify destination: consider linking to/
instead of/dashboard
Public 404s (e.g., shared invoice links) may be viewed by unauthenticated users. Sending them to /dashboard could prompt a login or 403. If that’s intentional, ignore; otherwise, default to Home.
- href="/dashboard" + href="/" @@ - Back to Dashboard + Go Home
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/app/i/[id]/helpers.ts
(1 hunks)src/app/i/[id]/page.tsx
(2 hunks)src/app/invoices/[ID]/helpers.ts
(1 hunks)src/app/invoices/[ID]/page.tsx
(2 hunks)src/app/not-found.tsx
(1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: rodrigopavezi
PR: RequestNetwork/easy-invoice#45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#8
File: src/app/invoice-me/page.tsx:21-21
Timestamp: 2025-02-14T12:48:42.125Z
Learning: In the easy-invoice codebase, error handling for TRPC queries is not required at the component level as confirmed by the maintainer.
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#83
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:127-138
Timestamp: 2025-06-23T09:14:42.979Z
Learning: In the RequestNetwork/easy-invoice codebase, when suggesting improvements like error handling for transaction approvals, the user bassgeta prefers consistency over isolated improvements. They prefer not to implement error handling in one place unless it's done consistently across all similar transaction flows in the codebase.
Learnt from: MantisClone
PR: RequestNetwork/easy-invoice#59
File: src/app/api/webhook/route.ts:77-95
Timestamp: 2025-05-22T18:19:12.366Z
Learning: For webhook handlers in the Easy Invoice project, unknown or unexpected subStatus values in payment processing should be treated as errors (using console.error) rather than warnings, and should return a 422 Unprocessable Entity status code.
📚 Learning: 2025-02-12T12:40:14.742Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:88-109
Timestamp: 2025-02-12T12:40:14.742Z
Learning: The payRequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicProcedure) to allow invoice sharing and payment by anyone with the payment reference, similar to how payment links work in other payment systems.
Applied to files:
src/app/i/[id]/page.tsx
src/app/invoices/[ID]/page.tsx
src/app/invoices/[ID]/helpers.ts
src/app/i/[id]/helpers.ts
📚 Learning: 2025-05-19T13:00:48.790Z
Learnt from: rodrigopavezi
PR: RequestNetwork/easy-invoice#45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Applied to files:
src/app/i/[id]/page.tsx
src/app/invoices/[ID]/page.tsx
📚 Learning: 2025-02-14T12:48:42.125Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#8
File: src/app/invoice-me/page.tsx:21-21
Timestamp: 2025-02-14T12:48:42.125Z
Learning: In the easy-invoice codebase, error handling for TRPC queries is not required at the component level as confirmed by the maintainer.
Applied to files:
src/app/i/[id]/page.tsx
src/app/invoices/[ID]/page.tsx
src/app/invoices/[ID]/helpers.ts
src/app/i/[id]/helpers.ts
📚 Learning: 2025-02-13T21:57:39.869Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#8
File: src/app/i/[id]/page.tsx:18-22
Timestamp: 2025-02-13T21:57:39.869Z
Learning: The invoice me links in the easy-invoice project do not have an expiration mechanism. The invoiceMeTable schema only includes id, label, userId, and createdAt fields.
Applied to files:
src/app/i/[id]/page.tsx
src/app/i/[id]/helpers.ts
📚 Learning: 2025-02-12T13:04:46.135Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:0-0
Timestamp: 2025-02-12T13:04:46.135Z
Learning: The `payRequest` function in the invoice router retrieves payment transactions calldata from the Request API and does not process the payment itself. The actual payment status update happens through the webhook handler.
Applied to files:
src/app/invoices/[ID]/page.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (5)
src/app/i/[id]/helpers.ts (1)
4-13
: Good centralization of 404 handlingReturning null for not-found and rethrowing others keeps page components clean and consistent with prior decisions.
src/app/invoices/[ID]/helpers.ts (1)
4-13
: Helper extraction looks goodMatches the invoice-me helper and keeps not-found concerns out of the page. Consistent and readable.
src/app/invoices/[ID]/page.tsx (1)
12-12
: Refactor to helper is clean and aligns with codebase conventionsUsing getInvoice + notFound() keeps the page lean and matches the established pattern.
Also applies to: 29-33
src/app/i/[id]/page.tsx (1)
10-10
: LGTM: helper usage + notFound()This follows the maintainers’ preference to keep component-level TRPC error handling minimal while centralizing 404 handling in helpers.
Also applies to: 22-26
src/app/not-found.tsx (1)
6-12
: Nice UX upliftThoughtful layout, spacing, and iconography. The page reads well and provides clear next steps.
src/app/not-found.tsx
Outdated
</span> | ||
</div> | ||
<p className="text-gray-600 mb-2"> | ||
Looks like this page got lost in the blockchain. Don't |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly these hardcoded comments don't make a lot of sense in the context of EasyInvoice, maybe we can find other comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworded it and just pushed an update. Still made it a bit quirky, just for the sake of it :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/app/not-found.tsx (1)
22-27
: Generalize the 404 copy; avoid domain-specific assumptions.The wallet-specific message and the “invoices are safe” reassurance can confuse users on unrelated 404s. Prior feedback already called out hardcoded error text here.
- <p className="text-gray-600 mb-2"> - This page seems to have been sent to a non-existent wallet address. - </p> - <p className="text-gray-600"> - Don't worry, your invoices are safe and sound!{" "} - </p> + <p className="text-gray-600 mb-2">That page doesn’t exist.</p> + <p className="text-gray-600"> + It may have been moved, or the link is invalid. + </p>Note: Next.js not-found.tsx can’t easily receive contextual error details; a neutral message keeps it correct across /invoices/:id, /i/:id, and arbitrary routes.
🧹 Nitpick comments (3)
src/app/not-found.tsx (3)
9-11
: Hide decorative brand mark from screen readers (a11y).The “EI” badge is decorative; hide it from assistive tech.
- <div className="w-16 h-16 rounded-xl bg-black flex items-center justify-center mx-auto mb-6"> + <div + className="w-16 h-16 rounded-xl bg-black flex items-center justify-center mx-auto mb-6" + aria-hidden="true" + > <span className="text-white font-bold text-xl">EI</span> </div>
15-15
: Make the “404” heading responsive to avoid overflow on small screens.- <h1 className="text-8xl font-bold text-gray-900 mb-4">404</h1> + <h1 className="text-6xl sm:text-8xl font-bold text-gray-900 mb-4">404</h1>
30-69
: Optional: reduce duplication by mapping a small config to cards.The three cards share nearly identical structure. Consider an array + map or a tiny Card component to DRY this block and simplify tweaks later (copy, icon, color).
Happy to provide a follow-up patch if you want this refactor in this PR.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/app/not-found.tsx
(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#117
File: src/app/not-found.tsx:29-67
Timestamp: 2025-08-08T09:52:43.308Z
Learning: Repository: RequestNetwork/easy-invoice
Context: src/app/not-found.tsx (Next.js, TypeScript/React)
Learning: It is acceptable (by current product decision) for the NotFound page to display links to /invoices/create and /subscription-plans even though these routes are auth-gated. Rationale: prevent the 404 from feeling empty; most users are authenticated. No lock icons or conditional rendering required for now; can be revisited later as an enhancement.
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#83
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:127-138
Timestamp: 2025-06-23T09:14:42.979Z
Learning: In the RequestNetwork/easy-invoice codebase, when suggesting improvements like error handling for transaction approvals, the user bassgeta prefers consistency over isolated improvements. They prefer not to implement error handling in one place unless it's done consistently across all similar transaction flows in the codebase.
Learnt from: rodrigopavezi
PR: RequestNetwork/easy-invoice#45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#8
File: src/app/invoice-me/page.tsx:21-21
Timestamp: 2025-02-14T12:48:42.125Z
Learning: In the easy-invoice codebase, error handling for TRPC queries is not required at the component level as confirmed by the maintainer.
Learnt from: MantisClone
PR: RequestNetwork/easy-invoice#59
File: src/app/api/webhook/route.ts:77-95
Timestamp: 2025-05-22T18:19:12.366Z
Learning: For webhook handlers in the Easy Invoice project, unknown or unexpected subStatus values in payment processing should be treated as errors (using console.error) rather than warnings, and should return a 422 Unprocessable Entity status code.
📚 Learning: 2025-08-08T09:52:43.308Z
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#117
File: src/app/not-found.tsx:29-67
Timestamp: 2025-08-08T09:52:43.308Z
Learning: Repository: RequestNetwork/easy-invoice
Context: src/app/not-found.tsx (Next.js, TypeScript/React)
Learning: It is acceptable (by current product decision) for the NotFound page to display links to /invoices/create and /subscription-plans even though these routes are auth-gated. Rationale: prevent the 404 from feeling empty; most users are authenticated. No lock icons or conditional rendering required for now; can be revisited later as an enhancement.
Applied to files:
src/app/not-found.tsx
📚 Learning: 2025-02-12T12:40:14.742Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:88-109
Timestamp: 2025-02-12T12:40:14.742Z
Learning: The payRequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicProcedure) to allow invoice sharing and payment by anyone with the payment reference, similar to how payment links work in other payment systems.
Applied to files:
src/app/not-found.tsx
📚 Learning: 2025-02-20T10:27:02.993Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#21
File: src/app/invoices/[ID]/page.tsx:113-148
Timestamp: 2025-02-20T10:27:02.993Z
Learning: The easy-invoice project prefers simpler, direct implementations over abstract utilities. For example, using `.toFixed(2)` directly instead of creating separate number formatting utilities.
Applied to files:
src/app/not-found.tsx
📚 Learning: 2025-07-14T14:17:05.340Z
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#91
File: drizzle/0007_messy_silver_fox.sql:1-12
Timestamp: 2025-07-14T14:17:05.340Z
Learning: In the RequestNetwork/easy-invoice codebase, monetary amounts are stored as text in the database schema rather than numeric types. This is a deliberate architectural decision to maintain consistency across all amount fields in the system.
Applied to files:
src/app/not-found.tsx
📚 Learning: 2025-06-23T09:14:42.979Z
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#83
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:127-138
Timestamp: 2025-06-23T09:14:42.979Z
Learning: In the RequestNetwork/easy-invoice codebase, when suggesting improvements like error handling for transaction approvals, the user bassgeta prefers consistency over isolated improvements. They prefer not to implement error handling in one place unless it's done consistently across all similar transaction flows in the codebase.
Applied to files:
src/app/not-found.tsx
📚 Learning: 2025-06-04T12:02:39.411Z
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#67
File: src/server/routers/payment.ts:47-49
Timestamp: 2025-06-04T12:02:39.411Z
Learning: In `src/server/routers/payment.ts`, the batchPay input validation already handles empty arrays correctly. The `batchPaymentFormSchema.shape.payouts.optional()` inherits the `.min(1, "At least one payment is required")` validation from the original schema, so empty payouts arrays are automatically rejected even when the field is made optional.
Applied to files:
src/app/not-found.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (1)
src/app/not-found.tsx (1)
44-53
: Keeping gated links visible on 404 is OK per product decision.Acknowledging the prior decision to keep “Create Invoice” and “Subscription Plans” visible to avoid an empty 404. No lock icons/conditional rendering needed for now.
Also applies to: 55-68
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8"> | ||
<Link | ||
href="/payouts" | ||
className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add visible focus states to all links (keyboard a11y).
Links currently have hover only. Add focus-visible rings so keyboard users can see focus.
- className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors"
+ className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2"
- className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors"
+ className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2"
- className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors"
+ className="shadow-md group p-6 bg-white rounded-lg border border-gray-200 hover:border-gray-300 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2"
- className="inline-flex items-center gap-2 px-6 py-3 bg-black text-white font-medium rounded-lg hover:bg-gray-800 transition-colors duration-200"
+ className="inline-flex items-center gap-2 px-6 py-3 bg-black text-white font-medium rounded-lg hover:bg-gray-800 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-black"
Also applies to: 46-46, 57-57, 73-73
🤖 Prompt for AI Agents
In src/app/not-found.tsx at lines 33, 46, 57, and 73, the links only have hover
styles but lack visible focus states for keyboard accessibility. Add
focus-visible ring styles to the className of these links to ensure keyboard
users can see when the link is focused. Use appropriate focus-visible utility
classes to create a visible ring or outline on focus without affecting mouse
users.
Problem
Our 404 page is rudimentary and we do not have not found handling when we fetch invoices and invoice me links.
Solution
Upgrade the design and handle 404 when the id from parameters yields no results.
Changes
Testing
/invalid-route
or whatever, and verify the 404 page shows upResolves #108
Summary by CodeRabbit