Skip to content

feat: add global loading, error, not-found states and reusable Logo component - #140

Merged
codebestia merged 3 commits into
ShadeProtocol:mainfrom
Johnalex-hub:feat/global-route-states-and-logo
Jul 31, 2026
Merged

feat: add global loading, error, not-found states and reusable Logo component#140
codebestia merged 3 commits into
ShadeProtocol:mainfrom
Johnalex-hub:feat/global-route-states-and-logo

Conversation

@Johnalex-hub

@Johnalex-hub Johnalex-hub commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the three global Next.js App Router route-state conventions and a reusable brand Logo component. None of these existed in src/app/ before, so unmatched routes, slow navigations, and render errors all fell back to Next's default unstyled UI.

All four screens reuse the existing centered full-screen layout pattern from sign-in-client.tsx (bg-background / max-w-md / rounded-lg border bg-card p-6 shadow-sm) so they read as part of the app.

Issues

Closes #106
Closes #107
Closes #108
Closes #109

Changes

src/components/logo.tsx (#109)

Reusable brand mark rendering /shade_logo_transparent.png alongside the "Shade" wordmark.

  • size prop — sm (24px), md (28px, default), lg (32px). The md and lg values match the sizes already hardcoded in Sidebar.tsx / MobileNav.tsx (28px) and PublicPaymentLayout.tsx (32px), so it is a drop-in for all existing call sites.
  • No layout shift: next/image receives explicit width/height per size and the wordmark uses a fixed text size, so the box is reserved before the image loads.
  • showWordmark={false} renders the mark alone for tight spaces (mobile nav, future public payment header).
  • Accessibility: with the wordmark visible the image is aria-hidden with an empty alt to avoid announcing "Shade" twice; mark-only mode carries alt="Shade".
  • Accepts className / wordmarkClassName (merged via cn) so contexts like the sidebar can apply text-sidebar-foreground.

src/app/loading.tsx (#106)

Centered Loader2 + animate-spin fallback matching the spinner pattern in sign-in-client.tsx and register-client.tsx. Marked role="status" / aria-live="polite" with an sr-only "Loading" label.

src/app/error.tsx (#107)

Client component implementing Next's error boundary contract (error, reset).

  • Logs the error via useEffect.
  • Shows the message in the destructive alert treatment already used for the sign-in error state, plus error.digest when present for support correlation.
  • "Try again" Button wired to reset().

src/app/not-found.tsx (#108)

Themed 404. Offers both a primary "Go to dashboard" link and an outline "Sign in" link — as a server component it cannot read the client-side wallet session, so it exposes both paths rather than guessing auth state.

src/lib/lucide-react.tsx (supporting)

tsconfig.json maps lucide-react to this vendored shim, which only exports a hand-maintained subset of icons. Added RotateCcw, Compass, and LogIn in the existing IconBase style to support the new screens.

Verification

  • npm run typecheck — passes clean
  • npm run lint — no errors (2 pre-existing <img> warnings remain, non-blocking)
  • npx prettier --check on all touched files — passes
  • npm run buildcompletes successfully, all 16 routes generated including /_not-found
  • npx vitest run — 27/27 tests passing across 7 files

Testing notes

  • Visit any unknown route (e.g. /does-not-exist) — themed 404 renders.
  • Throw inside a page component — themed fallback renders and "Try again" recovers.
  • <Logo size="sm" /> is rendered at the foot of both the 404 and error screens.

Note on the extra commit

The build's lint gate was already failing on main for four files unrelated to this feature work, which kept CI red. Since this PR could not otherwise go green, those are fixed here in a separate commit (fix: resolve pre-existing lint errors blocking the build). All four are no-op changes:

  • src/app/payment/[id]/page.tsx — dropped unused reject param
  • src/components/invoices/InvoiceRowActions.test.tsx — dropped unused waitFor import
  • src/components/profile/ProfileDetailsForm.test.tsx@ts-ignore@ts-expect-error
  • src/components/ui/api-key-reveal.tsx — optional catch binding for unused error

Happy to split these into their own PR if you'd rather keep this one scoped strictly to #106#109.

Add the three App Router global route-state conventions and a shared
brand mark component.

- src/components/logo.tsx: reusable Logo with sm/md/lg size prop,
  optional wordmark, and explicit image dimensions to avoid layout shift
- src/app/loading.tsx: centered Loader2 spinner route fallback
- src/app/error.tsx: client error boundary implementing the error/reset
  contract, themed with the existing card and destructive-alert styles
- src/app/not-found.tsx: themed 404 with links to /dashboard and /sign-in
- src/lib/lucide-react.tsx: add RotateCcw, Compass and LogIn icons to the
  vendored icon shim that tsconfig maps lucide-react onto

Closes ShadeProtocol#106
Closes ShadeProtocol#107
Closes ShadeProtocol#108
Closes ShadeProtocol#109
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Johnalex-hub Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

The build's lint gate failed on four files unrelated to this PR's
feature work, which kept CI red. All four are no-op fixes.

- payment/[id]/page.tsx: drop unused Promise reject param
- InvoiceRowActions.test.tsx: drop unused waitFor import
- ProfileDetailsForm.test.tsx: @ts-ignore -> @ts-expect-error
- ui/api-key-reveal.tsx: use optional catch binding for unused error

npm run build now completes; vitest passes 27/27.
@codebestia
codebestia merged commit bbe919e into ShadeProtocol:main Jul 31, 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.

Create Reusable Logo Component Add Global not-found.tsx Page Add Global error.tsx Route Error Boundary Add Global loading.tsx Route Loading UI

2 participants