Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
13 changes: 7 additions & 6 deletions package.json → client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@
"lint": "next lint"
},
"dependencies": {
"lucide-react": "^0.539.0",
"next": "15.4.5",
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.4.5"
"react-dom": "19.1.0"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.4.5",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4",
"typescript": "^5"
}
}
12 changes: 12 additions & 0 deletions pnpm-lock.yaml → client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
8 changes: 7 additions & 1 deletion src/app/layout.tsx → client/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";

const geistSans = Geist({
variable: "--font-geist-sans",
Expand All @@ -27,7 +29,11 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
<div className="mx-auto p-4 sm:max-w-xl md:max-w-2xl lg:max-w-3xl xl:max-w-6xl">
<Navbar />
{children}
<Footer />
</div>
</body>
</html>
);
Expand Down
23 changes: 23 additions & 0 deletions client/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import ProductList from "@/components/ProductList";
import Image from "next/image";

const Homepage = async ({
searchParams,
}: {
searchParams: Promise<{ category: string }>;
}) => {


const category = (await searchParams).category;

return (
<div className="">
<div className="relative aspect-[3/1] mb-12">
<Image src="/featured.png" alt="Featured Product" fill />
</div>
<ProductList category={category} params="homepage"/>
</div>
);
};

export default Homepage;
18 changes: 18 additions & 0 deletions client/src/app/products/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import ProductList from "@/components/ProductList"

const productPage = async ({
searchParams,
}: {
searchParams: Promise<{ category: string }>;
}) => {


const category = (await searchParams).category;
return (
<div className="">
<ProductList category={category} params="products"/>
</div>
)
}

export default productPage
93 changes: 93 additions & 0 deletions client/src/components/Categories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use client";

import {
Footprints,
Glasses,
Briefcase,
Shirt,
ShoppingBasket,
Hand,
Venus,
} from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";

const categories = [
{
name: "All",
icon: <ShoppingBasket className="w-4 h-4" />,
slug: "all",
},
{
name: "T-shirts",
icon: <Shirt className="w-4 h-4" />,
slug: "t-shirts",
},
{
name: "Shoes",
icon: <Footprints className="w-4 h-4" />,
slug: "shoes",
},
{
name: "Accessories",
icon: <Glasses className="w-4 h-4" />,
slug: "accessories",
},
{
name: "Bags",
icon: <Briefcase className="w-4 h-4" />,
slug: "bags",
},
{
name: "Dresses",
icon: <Venus className="w-4 h-4" />,
slug: "dresses",
},
{
name: "Jackets",
icon: <Shirt className="w-4 h-4" />,
slug: "jackets",
},
{
name: "Gloves",
icon: <Hand className="w-4 h-4" />,
slug: "gloves",
},
];

const Categories = () => {

const searchParams = useSearchParams();
const router = useRouter()
const pathname = usePathname()


const selectedCategory = searchParams.get("category");

// console.log(selectedCategory);

const handleChange = (value:string | null) => {
const params = new URLSearchParams(searchParams);
params.set("category", value || "all");
router.push(`${pathname}?${params.toString()}`, {scroll:false})
}




return (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2 bg-gray-100 p-2 rounded-lg md-4 text-sm">
{categories.map((category) => (
<div
className={`flex items-center justify-center gap-2 cursor-pointer px-2 py-1 rounded-md ${category.slug === selectedCategory ? "bg-white" : "text-gray-500"}`}
key={category.name}
onClick={() => handleChange(category.slug)}
>
{category.icon}
{category.name}
</div>
))}
</div>
);
};

export default Categories;
31 changes: 31 additions & 0 deletions client/src/components/Filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client"

import { usePathname, useRouter, useSearchParams } from "next/navigation";


const Filter = () => {
const searchParams = useSearchParams();
const router = useRouter();
const pathname = usePathname();

const handleFilter = (value: string) => {
const params = new URLSearchParams(searchParams);
params.set("sort", value);
router.push(`${pathname}?${params.toString()}`, {scroll:false})
}


return (
<div className="flex items-center justify-end gap-2 text-sm text-gray-500 my-6">
<span>Sort by:</span>
<select name="sort" id="sort" className="ring-1 ring-gray-200 shadow-md p-1 rounded-sm" onChange={(e)=>handleFilter(e.target.value)}>
<option value="newest">Newest</option>
<option value="oldest">Oldest</option>
<option value="asc">Price: Low o High</option>
<option value="desc">Price: High to Low</option>
</select>
</div>
);
};

export default Filter;
49 changes: 49 additions & 0 deletions client/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Image from "next/image";
import Link from "next/link";


const Footer = () => {
return (
<div className="mt-16 flex flex-col items-center gap-8 md:flex-row md:items-start md:justify-between md:gap-0 bg-gray-800 p-8 rounded-lg">
<div className=" flex flex-col gap-4 items-center md:items-start">
<Link href="/" className="flex items-center">
<Image
src="/logo.png"
alt="Trend Next"
width={36}
height={36}
className="w-6 h-6 md:w-9 md:h-9"
/>
<p className="hidden md:block text-md font-medium tracking-wider text-white">
Devnuru.
</p>
</Link>
<p className="text-sm text-gray-400">@ 2025 Devnuru</p>
<p className="text-sm text-gray-400">All rights reserved.</p>
</div>
<div className="flex flex-col gap-4 text-sm text-gray-400 items-center md:items-start">
<p className="text-sm text-amber-50">Links</p>
<Link href="/">Homepage</Link>
<Link href="/contact">Contact</Link>
<Link href="/tos">Terms of Service</Link>
<Link href="/privacy">Privacy Policy</Link>
</div>
<div className="flex flex-col gap-4 text-sm text-gray-400 items-center md:items-start">
<p className="text-sm text-amber-50">Links</p>
<Link href="/products">All Products</Link>
<Link href="/products">New Arrivals</Link>
<Link href="/sellers">Best Sellers</Link>
<Link href="/sale">Sale</Link>
</div>
<div className="flex flex-col gap-4 text-sm text-gray-400 items-center md:items-start">
<p className="text-sm text-amber-50">Links</p>
<Link href="/about">About</Link>
<Link href="/contact">Contact</Link>
<Link href="/blog">Blog</Link>
<Link href="/affiliate">Affiliate Programs</Link>
</div>
</div>
);
};

export default Footer;
38 changes: 38 additions & 0 deletions client/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Image from "next/image";
import Link from "next/link";
import SearchBar from "./SearchBar";
import { Bell, Home, ShoppingCart } from "lucide-react";

const Navbar = () => {
return (
<nav className="w-full flex items-center justify-between border-b border-gray-200 pb-4">
{/* LEFT */}
<Link href="/" className="flex items-center">
<Image
src="/logo.png"
alt="Trend Next"
width={36}
height={36}
className="w-6 h-6 md:w-9 md:h-9"
/>
<p className="hidden md:block text-md font-medium tracking-wider">
Devnuru.
</p>
</Link>
{/* RIGHT */}
<div className="flex items-center gap-6">
<SearchBar />
<Link href="/">
<Home className="w-4 h-4 text-gray-600" />
</Link>
<Bell className="w-4 h-4 text-gray-600" />
<ShoppingCart className="w-4 h-4 text-gray-600" />
<Link href='/login'>
Sign In
</Link>
</div>
</nav>
);
};

export default Navbar;
Loading