From f465b90111bf5570dbd9cfbfd7579f9b577b7e4d Mon Sep 17 00:00:00 2001 From: Lau Chaves Date: Mon, 25 Nov 2024 00:21:12 -0600 Subject: [PATCH] Feat: Enhance Filter UI --- frontend/app/components/ui/sidebar.tsx | 178 ++++++++++++++++--------- frontend/app/components/ui/slider.tsx | 60 +++++---- frontend/app/marketplace/page.tsx | 127 +++++++++++++----- 3 files changed, 242 insertions(+), 123 deletions(-) diff --git a/frontend/app/components/ui/sidebar.tsx b/frontend/app/components/ui/sidebar.tsx index 206228b..5dd41c5 100644 --- a/frontend/app/components/ui/sidebar.tsx +++ b/frontend/app/components/ui/sidebar.tsx @@ -1,6 +1,6 @@ -import React, { createContext, useContext, useState, ReactNode } from "react"; -import { cn } from "@/lib/utils"; -import { X, Menu } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Menu, X } from "lucide-react"; +import React, { createContext, useContext, useState, ReactNode, useRef, useEffect } from "react"; interface SidebarContextType { isOpen: boolean; @@ -28,75 +28,121 @@ export const useSidebar = () => { return context; }; -export const Sidebar = React.forwardRef>( - ({ className, ...props }, ref) => { - const { isOpen } = useSidebar(); - return ( -
- ); - } -); +export const Sidebar = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { isOpen, toggleSidebar } = useSidebar(); + const sidebarRef = useRef(null); + + // Close sidebar when clicking outside + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (sidebarRef.current && !sidebarRef.current.contains(event.target as Node)) { + // Close sidebar if the click is outside + if (isOpen) { + toggleSidebar(); + } + } + }; + + document.addEventListener("mousedown", handleClickOutside); + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen, toggleSidebar]); + + return ( +
{ + sidebarRef.current = node; + if (ref) { + if (typeof ref === "function") ref(node); + else ref.current = node; + } + }} + className={cn( + "fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-lg transition-transform duration-300", + isOpen ? "translate-x-0" : "-translate-x-full", + className + )} + {...props} + /> + ); +}); Sidebar.displayName = "Sidebar"; -export const SidebarHeader = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- {props.children} - -
- ) -); +export const SidebarHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ {props.children} + +
+)); SidebarHeader.displayName = "SidebarHeader"; -export const SidebarContent = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -); +export const SidebarContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); SidebarContent.displayName = "SidebarContent"; -export const SidebarTrigger = React.forwardRef>( - ({ className, ...props }, ref) => { - const { toggleSidebar } = useSidebar(); - return ( - - ); - } -); +export const SidebarTrigger = React.forwardRef< + HTMLButtonElement, + React.ButtonHTMLAttributes +>(({ className, ...props }, ref) => { + const { toggleSidebar } = useSidebar(); + return ( + + ); +}); SidebarTrigger.displayName = "SidebarTrigger"; -export const SidebarClose = React.forwardRef>( - ({ className, ...props }, ref) => { - const { toggleSidebar } = useSidebar(); - return ( - - ); - } -); +export const SidebarClose = React.forwardRef< + HTMLButtonElement, + React.ButtonHTMLAttributes +>(({ className, ...props }, ref) => { + const { toggleSidebar } = useSidebar(); + return ( + + ); +}); SidebarClose.displayName = "SidebarClose"; - -// export { SidebarProvider, Sidebar, SidebarHeader, SidebarContent, SidebarTrigger, SidebarClose }; diff --git a/frontend/app/components/ui/slider.tsx b/frontend/app/components/ui/slider.tsx index 43e5919..0d6d3ab 100644 --- a/frontend/app/components/ui/slider.tsx +++ b/frontend/app/components/ui/slider.tsx @@ -1,28 +1,42 @@ "use client" -import * as React from "react" -import * as SliderPrimitive from "@radix-ui/react-slider" - -import { cn } from "@/lib/utils" +import * as SliderPrimitive from "@radix-ui/react-slider"; +import * as React from "react"; +import { cn } from "@/lib/utils"; const Slider = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - - - -)) -Slider.displayName = SliderPrimitive.Root.displayName + React.ElementRef, + React.ComponentPropsWithoutRef & { value: [number, number]; onValueChange: (value: [number, number]) => void; } +>(({ className, value, onValueChange, ...props }, ref) => ( + + {/* Range */} + + + + + {/* Min Value */} + + + {/* Max Value */} + + +)); + +Slider.displayName = "Slider"; -export { Slider } \ No newline at end of file +export { Slider } diff --git a/frontend/app/marketplace/page.tsx b/frontend/app/marketplace/page.tsx index 178d7d5..5f25f16 100644 --- a/frontend/app/marketplace/page.tsx +++ b/frontend/app/marketplace/page.tsx @@ -1,12 +1,18 @@ 'use client'; -import { useState, Dispatch, SetStateAction } from 'react'; +import { type Dispatch, type SetStateAction, useState } from "react"; import { Slider } from "@/app/components/ui/slider"; import { Checkbox } from "@/app/components/ui/checkbox"; import { Button } from "@/app/components/ui/button"; import { Input } from "@/app/components/ui/input"; import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/app/components/ui/card"; import { Sidebar, SidebarContent, SidebarHeader, SidebarProvider, SidebarTrigger } from "@/app/components/ui/sidebar"; -import { Search, Menu as HamIcon } from "lucide-react"; +import { usePathname, useRouter } from "next/navigation"; +import { + Menu as HamIcon, + Search, + Home, +} from "lucide-react"; +import { PersonIcon } from '@radix-ui/react-icons'; interface Product { id: number; @@ -69,7 +75,10 @@ export default function Marketplace() { handleCategoryChange={handleCategoryChange} />
- +
@@ -81,12 +90,23 @@ function SidebarComponent({ priceRange, setPriceRange, selectedCategories, handl return ( -

Filters

+
+ +

Hello, User

+
- +
-
-

Price range

+ {/* Shop by Price */} +
+

Shop by Price

+
+
+ + ${priceRange[0].toFixed(2)} - ${priceRange[1].toFixed(2)}+ + +
+
-
- ${priceRange[0]} - ${priceRange[1]} -
-
-

Categories

+ {/* Divider */} +
+ {/* Shop by Department */} +
+

Shop by Department

{["Electronics", "Furniture", "Appliances", "Sports"].map((category) => (
@@ -121,29 +140,69 @@ function SidebarComponent({ priceRange, setPriceRange, selectedCategories, handl ); } -function HeaderComponent({ searchTerm, setSearchTerm }: HeaderComponentProps) { +function NavbarComponent({ searchTerm, setSearchTerm }: HeaderComponentProps) { + const pathname = usePathname(); + const router = useRouter(); + const showHomeButton = pathname?.includes("/marketplace"); + return ( -
- - - -
- setSearchTerm(e.target.value)} - className="w-[16rem] h-[3rem]" - /> - +
+ ); }