Skip to content

Commit

Permalink
feat: implement shadcn dark mode (#80)
Browse files Browse the repository at this point in the history
* feat: implement shadcn dark mode

* feat: implement shadcn dark mode and merge upstream changes
  • Loading branch information
AnoukRImola authored Dec 24, 2024
1 parent 92dcaf5 commit cd0d92c
Show file tree
Hide file tree
Showing 18 changed files with 1,172 additions and 9,463 deletions.
207 changes: 84 additions & 123 deletions frontend/app/components/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,135 +1,96 @@
"use client";

import Link from "next/link";
import { useEffect, useState } from "react";
import { SafeSwapLogo } from "@/app/components/ui/SafeSwapLogo";
import { Button } from "@/app/components/ui/button";
import { Input } from "@/app/components/ui/input";
import DeliveryLocationButton from "@/app/components/ui/delivery-location-button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/app/components/ui/dropdown-menu";

import DeliveryLocationButton from "@/app/components/ui/delivery-location-button";
import { MapPin } from 'lucide-react';

import { Input } from "@/app/components/ui/input";
import { ThemeToggle } from "@/app/components/ui/theme-toggle";
import {
Search,
ShoppingCart,
Wallet,
User,
Settings,
List,
History,
History,
List,
Search,
Settings,
ShoppingCart,
User,
Wallet,
} from "lucide-react";
import { IoMoon, IoSunny } from "react-icons/io5";
import SubHeader from "./subheader/SubHeader";

import Link from "next/link";
import { useState } from "react";

export default function Header() {
const [searchTerm, setSearchTerm] = useState<string>("");
const [dark, setDark] = useState(false);

useEffect(() => {
const darkMode = localStorage.getItem("darkMode");
if (darkMode) {
setDark(JSON.parse(darkMode));
}
}, []);

useEffect(() => {
if (typeof window !== "undefined") {
document.documentElement.classList.toggle("dark", dark);
localStorage.setItem("darkMode", JSON.stringify(dark));
}
}, [dark]);

const showSearchBar = searchTerm !== undefined && setSearchTerm !== undefined;
const [searchTerm, setSearchTerm] = useState<string>("");
const showSearchBar = searchTerm !== undefined && setSearchTerm !== undefined;

return (
<>
<header className="flex items-center justify-between px-6 py-4 border-b">
<div className="flex items-center gap-4 min-w-max">
<Link href={"/"}>
<SafeSwapLogo width={150} height={40} />
</Link>
</div>
<div className="flex items-center gap-4">
{showSearchBar ? (
<div className="relative w-full pl-2 max-w-[18.75rem] md:w-[18.75rem]">
<Input
type="search"
placeholder="Search products..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full h-8 pr-10"
/>
<Button
size="icon"
variant="ghost"
className="absolute right-0 top-0 h-full"
>
<Search className="h-5 w-5" />
<span className="sr-only">Search</span>
</Button>
</div>
) : null}


</div>
<div className="flex gap-4">
<Button size="lg" className="group">
<DeliveryLocationButton />
<MapPin />
Country
</Button>
<Button size="lg" className="group">
<Wallet className="mr-2 h-4 w-4 transition-transform group-hover:scale-110" />
Connect Wallet
</Button>
<Button className="group h-auto">
<ShoppingCart className="h-5 w-5 transition-transform group-hover:scale-110" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="group h-auto">
<User className="h-5 w-5 transition-transform group-hover:scale-110" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
Profile
</DropdownMenuItem>
<DropdownMenuItem>
<List className="mr-2 h-4 w-4" />
My Listings
</DropdownMenuItem>
<DropdownMenuItem>
<History className="mr-2 h-4 w-4" />
Transaction History
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
Settings
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<button
onClick={() => setDark(!dark)}
className="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
aria-label={dark ? "Switch to light mode" : "Switch to dark mode"}
>
{dark ? (
<IoSunny className="w-6 h-6" />
) : (
<IoMoon className="w-6 h-6" />
)}
</button>
</div>
</header>
</>
);
return (
<>
<header className="flex items-center justify-between px-6 py-4 border-b">
<div className="flex items-center gap-4 min-w-max">
<Link href={"/"}>
<SafeSwapLogo width={150} height={40} />
</Link>
</div>
{showSearchBar ? (
<div className="relative w-full pl-2 max-w-[18.75rem] md:w-[18.75rem]">
<Input
type="search"
placeholder="Search products..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full h-8 pr-10"
/>
<Button
size="icon"
variant="ghost"
className="absolute right-0 top-0 h-full"
>
<Search className="h-5 w-5" />
<span className="sr-only">Search</span>
</Button>
</div>
) : null}
<div className="flex gap-4">
<DeliveryLocationButton />
<Button size="lg" className="group">
<Wallet className="mr-2 h-4 w-4 transition-transform group-hover:scale-110" />
Connect Wallet
</Button>
<Button className="group h-auto">
<ShoppingCart className="h-5 w-5 transition-transform group-hover:scale-110" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="group h-auto">
<User className="h-5 w-5 transition-transform group-hover:scale-110" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48">
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
Profile
</DropdownMenuItem>
<DropdownMenuItem>
<List className="mr-2 h-4 w-4" />
My Listings
</DropdownMenuItem>
<DropdownMenuItem>
<History className="mr-2 h-4 w-4" />
Transaction History
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
Settings
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<ThemeToggle />
</div>
</header>
</>
);
}
41 changes: 19 additions & 22 deletions frontend/app/components/header/subheader/SubHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
"use client";

import { Button } from "@radix-ui/themes";
import BreadcrumbNavigation from "../../ui/breadcrumb-navigation";
import { CirclePlus } from "lucide-react";
import { useState } from "react";
import AddProductModal from "../../ui/add-product-modal";
import BreadcrumbNavigation from "../../ui/breadcrumb-navigation";

interface SubHeaderProps {
name: string;
name: string;
}

const SubHeader = ({ name }: SubHeaderProps) => {
const [showModal, setShowModal] = useState(false);
const [showModal, setShowModal] = useState(false);

return (
<>
<div className="flex justify-between items-center px-6 mt-4">
{/* Breadcrumb Navigation */}
<BreadcrumbNavigation />
return (
<>
<div className="flex justify-between items-center px-6 mt-4">
{/* Breadcrumb Navigation */}
<BreadcrumbNavigation />

<Button
className="flex items-center gap-2 bg-black cursor-pointer"
onClick={() => setShowModal(true)}
>
<CirclePlus className="w-5 h-5" />
Add {name}
</Button>
</div>
<Button
className="flex items-center gap-2 bg-black cursor-pointer"
onClick={() => setShowModal(true)}
>
<CirclePlus className="w-5 h-5" />
Add {name}
</Button>
</div>

<AddProductModal
isOpen={showModal}
onClose={() => setShowModal(false)}
/>
</>
);
<AddProductModal isOpen={showModal} onClose={() => setShowModal(false)} />
</>
);
};

export default SubHeader;
38 changes: 22 additions & 16 deletions frontend/app/components/products/ProductDetailModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button } from "@/app/components/ui/button";
import { ShoppingCart, Star } from "lucide-react";
import ImageCarousel from "../ui/image-carrousel";

import { ShoppingCart } from "lucide-react";
interface Product {
id: number;
images: { src: string; alt: string }[];
Expand All @@ -10,7 +10,6 @@ interface Product {
price: number;
category: string;
}
import { Star } from "lucide-react";

function ProductDetailModal({
isOpen,
Expand Down Expand Up @@ -38,34 +37,42 @@ function ProductDetailModal({

return (
<div
className={`fixed inset-0 z-50 flex items-center justify-center bg-gray-500 bg-opacity-50 transition-opacity ${
className={`fixed inset-0 z-50 flex items-center justify-center transition-opacity ${
isOpen ? "opacity-100" : "opacity-0 pointer-events-none"
}`}
>
<div className="fixed inset-0 bg-background/80 backdrop-blur-sm" />
<div
className="relative bg-white w-full max-w-md mx-auto p-6 rounded-lg shadow-lg"
className="relative w-full max-w-md mx-auto p-6 rounded-lg shadow-lg bg-background border"
role="dialog"
aria-labelledby="product-detail-modal"
>
<button
onClick={onClose}
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700"
className="absolute top-2 right-2 text-muted-foreground hover:text-foreground transition-colors"
>
<span className="sr-only">Close</span>
</button>

<h2 className="text-2xl font-bold" id="product-detail-modal">
<h2
className="text-2xl font-bold text-foreground"
id="product-detail-modal"
>
{product.name}
</h2>
<div className="mt-4">
<div className="flex flex-wrap justify-center gap-4">
<ImageCarousel images={product.images} />
</div>
<p className="mt-4 text-lg text-gray-600">{product.category}</p>
<p className="mt-2 text-lg">
<p className="mt-4 text-lg text-muted-foreground">
{product.category}
</p>
<p className="mt-2 text-lg text-foreground">
{product.price ? `$${product.price}` : "Price not available"}
</p>
<p className="mt-2 text-sm text-gray-500">{product.description}</p>
<p className="mt-2 text-sm text-muted-foreground">
{product.description}
</p>
</div>

<div className="mt-4 flex items-center">
Expand All @@ -80,26 +87,25 @@ function ProductDetailModal({
/>
);
} else {
return <Star key={index} className="h-5 w-5 text-gray-300" />;
return (
<Star key={index} className="h-5 w-5 text-muted-foreground" />
);
}
})}
<span className="ml-2 text-sm text-gray-600">
<span className="ml-2 text-sm text-muted-foreground">
{rating.toFixed(1)} / 5
</span>
</div>

<div className="mt-6 gap-4">
<Button className="bg-black text-white">
<Button>
<ShoppingCart className="mr-2 h-4 w-4" />
Add to Cart
</Button>
</div>

<div className="mt-6 flex justify-end">
<Button
onClick={onClose}
className="bg-gray-300 text-black px-4 py-2 rounded-md"
>
<Button variant="secondary" onClick={onClose}>
Close
</Button>
</div>
Expand Down
9 changes: 9 additions & 0 deletions frontend/app/components/providers/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes";
import * as React from "react";

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
Loading

0 comments on commit cd0d92c

Please sign in to comment.