Conversation
WalkthroughAdds a client-side SlidingMenu component, integrates it into brand and creator home pages, adds placeholder create-campaign pages for both roles, and normalizes the signup role value sent to /api/auth/signup. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Home as Home Page
participant Menu as SlidingMenu
participant Router as Next.js Link
User->>Home: Visit brand/creator home
Home->>Menu: Render <SlidingMenu role="brand|creator" />
User->>Menu: Click hamburger
Menu->>Menu: set open = true
Menu->>User: Show panel + backdrop
alt Click "Create Campaign"
User->>Menu: Click link
Menu->>Router: Navigate to /brand|creator/createcampaign
Router->>User: Route to placeholder page
else Press Escape or click backdrop
User->>Menu: Escape / outside click
Menu->>Menu: set open = false
Menu->>User: Hide panel
end
sequenceDiagram
participant User
participant SignupForm
participant API as /api/auth/signup
User->>SignupForm: Submit form (accountType)
SignupForm->>SignupForm: Normalize role ("Creator" -> "creator", else "brand")
SignupForm->>API: POST payload with normalized role
API->>User: Respond
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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 |
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
frontend/app/brand/createcampaign/page.tsx (1)
1-10: Code duplication noted.This duplication with the creator createcampaign page has been flagged in the review of
frontend/app/creator/createcampaign/page.tsx.
🧹 Nitpick comments (3)
frontend/app/creator/createcampaign/page.tsx (1)
1-10: Consider extracting shared placeholder component.This page is nearly identical to
frontend/app/brand/createcampaign/page.tsx. Both render the same "coming soon" placeholder UI. Consider creating a shared component to reduce duplication.Example refactor:
Create
frontend/components/PlaceholderPage.tsx:export default function PlaceholderPage({ title }: { title: string }) { return ( <main className="min-h-screen p-8"> <h1 className="text-2xl font-bold">{title}</h1> <p className="mt-4 text-slate-600"> Welcome to {title} — coming soon! </p> </main> ); }Then use it in both pages:
+import PlaceholderPage from "@/components/PlaceholderPage"; + export default function CreatorCreateCampaign() { - return ( - <main className="min-h-screen p-8"> - <h1 className="text-2xl font-bold">Create Campaign</h1> - <p className="mt-4 text-slate-600"> - Welcome to Create Campaign — coming soon! - </p> - </main> - ); + return <PlaceholderPage title="Create Campaign" />; }frontend/components/SlidingMenu.tsx (2)
70-98: Dialog semantics are good, but focus management is missing.The panel correctly uses
role="dialog"andaria-modal="true". However, when the menu opens, focus should move to the dialog for better keyboard navigation accessibility.Consider adding focus management:
export default function SlidingMenu({ role }: Props) { const [open, setOpen] = useState(false); const panelRef = useRef<HTMLDivElement | null>(null); + const closeButtonRef = useRef<HTMLButtonElement | null>(null); + // Focus management + useEffect(() => { + if (open && closeButtonRef.current) { + closeButtonRef.current.focus(); + } + }, [open]); // ... rest of component <button + ref={closeButtonRef} aria-label="Close menu" onClick={() => setOpen(false)} className="rounded p-1 hover:bg-slate-100" >
70-134: Consider adding body scroll lock when menu is open.When the sliding menu is open, users can still scroll the content behind it. Adding a scroll lock improves the user experience by preventing background scrolling.
You can implement this with an effect:
+ // Body scroll lock + useEffect(() => { + if (open) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = ""; + } + return () => { + document.body.style.overflow = ""; + }; + }, [open]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/app/brand/createcampaign/page.tsx(1 hunks)frontend/app/brand/home/page.tsx(2 hunks)frontend/app/creator/createcampaign/page.tsx(1 hunks)frontend/app/creator/home/page.tsx(2 hunks)frontend/components/SlidingMenu.tsx(1 hunks)
🔇 Additional comments (5)
frontend/app/creator/home/page.tsx (1)
4-4: Clean SlidingMenu integration.The import and usage of
SlidingMenuare properly implemented. The component is correctly positioned at the top of the component tree with the appropriaterole="creator"prop.Also applies to: 39-39
frontend/app/brand/home/page.tsx (1)
4-4: Clean SlidingMenu integration.The import and usage of
SlidingMenuare properly implemented. The component is correctly positioned with the appropriaterole="brand"prop, maintaining consistency with the creator home page integration.Also applies to: 39-39
frontend/components/SlidingMenu.tsx (3)
13-31: Good keyboard and outside click handling.The ESC key handler and outside click detection are properly implemented with appropriate cleanup. The dependency array for the outside click effect correctly includes
open.
40-60: Well-implemented hamburger button with good accessibility.The button includes proper ARIA labels (
aria-label,aria-expanded), visual feedback on hover, and appropriate icon transitions. Good work on accessibility.
63-67: Backdrop implementation is solid.The backdrop correctly uses
aria-hidden, has smooth opacity transitions, and provides an intuitive way to close the menu by clicking outside.
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
frontend/app/signup/page.tsx (1)
87-87: Consider explicit role mapping for better maintainability.The current ternary implicitly maps "Brand" to "brand" via the else clause. If the
accountTypeenum is extended in the future (line 16), this line would silently default new values to "brand".Consider using an explicit mapping:
- role: data.accountType === "Creator" ? "creator" : "brand", + role: data.accountType === "Creator" ? "creator" : data.accountType === "Brand" ? "brand" : (() => { throw new Error(`Unknown account type: ${data.accountType}`) })(),Or use a lookup:
const roleMap: Record<typeof data.accountType, string> = { Creator: "creator", Brand: "brand" }; // In the payload: role: roleMap[data.accountType],frontend/components/SlidingMenu.tsx (1)
43-63: Verify z-index layering for button and panel.Both the hamburger button (line 46,
z-50) and the sliding panel (line 78,z-50) use the same z-index. While this may work due to DOM order, it's clearer to give the button a higher z-index (z-[51]) to ensure it always appears above the panel.- className="fixed top-4 left-4 z-50 inline-flex transform items-center justify-center rounded-md border bg-white/90 p-2 shadow-sm transition hover:scale-105 dark:bg-slate-900/90" + className="fixed top-4 left-4 z-[51] inline-flex transform items-center justify-center rounded-md border bg-white/90 p-2 shadow-sm transition hover:scale-105 dark:bg-slate-900/90"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/app/signup/page.tsx(1 hunks)frontend/components/SlidingMenu.tsx(1 hunks)
🔇 Additional comments (5)
frontend/components/SlidingMenu.tsx (5)
1-9: LGTM: Type definitions and imports are correct.The strict
Roletype definition and "use client" directive are appropriate for this interactive component.
24-33: LGTM: Outside click detection is correctly implemented.The handler properly guards against null refs and only acts when the menu is open.
35-38: Routing logic is defensive and correct.The strict equality check addresses the previous review concern. The normalization via
String(role).trim().toLowerCase()is defensive but not strictly necessary since theRoletype already guarantees lowercase values.
65-70: LGTM: Backdrop implementation is correct.The z-index layering (backdrop at z-40 below the panel at z-50) and click-to-close behavior are appropriate.
106-124: LGTM: Link component uses modern Next.js syntax.The Link implementation correctly uses Next.js 13+ patterns without
legacyBehavior, addressing the previous review feedback.
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
frontend/components/SlidingMenu.tsx (2)
11-33: Add focus management and focus trap for keyboard accessibility.The component uses
role="dialog"andaria-modal="true"but lacks focus management. When the menu opens, focus should move to the dialog and be trapped inside; when it closes, focus should return to the trigger button. Without this, keyboard users can tab to elements behind the backdrop, violating WCAG 2.1 guidelines.Consider using a library like
react-focus-lockorfocus-trap-react, or implement manually:
- On open: move focus to the first focusable element in the panel or the close button
- While open: trap Tab/Shift+Tab within the panel
- On close: return focus to the hamburger button
Example using
react-focus-lock:+import FocusLock from 'react-focus-lock'; export default function SlidingMenu({ role }: Props) { const [open, setOpen] = useState(false); const panelRef = useRef<HTMLDivElement | null>(null); + const triggerRef = useRef<HTMLButtonElement | null>(null); // Close on ESC useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === "Escape") setOpen(false); } window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, []); // Click outside to close useEffect(() => { function onClick(e: MouseEvent) { if (!open) return; if (!panelRef.current) return; if (!panelRef.current.contains(e.target as Node)) setOpen(false); } document.addEventListener("mousedown", onClick); return () => document.removeEventListener("mousedown", onClick); }, [open]); + // Return focus to trigger on close + useEffect(() => { + if (!open && triggerRef.current) { + triggerRef.current.focus(); + } + }, [open]);Then wrap the
<aside>with<FocusLock disabled={!open}>...</FocusLock>and addref={triggerRef}to the hamburger button.
82-100: Add dark mode hover state for consistency.The close button in the header lacks a dark mode hover state, while the navigation link includes
dark:hover:bg-slate-800for consistency.Apply this diff:
<button aria-label="Close menu" onClick={() => setOpen(false)} - className="rounded p-1 hover:bg-slate-100" + className="rounded p-1 hover:bg-slate-100 dark:hover:bg-slate-800" >
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/components/SlidingMenu.tsx(1 hunks)
🔇 Additional comments (4)
frontend/components/SlidingMenu.tsx (4)
5-9: LGTM: Type definition is now strict.The strict Role type and Props definition correctly enforce type safety, addressing the previous concern about allowing arbitrary strings.
35-38: LGTM: Strict role comparison implemented.The routing logic now uses strict equality comparison, addressing the previous fragility concern with
startsWith. The defensive normalization is a good practice.
106-124: LGTM: Modern Next.js Link syntax in use.The Link component correctly uses the modern Next.js 13+ API without
legacyBehavior, addressing the previous concern.
78-78: LGTM: Flex layout enables footer positioning.The aside element now includes
flex flex-col, which allows the footer'smt-autoto work correctly, addressing the previous positioning concern.
📝 Description
This pull request introduces a new, reusable SlidingMenu hamburger menu component for post-login dashboard pages, and adds blank campaign creation pages for both creator and brand roles.
🔧 Changes Made
SlidingMenu Component:
Created SlidingMenu.tsx, a responsive, accessible sliding menu that appears on creator and brand home pages.
The menu is toggled by a fixed hamburger button and includes a "Create Campaign" action.
Uses ARIA attributes, keyboard navigation, and Tailwind CSS transitions for smooth UX.
Navigates to the appropriate campaign creation route based on user role.
Campaign Creation Pages:
Added page.tsx and page.tsx with friendly placeholder messages:
“Welcome to Create Campaign — coming soon!”
Integration:
Integrated into the creator home page.
Integrated into the brand home page.
📷 Screenshots or Visual Changes (if applicable)
✅ Checklist
Summary by CodeRabbit
New Features
Bug Fixes