├── .eslintrc.json ├── .gitignore ├── Context ├── AdminProvider.js ├── CartProvider.js ├── Context.js ├── CreateProduct.js └── Firebase.js ├── README.md ├── app ├── about │ └── page.js ├── api │ ├── allproducts │ │ ├── [product] │ │ │ └── route.js │ │ └── route.js │ ├── alluser │ │ └── route.js │ ├── cart-item │ │ └── route.js │ ├── cart │ │ └── route.js │ ├── category │ │ ├── [cate] │ │ │ └── route.js │ │ └── route.js │ ├── login-user │ │ └── route.js │ ├── login │ │ └── route.js │ ├── logout │ │ └── route.js │ ├── product │ │ ├── [_id] │ │ │ └── route.js │ │ └── route.js │ ├── relatedProducts │ │ ├── [id] │ │ │ └── route.js │ │ └── route.js │ └── signup │ │ └── route.js ├── category │ ├── [categories] │ │ ├── Category.js │ │ └── page.js │ └── page.js ├── contact │ ├── Contact.js │ └── page.js ├── dashboard │ ├── Form.js │ ├── Header.js │ ├── SInglePRoductSkeleton.js │ ├── TableSkeleton.js │ ├── create │ │ └── page.js │ ├── layout.js │ ├── page.js │ ├── products │ │ ├── [id] │ │ │ └── page.js │ │ └── page.js │ └── users │ │ └── page.js ├── favicon.ico ├── globals.css ├── layout.js ├── loginpage │ ├── Login.js │ └── page.js ├── page.js ├── products │ ├── [_id] │ │ └── page.js │ └── page.js └── signupPage │ ├── Signup.js │ └── page.js ├── components ├── CardSkeleton.js ├── Collection.js ├── Footer.js ├── Header.js ├── HeroBanner.js ├── HomeProducts.js ├── Loader.js ├── Mobile.js ├── RelatedProducts.js ├── SideCart.js ├── Skeleton.js └── TestUser.js ├── db └── Database.js ├── jsconfig.json ├── middleware.js ├── models ├── Cart.js ├── Order.js ├── Product.js └── User.js ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── hero-pattern.png ├── next.svg └── vercel.svg ├── tailwind.config.js └── utils └── Button.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /Context/AdminProvider.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import axios from "axios"; 4 | import { createContext, useEffect, useState } from "react"; 5 | 6 | export const AdminContext = createContext(); 7 | 8 | const AdminProvider = ({ children }) => { 9 | const [totalUser, setTotalUser] = useState([]); 10 | const [totalProduct, setTotalProduct] = useState([]); 11 | 12 | useEffect(() => { 13 | const AllUser = async () => { 14 | const { data } = await axios.get("/api/alluser"); 15 | setTotalUser(data.data); 16 | }; 17 | AllUser(); 18 | }, []); 19 | 20 | useEffect(() => { 21 | const Allproducts = async () => { 22 | const { data } = await axios.get("/api/allproducts"); 23 | setTotalProduct(data.data); 24 | }; 25 | Allproducts(); 26 | }, []); 27 | return ( 28 | 29 | {children} 30 | 31 | ); 32 | }; 33 | export default AdminProvider; 34 | -------------------------------------------------------------------------------- /Context/CartProvider.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import axios from "axios"; 3 | import { createContext, useContext, useState } from "react"; 4 | import { Context } from "./Context"; 5 | import toast from "react-hot-toast"; 6 | 7 | export const CartContext = createContext(); 8 | 9 | const CartProvider = ({ children }) => { 10 | const { user } = useContext(Context); 11 | const [cartdetails, setCartDetails] = useState({ 12 | quantity: 1, 13 | size: "Medium", 14 | }); 15 | 16 | // add item to cart 17 | const addItemToCart = async (e) => { 18 | try { 19 | const res = await axios.post("/api/cart", { 20 | userId: user?.data?._id, 21 | items: [ 22 | { 23 | productId: e._id, 24 | image: e.mainImage, 25 | price: e.price, 26 | name: e.name, 27 | quantity: cartdetails.quantity, 28 | size: cartdetails.size, 29 | }, 30 | ], 31 | }); 32 | 33 | toast.success(res?.data?.message); 34 | setCartDetails({ 35 | quantity: 1, 36 | size: "Medium", 37 | }); 38 | } catch (error) { 39 | console.log(error); 40 | toast.error("An error occurred"); 41 | } 42 | }; 43 | 44 | return ( 45 | 52 | {children} 53 | 54 | ); 55 | }; 56 | export default CartProvider; 57 | -------------------------------------------------------------------------------- /Context/Context.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import axios from "axios"; 3 | import { useRouter } from "next/navigation"; 4 | import { createContext, useEffect, useState } from "react"; 5 | import { debounce } from "lodash"; 6 | import toast from "react-hot-toast"; 7 | 8 | export const Context = createContext(); 9 | const ContextProvider = ({ children }) => { 10 | const [error, setError] = useState(false); 11 | const [loading, setLoading] = useState(false); 12 | const [message, setMessage] = useState(""); 13 | const [user, setUser] = useState(null); 14 | const [signup, setSignUp] = useState({ 15 | name: "", 16 | email: "", 17 | password: "", 18 | }); 19 | const [login, setLogin] = useState({ 20 | email: "", 21 | password: "", 22 | }); 23 | const router = useRouter(); 24 | // sign up user 25 | const handleSignUpSubmit = async (e) => { 26 | e.preventDefault(); 27 | setLoading(true); 28 | 29 | try { 30 | if (signup.password.length < 6) { 31 | setError(true); 32 | setMessage("Password must be at least 6 characters"); 33 | setLoading(false); 34 | } else { 35 | const response = await axios.post("/api/signup", signup); 36 | 37 | setLoading(false); 38 | setError(response.data.status !== 201); 39 | setMessage(response.data.message); 40 | 41 | if (response.data.message === "User created successfully") { 42 | router.push("/loginpage"); 43 | toast.success("User created successfully"); 44 | setSignUp({ 45 | name: "", 46 | email: "", 47 | password: "", 48 | }); 49 | } 50 | } 51 | } catch (error) { 52 | console.error(error); 53 | setLoading(false); 54 | setError(true); 55 | } 56 | }; 57 | 58 | // login user 59 | const handleLoginSubmit = async (e) => { 60 | e.preventDefault(); 61 | setLoading(true); 62 | 63 | try { 64 | const response = await axios.post("/api/login", login); 65 | 66 | setLoading(false); 67 | setError(response.data.status !== 201); 68 | setMessage(response.data.message); 69 | 70 | if (response.data.message === "User login successfully") { 71 | router.push("/"); 72 | toast.success("User login successfully"); 73 | setLogin({ 74 | email: "", 75 | password: "", 76 | }); 77 | } 78 | } catch (error) { 79 | console.error(error); 80 | setLoading(false); 81 | setError(true); 82 | } 83 | }; 84 | useEffect(() => { 85 | if (error) { 86 | setTimeout(() => { 87 | setError(false); 88 | setMessage(""); 89 | }, 3500); 90 | } 91 | }, [error]); 92 | 93 | // get user info 94 | useEffect(() => { 95 | let debouncedCurrentUser; 96 | const fetchUser = async () => { 97 | try { 98 | const response = await axios.get("/api/login-user"); 99 | setUser(response.data); 100 | } catch (error) { 101 | setUser(null); 102 | console.log(error); 103 | } 104 | }; 105 | debouncedCurrentUser = debounce(fetchUser, 1500); 106 | debouncedCurrentUser(); 107 | 108 | return () => { 109 | debouncedCurrentUser.cancel(); 110 | }; 111 | }, [login]); 112 | 113 | // logout user 114 | 115 | const handleLogout = async () => { 116 | try { 117 | const response = await axios.get("/api/logout"); 118 | setUser(null); 119 | router.push("/loginpage"); 120 | } catch (error) { 121 | console.log(error); 122 | } 123 | }; 124 | useEffect(() => { 125 | if (!user?.isAdmin) { 126 | router.push("/"); 127 | } 128 | }, [user?.isAdmin, router]); 129 | return ( 130 | 145 | {children} 146 | 147 | ); 148 | }; 149 | 150 | export default ContextProvider; 151 | -------------------------------------------------------------------------------- /Context/CreateProduct.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import axios from "axios"; 3 | import { createContext, useEffect, useState } from "react"; 4 | import { 5 | getStorage, 6 | ref, 7 | uploadBytesResumable, 8 | getDownloadURL, 9 | } from "firebase/storage"; 10 | import { app } from "./Firebase"; 11 | import toast from "react-hot-toast"; 12 | import { useRouter } from "next/navigation"; 13 | 14 | export const ProductContext = createContext(); 15 | 16 | export const ProductContextProvider = ({ children }) => { 17 | const [name, setName] = useState(""); 18 | const [price, setPrice] = useState(""); 19 | const [description, setDescription] = useState(""); 20 | const [category, setCategory] = useState(""); 21 | const [file, setFile] = useState(null); 22 | const [media, setMedia] = useState(""); 23 | const [uploading, setUploading] = useState(false); 24 | const storage = getStorage(app); 25 | const route = useRouter(); 26 | 27 | const [products, setProducts] = useState([]); 28 | 29 | // upload image 30 | useEffect(() => { 31 | const upload = () => { 32 | setUploading(true); 33 | const name = new Date().getTime() + "-" + file.name; 34 | const storageRef = ref(storage, name); 35 | 36 | const uploadTask = uploadBytesResumable(storageRef, file); 37 | 38 | uploadTask.on( 39 | "state_changed", 40 | (snapshot) => { 41 | const progress = 42 | (snapshot.bytesTransferred / snapshot.totalBytes) * 100; 43 | console.log("Upload is " + progress + "% done"); 44 | switch (snapshot.state) { 45 | case "paused": 46 | console.log("Upload is paused"); 47 | break; 48 | case "running": 49 | console.log("Upload is running"); 50 | break; 51 | } 52 | }, 53 | (error) => {}, 54 | () => { 55 | getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { 56 | setMedia(downloadURL); 57 | setUploading(false); 58 | }); 59 | } 60 | ); 61 | }; 62 | 63 | file && upload(); 64 | }, [file]); 65 | 66 | // create Product 67 | const fetchProduct = async (e) => { 68 | e.preventDefault(); 69 | try { 70 | if (media === undefined) { 71 | toast.error("Please upload an image"); 72 | 73 | return; 74 | } 75 | if (uploading) { 76 | toast.error("Please wait while image is uploading"); 77 | return; 78 | } 79 | { 80 | const res = await axios.post("/api/product", { 81 | name: name, 82 | price: price, 83 | description: description, 84 | category: category, 85 | mainImage: media, 86 | }); 87 | route.push("/products"); 88 | setName(""); 89 | setPrice(""); 90 | setDescription(""); 91 | setCategory(""); 92 | setFile(null); 93 | setMedia(""); 94 | toast.success("Product created successfully"); 95 | } 96 | } catch (error) { 97 | toast.error(error.message); 98 | console.log(error); 99 | 100 | return; 101 | } 102 | }; 103 | // get all products 104 | useEffect(() => { 105 | axios.get("/api/product").then((res) => { 106 | setProducts(res.data); 107 | }); 108 | }, []); 109 | return ( 110 | 129 | {children} 130 | 131 | ); 132 | }; 133 | -------------------------------------------------------------------------------- /Context/Firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | // TODO: Add SDKs for Firebase products that you want to use 4 | // https://firebase.google.com/docs/web/setup#available-libraries 5 | 6 | // Your web app's Firebase configuration 7 | const firebaseConfig = { 8 | apiKey: process.env.FIREBASE_KEY, 9 | authDomain: "store-37ff0.firebaseapp.com", 10 | projectId: "store-37ff0", 11 | storageBucket: "store-37ff0.appspot.com", 12 | messagingSenderId: "613917496753", 13 | appId: "1:613917496753:web:bce05b6c21bbd909ec9990", 14 | }; 15 | 16 | // Initialize Firebase 17 | export const app = initializeApp(firebaseConfig); 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Store 2 | 3 | This is a Next.js project. 4 | 5 | ![image](https://github.com/riteshk-007/nextjs-store/assets/135107962/67ffb6e6-787f-4db7-aefd-1a8ffe0e0d40) 6 | 7 | ![image](https://github.com/riteshk-007/nextjs-store/assets/135107962/efcd4ef8-54ef-4166-9e18-fdd6fd70562a) 8 | 9 | 10 | ## Environment Variables 11 | 12 | The project uses the following environment variables (.env): 13 | 14 | - `CONNECT_DB`: This is used to connect to the database. 15 | - `JWT_SECRET`: This is used for JSON Web Token (JWT) authentication. 16 | - `FIREBASE_KEY`: This is used to connect to Firebase. 17 | 18 | ## Setup 19 | 20 | 1. Clone the repository. 21 | 2. Install the dependencies using `npm install`. 22 | 3. Create a `.env` file in the root directory and fill in the environment variables. 23 | 4. Run the project using `npm run dev`. 24 | 25 | ## Contributing 26 | 27 | ![image](https://github.com/riteshk-007/nextjs-store/assets/135107962/5ff60fc1-09d5-416d-ba84-fc1271248c5f) 28 | ![image](https://github.com/riteshk-007/nextjs-store/assets/135107962/374d23c7-ca76-4eab-a95d-1543830f4cb0) 29 | 30 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 31 | 32 | ## License 33 | 34 | MIT 35 | -------------------------------------------------------------------------------- /app/api/allproducts/[product]/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const GET = async (req, { params }) => { 6 | await connectDB(); 7 | try { 8 | const product = await ClothingProduct.findById(params.product); 9 | return NextResponse.json({ 10 | status: 200, 11 | message: "success", 12 | data: product, 13 | }); 14 | } catch (error) { 15 | return NextResponse.json({ 16 | status: 500, 17 | message: error.message, 18 | }); 19 | } 20 | }; 21 | 22 | export const PUT = async (res, { params }) => { 23 | await connectDB(); 24 | const { product } = await res.json(); 25 | try { 26 | const updated = await ClothingProduct.findByIdAndUpdate( 27 | params.product, 28 | product 29 | ); 30 | return NextResponse.json({ 31 | status: 200, 32 | message: "success", 33 | data: updated, 34 | }); 35 | } catch (error) { 36 | return NextResponse.json({ 37 | status: 500, 38 | message: error.message, 39 | }); 40 | } 41 | }; 42 | 43 | export const DELETE = async (req, { params }) => { 44 | await connectDB(); 45 | try { 46 | const deleted = await ClothingProduct.findByIdAndDelete(params.product); 47 | return NextResponse.json({ 48 | status: 200, 49 | message: "success", 50 | data: deleted, 51 | }); 52 | } catch (error) { 53 | return NextResponse.json({ 54 | status: 500, 55 | message: error.message, 56 | }); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /app/api/allproducts/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const GET = async (req) => { 6 | await connectDB(); 7 | try { 8 | const products = await ClothingProduct.find({}); 9 | return NextResponse.json({ 10 | status: 200, 11 | message: "success", 12 | data: products, 13 | }); 14 | } catch (error) { 15 | return NextResponse.json({ 16 | status: 500, 17 | message: error.message, 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /app/api/alluser/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import User from "@/models/User"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const GET = async (req) => { 6 | await connectDB(); 7 | try { 8 | const user = await User.find({}); 9 | return NextResponse.json({ 10 | status: 200, 11 | message: "success", 12 | data: user, 13 | }); 14 | } catch (error) { 15 | return NextResponse.json({ 16 | status: 500, 17 | message: error.message, 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /app/api/cart-item/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import Cart from "@/models/Cart"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const POST = async (req) => { 6 | await connectDB(); 7 | const { userId } = await req.json(); 8 | try { 9 | const cartItem = await Cart.find({ userId }); 10 | if (!cartItem) { 11 | return NextResponse.json({ 12 | message: "Cart item not found", 13 | status: 404, 14 | }); 15 | } 16 | return NextResponse.json({ 17 | message: "Cart item found", 18 | cartItem, 19 | status: 200, 20 | }); 21 | } catch (error) { 22 | console.error("Error in POST /api/cart:", error.toString()); 23 | return NextResponse.json({ 24 | message: "Server error", 25 | error: error.toString(), 26 | status: 500, 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /app/api/cart/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import Cart from "@/models/Cart"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const POST = async (req) => { 6 | await connectDB(); 7 | const { userId, items } = await req.json(); 8 | 9 | try { 10 | let cart = await Cart.findOne({ 11 | userId, 12 | "items.productId": items[0].productId, 13 | }); 14 | 15 | if (cart) { 16 | return NextResponse.json({ 17 | message: "Item already added", 18 | }); 19 | } else { 20 | cart = await Cart.create({ userId, items }); 21 | return NextResponse.json({ 22 | message: "Item added", 23 | cart, 24 | }); 25 | } 26 | } catch (error) { 27 | console.log(error); 28 | 29 | return NextResponse.json({ 30 | message: "Item not added", 31 | error, 32 | }); 33 | } 34 | }; 35 | 36 | export const DELETE = async (req) => { 37 | await connectDB(); 38 | const { id } = await req.json(); 39 | try { 40 | const cart = await Cart.findByIdAndDelete(id); 41 | return NextResponse.json({ 42 | message: "Item deleted", 43 | cart, 44 | }); 45 | } catch (error) { 46 | console.log(error); 47 | 48 | return NextResponse.json({ 49 | message: "Item not deleted", 50 | error, 51 | }); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /app/api/category/[cate]/route.js: -------------------------------------------------------------------------------- 1 | import Category from "@/app/category/[categories]/Category"; 2 | import connectDB from "@/db/Database"; 3 | import ClothingProduct from "@/models/Product"; 4 | import { NextResponse } from "next/server"; 5 | 6 | export const GET = async (req, { params }) => { 7 | try { 8 | await connectDB(); 9 | const category = params.cate; 10 | const product = await ClothingProduct.find({ category }); 11 | return NextResponse.json( 12 | { 13 | status: "success", 14 | data: product, 15 | }, 16 | { 17 | status: 200, 18 | } 19 | ); 20 | } catch (error) { 21 | console.error(error); 22 | return NextResponse.json( 23 | { 24 | status: "error", 25 | message: error.message, 26 | }, 27 | { 28 | status: 500, 29 | } 30 | ); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /app/api/category/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const GET = async (req) => { 6 | try { 7 | await connectDB(); 8 | 9 | const categories = await ClothingProduct.distinct("category"); 10 | return NextResponse.json( 11 | { 12 | status: "success", 13 | data: categories, 14 | }, 15 | { 16 | status: 200, 17 | } 18 | ); 19 | } catch (error) { 20 | console.error(error); 21 | return NextResponse.json( 22 | { 23 | status: "error", 24 | message: error.message, 25 | }, 26 | { 27 | status: 500, 28 | } 29 | ); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /app/api/login-user/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import { cookies } from "next/headers"; 3 | import jwt from "jsonwebtoken"; 4 | import User from "@/models/User"; 5 | import { NextResponse } from "next/server"; 6 | export const GET = async (req) => { 7 | await connectDB(); 8 | const authToken = cookies().get("authToken")?.value || ""; 9 | try { 10 | const detail = jwt.verify(authToken, process.env.JWT_SECRET); 11 | const id = detail.id; 12 | const user = await User.findById(id).select("-password"); 13 | if (!user) { 14 | return NextResponse.json({ 15 | status: 401, 16 | error: "User not found", 17 | }); 18 | } else { 19 | return NextResponse.json({ 20 | status: 200, 21 | data: user, 22 | }); 23 | } 24 | } catch (error) { 25 | return NextResponse.json({ 26 | status: 401, 27 | error: "Invalid token", 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /app/api/login/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import User from "@/models/User"; 3 | import { NextResponse } from "next/server"; 4 | import bcrypt from "bcrypt"; 5 | import jwt from "jsonwebtoken"; 6 | import { cookies } from "next/headers"; 7 | 8 | export const POST = async (req) => { 9 | await connectDB(); 10 | const { email, password } = await req.json(); 11 | try { 12 | if (!email || !password) { 13 | return NextResponse.json({ 14 | message: "Please fill all the fields", 15 | status: 400, 16 | }); 17 | } else { 18 | const user = await User.findOne({ email }); 19 | if (!user) { 20 | return NextResponse.json({ 21 | status: 400, 22 | message: "User not found", 23 | }); 24 | } else { 25 | const isMatch = await bcrypt.compare(password, user.password); 26 | if (!isMatch) { 27 | return NextResponse.json({ 28 | status: 400, 29 | message: "Invalid password", 30 | }); 31 | } else { 32 | const authToken = jwt.sign({ id: user._id }, process.env.JWT_SECRET); 33 | cookies().set("authToken", authToken, { 34 | httpOnly: true, 35 | maxAge: 60 * 60 * 24 * 7, 36 | }); 37 | } 38 | return NextResponse.json({ 39 | status: 201, 40 | message: "User login successfully", 41 | }); 42 | } 43 | } 44 | } catch (error) { 45 | return NextResponse.json({ 46 | status: 400, 47 | message: "Something went wrong", 48 | }); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /app/api/logout/route.js: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { NextResponse } from "next/server"; 3 | 4 | export const GET = (req) => { 5 | const authToken = cookies().delete("authToken"); 6 | if (authToken === undefined) { 7 | return NextResponse.json({ 8 | message: "User logged out successfully", 9 | status: 200, 10 | }); 11 | } else { 12 | return NextResponse.json({ message: "User not logged out", status: 401 }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /app/api/product/[_id]/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const GET = async (req, { params }) => { 6 | await connectDB(); 7 | const { _id } = params; 8 | const product = await ClothingProduct.findById(_id); 9 | if (!product) { 10 | return NextResponse.json({ 11 | status: 404, 12 | message: "Product not found", 13 | }); 14 | } else { 15 | return NextResponse.json({ 16 | status: 200, 17 | message: "Product found", 18 | data: product, 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /app/api/product/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const POST = async (req) => { 6 | await connectDB(); 7 | const { name, price, description, category, mainImage } = await req.json(); 8 | const clothingProduct = await ClothingProduct.create({ 9 | name, 10 | price, 11 | description, 12 | category, 13 | mainImage, 14 | }); 15 | if (!clothingProduct) 16 | return NextResponse.json({ status: 400, message: "Product not created" }); 17 | return NextResponse.json({ 18 | status: 201, 19 | message: "Product created successfully", 20 | data: clothingProduct, 21 | }); 22 | }; 23 | 24 | export const GET = async (req) => { 25 | await connectDB(); 26 | const products = await ClothingProduct.find({}); 27 | if (!products) 28 | return NextResponse.json({ status: 400, message: "No products found" }); 29 | return NextResponse.json({ 30 | status: 200, 31 | message: "Products fetched successfully", 32 | data: products, 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /app/api/relatedProducts/[id]/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | function shuffleArray(array) { 6 | for (let i = array.length - 1; i > 0; i--) { 7 | const j = Math.floor(Math.random() * (i + 1)); 8 | [array[i], array[j]] = [array[j], array[i]]; 9 | } 10 | return array; 11 | } 12 | export const GET = async (req, { params }) => { 13 | await connectDB(); 14 | const { id } = params; 15 | const product = await ClothingProduct.findById(id); 16 | 17 | if (!product) { 18 | return NextResponse.json({ 19 | status: 404, 20 | message: "Product not found", 21 | }); 22 | } else { 23 | const relatedProducts = await ClothingProduct.find({ 24 | category: product.category, 25 | }); 26 | const shuffledProducts = shuffleArray(relatedProducts); 27 | const randomRelatedProducts = shuffledProducts.slice(0, 4); 28 | return NextResponse.json({ 29 | status: 200, 30 | message: "Product found", 31 | relatedProducts: randomRelatedProducts, 32 | }); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /app/api/relatedProducts/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import ClothingProduct from "@/models/Product"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export const GET = async (req) => { 6 | await connectDB(); 7 | const products = await ClothingProduct.find({}); 8 | if (!products) 9 | return NextResponse.json({ status: 400, message: "No products found" }); 10 | return NextResponse.json({ 11 | status: 200, 12 | message: "Products fetched successfully", 13 | data: products, 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /app/api/signup/route.js: -------------------------------------------------------------------------------- 1 | import connectDB from "@/db/Database"; 2 | import { NextResponse } from "next/server"; 3 | import bcrypt from "bcrypt"; 4 | import User from "@/models/User"; 5 | 6 | export const POST = async (req) => { 7 | const { name, email, password } = await req.json(); 8 | await connectDB(); 9 | 10 | try { 11 | if (!name || !email || !password) { 12 | return NextResponse.json({ 13 | message: "Please fill all the fields", 14 | status: 400, 15 | }); 16 | } else { 17 | const existingUser = await User.findOne({ email: email }); 18 | if (existingUser) { 19 | return NextResponse.json({ 20 | message: "User already exists", 21 | status: 400, 22 | }); 23 | } else { 24 | const hashedPassword = await bcrypt.hash(password, 10); 25 | const user = await User.create({ 26 | name, 27 | email, 28 | password: hashedPassword, 29 | }); 30 | if (user) { 31 | return NextResponse.json({ 32 | status: 201, 33 | message: "User created successfully", 34 | }); 35 | } 36 | } 37 | } 38 | } catch (error) { 39 | console.error(error); 40 | return NextResponse.json({ 41 | status: 500, 42 | message: "Server error", 43 | }); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /app/category/[categories]/Category.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import CardSkeleton from "@/components/CardSkeleton"; 3 | import axios from "axios"; 4 | import Image from "next/image"; 5 | import Link from "next/link"; 6 | import { useEffect, useState } from "react"; 7 | 8 | const Category = ({ params }) => { 9 | const [data, setData] = useState([]); 10 | useEffect(() => { 11 | const fetchData = async () => { 12 | const res = await axios.get(`/api/category/${params}`); 13 | setData(res.data.data); 14 | }; 15 | fetchData(); 16 | window.scrollTo(0, 0); 17 | }, [params]); 18 | 19 | if (!data[0]?.mainImage) { 20 | return ( 21 |
22 | 23 |
24 | ); 25 | } 26 | 27 | return ( 28 | <> 29 |
30 |
31 |

{params}

32 |

33 | For unique and stylish clothing in the collection you can select the 34 | best one for you. 35 |

36 |
37 |
38 |
42 | Home / {params} 43 |
44 |
45 | {data?.map((product) => ( 46 | 51 |
52 | {product?.name} 59 |
60 |

{product?.name}

61 |

62 | ₹{product?.price} 63 |

64 | 65 | ))} 66 |
67 | 68 | ); 69 | }; 70 | 71 | export default Category; 72 | -------------------------------------------------------------------------------- /app/category/[categories]/page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useParams } from "next/navigation"; 3 | import Category from "./Category"; 4 | 5 | const Cate = () => { 6 | const params = useParams(); 7 | return ( 8 |
9 | 10 |
11 | ); 12 | }; 13 | 14 | export default Cate; 15 | -------------------------------------------------------------------------------- /app/category/page.js: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | 4 | const page = () => { 5 | const categories = [ 6 | { 7 | name: "Man's Collection", 8 | link: "men", 9 | image: 10 | "https://images.unsplash.com/photo-1610384104075-e05c8cf200c3?q=80&w=1964&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 11 | }, 12 | { 13 | name: "Kids's Collection", 14 | link: "kids", 15 | image: 16 | "https://images.unsplash.com/photo-1627859774205-83c1279a6382?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 17 | }, 18 | { 19 | name: "Women's Collection", 20 | link: "women", 21 | image: 22 | "https://images.unsplash.com/photo-1552874869-5c39ec9288dc?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 23 | }, 24 | ]; 25 | return ( 26 |
27 | 55 |
56 | ); 57 | }; 58 | 59 | export default page; 60 | -------------------------------------------------------------------------------- /app/contact/Contact.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useForm, ValidationError } from "@formspree/react"; 4 | import { useState, useEffect } from "react"; 5 | const Contact = () => { 6 | const [state, handleSubmit] = useForm("xpzvzdkk"); 7 | const [showSuccess, setShowSuccess] = useState(false); 8 | 9 | useEffect(() => { 10 | if (state.succeeded) { 11 | setShowSuccess(true); 12 | setTimeout(() => { 13 | setShowSuccess(false); 14 | }, 5000); 15 | } 16 | }, [state.succeeded]); 17 | 18 | if (showSuccess) { 19 | return ( 20 |
21 |
22 | Success! 23 | 24 | Your message has been sent successfully. 25 | 26 |
27 |
28 | ); 29 | } 30 | return ( 31 |
32 |
33 |
34 |
35 |

Contact us

36 | 37 |

38 | Chat to our friendly team 39 |

40 | 41 |

42 | We’d love to hear from you. Please fill out this form or shoot us 43 | an email. 44 |

45 |
46 | 47 |
48 |
49 |
50 | 51 | 59 | 64 | 65 | 66 | 67 |

68 | Email 69 |

70 |

71 | Our friendly team is here to help. 72 |

73 |

test@gmail.com

74 |
75 | 76 |
77 | 78 | 86 | 91 | 96 | 97 | 98 | 99 |

100 | Live chat 101 |

102 |

103 | Our friendly team is here to help. 104 |

105 |

Start new chat

106 |
107 | 108 |
109 | 110 | 118 | 123 | 128 | 129 | 130 | 131 |

132 | Office 133 |

134 |

135 | Come say hello at our office HQ. 136 |

137 |

New Delhi, India

138 |
139 | 140 |
141 | 142 | 150 | 155 | 156 | 157 | 158 |

159 | Phone 160 |

161 | 162 |

+91 9876543210

163 |
164 |
165 | 166 |
167 |
168 |
169 |
170 | 173 | 179 | 184 |
185 | 186 |
187 | 190 | 196 | 201 |
202 |
203 | 204 |
205 | 208 | 214 | 219 |
220 | 221 |
222 | 225 | 230 | 235 |
236 | 237 | 244 |
245 |
246 |
247 |
248 |
249 |
250 | ); 251 | }; 252 | 253 | export default Contact; 254 | -------------------------------------------------------------------------------- /app/contact/page.js: -------------------------------------------------------------------------------- 1 | import Contact from "./Contact"; 2 | 3 | const page = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default page; 12 | -------------------------------------------------------------------------------- /app/dashboard/Form.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ProductContext } from "@/Context/CreateProduct"; 4 | import Image from "next/image"; 5 | import { useContext } from "react"; 6 | 7 | const Form = () => { 8 | const { 9 | fetchProduct, 10 | name, 11 | setName, 12 | price, 13 | setPrice, 14 | description, 15 | setDescription, 16 | category, 17 | setCategory, 18 | setFile, 19 | media, 20 | uploading, 21 | } = useContext(ProductContext); 22 | 23 | const handleDragOver = (e) => { 24 | e.preventDefault(); 25 | }; 26 | const handleDrop = (e) => { 27 | e.preventDefault(); 28 | setFile(e.dataTransfer.files[0]); 29 | }; 30 | 31 | const handleChange = (e) => { 32 | switch (e.target.name) { 33 | case "name": 34 | setName(e.target.value); 35 | break; 36 | case "price": 37 | setPrice(e.target.value); 38 | break; 39 | case "description": 40 | setDescription(e.target.value); 41 | break; 42 | case "category": 43 | setCategory(e.target.value); 44 | break; 45 | default: 46 | break; 47 | } 48 | }; 49 | const handleSubmit = (e) => { 50 | e.preventDefault(); 51 | 52 | fetchProduct(e); 53 | }; 54 | 55 | return ( 56 |
57 |

58 | 59 | Create a new product 60 | 61 |

62 |
66 |
67 | 73 | 83 |
84 |
85 | 91 | 101 |
102 |
103 | 109 | 122 |
123 |
124 | 130 | 146 |
147 |
148 | 154 | {!media ? ( 155 | uploading ? ( 156 |
157 | 158 | please wait while image is uploading.... 159 | 160 |
161 | ) : ( 162 |
167 | 199 |
200 | ) 201 | ) : ( 202 | product image 203 | )} 204 |
205 | 206 |
207 | {uploading || 208 | !name || 209 | !category || 210 | !media || 211 | !description || 212 | !price ? ( 213 | 220 | ) : ( 221 | 229 | )} 230 |
231 |
232 |
233 | ); 234 | }; 235 | 236 | export default Form; 237 | -------------------------------------------------------------------------------- /app/dashboard/Header.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Context } from "@/Context/Context"; 3 | import Link from "next/link"; 4 | import { useContext, useState } from "react"; 5 | 6 | const Header = () => { 7 | const [show, setShow] = useState(false); 8 | const { user } = useContext(Context); 9 | const name = user?.data?.name.replace(/ .*/, ""); 10 | return ( 11 |
12 | 67 |
68 | ); 69 | }; 70 | 71 | export default Header; 72 | -------------------------------------------------------------------------------- /app/dashboard/SInglePRoductSkeleton.js: -------------------------------------------------------------------------------- 1 | const SInglePRoductSkeleton = () => { 2 | return ( 3 |
8 |
12 |
13 | 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Loading... 32 |
33 |
34 | ); 35 | }; 36 | 37 | export default SInglePRoductSkeleton; 38 | -------------------------------------------------------------------------------- /app/dashboard/TableSkeleton.js: -------------------------------------------------------------------------------- 1 | const TableSkeleton = () => { 2 | return ( 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading... 17 |
18 | ); 19 | }; 20 | 21 | export default TableSkeleton; 22 | -------------------------------------------------------------------------------- /app/dashboard/create/page.js: -------------------------------------------------------------------------------- 1 | import Form from "../Form"; 2 | 3 | const page = () => { 4 | return ( 5 |
6 |
7 |
8 | ); 9 | }; 10 | 11 | export default page; 12 | -------------------------------------------------------------------------------- /app/dashboard/layout.js: -------------------------------------------------------------------------------- 1 | import Header from "./Header"; 2 | 3 | const Layout = ({ children }) => { 4 | return ( 5 |
6 |
7 | {children} 8 |
9 | ); 10 | }; 11 | 12 | export default Layout; 13 | -------------------------------------------------------------------------------- /app/dashboard/page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AdminContext } from "@/Context/AdminProvider"; 4 | import { useContext } from "react"; 5 | import { Bar } from "react-chartjs-2"; 6 | import { Chart } from "chart.js"; 7 | import { FaUser } from "react-icons/fa6"; 8 | import { BsFillBoxSeamFill } from "react-icons/bs"; 9 | import { 10 | LinearScale, 11 | CategoryScale, 12 | BarElement, 13 | Title, 14 | Tooltip, 15 | Legend, 16 | } from "chart.js"; 17 | 18 | Chart.register(LinearScale, CategoryScale, BarElement, Title, Tooltip, Legend); 19 | 20 | const Dashboard = () => { 21 | const { totalUser, totalProduct } = useContext(AdminContext); 22 | 23 | const data = { 24 | labels: ["Users", "Products"], 25 | datasets: [ 26 | { 27 | label: "# of Items", 28 | data: [totalUser?.length, totalProduct?.length], 29 | backgroundColor: ["rgba(75, 192, 192, 0.2)", "rgba(255, 99, 132, 0.2)"], 30 | borderColor: ["rgba(75, 192, 192, 1)", "rgba(255, 99, 132, 1)"], 31 | borderWidth: 1, 32 | }, 33 | ], 34 | }; 35 | 36 | const options = { 37 | scales: { 38 | y: { 39 | type: "linear", 40 | beginAtZero: true, 41 | }, 42 | }, 43 | }; 44 | 45 | return ( 46 |
47 |

Admin Dashboard

48 |
49 |
50 |

Users

51 | 52 |

{totalUser?.length}

53 |
54 |
55 |

Products

56 | 57 |

58 | {totalProduct?.length} 59 |

60 |
61 |
62 |
63 | 64 |
65 |
66 | ); 67 | }; 68 | 69 | export default Dashboard; 70 | -------------------------------------------------------------------------------- /app/dashboard/products/[id]/page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import axios from "axios"; 3 | import Image from "next/image"; 4 | import { useParams, useRouter } from "next/navigation"; 5 | import { useEffect, useState } from "react"; 6 | import toast from "react-hot-toast"; 7 | import SInglePRoductSkeleton from "../../SInglePRoductSkeleton"; 8 | 9 | const Product = () => { 10 | const params = useParams(); 11 | const [product, setProduct] = useState({}); 12 | const route = useRouter(); 13 | 14 | // get product details 15 | useEffect(() => { 16 | const GetProductDetails = async () => { 17 | const res = await axios.get(`/api/allproducts/${params.id}`); 18 | setProduct(res?.data?.data); 19 | }; 20 | GetProductDetails(); 21 | }, [params]); 22 | 23 | const handleInputChange = (e) => { 24 | setProduct({ ...product, [e.target.name]: e.target.value }); 25 | }; 26 | 27 | // Handle update product details 28 | const UdateProducts = async () => { 29 | const res = await axios.put(`/api/allproducts/${params.id}`, { 30 | product, 31 | }); 32 | route.push("/dashboard/products"); 33 | toast.success("Product updated successfully"); 34 | return res; 35 | }; 36 | 37 | // Handle delete product 38 | const DeleteProduct = async () => { 39 | const res = await axios.delete(`/api/allproducts/${params.id}`); 40 | route.push("/dashboard/products"); 41 | toast.success("Product deleted successfully"); 42 | return res; 43 | }; 44 | 45 | if (!product?.mainImage) return ; 46 | return ( 47 |
48 |

49 | 50 | Edit Product OR Delete Product 51 | 52 |

53 |
54 | 60 | 61 | product image 67 |
68 | { 70 | e.preventDefault(); 71 | UdateProducts(); 72 | }} 73 | className="w-full lg:w-11/12 mx-auto md:grid grid-cols-2 grid-rows-1 gap-3 mt-5 p-4" 74 | > 75 |
76 | 82 | 92 |
93 |
94 | 100 | 110 |
111 |
112 | 118 | 131 |
132 |
133 | 139 | 155 |
156 | 157 |
158 | 164 |
165 |
166 | 174 |
175 | 176 |
177 | ); 178 | }; 179 | 180 | export default Product; 181 | -------------------------------------------------------------------------------- /app/dashboard/products/page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState, useEffect } from "react"; 4 | import axios from "axios"; 5 | import Link from "next/link"; 6 | import Image from "next/image"; 7 | import TableSkeleton from "../TableSkeleton"; 8 | 9 | const Products = () => { 10 | const [products, setproducts] = useState([]); 11 | const [currentPage, setCurrentPage] = useState(1); 12 | const [itemsPerPage, setItemsPerPage] = useState(6); 13 | 14 | useEffect(() => { 15 | const Allproducts = async () => { 16 | const { data } = await axios.get("/api/allproducts"); 17 | setproducts(data.data); 18 | }; 19 | Allproducts(); 20 | }, []); 21 | 22 | const indexOfLastItem = currentPage * itemsPerPage; 23 | const indexOfFirstItem = indexOfLastItem - itemsPerPage; 24 | const currentItems = products.slice(indexOfFirstItem, indexOfLastItem); 25 | 26 | const pageNumbers = []; 27 | for (let i = 1; i <= Math.ceil(products.length / itemsPerPage); i++) { 28 | pageNumbers.push(i); 29 | } 30 | return ( 31 | <> 32 |
33 |

34 | Products 35 | 36 | ({products?.length}) 37 | 38 |

39 |
40 |
41 |
42 |
Image
43 |
Product Name
44 |
Price
45 |
Category
46 |
47 | 48 | {currentItems.length ? ( 49 | currentItems.map((item, index) => ( 50 | 57 |
58 | {item?.name} 65 |
66 |
67 | {item?.name} 68 |
69 |
{item?.price}
70 |
{item?.category}
71 | 72 | )) 73 | ) : ( 74 | 75 | )} 76 | 77 |
78 | {pageNumbers.length > 1 && 79 | pageNumbers.map((number) => ( 80 | 89 | ))} 90 |
91 |
92 | 93 | ); 94 | }; 95 | 96 | export default Products; 97 | -------------------------------------------------------------------------------- /app/dashboard/users/page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState, useEffect } from "react"; 4 | import axios from "axios"; 5 | import TableSkeleton from "../TableSkeleton"; 6 | 7 | const User = () => { 8 | const [user, setUser] = useState([]); 9 | const [currentPage, setCurrentPage] = useState(1); 10 | const [itemsPerPage, setItemsPerPage] = useState(6); 11 | 12 | useEffect(() => { 13 | const AllUser = async () => { 14 | const { data } = await axios.get("/api/alluser"); 15 | setUser(data.data); 16 | }; 17 | AllUser(); 18 | }, []); 19 | 20 | const indexOfLastItem = currentPage * itemsPerPage; 21 | const indexOfFirstItem = indexOfLastItem - itemsPerPage; 22 | const currentItems = user.slice(indexOfFirstItem, indexOfLastItem); 23 | 24 | const pageNumbers = []; 25 | for (let i = 1; i <= Math.ceil(user.length / itemsPerPage); i++) { 26 | pageNumbers.push(i); 27 | } 28 | 29 | return ( 30 | <> 31 |
32 |

33 | Users 34 | 35 | ({user.length}) 36 | 37 |

38 |
39 |
40 |
41 |
Name
42 |
Email
43 |
Role
44 |
45 | 46 | {currentItems.length ? ( 47 | currentItems.map((item, index) => ( 48 |
54 |
{item?.name}
55 |
{item?.email}
56 |
57 | {item?.isAdmin ? "Admin" : "User"} 58 |
59 |
60 | )) 61 | ) : ( 62 | 63 | )} 64 | 65 |
66 | {pageNumbers.length > 1 && 67 | pageNumbers.map((number) => ( 68 | 77 | ))} 78 |
79 |
80 | 81 | ); 82 | }; 83 | 84 | export default User; 85 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riteshk-007/nextjs-store/62f306540e36742e8333c4c4c01bde8d9cb5e1d1/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | input[type="number"]::-webkit-inner-spin-button, 7 | input[type="number"]::-webkit-outer-spin-button { 8 | -webkit-appearance: none; 9 | margin: 0; 10 | } 11 | } -------------------------------------------------------------------------------- /app/layout.js: -------------------------------------------------------------------------------- 1 | import { Montserrat } from "next/font/google"; 2 | import "./globals.css"; 3 | import NextTopLoader from "nextjs-toploader"; 4 | import Header from "@/components/Header"; 5 | import ContextProvider from "@/Context/Context"; 6 | import Footer from "@/components/Footer"; 7 | import { ProductContextProvider } from "@/Context/CreateProduct"; 8 | import { Toaster } from "react-hot-toast"; 9 | import CartProvider from "@/Context/CartProvider"; 10 | import AdminProvider from "@/Context/AdminProvider"; 11 | 12 | const inter = Montserrat({ subsets: ["latin"] }); 13 | 14 | export const metadata = { 15 | title: "Store", 16 | description: "A store for all your needs", 17 | }; 18 | 19 | export default function RootLayout({ children }) { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | {children} 31 |