feat(web): add canonical private routes#346
Conversation
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
|
Warning Review limit reached
More reviews will be available in 19 minutes and 42 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughThis PR reorganizes the web app's shopper routing from ChangesShopper Route Restructuring
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/src/middleware.ts (1)
19-43:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve redirects for legacy
@handlestore URLs.This middleware no longer carries the old
/@handle→/stores/:handlenormalization, so existing public store links now fall through to 404. The PR explicitly drops old private/buyer/*paths, but breaking previously shareable public profile URLs is a much rougher migration. Adding a small redirect here would keep old links working while still making/stores/[handle]canonical.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/middleware.ts` around lines 19 - 43, Add a small redirect for legacy public profile URLs by detecting paths of the form "/@handle" early in middleware (in function middleware) before the protected/auth checks; extract the handle from request.nextUrl.pathname (e.g., strip the leading "@"), construct a new URL with pathname `/stores/:handle` while preserving request.nextUrl.search/query, and return NextResponse.redirect(newUrl). Keep existing helpers (hasSession, isProtectedRoute, isAuthPageRoute, getLoginRedirectPath) and do not reintroduce dropped private "/buyer/*" behavior.apps/web/AGENTS.md (1)
58-95:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winDocument the new public/profile and settings routes here.
This tree still omits the new
/u/[username],/notifications, and/(settings)/settings/*paths, so the canonical App Router map is already stale inside the same PR. Based on learnings: The App Router structure uses (public), (auth), (app), and (store) route groups with distinct layouts — do not flatten this hierarchy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/AGENTS.md` around lines 58 - 95, Update the App Router tree to include the missing routes without flattening route-group hierarchy: add the public profile route entry /u/[username] under the appropriate group (likely (public) or (app) depending on auth requirements), add /notifications under the (app) authenticated pages, and add the settings group as /(settings)/settings/* (with its own layout entry) rather than inlining settings pages; reference the route keys shown in the diff (e.g., page.tsx files for p/[code], stores/[handle], home/page.tsx) when inserting the new entries so the tree matches the actual filesystem layout and preserves (public), (auth), (app), and (store) groups.
🧹 Nitpick comments (3)
apps/web/src/app/(public)/p/[code]/ProductDetailClient.tsx (1)
392-402: ⚡ Quick winUse the image radius token for the store logo.
These containers now use
rounded-xl(12px), which is the card radius. The repo token for images is 20px, so this drifts from the web design system.As per coding guidelines "Use border-radius tokens: 6px for small elements, 12px for cards, 16px for modals, 20px for images".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(public)/p/[code]/ProductDetailClient.tsx around lines 392 - 402, In ProductDetailClient (the store logo container) replace the card radius class "rounded-xl" with the image-radius token (20px) on both the Image wrapper and the fallback container so the logo uses the image border-radius; specifically update the divs around Image and the fallback div that currently use "rounded-xl" to use the image token (e.g. "rounded-[20px]" or your project's image radius utility) so both branches match the design system.apps/web/src/components/layout/AppShell.tsx (1)
52-67: ⚡ Quick winMove these new shell dimensions behind design tokens.
This hunk adds several arbitrary pixel values (
72px,240px,924px,960px,600px) directly in TSX. Please swap them to CSS-variable-backed tokens so the shell stays aligned with the shared layout system.As per coding guidelines,
apps/web/src/**/*.{tsx,css}: Use CSS variables (design tokens) only for colors, spacing, and typography — never hardcode hex values or fixed pixels.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/layout/AppShell.tsx` around lines 52 - 67, The JSX in AppShell.tsx hardcodes layout pixel values in className strings (e.g., "pt-14", "md:ml-[72px]", "lg:ml-[240px]", "xl:max-w-[924px]", "max-w-[960px]", "max-w-[600px]") — replace those literal pixel sizes with CSS-variable-backed design tokens and reference them in the Tailwind/className expressions (keep the conditional branches using showRightPanel, isStoreMode, centerMaxWidth intact); add or use existing CSS vars like --shell-pt, --shell-md-ml, --shell-lg-ml, --shell-xl-maxw, --shell-default-maxw and update the stylesheet to define those tokens so the component uses var(--shell-...) instead of hardcoded px values.apps/web/src/app/(public)/u/[username]/page.tsx (1)
23-24: ⚡ Quick winRoute this through the shared API client instead of raw
fetch.This page reimplements base URL handling and response-envelope parsing locally, which can drift from the rest of the app’s API behavior. Prefer
lib/api.tshere so the page inherits the shared baseURL and{ success, data }unwrapping contract. As per coding guidelines, "Use axios API client instance from lib/api.ts with baseURL and response interceptor to unwrap { success, data } envelope".Also applies to: 148-177
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(public)/u/[username]/page.tsx around lines 23 - 24, Replace the ad-hoc API_BASE and raw fetch calls in this page component with the shared axios API client from lib/api.ts: import the client (e.g., `api`) and call `await api.get(...)`/`api.post(...)` for the same endpoints used in this file (remove the manual API_BASE variable and any URL concatenation), then read the unwrapped payload from `response.data.data` (the shared `{ success, data }` envelope) instead of manually parsing responses; apply this change to every fetch in this file (including the blocks around lines 148-177) so the page uses the centralized baseURL and response interceptor behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/AGENTS.md`:
- Around line 587-600: Replace the hardcoded protected-route checks in the
middleware (the explicit pathname.startsWith('/home') || ... block) with the
shared route policy exported from route-policy.ts: import and call the exported
helper (e.g., isProtectedRoute or protectedPrefixes) to determine if a pathname
is protected, and use that result in the session check that performs
NextResponse.redirect; also update the example to reference the centralized
publicRoutes/authRoutes constants (or use exported helpers) instead of
duplicating lists so the middleware covers /checkout, /wishlist, /saved,
/messages, /notifications, /settings/* and stays in sync.
In `@apps/web/src/app/`(app)/cart/page.tsx:
- Line 5: The page metadata title in page.tsx uses a hyphen ("Your cart -
twizrr") which is inconsistent with the home page's pipe separator; update the
metadata title value to use the pipe separator ("Your cart | twizrr") so all
page titles follow the same format by editing the title entry in the page's
metadata definition (the "title" field in apps/web/src/app/(app)/cart/page.tsx).
In `@apps/web/src/app/`(app)/checkout/[productId]/page.tsx:
- Line 5: Update the page metadata title string to use the same separator as the
rest of the site—replace the hyphen in the current title literal title:
"Checkout - twizrr" with a pipe so it matches the pattern used elsewhere (e.g.,
"Checkout | twizrr"); locate and modify the title: "Checkout - twizrr" entry in
the checkout page metadata to use title: "Checkout | twizrr".
In `@apps/web/src/app/`(app)/home/page.tsx:
- Around line 14-15: The h1 heading uses the restricted Syne font class; replace
the class "font-syne" with "font-cabinet" on the <h1 className="font-syne
text-xl font-bold text-[var(--color-espresso)]"> element so the Home page
follows the guideline (use font-cabinet for UI/body text and reserve font-syne
for landing/404/500 hero headlines).
In `@apps/web/src/app/`(app)/messages/page.tsx:
- Line 5: The page metadata title uses an inconsistent separator ("Chats -
twizrr"); update the metadata title in apps/web/src/app/(app)/messages/page.tsx
(the title string in the exported metadata object or variable) to use the pipe
separator to match other pages (e.g., "Chats | twizrr"), ensuring consistency
across the site.
In `@apps/web/src/app/`(app)/notifications/page.tsx:
- Line 5: The metadata title in page.tsx uses a hyphen separator ("title:
\"Notifications - twizrr\"") which is inconsistent with other pages that use a
pipe; update the title metadata value to use the pipe separator (e.g., change
the string to "Notifications | twizrr") so the metadata key title in this file
matches the site's standard punctuation for page titles.
In `@apps/web/src/app/`(app)/orders/[id]/page.tsx:
- Line 5: The page metadata title uses a hyphen separator ("Order details -
twizrr"); update the exported metadata title (the title property in page.tsx) to
use the pipe separator to match the site's convention (e.g., "Order details |
twizrr"), ensuring consistency with other pages like the home page that use the
pipe.
In `@apps/web/src/app/`(public)/u/[username]/page.tsx:
- Around line 56-57: Change the heading elements that currently use the Syne
font to Cabinet: replace the className token "font-syne" with "font-cabinet" on
the h1 at the "We could not load this profile" JSX element and the other heading
JSX block around lines referenced (the h1/h2 group at the 91-93 region); ensure
you only change the font class (leave other classes like "text-2xl",
"font-bold", and color variables intact) so the headings follow the UI/body font
guideline.
- Around line 28-47: The branch that handles missing user profiles currently
returns JSX (profile.status === "not-found") causing a soft-404; change it to
call Next's notFound() to return a real 404 (replace the JSX return in the
profile.status === "not-found" branch with notFound()). Also ensure typography
rules: use font-syne only for landing page and 404/500 hero headlines—keep the
404 title (where "Profile not found" is rendered) in font-syne but revert the
profile display name and other generic text to font-cabinet (adjust className
usage around the title and display name). Finally, stop using
process.env.NEXT_PUBLIC_API_URL + manual fetch in this page and switch to the
shared axios client exported from lib/api.ts (replace raw fetch calls and
parsing with the client functions or axios instance used elsewhere in the repo,
e.g., import and use the client from apps/web/src/lib/api.ts in the data-loading
logic such as the function that currently builds requests around lines 148-186).
In `@apps/web/src/components/feed/FeedCard.tsx`:
- Around line 327-360: The card currently wraps the entire content (variable
content) in a Link when store.handle exists which results in an <a> containing a
native <button> from the Button component; update FeedCard.tsx so
getPublicStoreHref(store.handle) Link only wraps the non-interactive part (the
article content excluding the Follow Button) and render the Button (from
Button.tsx) as a sibling outside that Link so you no longer nest interactive
elements; ensure layout and spacing remain identical (keep the same wrapper
classes on the article and the Button rendered after it) and preserve the
disabled state and getPublicStoreHref/store.handle checks.
In `@apps/web/src/components/feed/QuickBuySheet.tsx`:
- Around line 23-25: handleProceedToCheckout closes the sheet then navigates to
checkout using only product.id which drops the required product code and
triggers CheckoutClient's "missing product reference" bail-out; update the
navigation in handleProceedToCheckout to include the product code (e.g.,
router.push(`/checkout/${product.id}?code=${product.code}`) or the appropriate
productCode property) so the checkout route receives productCode, ensuring
CheckoutClient can proceed, while keeping the onOpenChange(false) call intact.
In `@apps/web/src/components/layout/ProfileMenuDrawer.tsx`:
- Around line 157-174: Two DrawerItem entries point to the same href
"/settings/profile" (the items with labels "Profile" and "Public profile
settings"); update the "Public profile settings" DrawerItem (the one using icon
User and onClose={close}) so it no longer duplicates the "Profile" link — either
remove that DrawerItem or change its href to the correct public profile route
(e.g., the current user's public route such as `/u/${username}` or a dedicated
settings page like `/settings/public-profile`) so each drawer entry targets a
unique destination.
In `@apps/web/src/hooks/useOwnerStore.ts`:
- Around line 21-35: The helper fetchOwnerStoreWithTimeout currently resolves
the timeout branch to null which callers cache as "none"; instead make the
timeout reject (e.g., throw a distinct Timeout Error) so the race will throw
rather than return null and callers won't memoize a false-negative. Locate
fetchOwnerStoreWithTimeout and change the timeout promise from resolve(null) to
reject(new Error('OwnerStoreTimeout') or similar), keep clearing timeoutId in
the finally block, and ensure callers handle the thrown timeout error (or let it
bubble) rather than treating null as "no store".
- Around line 41-66: The refresh function and the load() inside the useEffect
call fetchOwnerStoreWithTimeout() without error handling, so a rejection leaves
cachedState as "loading" and UI stuck; wrap each await
fetchOwnerStoreWithTimeout() in a try/catch (both in refresh and in the load()
closure), and on catch set cachedStore = null, cachedState = "none" (and call
setStore(null) and setState(cachedState)); preserve the existing active check in
load() before applying state and ensure the catch branches also respect active
before calling setStore/setState so rejected fetches never leave the hook in a
perpetual "loading" state.
In `@apps/web/src/lib/routes.ts`:
- Around line 15-17: getPublicUserHref currently returns "/settings/profile"
when normalizePublicHandle(username) yields no value, which risks redirecting
public requests to a protected/private route; change getPublicUserHref to return
a safe public fallback (e.g., null or a public route like "/") instead of
"/settings/profile" so anonymous users aren't bounced to login and authenticated
users don't land on a private page—locate getPublicUserHref and its call to
normalizePublicHandle(username) and replace the protected fallback with null or
another explicit public path.
---
Outside diff comments:
In `@apps/web/AGENTS.md`:
- Around line 58-95: Update the App Router tree to include the missing routes
without flattening route-group hierarchy: add the public profile route entry
/u/[username] under the appropriate group (likely (public) or (app) depending on
auth requirements), add /notifications under the (app) authenticated pages, and
add the settings group as /(settings)/settings/* (with its own layout entry)
rather than inlining settings pages; reference the route keys shown in the diff
(e.g., page.tsx files for p/[code], stores/[handle], home/page.tsx) when
inserting the new entries so the tree matches the actual filesystem layout and
preserves (public), (auth), (app), and (store) groups.
In `@apps/web/src/middleware.ts`:
- Around line 19-43: Add a small redirect for legacy public profile URLs by
detecting paths of the form "/@handle" early in middleware (in function
middleware) before the protected/auth checks; extract the handle from
request.nextUrl.pathname (e.g., strip the leading "@"), construct a new URL with
pathname `/stores/:handle` while preserving request.nextUrl.search/query, and
return NextResponse.redirect(newUrl). Keep existing helpers (hasSession,
isProtectedRoute, isAuthPageRoute, getLoginRedirectPath) and do not reintroduce
dropped private "/buyer/*" behavior.
---
Nitpick comments:
In `@apps/web/src/app/`(public)/p/[code]/ProductDetailClient.tsx:
- Around line 392-402: In ProductDetailClient (the store logo container) replace
the card radius class "rounded-xl" with the image-radius token (20px) on both
the Image wrapper and the fallback container so the logo uses the image
border-radius; specifically update the divs around Image and the fallback div
that currently use "rounded-xl" to use the image token (e.g. "rounded-[20px]" or
your project's image radius utility) so both branches match the design system.
In `@apps/web/src/app/`(public)/u/[username]/page.tsx:
- Around line 23-24: Replace the ad-hoc API_BASE and raw fetch calls in this
page component with the shared axios API client from lib/api.ts: import the
client (e.g., `api`) and call `await api.get(...)`/`api.post(...)` for the same
endpoints used in this file (remove the manual API_BASE variable and any URL
concatenation), then read the unwrapped payload from `response.data.data` (the
shared `{ success, data }` envelope) instead of manually parsing responses;
apply this change to every fetch in this file (including the blocks around lines
148-177) so the page uses the centralized baseURL and response interceptor
behavior.
In `@apps/web/src/components/layout/AppShell.tsx`:
- Around line 52-67: The JSX in AppShell.tsx hardcodes layout pixel values in
className strings (e.g., "pt-14", "md:ml-[72px]", "lg:ml-[240px]",
"xl:max-w-[924px]", "max-w-[960px]", "max-w-[600px]") — replace those literal
pixel sizes with CSS-variable-backed design tokens and reference them in the
Tailwind/className expressions (keep the conditional branches using
showRightPanel, isStoreMode, centerMaxWidth intact); add or use existing CSS
vars like --shell-pt, --shell-md-ml, --shell-lg-ml, --shell-xl-maxw,
--shell-default-maxw and update the stylesheet to define those tokens so the
component uses var(--shell-...) instead of hardcoded px values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 752530e4-07ad-4c86-b255-b8bed432268c
📒 Files selected for processing (50)
apps/web/AGENTS.mdapps/web/WEB_DESIGN.mdapps/web/next.config.mjsapps/web/src/app/(app)/cart/page.tsxapps/web/src/app/(app)/checkout/[productId]/page.tsxapps/web/src/app/(app)/home/page.tsxapps/web/src/app/(app)/layout.tsxapps/web/src/app/(app)/messages/page.tsxapps/web/src/app/(app)/notifications/page.tsxapps/web/src/app/(app)/orders/[id]/page.tsxapps/web/src/app/(app)/orders/confirmed/[orderId]/page.tsxapps/web/src/app/(app)/orders/page.tsxapps/web/src/app/(app)/saved/page.tsxapps/web/src/app/(app)/wishlist/page.tsxapps/web/src/app/(public)/p/[code]/ProductDetailClient.tsxapps/web/src/app/(public)/u/[username]/page.tsxapps/web/src/app/(settings)/settings/account/page.tsxapps/web/src/app/(settings)/settings/page.tsxapps/web/src/app/(settings)/settings/profile/page.tsxapps/web/src/app/(settings)/settings/store/account/page.tsxapps/web/src/app/(settings)/settings/store/page.tsxapps/web/src/app/(settings)/settings/store/profile/page.tsxapps/web/src/app/(shopper)/buyer/_components/ShopperShell.tsxapps/web/src/app/(shopper)/buyer/cart/CartCheckoutSheet.tsxapps/web/src/app/(shopper)/buyer/cart/CartClient.tsxapps/web/src/app/(shopper)/buyer/checkout/[productId]/CheckoutClient.tsxapps/web/src/app/(shopper)/buyer/measurements/page.tsxapps/web/src/app/(shopper)/buyer/orders/[id]/OrderDetailClient.tsxapps/web/src/app/(shopper)/buyer/orders/confirmed/[orderId]/OrderConfirmedClient.tsxapps/web/src/app/(shopper)/buyer/profile/page.tsxapps/web/src/app/(shopper)/buyer/settings/page.tsxapps/web/src/app/(store)/store/my-store/MyStoreClient.tsxapps/web/src/app/(store)/store/my-store/page.tsxapps/web/src/app/(store)/store/settings/page.tsxapps/web/src/components/feed/FeedCard.tsxapps/web/src/components/feed/QuickBuySheet.tsxapps/web/src/components/layout/AppShell.tsxapps/web/src/components/layout/BottomNav.tsxapps/web/src/components/layout/LeftNav.tsxapps/web/src/components/layout/ProfileMenuDrawer.tsxapps/web/src/components/layout/StoreMenuDrawer.tsxapps/web/src/components/layout/navigation.tsapps/web/src/components/order/OrderCard.tsxapps/web/src/components/settings/AccountSettingsContent.tsxapps/web/src/hooks/useOwnProfile.tsapps/web/src/hooks/useOwnerStore.tsapps/web/src/lib/route-policy.tsapps/web/src/lib/routes.tsapps/web/src/lib/wishlist.tsapps/web/src/middleware.ts
💤 Files with no reviewable changes (7)
- apps/web/src/app/(shopper)/buyer/measurements/page.tsx
- apps/web/src/app/(shopper)/buyer/profile/page.tsx
- apps/web/src/app/(store)/store/settings/page.tsx
- apps/web/src/app/(store)/store/my-store/MyStoreClient.tsx
- apps/web/src/app/(store)/store/my-store/page.tsx
- apps/web/src/app/(shopper)/buyer/settings/page.tsx
- apps/web/next.config.mjs
| const publicRoutes = ['/', '/explore', '/stores', '/u/', '/p/', '/c/', '/about', '/terms', '/privacy', '/help'] | ||
| const authRoutes = ['/login', '/register', '/forgot-password', '/verify-email'] | ||
|
|
||
| // Public route — allow through: | ||
| if (publicRoutes.some(r => pathname.startsWith(r))) return NextResponse.next() | ||
|
|
||
| // Auth page + already logged in — redirect to feed: | ||
| // Auth page + already logged in — redirect to home: | ||
| if (authRoutes.some(r => pathname.startsWith(r)) && session) { | ||
| return NextResponse.redirect(new URL('/buyer/feed', request.url)) | ||
| return NextResponse.redirect(new URL('/home', request.url)) | ||
| } | ||
|
|
||
| // Protected route + no session — redirect to login: | ||
| if (!session && (pathname.startsWith('/buyer/') || pathname.startsWith('/store/'))) { | ||
| if (!session && (pathname.startsWith('/home') || pathname.startsWith('/cart') || pathname.startsWith('/orders') || pathname.startsWith('/store/'))) { | ||
| return NextResponse.redirect(new URL(`/login?redirect=${pathname}`, request.url)) |
There was a problem hiding this comment.
Update the middleware example to match the shared route policy.
The protected-route snippet still only covers /home, /cart, /orders, and /store/, so anyone copying it will miss /checkout, /wishlist, /saved, /messages, /notifications, and /settings/*. At this point it would be safer to reference apps/web/src/lib/route-policy.ts directly instead of duplicating prefix lists here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/AGENTS.md` around lines 587 - 600, Replace the hardcoded
protected-route checks in the middleware (the explicit
pathname.startsWith('/home') || ... block) with the shared route policy exported
from route-policy.ts: import and call the exported helper (e.g.,
isProtectedRoute or protectedPrefixes) to determine if a pathname is protected,
and use that result in the session check that performs NextResponse.redirect;
also update the example to reference the centralized publicRoutes/authRoutes
constants (or use exported helpers) instead of duplicating lists so the
middleware covers /checkout, /wishlist, /saved, /messages, /notifications,
/settings/* and stays in sync.
What does this PR do?
Adds the canonical pre-launch private route foundation for Twizrr web. Private shopper routes move away from
/buyer/*into short app routes such as/home,/cart,/orders,/wishlist,/saved,/messages, and/settings/*;/u/:usernameis added for public user profiles; route helpers/policy and middleware are updated so protected routes redirect through login while removed legacy physical routes now fall through to the normal 404 policy.Type of change
Area affected
How to test this
apps/web, runpnpm.cmd run lint,npx.cmd tsc --noEmit, andpnpm.cmd run build./home,/cart,/orders,/orders/[id],/orders/confirmed/[orderId],/wishlist,/saved,/messages,/notifications,/settings/profile,/settings/account,/settings/store/profile,/settings/store/account, and/u/[username]./buyer/profile,/account/profile,/store/my-store, and/store/settingsare no longer present as app routes and should fall through to the normal 404 behavior.Expected result: canonical routes compile/build successfully, protected routes are governed by the new route policy/middleware, and old pre-launch route styles are not kept as compatibility redirects.
Pre-commit checklist
console.logleft in production code.envfiles committedanytypes addeddb pushScreenshots
N/A. This PR is route foundation/navigation wiring only; visible UI polish and screenshots are intentionally left for later UX cleanup PRs.
Notes for reviewer
apps/web/src/lib/route-policy.tsand canonical path helpers inapps/web/src/lib/routes.ts./u/:usernameis public and owner-aware at a lightweight route-foundation level./buyer/*physical route pages were removed instead of keeping redirects because Twizrr has not launched yet.apps/web/AGENTS.mdwas updated to remove stale/@chiastylesand/@[handle]examples in favor of/stores/chiastylesand/stores/[handle].Validation completed:
pnpm.cmd run lint: passednpx.cmd tsc --noEmit: passedpnpm.cmd run build: passedpnpm.cmd --filter @twizrr/shared run build: passed locally for the pre-commit hook prerequisitenpx.cmd prisma generate --schema=prisma/schema.prisma: passed locally for the pre-commit hook prerequisitepnpm.cmd --filter @twizrr/backend exec tsc --noEmit: passed via pre-commit hookgit diff --cached --check: passed before commitSummary by CodeRabbit
New Features
Improvements