diff --git a/.gitignore b/client/.gitignore similarity index 100% rename from .gitignore rename to client/.gitignore diff --git a/README.md b/client/README.md similarity index 100% rename from README.md rename to client/README.md diff --git a/eslint.config.mjs b/client/eslint.config.mjs similarity index 100% rename from eslint.config.mjs rename to client/eslint.config.mjs diff --git a/next.config.ts b/client/next.config.ts similarity index 100% rename from next.config.ts rename to client/next.config.ts diff --git a/package.json b/client/package.json similarity index 78% rename from package.json rename to client/package.json index 6f968124..252efff7 100644 --- a/package.json +++ b/client/package.json @@ -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" } } diff --git a/pnpm-lock.yaml b/client/pnpm-lock.yaml similarity index 99% rename from pnpm-lock.yaml rename to client/pnpm-lock.yaml index fbeef25e..273088b9 100644 --- a/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + lucide-react: + specifier: ^0.539.0 + version: 0.539.0(react@19.1.0) next: specifier: 15.4.5 version: 15.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1347,6 +1350,11 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lucide-react@0.539.0: + resolution: {integrity: sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -3200,6 +3208,10 @@ snapshots: dependencies: js-tokens: 4.0.0 + lucide-react@0.539.0(react@19.1.0): + dependencies: + react: 19.1.0 + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.4 diff --git a/postcss.config.mjs b/client/postcss.config.mjs similarity index 100% rename from postcss.config.mjs rename to client/postcss.config.mjs diff --git a/public/cards.png b/client/public/cards.png similarity index 100% rename from public/cards.png rename to client/public/cards.png diff --git a/public/featured.png b/client/public/featured.png similarity index 100% rename from public/featured.png rename to client/public/featured.png diff --git a/public/klarna.png b/client/public/klarna.png similarity index 100% rename from public/klarna.png rename to client/public/klarna.png diff --git a/public/logo.png b/client/public/logo.png similarity index 100% rename from public/logo.png rename to client/public/logo.png diff --git a/public/products/1g.png b/client/public/products/1g.png similarity index 100% rename from public/products/1g.png rename to client/public/products/1g.png diff --git a/public/products/1gr.png b/client/public/products/1gr.png similarity index 100% rename from public/products/1gr.png rename to client/public/products/1gr.png diff --git a/public/products/1p.png b/client/public/products/1p.png similarity index 100% rename from public/products/1p.png rename to client/public/products/1p.png diff --git a/public/products/2g.png b/client/public/products/2g.png similarity index 100% rename from public/products/2g.png rename to client/public/products/2g.png diff --git a/public/products/2gr.png b/client/public/products/2gr.png similarity index 100% rename from public/products/2gr.png rename to client/public/products/2gr.png diff --git a/public/products/3b.png b/client/public/products/3b.png similarity index 100% rename from public/products/3b.png rename to client/public/products/3b.png diff --git a/public/products/3bl.png b/client/public/products/3bl.png similarity index 100% rename from public/products/3bl.png rename to client/public/products/3bl.png diff --git a/public/products/3gr.png b/client/public/products/3gr.png similarity index 100% rename from public/products/3gr.png rename to client/public/products/3gr.png diff --git a/public/products/4p.png b/client/public/products/4p.png similarity index 100% rename from public/products/4p.png rename to client/public/products/4p.png diff --git a/public/products/4w.png b/client/public/products/4w.png similarity index 100% rename from public/products/4w.png rename to client/public/products/4w.png diff --git a/public/products/5bl.png b/client/public/products/5bl.png similarity index 100% rename from public/products/5bl.png rename to client/public/products/5bl.png diff --git a/public/products/5o.png b/client/public/products/5o.png similarity index 100% rename from public/products/5o.png rename to client/public/products/5o.png diff --git a/public/products/5r.png b/client/public/products/5r.png similarity index 100% rename from public/products/5r.png rename to client/public/products/5r.png diff --git a/public/products/6g.png b/client/public/products/6g.png similarity index 100% rename from public/products/6g.png rename to client/public/products/6g.png diff --git a/public/products/6w.png b/client/public/products/6w.png similarity index 100% rename from public/products/6w.png rename to client/public/products/6w.png diff --git a/public/products/7g.png b/client/public/products/7g.png similarity index 100% rename from public/products/7g.png rename to client/public/products/7g.png diff --git a/public/products/7p.png b/client/public/products/7p.png similarity index 100% rename from public/products/7p.png rename to client/public/products/7p.png diff --git a/public/products/8b.png b/client/public/products/8b.png similarity index 100% rename from public/products/8b.png rename to client/public/products/8b.png diff --git a/public/products/8gr.png b/client/public/products/8gr.png similarity index 100% rename from public/products/8gr.png rename to client/public/products/8gr.png diff --git a/public/stripe.png b/client/public/stripe.png similarity index 100% rename from public/stripe.png rename to client/public/stripe.png diff --git a/src/app/favicon.ico b/client/src/app/favicon.ico similarity index 100% rename from src/app/favicon.ico rename to client/src/app/favicon.ico diff --git a/src/app/globals.css b/client/src/app/globals.css similarity index 100% rename from src/app/globals.css rename to client/src/app/globals.css diff --git a/src/app/layout.tsx b/client/src/app/layout.tsx similarity index 72% rename from src/app/layout.tsx rename to client/src/app/layout.tsx index f7fa87eb..21b0c2e6 100644 --- a/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -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", @@ -27,7 +29,11 @@ export default function RootLayout({ - {children} +
+ + {children} +
); diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx new file mode 100644 index 00000000..5bdc09ed --- /dev/null +++ b/client/src/app/page.tsx @@ -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 ( +
+
+ Featured Product +
+ +
+ ); +}; + +export default Homepage; diff --git a/client/src/app/products/page.tsx b/client/src/app/products/page.tsx new file mode 100644 index 00000000..8016efac --- /dev/null +++ b/client/src/app/products/page.tsx @@ -0,0 +1,18 @@ +import ProductList from "@/components/ProductList" + +const productPage = async ({ + searchParams, +}: { + searchParams: Promise<{ category: string }>; +}) => { + + + const category = (await searchParams).category; + return ( +
+ +
+ ) +} + +export default productPage \ No newline at end of file diff --git a/client/src/components/Categories.tsx b/client/src/components/Categories.tsx new file mode 100644 index 00000000..ab2ca781 --- /dev/null +++ b/client/src/components/Categories.tsx @@ -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: , + slug: "all", + }, + { + name: "T-shirts", + icon: , + slug: "t-shirts", + }, + { + name: "Shoes", + icon: , + slug: "shoes", + }, + { + name: "Accessories", + icon: , + slug: "accessories", + }, + { + name: "Bags", + icon: , + slug: "bags", + }, + { + name: "Dresses", + icon: , + slug: "dresses", + }, + { + name: "Jackets", + icon: , + slug: "jackets", + }, + { + name: "Gloves", + icon: , + 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 ( +
+ {categories.map((category) => ( +
handleChange(category.slug)} + > + {category.icon} + {category.name} +
+ ))} +
+ ); +}; + +export default Categories; diff --git a/client/src/components/Filter.tsx b/client/src/components/Filter.tsx new file mode 100644 index 00000000..9f84741b --- /dev/null +++ b/client/src/components/Filter.tsx @@ -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 ( +
+ Sort by: + +
+); +}; + +export default Filter; diff --git a/client/src/components/Footer.tsx b/client/src/components/Footer.tsx new file mode 100644 index 00000000..24558fe7 --- /dev/null +++ b/client/src/components/Footer.tsx @@ -0,0 +1,49 @@ +import Image from "next/image"; +import Link from "next/link"; + + +const Footer = () => { + return ( +
+
+ + Trend Next +

+ Devnuru. +

+ +

@ 2025 Devnuru

+

All rights reserved.

+
+
+

Links

+ Homepage + Contact + Terms of Service + Privacy Policy +
+
+

Links

+ All Products + New Arrivals + Best Sellers + Sale +
+
+

Links

+ About + Contact + Blog + Affiliate Programs +
+
+ ); +}; + +export default Footer; diff --git a/client/src/components/Navbar.tsx b/client/src/components/Navbar.tsx new file mode 100644 index 00000000..8429e052 --- /dev/null +++ b/client/src/components/Navbar.tsx @@ -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 ( + + ); +}; + +export default Navbar; diff --git a/client/src/components/ProductCard.tsx b/client/src/components/ProductCard.tsx new file mode 100644 index 00000000..fca440b5 --- /dev/null +++ b/client/src/components/ProductCard.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { ProductType } from "@/types"; +import { ShoppingCart } from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; +import { useState } from "react"; + +const ProductCard = ({ product }: { product: ProductType }) => { + const [productTypes, setProductTypes] = useState({ + size: product.sizes[0], + color: product.colors[0], + }); + + const handleProductType = ({ + type, + value, + }: { + type: "size" | "color"; + value: string; + }) => { + setProductTypes((prev) => ({ + ...prev, + [type]: value, + })); + }; + + return ( +
+ +
+ {product.name} +
+ + {/* Product Details */} +
+

{product.name}

+

{product.shortDescription}

+ {/* Product Types */} +
+ {/* SIZES */} +
+ Size + +
+ + {/* COLORS */} +
+ Color +
+ {product.colors.map((color) => ( +
+ handleProductType({ type: "color", value: color }) + } + > +
+
+ ))} +
+
+
+ + {/* PRICE AND ADD TO CART BUTTON */} +
+

${product.price.toFixed(2)}

+ +
+
+
+ ); +}; + +export default ProductCard; diff --git a/client/src/components/ProductList.tsx b/client/src/components/ProductList.tsx new file mode 100644 index 00000000..a152bdda --- /dev/null +++ b/client/src/components/ProductList.tsx @@ -0,0 +1,139 @@ +import { ProductsType } from "@/types"; +import Categories from "./Categories"; +import ProductCard from "./ProductCard"; +import Link from "next/link"; +import Filter from "./Filter"; + +// Temporary +const products: ProductsType = [ + { + id: 1, + name: "Adidas CoreFit T-Shirt", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 39.9, + sizes: ["s", "m", "l", "xl", "xxl"], + colors: ["gray", "purple", "green"], + images: { + gray: "/products/1g.png", + purple: "/products/1p.png", + green: "/products/1gr.png", + }, + }, + { + id: 2, + name: "Puma Ultra Warm Zip", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 59.9, + sizes: ["s", "m", "l", "xl"], + colors: ["gray", "green"], + images: { gray: "/products/2g.png", green: "/products/2gr.png" }, + }, + { + id: 3, + name: "Nike Air Essentials Pullover", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 69.9, + sizes: ["s", "m", "l"], + colors: ["green", "blue", "black"], + images: { + green: "/products/3gr.png", + blue: "/products/3b.png", + black: "/products/3bl.png", + }, + }, + { + id: 4, + name: "Nike Dri Flex T-Shirt", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 29.9, + sizes: ["s", "m", "l"], + colors: ["white", "pink"], + images: { white: "/products/4w.png", pink: "/products/4p.png" }, + }, + { + id: 5, + name: "Under Armour StormFleece", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 49.9, + sizes: ["s", "m", "l"], + colors: ["red", "orange", "black"], + images: { + red: "/products/5r.png", + orange: "/products/5o.png", + black: "/products/5bl.png", + }, + }, + { + id: 6, + name: "Nike Air Max 270", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 59.9, + sizes: ["40", "42", "43", "44"], + colors: ["gray", "white"], + images: { gray: "/products/6g.png", white: "/products/6w.png" }, + }, + { + id: 7, + name: "Nike Ultraboost Pulse ", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 69.9, + sizes: ["40", "42", "43"], + colors: ["gray", "pink"], + images: { gray: "/products/7g.png", pink: "/products/7p.png" }, + }, + { + id: 8, + name: "Levi’s Classic Denim", + shortDescription: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + description: + "Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit. Lorem ipsum dolor sit amet consect adipisicing elit lorem ipsum dolor sit.", + price: 59.9, + sizes: ["s", "m", "l"], + colors: ["blue", "green"], + images: { blue: "/products/8b.png", green: "/products/8gr.png" }, + }, +]; + +const ProductList = ({ category, params }: { category: string, params:"homepage" | "products" }) => { + return ( +
+ + {params === "products" && } +
+ {products.map((product) => ( + + ))} +
+ + View all product + +
+ ); +}; + +export default ProductList; diff --git a/client/src/components/SearchBar.tsx b/client/src/components/SearchBar.tsx new file mode 100644 index 00000000..46b4317f --- /dev/null +++ b/client/src/components/SearchBar.tsx @@ -0,0 +1,17 @@ +import { Search } from "lucide-react"; + +const SearchBar = () => { + return ( +
+ + +
+ ); +}; + +export default SearchBar; diff --git a/client/src/types.ts b/client/src/types.ts new file mode 100644 index 00000000..46b6294e --- /dev/null +++ b/client/src/types.ts @@ -0,0 +1,12 @@ +export type ProductType = { + id: string | number; + name: string; + shortDescription: string; + description: string; + price: number; + sizes: string[]; + colors: string[]; + images: Record +}; + +export type ProductsType = ProductType[]; \ No newline at end of file diff --git a/tsconfig.json b/client/tsconfig.json similarity index 100% rename from tsconfig.json rename to client/tsconfig.json diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index 3d365a49..00000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -const Homepage = () => { - return ( -
Homepage
- ) -} - -export default Homepage \ No newline at end of file