Feat/web shopper layout#293
Conversation
Middleware (middleware.ts): - Protects /buyer/* and /store/* — redirects to /login?redirect=[url] if twizrr_access_token cookie is absent (presence-gated; JWT validity is enforced by the backend on every API call) - Auth pages (/login /register /forgot-password /verify-email) redirect to /explore when a session cookie already exists - Preserves existing /@handle → /stores/:handle rewrite Shopper layout: - apps/web/src/app/(shopper)/buyer/layout.tsx — Server Component wrapper - apps/web/src/app/(shopper)/buyer/_components/ShopperShell.tsx — thin 'use client' wrapper that reads usePathname() and forwards it to AppShell as activeHref; avoids making the layout itself a client component ProfileMenuDrawer: - apps/web/src/components/layout/ProfileMenuDrawer.tsx - Uses existing BottomSheet primitive (W-01) + Radix Dialog - Sections: Shopping (Orders, Cart, Wishlist, Saved), Account (Profile, Settings, Measurements, Reviews-soon), Support (Help Center) - Manage Store link only renders when hasStore prop is true - Exported from components/layout/index.ts No backend files changed. No env files changed. Auth assumption: cookie presence only at the edge; real JWT validation happens backend-side on every API request. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- aria-disabled="true" (string) → aria-disabled={true} (boolean)
- & HTML entity → literal & in Measurements & Delivery label
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a buyer shopping layout shell with client-side routing awareness, a profile navigation drawer with conditional store management, and middleware-enforced authentication gates that protect buyer/store routes while redirecting authenticated users away from login pages. ChangesBuyer Shopping Interface and Auth Protection
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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: 2
🧹 Nitpick comments (2)
apps/web/src/components/layout/ProfileMenuDrawer.tsx (2)
195-216: 💤 Low valueConsider routing the Manage Store entry through
DrawerItem.The Manage Store
<Link>duplicates the layout, sizing, focus ring, and hover styles ofDrawerItem— only the text color and weight differ. ExtendingDrawerItemwith an optionalvariant/accentprop would keep all menu rows visually consistent and avoid drift if the base styles change later. Skip if you'd rather keep it explicit.🤖 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/ProfileMenuDrawer.tsx` around lines 195 - 216, Replace the duplicated styled Link for "Manage Store" with the shared DrawerItem component: add an optional prop (e.g., variant or accent) to DrawerItem that toggles the saffron text color and font weight, ensure DrawerItem accepts and forwards href, onClick, icon (LayoutDashboard) and children, and update ProfileMenuDrawer's hasStore branch to render <DrawerItem variant="accent" href="/store/dashboard" onClick={close}>Manage Store</DrawerItem> so all sizing, focus ring, hover and layout remain centralized in DrawerItem.
29-65: 💤 Low valueMake
onCloseoptional forsoonitems.When
soonis true the item renders as a<div>and never invokesonClose, but the prop is still required by the type — every disabled item has to pass a dead handler. Marking it optional (or splitting the variants) tightens the contract.♻️ Proposed change
interface DrawerItemProps { href: string; icon: LucideIcon; label: string; /** Mark item as coming soon — renders disabled with a label */ soon?: boolean; - onClose: () => void; + onClose?: () => void; }🤖 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/ProfileMenuDrawer.tsx` around lines 29 - 65, The DrawerItemProps type currently requires onClose even for disabled "soon" items; update DrawerItemProps to make onClose optional (onClose?: () => void) and adjust the DrawerItem function signature to accept the optional onClose, ensuring any code paths that would call onClose (the non-soon rendering) still call it only when defined; leave the soon block as-is (it won't call onClose) and ensure any callers creating "soon" items no longer must pass a dummy handler.
🤖 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/src/middleware.ts`:
- Line 10: PROTECTED_PREFIXES currently contains trailing slashes which makes
pathname.startsWith("/buyer/") miss the exact "/buyer" route; update the
matching strategy in middleware.ts: either remove trailing slashes in
PROTECTED_PREFIXES (use "/buyer" and "/store") and change the check to (pathname
=== prefix || pathname.startsWith(prefix + "/")), or keep prefixes without
slashes and use segment-boundary-aware logic so both the root ("/buyer") and
nested routes ("/buyer/...") are gated; adjust the code that iterates
PROTECTED_PREFIXES and uses startsWith accordingly.
- Around line 12-17: AUTH_PAGE_PREFIXES currently uses plain prefixes which
makes pathname.startsWith("/login") over-match routes like "/login-help"; update
the check to match only exact auth routes or a path segment by changing
AUTH_PAGE_PREFIXES entries to include a trailing "/" (e.g. "/login/") or by
performing a segment-aware test (compare pathname === "/login" ||
pathname.startsWith("/login/")) wherever pathname.startsWith is used (refer to
AUTH_PAGE_PREFIXES and the middleware check using pathname.startsWith around the
code block at lines ~49-55) so authenticated users are only matched for the
intended auth pages.
---
Nitpick comments:
In `@apps/web/src/components/layout/ProfileMenuDrawer.tsx`:
- Around line 195-216: Replace the duplicated styled Link for "Manage Store"
with the shared DrawerItem component: add an optional prop (e.g., variant or
accent) to DrawerItem that toggles the saffron text color and font weight,
ensure DrawerItem accepts and forwards href, onClick, icon (LayoutDashboard) and
children, and update ProfileMenuDrawer's hasStore branch to render <DrawerItem
variant="accent" href="/store/dashboard" onClick={close}>Manage
Store</DrawerItem> so all sizing, focus ring, hover and layout remain
centralized in DrawerItem.
- Around line 29-65: The DrawerItemProps type currently requires onClose even
for disabled "soon" items; update DrawerItemProps to make onClose optional
(onClose?: () => void) and adjust the DrawerItem function signature to accept
the optional onClose, ensuring any code paths that would call onClose (the
non-soon rendering) still call it only when defined; leave the soon block as-is
(it won't call onClose) and ensure any callers creating "soon" items no longer
must pass a dummy handler.
🪄 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: f09fadb9-e990-42e4-a3f8-74961fb8ea5a
📒 Files selected for processing (5)
apps/web/src/app/(shopper)/buyer/_components/ShopperShell.tsxapps/web/src/app/(shopper)/buyer/layout.tsxapps/web/src/components/layout/ProfileMenuDrawer.tsxapps/web/src/components/layout/index.tsapps/web/src/middleware.ts
pathname.startsWith("/buyer/") missed the exact path /buyer.
Now checks pathname === p || pathname.startsWith(p + "/") so both
/buyer and /buyer/* (and equivalently /store, /store/*) are gated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
What does this PR do?
Type of change
What does this PR do?
Builds W-08: Shopper Layout + Middleware.
This PR adds the authenticated shopper layout foundation for
/buyer/*routes, protects shopper routes through middleware, preserves redirect URLs for unauthenticated users, and adds the Profile Menu Drawer foundation for future shopper pages.Type of change
How to test this
Checkout
feat/web-shopper-layoutRun:
cd apps/web
pnpm run lint
npx tsc --noEmit
pnpm run build
Visit a protected buyer route while unauthenticated, for example:
/buyer/cart
Confirm it redirects to:
/login?redirect=/buyer/cart
Confirm shopper layout uses the existing W-02 shell.
Confirm
hasStoreProfile={false}is passed for shopper routes so “Manage Store” does not show for every user.Confirm ProfileMenuDrawer includes:
Pre-commit checklist
Notes
The previous retrospective audit found that
AppShelldefaultshasStoreProfiletotrue. W-08 explicitly passeshasStoreProfile={false}in the shopper shell, so shopper-route users will not accidentally see the Manage Store link.Follow-up cleanup items from older tasks will be handled in a separate PR, not in this branch.
Summary by CodeRabbit
Release Notes
New Features
Security & Authentication