├── .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 | 
6 |
7 | 
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 | 
28 | 
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 |
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 |
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 |
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 |
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 |
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 |
156 |
157 |
158 |
159 | Phone
160 |
161 |
162 |
+91 9876543210
163 |
164 |
165 |
166 |
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 |
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 |
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 |
67 |
68 |
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 |
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 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/app/loginpage/Login.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Context } from "@/Context/Context";
3 | import Loader from "@/components/Loader";
4 | import TestUser from "@/components/TestUser";
5 | import Image from "next/image";
6 | import Link from "next/link";
7 | import { useContext } from "react";
8 |
9 | const Login = () => {
10 | const { loading, error, message, login, setLogin, handleLoginSubmit } =
11 | useContext(Context);
12 | const handleChange = (e) => {
13 | setLogin({
14 | ...login,
15 | [e.target.name]: e.target.value,
16 | });
17 | };
18 | return (
19 |
20 |
21 |
22 |
23 |
24 | Welcome to Shop 🛒
25 |
26 |
27 |
28 | Login to your account to continue shopping
29 |
30 |
31 | {error && (
32 |
36 | Danger alert! {message}
37 |
38 | )}
39 |
95 |
96 |
97 |
98 |
99 |
105 |
106 |
107 |
108 | );
109 | };
110 |
111 | export default Login;
112 |
--------------------------------------------------------------------------------
/app/loginpage/page.js:
--------------------------------------------------------------------------------
1 | import Login from "./Login";
2 |
3 | const page = () => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default page;
12 |
--------------------------------------------------------------------------------
/app/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Collection from "@/components/Collection";
3 | import HeroBanner from "@/components/HeroBanner";
4 | import HomeProducts from "@/components/HomeProducts";
5 | import { useState } from "react";
6 |
7 | export default function Home() {
8 | const [show] = useState(true);
9 | return (
10 |
11 |
12 |
13 |
14 | Recent Products List
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/app/products/[_id]/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { CartContext } from "@/Context/CartProvider";
3 | import { Context } from "@/Context/Context";
4 | import RelatedProducts from "@/components/RelatedProducts";
5 | import Skeleton from "@/components/Skeleton";
6 | import axios from "axios";
7 | import Image from "next/image";
8 | import Link from "next/link";
9 | import { useParams } from "next/navigation";
10 | import { useContext, useEffect, useState } from "react";
11 |
12 | const Product = () => {
13 | const [product, setProduct] = useState({});
14 |
15 | const { _id } = useParams();
16 | const { cartdetails, setCartDetails, addItemToCart } =
17 | useContext(CartContext);
18 | const { user } = useContext(Context);
19 | useEffect(() => {
20 | const fetchProduct = async () => {
21 | const res = await axios.get(`/api/product/${_id}`);
22 | setProduct(res.data.data);
23 | };
24 | fetchProduct();
25 | }, [_id]);
26 | useEffect(() => {
27 | window.scrollTo(0, 0);
28 | }, []);
29 | if (!product?.mainImage) return ;
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
57 | )
58 | }
59 | alt="product"
60 | className="object-cover w-full lg:h-full "
61 | />
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | {product?.category}
70 |
71 |
72 | {product?.name}
73 |
74 |
75 |
133 |
134 | (2 customer reviews)
135 |
136 |
137 |
138 | {product?.description}
139 |
140 |
141 | ₹ {product?.price}
142 |
143 | ₹ {product?.price * 3}
144 |
145 |
146 |
147 |
148 |
149 |
150 | Size:
151 |
152 |
153 |
172 |
173 |
174 |
175 |
181 |
182 |
197 |
203 |
215 |
216 |
217 |
218 |
219 | {user?.data ? (
220 |
226 | ) : (
227 |
231 | Add to Cart
232 |
233 | )}
234 |
235 |
236 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 | );
249 | };
250 |
251 | export default Product;
252 |
--------------------------------------------------------------------------------
/app/products/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Fragment, useState } from "react";
3 | import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react";
4 | import { XMarkIcon } from "@heroicons/react/24/outline";
5 | import {
6 | ChevronDownIcon,
7 | FunnelIcon,
8 | MinusIcon,
9 | PlusIcon,
10 | Squares2X2Icon,
11 | } from "@heroicons/react/20/solid";
12 | import HomeProducts from "@/components/HomeProducts";
13 |
14 | const sortOptions = [
15 | { name: "Most Popular", href: "#", current: true },
16 | { name: "Best Rating", href: "#", current: false },
17 | { name: "Newest", href: "#", current: false },
18 | { name: "Price: Low to High", href: "#", current: false },
19 | { name: "Price: High to Low", href: "#", current: false },
20 | ];
21 | const subCategories = [
22 | { name: "Totes", href: "#" },
23 | { name: "Backpacks", href: "#" },
24 | { name: "Travel Bags", href: "#" },
25 | { name: "Hip Bags", href: "#" },
26 | { name: "Laptop Sleeves", href: "#" },
27 | ];
28 | const filters = [
29 | {
30 | id: "color",
31 | name: "Color",
32 | options: [
33 | { value: "white", label: "White", checked: false },
34 | { value: "beige", label: "Beige", checked: false },
35 | { value: "blue", label: "Blue", checked: true },
36 | { value: "brown", label: "Brown", checked: false },
37 | { value: "green", label: "Green", checked: false },
38 | { value: "purple", label: "Purple", checked: false },
39 | ],
40 | },
41 | {
42 | id: "category",
43 | name: "Category",
44 | options: [
45 | { value: "new-arrivals", label: "New Arrivals", checked: false },
46 | { value: "sale", label: "Sale", checked: false },
47 | { value: "travel", label: "Travel", checked: true },
48 | { value: "organization", label: "Organization", checked: false },
49 | { value: "accessories", label: "Accessories", checked: false },
50 | ],
51 | },
52 | {
53 | id: "size",
54 | name: "Size",
55 | options: [
56 | { value: "2l", label: "2L", checked: false },
57 | { value: "6l", label: "6L", checked: false },
58 | { value: "12l", label: "12L", checked: false },
59 | { value: "18l", label: "18L", checked: false },
60 | { value: "20l", label: "20L", checked: false },
61 | { value: "40l", label: "40L", checked: true },
62 | ],
63 | },
64 | ];
65 |
66 | function classNames(...classes) {
67 | return classes.filter(Boolean).join(" ");
68 | }
69 | const Products = () => {
70 | const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);
71 | return (
72 |
73 |
74 | {/* Mobile filter dialog */}
75 |
76 |
196 |
197 |
198 |
199 |
200 |
201 | New Arrivals
202 |
203 |
204 |
205 |
249 |
250 |
257 |
265 |
266 |
267 |
268 |
269 |
270 | Products
271 |
272 |
273 |
274 | {/* Filters */}
275 |
346 |
347 | {/* Product grid */}
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 | );
357 | };
358 |
359 | export default Products;
360 |
--------------------------------------------------------------------------------
/app/signupPage/Signup.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Context } from "@/Context/Context";
3 | import Loader from "@/components/Loader";
4 | import Image from "next/image";
5 | import Link from "next/link";
6 | import { useContext } from "react";
7 |
8 | const Signup = () => {
9 | const { signup, setSignUp, handleSignUpSubmit, loading, error, message } =
10 | useContext(Context);
11 |
12 | const handleChnage = (e) => {
13 | setSignUp({ ...signup, [e.target.name]: e.target.value });
14 | };
15 | return (
16 |
17 |
18 |
27 |
28 |
29 |
30 |
31 |
Home
32 |
51 |
52 |
53 |
54 | Welcome to Shop 🛒
55 |
56 |
57 |
58 | Signup to get started with Shop
59 |
60 | {error && (
61 |
65 | Danger alert! {message}
66 |
67 | )}
68 |
69 |
172 |
173 |
174 |
175 |
176 | );
177 | };
178 |
179 | export default Signup;
180 |
--------------------------------------------------------------------------------
/app/signupPage/page.js:
--------------------------------------------------------------------------------
1 | import Signup from "./Signup";
2 |
3 | const page = () => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default page;
12 |
--------------------------------------------------------------------------------
/components/CardSkeleton.js:
--------------------------------------------------------------------------------
1 | const CardSkeleton = () => {
2 | return (
3 |
4 |
9 |
18 |
Loading...
19 |
20 |
25 |
34 |
Loading...
35 |
36 |
41 |
50 |
Loading...
51 |
52 |
57 |
66 |
Loading...
67 |
68 |
69 | );
70 | };
71 |
72 | export default CardSkeleton;
73 |
--------------------------------------------------------------------------------
/components/Collection.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 |
4 | const Collection = () => {
5 | return (
6 |
7 |
8 |
18 |
19 |
20 | -
21 |
22 |
29 |
30 |
31 |
32 | {"Man's Collection"}
33 |
34 |
35 |
36 | Shop Now
37 |
38 |
39 |
40 |
41 |
42 | -
43 |
44 |
51 |
52 |
53 |
54 | {"Kids Collection"}
55 |
56 |
57 |
58 | Shop Now
59 |
60 |
61 |
62 |
63 |
64 | -
65 |
66 |
73 |
74 |
75 |
76 | {"Women's Collection"}
77 |
78 |
79 |
80 | Shop Now
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | );
89 | };
90 |
91 | export default Collection;
92 |
--------------------------------------------------------------------------------
/components/Footer.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Context } from "@/Context/Context";
3 | import Image from "next/image";
4 | import Link from "next/link";
5 | import { useContext } from "react";
6 |
7 | const Footer = () => {
8 | const { user } = useContext(Context);
9 | return (
10 | <>
11 | {!user?.data?.isAdmin && (
12 |
13 |
14 |
15 | {" "}
16 | contact us now
17 |
18 | for more information about our services and products, please
19 | visit our website{" "}
20 |
25 | GitHub
26 | {" "}
27 | OR click this link
28 |
29 |
30 |
31 |
35 |
Lets Get Started
36 |
37 |
51 |
52 |
53 |
54 | )}
55 |
313 | >
314 | );
315 | };
316 |
317 | export default Footer;
318 |
--------------------------------------------------------------------------------
/components/Header.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "next/link";
3 | import { BsBag } from "react-icons/bs";
4 | import Mobile from "./Mobile";
5 | import { useContext, useEffect, useState } from "react";
6 | import SideCart from "./SideCart";
7 | import { Context } from "@/Context/Context";
8 | import axios from "axios";
9 |
10 | const Header = () => {
11 | const [isOpen, setIsOpen] = useState(false);
12 | const [isCartOpen, setIsCartOpen] = useState(false);
13 | const { user, handleLogout } = useContext(Context);
14 | const [categories, setCategories] = useState([]);
15 | const [isHovered, setIsHovered] = useState(false);
16 | const name = user?.data?.name.replace(/ .*/, "");
17 |
18 | useEffect(() => {
19 | const fetchCategories = async () => {
20 | const res = await axios.get("/api/category");
21 | setCategories(res?.data?.data);
22 | };
23 | fetchCategories();
24 | }, []);
25 | return (
26 |
27 |
28 |
29 |
30 |
Home
31 |
51 |
52 |
53 |
54 |
124 |
125 |
126 |
setIsCartOpen(!isCartOpen)}
128 | className="text-gray-800 transition hover:text-gray-800/75 cursor-pointer"
129 | >
130 |
131 |
132 | {user?.data ? (
133 |
134 |
135 | {name}
136 |
137 |
141 | Logout
142 |
143 |
144 | ) : (
145 |
146 |
150 | Login
151 |
152 |
153 |
157 | Register
158 |
159 |
160 | )}
161 |
162 |
182 |
183 |
184 |
185 |
186 | {isOpen && (
187 |
190 |
191 |
192 | )}
193 | {isCartOpen && (
194 |
195 | )}
196 |
197 | );
198 | };
199 |
200 | export default Header;
201 |
--------------------------------------------------------------------------------
/components/HeroBanner.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 |
4 | const HeroBanner = () => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 | Summer styles are finally here
13 |
14 |
15 | This year, our new summer collection will shelter you from the
16 | harsh elements of a world that doesnt care if you live or die.
17 |
18 |
19 |
20 |
21 | {/* Decorative image grid */}
22 |
26 |
27 |
28 |
29 |
30 |
37 |
38 |
39 |
46 |
47 |
48 |
49 |
50 |
57 |
58 |
59 |
66 |
67 |
68 |
75 |
76 |
77 |
78 |
79 |
86 |
87 |
88 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
105 | Shop Collection
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | );
114 | };
115 |
116 | export default HeroBanner;
117 |
--------------------------------------------------------------------------------
/components/HomeProducts.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { ProductContext } from "@/Context/CreateProduct";
3 | import Image from "next/image";
4 | import Link from "next/link";
5 | import { useContext } from "react";
6 | import CardSkeleton from "./CardSkeleton";
7 |
8 | const HomeProducts = ({ show }) => {
9 | const { products } = useContext(ProductContext);
10 |
11 | if (!products?.data) {
12 | return (
13 |
16 |
17 |
18 | );
19 | }
20 | return (
21 |
22 |
23 |
24 |
Products
25 |
26 |
27 | {show
28 | ? products?.data
29 | ?.slice(-4)
30 | .map((product) => (
31 |
36 |
37 |
44 |
45 |
46 | {product?.name}
47 |
48 |
49 | ₹{product?.price}
50 |
51 |
52 | ))
53 | .reverse()
54 | : products?.data?.map((product) => (
55 |
60 |
61 |
68 |
69 |
70 | {product?.name}
71 |
72 |
73 | ₹{product?.price}
74 |
75 |
76 | ))}
77 |
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | export default HomeProducts;
85 |
--------------------------------------------------------------------------------
/components/Loader.js:
--------------------------------------------------------------------------------
1 | const Loader = () => {
2 | return (
3 |
7 |
8 | Loading...
9 |
10 |
11 | );
12 | };
13 |
14 | export default Loader;
15 |
--------------------------------------------------------------------------------
/components/Mobile.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Context } from "@/Context/Context";
3 | import Link from "next/link";
4 | import { useContext } from "react";
5 |
6 | const Mobile = ({ setIsOpen, categories }) => {
7 | const { user, handleLogout } = useContext(Context);
8 | const name = user?.data?.name.replace(/ .*/, "");
9 | return (
10 |
11 |
12 |
13 |
setIsOpen(false)} href="/">
14 |
33 |
34 |
35 |
36 | -
37 | setIsOpen(false)}
40 | className="block rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700"
41 | >
42 | Home
43 |
44 |
45 | {user?.data?.isAdmin && (
46 | -
47 | setIsOpen(false)}
50 | className="block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"
51 | >
52 | Admin
53 |
54 |
55 | )}
56 | -
57 |
58 |
59 | Categories
60 |
61 |
62 |
74 |
75 |
76 |
77 |
78 | {categories?.map((category) => {
79 | return (
80 | -
81 |
85 | {category}
86 |
87 |
88 | );
89 | })}
90 |
91 |
92 |
93 |
94 | -
95 | setIsOpen(false)}
98 | className="block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"
99 | >
100 | About
101 |
102 |
103 |
104 | -
105 | setIsOpen(false)}
108 | className="block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"
109 | >
110 | Products
111 |
112 |
113 |
114 | -
115 |
116 |
117 | Account
118 |
119 |
120 |
132 |
133 |
134 |
135 |
136 | {user?.data ? (
137 | <>
138 |
148 | >
149 | ) : (
150 | <>
151 | setIsOpen(false)}
154 | >
155 |
161 |
162 | setIsOpen(false)}
165 | >
166 |
172 |
173 | >
174 | )}
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | {user?.data ? (
185 |
186 | {name}
187 |
188 | {user?.data?.email}
189 |
190 | ) : (
191 |
192 |
193 | Welcome to our store
194 |
195 |
196 | )}
197 |
198 |
199 |
200 |
201 |
202 | );
203 | };
204 |
205 | export default Mobile;
206 |
--------------------------------------------------------------------------------
/components/RelatedProducts.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import axios from "axios";
3 | import Image from "next/image";
4 | import Link from "next/link";
5 | import { useEffect, useState } from "react";
6 | import CardSkeleton from "./CardSkeleton";
7 |
8 | const RelatedProducts = ({ id }) => {
9 | const [product, setProduct] = useState([]);
10 | useEffect(() => {
11 | const fetchProduct = async () => {
12 | const res = await axios(`/api/relatedProducts/${id}`);
13 |
14 | setProduct(res.data);
15 | };
16 | fetchProduct();
17 | }, [id]);
18 |
19 | console.log();
20 |
21 | if (!product?.relatedProducts) {
22 | return (
23 | <>
24 |
25 | {" "}
26 | Related Products
27 |
28 |
29 |
30 |
31 | >
32 | );
33 | }
34 | return (
35 | <>
36 |
37 | {" "}
38 | Related Products
39 |
40 |
41 |
42 | {product?.relatedProducts?.map((product) => (
43 |
48 |
49 |
56 |
57 |
{product?.name}
58 |
59 | ₹{product?.price}
60 |
61 |
62 | ))}
63 |
64 | >
65 | );
66 | };
67 |
68 | export default RelatedProducts;
69 |
--------------------------------------------------------------------------------
/components/SideCart.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext, useEffect, useState } from "react";
2 | import { Dialog, Transition } from "@headlessui/react";
3 | import { XMarkIcon } from "@heroicons/react/24/outline";
4 | import Link from "next/link";
5 | import { Context } from "@/Context/Context";
6 | import Image from "next/image";
7 | import axios from "axios";
8 | import Button from "@/utils/Button";
9 |
10 | const SideCart = ({ setIsCartOpen, isCartOpen }) => {
11 | const { user } = useContext(Context);
12 | const [usersCart, setUserCart] = useState([]);
13 | const totalPrice = usersCart?.reduce(
14 | (total, item) =>
15 | total +
16 | item?.items?.reduce(
17 | (total, item) => total + item?.price * item?.quantity,
18 | 0
19 | ),
20 | 0
21 | );
22 |
23 | // get user cart data
24 | useEffect(() => {
25 | const getCart = async () => {
26 | try {
27 | const res = await axios.post("/api/cart-item", {
28 | userId: user?.data?._id,
29 | });
30 | if (res?.data?.cartItem?.length === 0) {
31 | return null;
32 | }
33 |
34 | setUserCart(res?.data?.cartItem);
35 | } catch (error) {
36 | console.log(error);
37 | }
38 | };
39 | user && getCart();
40 | }, [user]);
41 |
42 | // remove item from cart
43 | const removeItem = async (productId) => {
44 | try {
45 | const res = await axios.delete("/api/cart", {
46 | data: { id: productId },
47 | });
48 | if (res.status === 200) {
49 | setUserCart(usersCart?.filter((item) => item?._id !== productId));
50 | }
51 | } catch (error) {
52 | console.log(error);
53 | }
54 | };
55 |
56 | return (
57 |
58 |
260 |
261 | );
262 | };
263 |
264 | export default SideCart;
265 |
--------------------------------------------------------------------------------
/components/Skeleton.js:
--------------------------------------------------------------------------------
1 | const Skeleton = () => {
2 | return (
3 |
8 |
12 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
Loading...
38 |
39 |
40 | );
41 | };
42 |
43 | export default Skeleton;
44 |
--------------------------------------------------------------------------------
/components/TestUser.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const TestUser = () => {
4 | return (
5 |
6 |
Test Account Details
7 |
8 | Email: test@gmail.com
9 |
10 |
11 | Password: test4321
12 |
13 |
14 | );
15 | };
16 |
17 | export default TestUser;
18 |
--------------------------------------------------------------------------------
/db/Database.js:
--------------------------------------------------------------------------------
1 | const { default: mongoose } = require("mongoose");
2 |
3 | const connectDB = async () => {
4 | try {
5 | await mongoose.connect(process.env.CONNECT_DB);
6 | console.log("Connected to MongoDB");
7 | } catch (error) {
8 | console.log(error);
9 | }
10 | };
11 |
12 | export default connectDB;
13 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/middleware.js:
--------------------------------------------------------------------------------
1 | import { cookies } from "next/headers";
2 | import { NextResponse } from "next/server";
3 |
4 | export const middleware = (request) => {
5 | const authToken = cookies().get("authToken")?.value || "";
6 | let path = request.nextUrl.pathname;
7 | if (
8 | path === "/api/login" ||
9 | path === "/api/signup" ||
10 | path === "/api/login-user" ||
11 | path === "/api/product" ||
12 | /^\/api\/product\/\w+$/.test(path) ||
13 | path === "/api/cart" ||
14 | path === "/api/category" ||
15 | /^\/api\/category\/\w+$/.test(path) ||
16 | path === "/api/relatedProducts" ||
17 | /^\/api\/relatedProducts\/\w+$/.test(path)
18 | ) {
19 | return null;
20 | }
21 | const loggedInUserNotAccessPath =
22 | path === "/loginpage" || path === "/signupPage";
23 |
24 | if (loggedInUserNotAccessPath) {
25 | if (authToken) {
26 | return NextResponse.redirect(new URL("/", request.nextUrl));
27 | }
28 | } else {
29 | if (!authToken) {
30 | if (path.startsWith("/api") || path === "/dashboard") {
31 | return NextResponse.redirect(new URL("/", request.nextUrl));
32 | }
33 | }
34 | }
35 | };
36 | export const config = {
37 | matcher: ["/", "/loginpage", "/signupPage", "/dashboard", "/api/:path*"],
38 | };
39 |
--------------------------------------------------------------------------------
/models/Cart.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const cartSchema = new mongoose.Schema({
4 | userId: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | ref: "User",
7 | required: true,
8 | },
9 | items: [
10 | {
11 | productId: {
12 | type: mongoose.Schema.Types.ObjectId,
13 | ref: "ClothingProduct",
14 | required: true,
15 | },
16 | quantity: {
17 | type: Number,
18 | required: true,
19 | min: 1,
20 | },
21 | size: {
22 | type: String,
23 | required: true,
24 | default: "Medium",
25 | },
26 | image: {
27 | type: String,
28 | required: true,
29 | },
30 | price: {
31 | type: Number,
32 | required: true,
33 | },
34 | name: {
35 | type: String,
36 | required: true,
37 | },
38 | },
39 | ],
40 | });
41 |
42 | const Cart = mongoose.models.Cart || mongoose.model("Cart", cartSchema);
43 |
44 | export default Cart;
45 |
--------------------------------------------------------------------------------
/models/Order.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const orderSchema = new mongoose.Schema({
4 | userId: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | ref: "User",
7 | required: true,
8 | },
9 | items: [
10 | {
11 | productId: {
12 | type: mongoose.Schema.Types.ObjectId,
13 | ref: "ClothingProduct",
14 | required: true,
15 | },
16 | quantity: {
17 | type: Number,
18 | required: true,
19 | min: 1,
20 | },
21 | },
22 | ],
23 | total: {
24 | type: Number,
25 | required: true,
26 | min: 0,
27 | },
28 |
29 | shippingAddress: {
30 | type: String,
31 | },
32 | });
33 |
34 | const Order = mongoose.models.Order || mongoose.model("Order", orderSchema);
35 |
36 | export default Order;
37 |
--------------------------------------------------------------------------------
/models/Product.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const clothingProduct = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true,
7 | trim: true,
8 | },
9 | description: {
10 | type: String,
11 | required: true,
12 | },
13 | price: {
14 | type: Number,
15 | required: true,
16 | min: 0,
17 | },
18 | category: {
19 | type: String,
20 | required: true,
21 | enum: ["Men", "Women", "Kids"],
22 | },
23 | size: {
24 | type: [String],
25 | enum: ["Small", "Medium", "Large", "Extra Large"],
26 | default: ["Small", "Medium", "Large", "Extra Large"],
27 | },
28 | mainImage: {
29 | type: String,
30 | required: true,
31 | },
32 | });
33 |
34 | const ClothingProduct =
35 | mongoose.models.ClothingProduct ||
36 | mongoose.model("ClothingProduct", clothingProduct);
37 |
38 | export default ClothingProduct;
39 |
--------------------------------------------------------------------------------
/models/User.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const userSchema = mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: [true, "Please enter your name"],
7 | },
8 | email: {
9 | type: String,
10 | required: [true, "Please enter your email"],
11 | },
12 | password: {
13 | type: String,
14 | required: [true, "Please enter your password"],
15 | },
16 | isAdmin: {
17 | type: Boolean,
18 | required: true,
19 | default: false,
20 | },
21 | });
22 | const User = mongoose.models.User || mongoose.model("User", userSchema);
23 | export default User;
24 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | images: {
4 | domains: [
5 | "tailwindui.com",
6 | "images.unsplash.com",
7 | "i.ibb.co",
8 | "i.postimg.cc",
9 | "firebasestorage.googleapis.com",
10 | ],
11 | },
12 | };
13 |
14 | module.exports = nextConfig;
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "store",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@formspree/react": "^2.5.1",
13 | "@headlessui/react": "^1.7.17",
14 | "@heroicons/react": "^2.1.1",
15 | "axios": "^1.6.2",
16 | "bcrypt": "^5.1.1",
17 | "chart.js": "^4.4.1",
18 | "firebase": "^10.7.1",
19 | "jsonwebtoken": "^9.0.2",
20 | "lodash": "^4.17.21",
21 | "mongoose": "^8.0.3",
22 | "next": "14.0.4",
23 | "nextjs-toploader": "^1.6.4",
24 | "react": "^18",
25 | "react-chartjs-2": "^5.2.0",
26 | "react-dom": "^18",
27 | "react-hot-toast": "^2.4.1",
28 | "react-icons": "^4.12.0"
29 | },
30 | "devDependencies": {
31 | "autoprefixer": "^10.0.1",
32 | "eslint": "^8",
33 | "eslint-config-next": "14.0.4",
34 | "postcss": "^8",
35 | "tailwindcss": "^3.3.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/hero-pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riteshk-007/nextjs-store/62f306540e36742e8333c4c4c01bde8d9cb5e1d1/public/hero-pattern.png
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: (theme) => ({
11 | "hero-pattern": "url('/hero-pattern.png')",
12 | }),
13 | backgroundPosition: {
14 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
15 | "gradient-conic":
16 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
17 | },
18 | },
19 | },
20 | plugins: [],
21 | };
22 |
--------------------------------------------------------------------------------
/utils/Button.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useState } from "react";
3 |
4 | const Button = () => {
5 | const [isLoading, setIsLoading] = useState(false);
6 | return (
7 |
15 | );
16 | };
17 |
18 | export default Button;
19 |
--------------------------------------------------------------------------------