Skip to content

FEAT: add SlidingMenu component#144

Merged
Saahi30 merged 8 commits intodevfrom
dashboard_menu
Nov 7, 2025
Merged

FEAT: add SlidingMenu component#144
Saahi30 merged 8 commits intodevfrom
dashboard_menu

Conversation

@Saahi30
Copy link
Collaborator

@Saahi30 Saahi30 commented Nov 7, 2025

📝 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)

Screenshot 2025-11-07 at 6 08 48 AM Screenshot 2025-11-07 at 6 08 42 AM Screenshot 2025-11-07 at 6 08 34 AM

✅ Checklist

  • I have read the contributing guidelines.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if applicable).
  • Any dependent changes have been merged and published in downstream modules.

Summary by CodeRabbit

  • New Features

    • Added "Create Campaign" placeholder pages for Brand and Creator users ("coming soon").
    • Added a sliding navigation menu on Brand and Creator home pages with role-aware links, keyboard accessibility, outside-click closing, and dark-mode styling.
  • Bug Fixes

    • Normalized signup payload role values to consistent lowercase identifiers.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

Walkthrough

Adds 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

Cohort / File(s) Change Summary
SlidingMenu Component
frontend/components/SlidingMenu.tsx
New client-side React component implementing a hamburger-controlled sliding navigation panel with backdrop, close button, Escape/outside-click handling, ARIA attributes, and role-based link generation.
Brand pages
frontend/app/brand/home/page.tsx, frontend/app/brand/createcampaign/page.tsx
SlidingMenu imported and rendered in the brand home page with role="brand". New placeholder create campaign page added (BrandCreateCampaign).
Creator pages
frontend/app/creator/home/page.tsx, frontend/app/creator/createcampaign/page.tsx
SlidingMenu imported and rendered in the creator home page with role="creator". New placeholder create campaign page added (CreatorCreateCampaign).
Signup form
frontend/app/signup/page.tsx
Signup form payload changed: accountType values are normalized before send — "Creator" -> "creator", otherwise "brand".

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
Loading
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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect SlidingMenu: outside-click detection, Escape handler removal, focus management, and ARIA attributes.
  • Verify role-based link paths match new createcampaign pages and Next.js routing.
  • Confirm signup normalization logic aligns with backend expectations and test payload shape.

Poem

🐇 I hopped in code and found a door,
A sliding path across the floor.
Creators, brands — a cheerful band,
New pages bloom at my small hand.
Click, escape, and off we soar!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'FEAT: add SlidingMenu component' accurately summarizes the primary change in the PR, which is the introduction of a reusable SlidingMenu component and its integration into home pages.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dashboard_menu

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Saahi30
Copy link
Collaborator Author

Saahi30 commented Nov 7, 2025

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" and aria-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

📥 Commits

Reviewing files that changed from the base of the PR and between 94feb3f and 1178ce1.

📒 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 SlidingMenu are properly implemented. The component is correctly positioned at the top of the component tree with the appropriate role="creator" prop.

Also applies to: 39-39

frontend/app/brand/home/page.tsx (1)

4-4: Clean SlidingMenu integration.

The import and usage of SlidingMenu are properly implemented. The component is correctly positioned with the appropriate role="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.

@Saahi30
Copy link
Collaborator Author

Saahi30 commented Nov 7, 2025

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (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 accountType enum 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1178ce1 and bd4849e.

📒 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 Role type 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 the Role type 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.

@Saahi30
Copy link
Collaborator Author

Saahi30 commented Nov 7, 2025

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" and aria-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-lock or focus-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 add ref={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-800 for 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

📥 Commits

Reviewing files that changed from the base of the PR and between bd4849e and 237b2ae.

📒 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's mt-auto to work correctly, addressing the previous positioning concern.

@Saahi30 Saahi30 merged commit 3f40234 into dev Nov 7, 2025
1 check passed
@Saahi30 Saahi30 deleted the dashboard_menu branch November 7, 2025 01:16
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.

1 participant