├── server ├── .gitignore ├── controllers │ ├── basicController.js │ ├── userController.js │ ├── cartController.js │ ├── authController.js │ └── productController.js ├── routes │ ├── router.js │ ├── cartRoutes.js │ ├── userRoutes.js │ ├── authRoutes.js │ └── productRoutes.js ├── config │ └── cloudinary.js ├── vercel.json ├── db │ └── config.js ├── middlewares │ ├── multer.js │ ├── adminAuthMiddleware.js │ └── authMiddleware.js ├── models │ ├── adminSchema.js │ ├── productSchema.js │ └── userSchema.js ├── utils │ └── sendEmail.js ├── package.json └── index.js ├── client ├── public │ ├── ads.txt │ ├── admin.jpg │ ├── forgot.jpg │ ├── hero1.jpg │ ├── login.jpg │ ├── reset.jpg │ ├── signup.jpg │ ├── tshirt.jpg │ ├── casuals.jpg │ ├── profile.png │ └── profile-genk.jpg ├── jsconfig.json ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── admin │ │ │ ├── secure │ │ │ │ ├── dashboard │ │ │ │ │ └── page.jsx │ │ │ │ ├── categories │ │ │ │ │ └── page.jsx │ │ │ │ ├── layout.jsx │ │ │ │ ├── home │ │ │ │ │ └── page.jsx │ │ │ │ ├── users │ │ │ │ │ └── page.jsx │ │ │ │ └── products │ │ │ │ │ ├── create │ │ │ │ │ └── page.jsx │ │ │ │ │ └── update │ │ │ │ │ └── [id] │ │ │ │ │ └── page.jsx │ │ │ ├── layout.jsx │ │ │ └── page.jsx │ │ ├── robots.js │ │ ├── globals.css │ │ ├── page.jsx │ │ ├── sitemap.js │ │ ├── cart │ │ │ └── page.jsx │ │ ├── profile │ │ │ ├── page.jsx │ │ │ └── edit │ │ │ │ └── page.jsx │ │ ├── layout.jsx │ │ ├── categories │ │ │ ├── [name] │ │ │ │ └── page.jsx │ │ │ └── page.jsx │ │ ├── offer │ │ │ └── page.jsx │ │ ├── trend │ │ │ └── page.jsx │ │ ├── product │ │ │ ├── page.jsx │ │ │ └── [id] │ │ │ │ └── page.jsx │ │ └── (auth) │ │ │ ├── forgot-password │ │ │ └── page.jsx │ │ │ ├── login │ │ │ └── page.jsx │ │ │ ├── reset-password │ │ │ └── page.jsx │ │ │ └── signup │ │ │ └── page.jsx │ ├── utils │ │ └── axiosConfig.js │ ├── components │ │ ├── Loader.jsx │ │ ├── secure │ │ │ ├── UserAuth.jsx │ │ │ └── SetUserAuth.jsx │ │ ├── NavbarHandler.jsx │ │ ├── FooterHandler.jsx │ │ ├── Admin │ │ │ ├── DeleteProduct.jsx │ │ │ └── AdminNavbar.jsx │ │ ├── SmallFooter.jsx │ │ ├── seo │ │ │ └── SEO.jsx │ │ ├── Pagination.jsx │ │ ├── Home │ │ │ └── Hero.jsx │ │ ├── Search.jsx │ │ ├── CartProduct.jsx │ │ ├── ProductCard.jsx │ │ ├── Footer.jsx │ │ ├── Filter.jsx │ │ └── Navbar.jsx │ ├── actions │ │ ├── removeCookie.js │ │ └── setCookie.js │ ├── middlewares │ │ ├── adminAuth.js │ │ └── normalUserAuth.js │ ├── middleware.js │ └── context │ │ └── GlobalProvider.jsx ├── .eslintrc.json ├── postcss.config.mjs ├── next.config.mjs ├── .gitignore ├── tailwind.config.js ├── package.json └── README.md ├── readme-assets ├── herosc.png ├── adminsc.png ├── postman.png ├── exploresc.png └── productsc.png ├── .gitignore ├── LICENSE └── README.md /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env* 3 | ./.env 4 | 5 | -------------------------------------------------------------------------------- /client/public/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-3190426345078074, DIRECT, f08c47fec0942fa0 -------------------------------------------------------------------------------- /client/public/admin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/admin.jpg -------------------------------------------------------------------------------- /client/public/forgot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/forgot.jpg -------------------------------------------------------------------------------- /client/public/hero1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/hero1.jpg -------------------------------------------------------------------------------- /client/public/login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/login.jpg -------------------------------------------------------------------------------- /client/public/reset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/reset.jpg -------------------------------------------------------------------------------- /client/public/signup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/signup.jpg -------------------------------------------------------------------------------- /client/public/tshirt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/tshirt.jpg -------------------------------------------------------------------------------- /readme-assets/herosc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/readme-assets/herosc.png -------------------------------------------------------------------------------- /client/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/public/casuals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/casuals.jpg -------------------------------------------------------------------------------- /client/public/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/profile.png -------------------------------------------------------------------------------- /client/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/src/app/favicon.ico -------------------------------------------------------------------------------- /readme-assets/adminsc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/readme-assets/adminsc.png -------------------------------------------------------------------------------- /readme-assets/postman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/readme-assets/postman.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /node_modules 3 | 4 | /build 5 | 6 | .env 7 | 8 | ./client/.env 9 | ./server/.env 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /readme-assets/exploresc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/readme-assets/exploresc.png -------------------------------------------------------------------------------- /readme-assets/productsc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/readme-assets/productsc.png -------------------------------------------------------------------------------- /client/public/profile-genk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2/HEAD/client/public/profile-genk.jpg -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | // { 5 | // "extends": ["next/babel","next/core-web-vitals"] 6 | // } -------------------------------------------------------------------------------- /server/controllers/basicController.js: -------------------------------------------------------------------------------- 1 | const normal = async (req, res) => { 2 | res.status(200).send("server started"); 3 | }; 4 | 5 | module.exports = { normal }; 6 | -------------------------------------------------------------------------------- /client/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /client/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | domains: ["res.cloudinary.com"], 5 | }, 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /server/routes/router.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = new express.Router(); 3 | const { normal } = require("../controllers/basicController"); 4 | 5 | router.get("/", normal); 6 | 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /client/src/app/admin/secure/dashboard/page.jsx: -------------------------------------------------------------------------------- 1 | export default function Page() { 2 | return ( 3 |
4 |

Coming Soon..

5 |
6 | ); 7 | } -------------------------------------------------------------------------------- /client/src/utils/axiosConfig.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export const axiosConfig = { 4 | withCredentials: true, 5 | }; 6 | 7 | export const axiosInstance = axios.create({ 8 | baseURL: process.env.NEXT_PUBLIC_API, 9 | withCredentials: true, 10 | 11 | }); -------------------------------------------------------------------------------- /client/src/app/robots.js: -------------------------------------------------------------------------------- 1 | export default function robots() { 2 | return { 3 | rules: { 4 | userAgent: '*', 5 | allow: '/', 6 | disallow: '/admin/', 7 | }, 8 | sitemap: `${process.env.NEXT_PUBLIC_CLIENT_URL}/sitemap.xml`, 9 | } 10 | } -------------------------------------------------------------------------------- /client/src/app/admin/secure/categories/page.jsx: -------------------------------------------------------------------------------- 1 | export default function Page() { 2 | return ( 3 |
4 |
5 |

Coming Soon..

6 |
7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /server/config/cloudinary.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require('cloudinary').v2; 2 | 3 | cloudinary.config({ 4 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 5 | api_key: process.env.CLOUDINARY_API_KEY, 6 | api_secret: process.env.CLOUDINARY_API_SECRET, 7 | }); 8 | 9 | module.exports = cloudinary; 10 | -------------------------------------------------------------------------------- /client/src/app/admin/layout.jsx: -------------------------------------------------------------------------------- 1 | 2 | // pages/dashboard.js (protected route) 3 | 4 | 5 | export default function Layout({children,params}) { 6 | 7 | return ( 8 |
9 | {children} 10 | {/* */} 11 | 12 |
13 | ); 14 | } -------------------------------------------------------------------------------- /server/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "index.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "index.js" 13 | } 14 | ], 15 | "regions":["bom1"] 16 | } -------------------------------------------------------------------------------- /server/db/config.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | require('dotenv').config(); 3 | 4 | const DB = process.env.MONGO_DB_URI 5 | 6 | mongoose.connect(DB,{ 7 | useUnifiedTopology: true, 8 | useNewUrlParser: true 9 | }).then(()=>console.log("DATABASE connected")) 10 | .catch((err)=>console.log("error : "+err.message)) 11 | 12 | -------------------------------------------------------------------------------- /client/src/components/Loader.jsx: -------------------------------------------------------------------------------- 1 | import { Spinner } from '@material-tailwind/react' 2 | import React from 'react' 3 | 4 | const Loader = () => { 5 | return ( 6 |
7 | 8 |
9 | ) 10 | } 11 | 12 | export default Loader 13 | -------------------------------------------------------------------------------- /client/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* Chrome, Safari, Edge, Opera */ 6 | input::-webkit-outer-spin-button, 7 | input::-webkit-inner-spin-button { 8 | -webkit-appearance: none; 9 | margin: 0; 10 | } 11 | 12 | /* Firefox */ 13 | input[type=number] { 14 | -moz-appearance: textfield; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /server/middlewares/multer.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const multer = require('multer'); 3 | const path = require('path'); 4 | 5 | 6 | const storage = multer.diskStorage({ 7 | filename: function (req, file, cb) { 8 | cb(null, Date.now() + path.extname(file.originalname)); 9 | } 10 | }); 11 | 12 | const upload = multer({ 13 | storage: storage, 14 | 15 | }); 16 | 17 | 18 | 19 | module.exports = upload; 20 | -------------------------------------------------------------------------------- /client/src/app/admin/secure/layout.jsx: -------------------------------------------------------------------------------- 1 | import AdminNavbar from "@/components/Admin/AdminNavbar"; 2 | import SmallFooter from "@/components/SmallFooter"; 3 | 4 | export default function Layout({ children }) { 5 | return ( 6 |
7 | 8 |
9 | {children} 10 | 11 |
12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /server/routes/cartRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = new express.Router(); 3 | const { 4 | addToCart, 5 | removeFromCart, 6 | getCart, 7 | } = require("../controllers/cartController"); 8 | 9 | const authMiddleWare = require("../middlewares/authMiddleware"); 10 | 11 | router.post("/", authMiddleWare, addToCart); 12 | 13 | router.delete("/:id", authMiddleWare, removeFromCart); 14 | 15 | router.get("/", authMiddleWare, getCart); 16 | 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /client/src/actions/removeCookie.js: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { cookies } from 'next/headers'; 4 | 5 | export async function removeCookie(name) { 6 | const cookieStore = cookies(); 7 | 8 | // Clear the cookie by setting it to an empty value and a past expiration date 9 | cookieStore.set({ 10 | name: name, 11 | value: '', 12 | expires: new Date(0), // Expire the cookie immediately 13 | path: '/', // Ensure the cookie is deleted for the entire site 14 | }); 15 | 16 | return { success: true }; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/app/page.jsx: -------------------------------------------------------------------------------- 1 | import Hero from "@/components/Home/Hero"; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 | 7 | {/*
8 | 13 |
*/} 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /client/.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 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /client/src/components/secure/UserAuth.jsx: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import React from "react"; 3 | import SetUserAuth from "./SetUserAuth"; 4 | 5 | const userAuth = () => { 6 | const cookieStore = cookies(); 7 | const userToken = cookieStore.get("token"); 8 | let auth = false; 9 | if(!userToken){ 10 | console.log("No token available"); 11 | }else{ 12 | auth = true; 13 | console.log("Token available"); 14 | } 15 | return ( 16 | 17 | ) 18 | }; 19 | 20 | export default userAuth; 21 | -------------------------------------------------------------------------------- /server/models/adminSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const adminSchema = new mongoose.Schema({ 4 | email: { 5 | type: String, 6 | required: true, 7 | unique: [true,'email are in use'], 8 | }, 9 | password: { 10 | type: String, 11 | required: true, 12 | }, 13 | }); 14 | 15 | // adminSchema.methods.matchPassword = async function (enteredPassword) { 16 | // return await bcrypt.compare(enteredPassword, this.password); 17 | // }; 18 | 19 | const Admin = new mongoose.model("Admin", adminSchema); 20 | 21 | module.exports = Admin; 22 | -------------------------------------------------------------------------------- /server/middlewares/adminAuthMiddleware.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | require("dotenv").config(); 3 | const adminAuthMiddleware = (req, res, next) => { 4 | const token = req.cookies.adminUserToken; 5 | if (!token) { 6 | return res.status(401).json({ message: "Not authenticated" }); 7 | } 8 | try { 9 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 10 | req.userId = decoded.userId; 11 | next(); 12 | } catch (error) { 13 | res.status(401).json({ message: "Invalid token" }); 14 | } 15 | }; 16 | 17 | module.exports = adminAuthMiddleware ; 18 | -------------------------------------------------------------------------------- /client/src/middlewares/adminAuth.js: -------------------------------------------------------------------------------- 1 | import { jwtVerify } from "jose"; 2 | import { NextResponse } from "next/server"; 3 | 4 | export const adminAuth = async (req) => { 5 | const token = req.cookies.get("adminToken")?.value; 6 | 7 | if (!token) { 8 | return NextResponse.redirect(new URL("/admin", req.url)); 9 | } 10 | 11 | try { 12 | const { payload } = await jwtVerify( 13 | token, 14 | new TextEncoder().encode(process.env.NEXT_PUBLIC_JWT_SECRET) 15 | ); 16 | 17 | return null; 18 | } catch (error) { 19 | return NextResponse.redirect(new URL("/admin", req.url)); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /client/src/components/NavbarHandler.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { usePathname } from "next/navigation"; 3 | import React from "react"; 4 | import Navbar from "./Navbar"; 5 | 6 | const NavbarHandler = () => { 7 | const path = usePathname(); 8 | return ( 9 |
10 | {path.startsWith("/admin") || 11 | path.startsWith("/login") || 12 | path.startsWith("/signup") || 13 | path.startsWith("/forgot-password") || 14 | path.startsWith("/reset-password") ? ( 15 | "" 16 | ) : ( 17 | 18 | )} 19 |
20 | ); 21 | }; 22 | 23 | export default NavbarHandler; 24 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | const withMT = require("@material-tailwind/react/utils/withMT"); 4 | 5 | module.exports = withMT({ 6 | content: [ 7 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 8 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 9 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 10 | ], 11 | theme: { 12 | extend: { 13 | backgroundImage: { 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 | -------------------------------------------------------------------------------- /server/middlewares/authMiddleware.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const jwt = require("jsonwebtoken"); 3 | require("dotenv").config(); 4 | 5 | 6 | const authMiddleware = (req, res, next) => { 7 | const token = req.cookies.userToken; 8 | console.log("token",token) 9 | if (!token) { 10 | return res.status(401).json({ message: "Not authenticated" }); 11 | } 12 | try { 13 | const decoded = jwt.verify(token, process.env.JWT_USER_SECRET); 14 | req.userId = decoded.userId; 15 | next(); 16 | } catch (error) { 17 | res.status(401).json({ message: "Invalid token" }); 18 | } 19 | }; 20 | 21 | module.exports = authMiddleware ; 22 | -------------------------------------------------------------------------------- /client/src/actions/setCookie.js: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { cookies } from 'next/headers'; 4 | 5 | export async function setCookie(name,value) { 6 | const cookieStore = cookies(); 7 | 8 | // Set the cookie with httpOnly, secure, and other attributes 9 | cookieStore.set({ 10 | name: name, 11 | value: value, 12 | httpOnly: true, 13 | secure: process.env.NEXT_PUBLIC_NODE_ENV === 'production', // Use secure only in production 14 | path: '/', 15 | sameSite: process.env.NEXT_PUBLIC_NODE_ENV === "production" ? "None" : "Lax", // CSRF protection 16 | maxAge: 60 * 60 * 24, // 1 day 17 | }); 18 | 19 | return { success: true }; 20 | } 21 | -------------------------------------------------------------------------------- /server/routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = new express.Router(); 3 | const upload = require("../middlewares/multer"); 4 | 5 | const { 6 | singleUserData, 7 | allUsers, 8 | editUserData, 9 | } = require("../controllers/userController"); 10 | 11 | const authMiddleWare = require("../middlewares/authMiddleware"); 12 | 13 | const adminAuthMiddleware = require("../middlewares/adminAuthMiddleware"); 14 | 15 | router.get("/me", authMiddleWare, singleUserData); 16 | 17 | router.get("/all", adminAuthMiddleware, allUsers); 18 | 19 | router.put("/me",authMiddleWare, upload.single("image"), editUserData); 20 | 21 | module.exports = router; 22 | -------------------------------------------------------------------------------- /client/src/components/FooterHandler.jsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import React from "react"; 3 | import Footer from "./Footer"; 4 | import { usePathname } from "next/navigation"; 5 | 6 | const FooterHandler = () => { 7 | const path = usePathname(); 8 | return ( 9 |
10 |
11 | {path.startsWith("/admin") || 12 | path.startsWith("/login") || 13 | path.startsWith("/signup") || 14 | path.startsWith("/forgot-password") || 15 | path.startsWith("/reset-password") ? ( 16 | "" 17 | ) : ( 18 | 19 |
20 | {/*
*/} 21 |
22 | )} 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default FooterHandler; 29 | -------------------------------------------------------------------------------- /server/utils/sendEmail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require('nodemailer'); 2 | 3 | const sendEmail = async (to, subject, text) => { 4 | try { 5 | const transporter = nodemailer.createTransport({ 6 | service: 'gmail', 7 | auth: { 8 | user: process.env.EMAIL_USER, 9 | pass: process.env.EMAIL_PASS, 10 | }, 11 | }); 12 | 13 | await transporter.sendMail({ 14 | from: process.env.EMAIL_USER, 15 | to, 16 | subject, 17 | text, 18 | }); 19 | 20 | console.log('Email sent'); 21 | } catch (error) { 22 | console.error('Error sending email', error); 23 | } 24 | }; 25 | 26 | module.exports = sendEmail; 27 | -------------------------------------------------------------------------------- /client/src/middlewares/normalUserAuth.js: -------------------------------------------------------------------------------- 1 | import { jwtVerify } from "jose"; 2 | import { NextResponse } from "next/server"; 3 | 4 | export const normalUserAuth = async (req) => { 5 | const token = req.cookies.get("token")?.value; 6 | // console.log(token, "token"); 7 | if (!token) { 8 | 9 | return NextResponse.redirect(new URL("/login", req.url)); 10 | } 11 | 12 | try { 13 | // console.log("token verifying"); 14 | const { payload } = await jwtVerify( 15 | token, 16 | new TextEncoder().encode(process.env.NEXT_PUBLIC_JWT_USER_SECRET) 17 | ); 18 | 19 | return null; 20 | } catch (error) { 21 | // console.log("token validation failed"); 22 | 23 | return NextResponse.redirect(new URL("/login", req.url)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /client/src/middleware.js: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { jwtVerify } from "jose"; 3 | import { adminAuth } from "./middlewares/adminAuth"; 4 | import { normalUserAuth } from "./middlewares/normalUserAuth"; 5 | 6 | export async function middleware(req,res) { 7 | if (req.nextUrl.pathname.startsWith("/admin")) { 8 | const adminResponse = await adminAuth(req); 9 | if (adminResponse) return adminResponse; 10 | } else if ( 11 | req.nextUrl.pathname.startsWith("/profile") || 12 | req.nextUrl.pathname.startsWith("/cart") 13 | ) { 14 | 15 | const userResponse = await normalUserAuth(req); 16 | if (userResponse) return userResponse; 17 | } 18 | 19 | return NextResponse.next(); 20 | } 21 | //path 22 | export const config = { 23 | matcher: ["/admin/secure/:path*", "/profile/:path", "/cart/:path"], 24 | }; 25 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "supervisor index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^5.1.1", 15 | "bcryptjs": "^2.4.3", 16 | "body-parser": "^1.20.2", 17 | "cloudinary": "^2.4.0", 18 | "cookie": "^0.6.0", 19 | "cookie-parser": "^1.4.6", 20 | "cors": "^2.8.5", 21 | "crypto": "^1.0.1", 22 | "dotenv": "^16.4.5", 23 | "express": "^4.19.2", 24 | "jsonwebtoken": "^9.0.2", 25 | "moment": "^2.30.1", 26 | "mongoose": "^8.6.0", 27 | "multer": "^1.4.5-lts.1", 28 | "nodemailer": "^6.9.15", 29 | "supervisor": "^0.12.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/routes/authRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = new express.Router(); 3 | const { 4 | login, 5 | logout, 6 | signup, 7 | forgotPassword, 8 | resetPassword, 9 | adminLogin, 10 | adminSignup, 11 | adminLogout, 12 | } = require("../controllers/authController"); 13 | const Admin = require("../models/adminSchema"); 14 | const bcrypt = require('bcryptjs') 15 | 16 | router.post("/login", login); 17 | 18 | router.post("/logout", logout); 19 | 20 | router.post("/signup", signup); 21 | 22 | router.post("/forgot-password", forgotPassword); 23 | 24 | router.put("/reset-password", resetPassword); 25 | 26 | router.post("/admin/login", adminLogin); 27 | 28 | router.post("/admin/logout", adminLogout); 29 | 30 | 31 | //create admin 32 | 33 | // router.post("/admin/signup", adminSignup); 34 | 35 | 36 | module.exports = router; 37 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 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 | "@emotion/react": "^11.13.3", 13 | "@emotion/styled": "^11.13.0", 14 | "@heroicons/react": "^2.1.5", 15 | "@material-tailwind/react": "^2.1.10", 16 | "@mui/material": "^6.0.2", 17 | "axios": "^1.7.7", 18 | "jose": "^5.8.0", 19 | "js-cookie": "^3.0.5", 20 | "jsonwebtoken": "^9.0.2", 21 | "next": "14.2.21", 22 | "next-client-cookies": "^1.1.1", 23 | "react": "^18", 24 | "react-dom": "^18", 25 | "react-icons": "^5.3.0", 26 | "react-toastify": "^10.0.5" 27 | }, 28 | "devDependencies": { 29 | "eslint": "^8", 30 | "eslint-config-next": "14.2.7", 31 | "postcss": "^8", 32 | "tailwindcss": "^3.4.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/src/app/sitemap.js: -------------------------------------------------------------------------------- 1 | export default function sitemap() { 2 | return [ 3 | { 4 | url: `${process.env.NEXT_PUBLIC_CLIENT_URL}`, 5 | lastModified: new Date(), 6 | changeFrequency: 'yearly', 7 | priority: 1, 8 | }, 9 | { 10 | url: `${process.env.NEXT_PUBLIC_CLIENT_URL}/product`, 11 | lastModified: new Date(), 12 | changeFrequency: 'daily', 13 | priority: 0.8, 14 | }, 15 | { 16 | url: `${process.env.NEXT_PUBLIC_CLIENT_URL}/categories`, 17 | lastModified: new Date(), 18 | changeFrequency: 'monthly', 19 | priority: 0.5, 20 | }, 21 | { 22 | url: `${process.env.NEXT_PUBLIC_CLIENT_URL}/trend`, 23 | lastModified: new Date(), 24 | changeFrequency: 'monthly', 25 | priority: 0.3, 26 | }, 27 | { 28 | url: `${process.env.NEXT_PUBLIC_CLIENT_URL}/offer`, 29 | lastModified: new Date(), 30 | changeFrequency: 'monthly', 31 | priority: 0.3, 32 | }, 33 | ] 34 | } -------------------------------------------------------------------------------- /server/routes/productRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = new express.Router(); 3 | const { 4 | createProduct, 5 | updateProduct, 6 | deleteProduct, 7 | getProducts, 8 | getProductById, 9 | trendProducts, 10 | offerProducts, 11 | } = require("../controllers/productController"); 12 | 13 | const upload = require("../middlewares/multer"); 14 | 15 | const adminAuthMiddleWare = require("../middlewares/adminAuthMiddleware"); 16 | 17 | router.post("/", adminAuthMiddleWare, upload.fields([{ name: 'image', maxCount: 1 }, { name: 'additionalImages', maxCount: 3 }]), createProduct); 18 | 19 | router.put("/:id", adminAuthMiddleWare, upload.fields([{ name: 'image', maxCount: 1 }, { name: 'additionalImages', maxCount: 3 }]), updateProduct); 20 | 21 | router.delete("/:id", adminAuthMiddleWare, deleteProduct); 22 | 23 | router.get("/", getProducts); 24 | 25 | router.get("/:id", getProductById); 26 | 27 | router.get("/filter/trend", trendProducts); 28 | 29 | router.get("/filter/offer", offerProducts); 30 | 31 | module.exports = router; 32 | -------------------------------------------------------------------------------- /client/src/app/admin/secure/home/page.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | 4 | export default function Page() { 5 | return ( 6 |
7 |
8 |
9 | 16 |
17 |
18 |

Welcome Mr.Sebe

19 |

We are awaiting for you !!

20 |
Go and provide the details needed for the website . hurry up!!
21 |
22 | Go to Products Page 23 |
24 |
25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 SEBE S 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /client/src/components/Admin/DeleteProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | Button, 4 | Dialog, 5 | DialogHeader, 6 | DialogBody, 7 | DialogFooter, 8 | Typography, 9 | } from "@material-tailwind/react"; 10 | const DeleteProduct = () => { 11 | const [open, setOpen] = useState(false); 12 | 13 | const handleOpen = (data) => { 14 | setCurrent(data); 15 | setOpen(!open); 16 | }; 17 | return ( 18 |
19 | 20 | 21 |
22 |

View

23 | 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | ) 40 | } 41 | 42 | export default DeleteProduct 43 | -------------------------------------------------------------------------------- /client/src/components/SmallFooter.jsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | import { FaGithub, FaLinkedin } from 'react-icons/fa' 4 | 5 | const SmallFooter = () => { 6 | return ( 7 |
8 | {/*
9 |
10 |

11 | A product by{" "} 12 | 13 | 18 | {" "} 19 | Sebe{" "} 20 | 21 | {" "} 22 | | GenRio 23 |

24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |
*/} 34 |
35 | ) 36 | } 37 | 38 | export default SmallFooter 39 | -------------------------------------------------------------------------------- /server/models/productSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const productSchema = new mongoose.Schema( 4 | { 5 | name: { 6 | type: String, 7 | required: true, 8 | }, 9 | category: { 10 | type: String, 11 | required: true, 12 | }, 13 | vendor: { 14 | type: String, 15 | required: true, 16 | default:"GenRio" 17 | }, 18 | MRPprice: { 19 | type: Number, 20 | required: true, 21 | }, 22 | sellingPrice: { 23 | type: Number, 24 | required: true, 25 | }, 26 | description: { 27 | type: String, 28 | required: true, 29 | }, 30 | image: { 31 | type: String, 32 | required: true, 33 | }, 34 | additionalImages: { 35 | type: [String], 36 | required: true, 37 | }, 38 | quantity: { 39 | type: Number, 40 | required: true, 41 | default: 10, 42 | }, 43 | trend: { 44 | type: Boolean, 45 | default: false, 46 | required: true, 47 | }, 48 | offer: { 49 | type: Boolean, 50 | default: false, 51 | required: true, 52 | }, 53 | }, 54 | { 55 | timestamps: true, 56 | } 57 | ); 58 | 59 | const Products = new mongoose.model("Products", productSchema); 60 | module.exports = Products; 61 | -------------------------------------------------------------------------------- /client/src/components/secure/SetUserAuth.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useGlobalContext } from "@/context/GlobalProvider"; 3 | import { axiosInstance } from "@/utils/axiosConfig"; 4 | import { usePathname, useRouter } from "next/navigation"; 5 | import React, { useEffect } from "react"; 6 | 7 | const SetUserAuth = ({ token, auth }) => { 8 | // const router = useRouter(); 9 | console.log("setuserauth",auth) 10 | const path = usePathname(); 11 | const { userAuth, setUserAuth, userData, setUserData } = useGlobalContext(); 12 | useEffect(() => { 13 | const fetchUserData = async () => { 14 | try { 15 | const res = await axiosInstance.get("/user/me"); 16 | console.log(res.data); 17 | setUserData(res.data); 18 | } catch (error) { 19 | console.log(error); 20 | } 21 | }; 22 | 23 | const authCheck = () => { 24 | if (auth) { 25 | setUserAuth(auth); 26 | console.log("token is available"); 27 | if (userData) { 28 | console.log("user data is available"); 29 | } else { 30 | fetchUserData(); 31 | console.log("user data is not available"); 32 | } 33 | } else { 34 | setUserAuth(false); 35 | setUserData(null); 36 | } 37 | }; 38 | authCheck(); 39 | }, [path,auth]); 40 | return
; 41 | }; 42 | 43 | export default SetUserAuth; 44 | -------------------------------------------------------------------------------- /client/src/components/seo/SEO.jsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | 3 | const SEO = ({ title, description, keywords, image, url,author }) => { 4 | return ( 5 | 6 | {/* Basic Meta Tags */} 7 | {title} 8 | 9 | 10 | 11 | 12 | 13 | {/* Open Graph / Facebook */} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {/* Twitter */} 21 | 22 | 23 | 24 | 25 | 26 | 27 | {/* Other Meta Tags */} 28 | 29 | 30 | 31 | 32 | ); 33 | }; 34 | 35 | export default SEO; 36 | -------------------------------------------------------------------------------- /client/src/context/GlobalProvider.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { createContext, useContext, useState } from "react"; 3 | 4 | export const GlobalContext = createContext(); 5 | 6 | const GlobalProvider = ({ children }) => { 7 | const [products, setProducts] = useState([]); 8 | const [searchTerm, setSearchTerm] = useState(""); 9 | const [filters, setFilters] = useState({ 10 | category: "", 11 | minPice: 0, 12 | maxPrice: 1000000, 13 | }); 14 | const [pagination, setPagination] = useState({ 15 | currentPage: 1, 16 | totalPages: 1, 17 | }); 18 | const [userData, setUserData] = useState(null); 19 | const [userAuth, setUserAuth] = useState(false); 20 | const [cartModified,setCartModified] = useState(false); 21 | 22 | return ( 23 | 40 | {children} 41 | 42 | ); 43 | }; 44 | 45 | export default GlobalProvider; 46 | 47 | export const useGlobalContext = () => { 48 | const context = useContext(GlobalContext); 49 | if (!context) { 50 | throw new Error("useGlobalContext must be used within a GlobalProvider"); 51 | } 52 | return context; 53 | }; 54 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const cors = require("cors"); 4 | const cookieParser = require("cookie-parser"); 5 | 6 | const bodyParser = require('body-parser') 7 | 8 | const router = require("./routes/router"); 9 | const authRoutes = require("./routes/authRoutes"); 10 | const cartRoutes = require("./routes/cartRoutes"); 11 | const productRoutes = require("./routes/productRoutes"); 12 | const userRoutes = require("./routes/userRoutes") 13 | 14 | require("dotenv").config(); 15 | require("./db/config"); 16 | 17 | // app.use(cors()); 18 | 19 | const PORT = process.env.PORT || 5555; 20 | app.use(express.json()); 21 | app.use(express.urlencoded({ extended: true })); 22 | app.use(bodyParser.json()) 23 | app.use(cors({ origin: process.env.CLIENT_URL, credentials: true })); 24 | 25 | 26 | app.use(cookieParser()); 27 | app.use('/',router); 28 | app.use('/api/auth',authRoutes); 29 | app.use('/api/cart',cartRoutes); 30 | app.use('/api/product',productRoutes); 31 | app.use('/api/user',userRoutes); 32 | 33 | 34 | 35 | // const allowCrossDomain = (req, res, next) => { 36 | // res.header(`Access-Control-Allow-Origin`, `*`); 37 | // res.header(`Access-Control-Allow-Methods`, `GET,PUT,POST,DELETE`); 38 | // res.header(`Access-Control-Allow-Headers`, `*`); 39 | // next(); 40 | // }; 41 | // app.use(allowCrossDomain); 42 | 43 | 44 | // for parsing application/json 45 | // app.use(bodyParser.urlencoded({ extended: true })) 46 | 47 | 48 | 49 | app.listen(PORT, () => { 50 | console.log(`server running on http://localhost:${PORT}`); 51 | }); 52 | -------------------------------------------------------------------------------- /client/src/components/Pagination.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { IconButton, Typography } from "@material-tailwind/react"; 3 | import { ArrowRightIcon, ArrowLeftIcon } from "@heroicons/react/24/outline"; 4 | import { useGlobalContext } from "@/context/GlobalProvider"; 5 | 6 | const Pagination = () => { 7 | const { pagination, setPagination } = useGlobalContext(); 8 | const next = () => { 9 | if (pagination.currentPage === pagination.totalPages) return; 10 | 11 | setPagination({ ...pagination, currentPage: pagination.currentPage + 1 }); 12 | }; 13 | 14 | const prev = () => { 15 | if (pagination === 1) return; 16 | setPagination({ ...pagination, currentPage: pagination.currentPage - 1 }); 17 | }; 18 | 19 | return ( 20 |
21 | 27 | 28 | 29 | 30 | Page {pagination.currentPage}{" "} 31 | of {pagination.totalPages} 32 | 33 | 39 | 40 | 41 |
42 | ); 43 | }; 44 | 45 | export default Pagination; 46 | -------------------------------------------------------------------------------- /client/src/components/Home/Hero.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | import React from "react"; 4 | 5 | const Hero = () => { 6 | return ( 7 |
8 |
9 |
10 |
11 | 18 |
19 |
20 |

21 | Don't be slow! Our prices are low..! 22 |

23 |

24 | Genkart is a ecommerce platform to enhance online shopping 25 | performance for the customers to provide what they need ?{" "} 26 |

27 |
28 | 32 | Explore More 33 | 34 |
35 |
36 |
37 |
38 |
39 | {/*

Genkart is an e-commerce platform that connects local businesses with customers. Our mission is to provide a safe, convenient, and affordable shopping experience for our customers.

*/} 40 |
41 |
42 | ); 43 | }; 44 | 45 | export default Hero; 46 | -------------------------------------------------------------------------------- /server/models/userSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const bcrypt = require("bcryptjs"); 3 | 4 | const userSchema = new mongoose.Schema( 5 | { 6 | name: { 7 | type: String, 8 | required: true, 9 | }, 10 | email: { 11 | type: String, 12 | required: true, 13 | unique: true, 14 | }, 15 | password: { 16 | type: String, 17 | required: true, 18 | }, 19 | profileImage: { 20 | type: String, 21 | default:"https://res.cloudinary.com/ded1o1e26/image/upload/v1733411240/istockphoto-1223671392-612x612_hemk0v.jpg" 22 | //default:"https://res.cloudinary.com/ded1o1e26/image/upload/v1726405493/profile_ioufgc.png" 23 | }, 24 | resetPasswordToken: { 25 | type: String, 26 | }, 27 | resetPasswordExpires: { 28 | type: Date, 29 | }, 30 | 31 | cart: [ 32 | { 33 | product: { 34 | type: mongoose.Schema.Types.ObjectId, 35 | ref: "Products", 36 | required: true, 37 | }, 38 | quantity: { 39 | type: Number, 40 | required: true, 41 | default: 1, 42 | }, 43 | }, 44 | ], 45 | }, 46 | { 47 | timestamps: true, 48 | } 49 | ); 50 | // userSchema.pre("save", async function (next) { 51 | // if (!this.isModified("password")) return next(); 52 | // const salt = await bcrypt.genSalt(10); 53 | // this.password = await bcrypt.hash(this.password, salt); 54 | // next(); 55 | // }); 56 | 57 | userSchema.methods.matchPassword = async function (enteredPassword) { 58 | return await bcrypt.compare(enteredPassword, this.password); 59 | }; 60 | 61 | const Users = new mongoose.model("Users", userSchema); 62 | 63 | module.exports = Users; 64 | -------------------------------------------------------------------------------- /client/src/components/Search.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useGlobalContext } from "@/context/GlobalProvider"; 3 | import { usePathname, useRouter } from "next/navigation"; 4 | import React, { useContext, useEffect, useState } from "react"; 5 | import { CiSearch } from "react-icons/ci"; 6 | const Search = () => { 7 | const router = useRouter(); 8 | const path = usePathname(); 9 | const { searchTerm, setSearchTerm } = useGlobalContext(); 10 | 11 | const [term, setTerm] = useState(""); 12 | const handleInputChange = (e) => { 13 | setTerm(e.target.value); 14 | }; 15 | 16 | const handleSearch = () => { 17 | setSearchTerm(term); 18 | // if (path === "/product" || path.startsWith("/admin")) { 19 | // console.log("searchpath"); 20 | // } else { 21 | // router.push("/product"); 22 | // } 23 | if(path=== "/"){ 24 | router.push("/product") 25 | } 26 | }; 27 | 28 | useEffect(() => { 29 | // console.log(searchTerm); 30 | setTerm(searchTerm) 31 | }, [searchTerm]); 32 | 33 | return ( 34 |
35 |
36 |
37 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 | ); 54 | }; 55 | 56 | export default Search; 57 | -------------------------------------------------------------------------------- /client/src/app/cart/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import CartProduct from "@/components/CartProduct"; 3 | import Loader from "@/components/Loader"; 4 | import { useGlobalContext } from "@/context/GlobalProvider"; 5 | import { axiosInstance } from "@/utils/axiosConfig"; 6 | import { useRouter } from "next/navigation"; 7 | import { useEffect, useState } from "react"; 8 | import { toast } from "react-toastify"; 9 | 10 | export default function Page() { 11 | const { userAuth, userData, setUserData,setCartModified,cartModified } = useGlobalContext(); 12 | const [loading, setLoading] = useState(true); 13 | const router = useRouter(); 14 | const [cart,setCart] = useState([]) 15 | useEffect(() => { 16 | setLoading(true); 17 | const fetchCart = async () => { 18 | try { 19 | const res = await axiosInstance.get("/cart"); 20 | // console.log(res.data.cart); 21 | // setUserData({ ...userData, cart: res.data }); 22 | setCart(res.data) 23 | setLoading(false); 24 | } catch (error) { 25 | console.log(error); 26 | toast.error("Error fetching cart"); 27 | } 28 | }; 29 | 30 | fetchCart(); 31 | }, [cartModified]); 32 | return ( 33 |
34 |
35 |

My Cart

36 |
37 | {loading ? ( 38 | 39 | ) : ( 40 |
41 | {cart && 42 | cart.map((product, index) => { 43 | return ( 44 |
45 | 49 |
50 | ); 51 | })} 52 |
53 | )} 54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /client/src/app/profile/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Loader from "@/components/Loader"; 4 | import { useGlobalContext } from "@/context/GlobalProvider"; 5 | import { axiosInstance } from "@/utils/axiosConfig"; 6 | import Link from "next/link"; 7 | import { useEffect, useState } from "react"; 8 | 9 | export default function Page() { 10 | const { userData, setUserData } = useGlobalContext(); 11 | 12 | useEffect(() => { 13 | const fecthUser = async () => { 14 | try { 15 | const res = await axiosInstance.get("/user/me"); 16 | setUserData(res.data); 17 | // console.log(res.data); 18 | } catch (error) { 19 | console.error(error); 20 | } 21 | }; 22 | fecthUser(); 23 | }, []); 24 | 25 | // console.log(userData); 26 | return ( 27 |
28 |
29 | {userData ? ( 30 |
31 |

Profile

32 |
33 |
34 | 39 |
40 |
41 |
42 |

Name :

43 |

{userData.name}

44 |
45 |
46 |

Email :

47 | 48 |

{userData.email}

49 |
50 |
51 | Edit Profile 52 |
53 |
54 |
55 |
56 | ) : ( 57 | 58 | )} 59 |
60 |
61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /client/src/app/layout.jsx: -------------------------------------------------------------------------------- 1 | import { Inter } from "next/font/google"; 2 | import "./globals.css"; 3 | import UserAuth from "@/components/secure/UserAuth"; 4 | 5 | import { ToastContainer } from "react-toastify"; 6 | import "react-toastify/dist/ReactToastify.css"; 7 | import NavbarHandler from "@/components/NavbarHandler"; 8 | import DataProvider from "@/context/GlobalProvider"; 9 | import SEO from "@/components/seo/SEO"; 10 | import FooterHandler from "@/components/FooterHandler"; 11 | // axios.defaults.withCredentials = true; 12 | const inter = Inter({ subsets: ["latin"] }); 13 | 14 | export const metadata = { 15 | title: "Genkart | Ecommerce", 16 | description: 17 | "Shop the best casuals and t-shirts at Genkart. Latest fashion trends in comfortable clothing , Explore Genkart's tech projects on Next.js, Node.js, and full-stack development.", 18 | }; 19 | 20 | export default function RootLayout({ children }) { 21 | return ( 22 | 23 | 24 | 28 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 44 | 45 | {children} 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /server/controllers/userController.js: -------------------------------------------------------------------------------- 1 | const Users = require("../models/userSchema"); 2 | const cloudinary = require("../config/cloudinary"); 3 | require("dotenv").config(); 4 | 5 | const singleUserData = async (req, res) => { 6 | try { 7 | // Assuming the user ID is available in req.user from authentication middleware 8 | const user = await Users.findById(req.userId).select("-password"); // exclude the password 9 | if (!user) { 10 | return res.status(404).json({ message: "User not found" }); 11 | } 12 | res.status(200).json(user); 13 | } catch (error) { 14 | res.status(500).json({ message: "Server Error" }); 15 | } 16 | }; 17 | 18 | const allUsers = async (req, res) => { 19 | const { page = 1, limit = 10 } = req.query; 20 | console.log(page, limit); 21 | try { 22 | // const user = await Users.find(); 23 | // if (!user) { 24 | // return res.status(404).json({ error: "User not found" }); 25 | // } else { 26 | // return res.status(200).json({ user }); 27 | // } 28 | const users = await Users.find() 29 | .skip((page - 1) * limit) 30 | .limit(Number(limit)); 31 | console.log(users, "user"); 32 | const totalUsers = users.length; 33 | console.log(totalUsers, "users"); 34 | const totalPages = Math.ceil(totalUsers / limit); 35 | console.log(totalPages); 36 | res.status(200).json({ users, totalPages, totalUsers }); 37 | } catch (error) { 38 | res.status(500).json({ message: "Server Error" }); 39 | } 40 | }; 41 | 42 | const editUserData = async (req, res) => { 43 | const { name } = req.body; 44 | console.log(name); 45 | try { 46 | const user = await Users.findById(req.userId); // exclude the password 47 | if (!user) { 48 | return res.status(404).json({ message: "User not found" }); 49 | } 50 | 51 | console.log(req.file, "file"); 52 | if (req.file) { 53 | const result = await cloudinary.uploader.upload(req.file.path, { 54 | folder: process.env.CLOUDINARY_FOLDER_NAME, 55 | }); 56 | user.profileImage = result.secure_url; 57 | 58 | console.log(result.secure_url); 59 | } 60 | 61 | user.name = name || user.name; 62 | const updatedUserData = await user.save(); 63 | res.status(200).json(updatedUserData); 64 | } catch (error) { 65 | res.status(500).json({ message: "Server Error", error: error.message }); 66 | } 67 | }; 68 | 69 | module.exports = { 70 | singleUserData, 71 | allUsers, 72 | editUserData, 73 | }; 74 | -------------------------------------------------------------------------------- /client/src/app/categories/[name]/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Loader from "@/components/Loader"; 3 | import Pagination from "@/components/Pagination"; 4 | import ProductCard from "@/components/ProductCard"; 5 | import { useGlobalContext } from "@/context/GlobalProvider"; 6 | import { axiosInstance } from "@/utils/axiosConfig"; 7 | import { useParams } from "next/navigation"; 8 | import { useEffect, useState } from "react"; 9 | import { toast } from "react-toastify"; 10 | 11 | export default function Page() { 12 | const { name } = useParams(); 13 | const [products, setProducts] = useState([]); 14 | const [PaginatedValue, setPaginatedValue] = useState(1); 15 | const [loading, setLoading] = useState(true); 16 | 17 | const { searchTerm, setSearchTerm, pagination, setPagination } = 18 | useGlobalContext(); 19 | 20 | useEffect(() => { 21 | setPagination({ ...pagination, currentPage: 1 }); 22 | setSearchTerm(""); 23 | }, []); 24 | 25 | useEffect(() => { 26 | if (PaginatedValue == pagination.totalPages) { 27 | } else { 28 | setPagination({ ...pagination, totalPages: PaginatedValue }); 29 | } 30 | }, [PaginatedValue]); 31 | 32 | useEffect(() => { 33 | setLoading(true); 34 | const fetchCategory = async () => { 35 | const query = new URLSearchParams({ 36 | search: searchTerm, 37 | category: name, 38 | pagination: pagination.currentPage, 39 | }).toString(); 40 | try { 41 | const res = await axiosInstance.get(`/product?${query}`); 42 | // console.log(res.data.products); 43 | setProducts(res.data.products); 44 | setPaginatedValue(res.data.totalPages); 45 | setLoading(false); 46 | } catch (error) { 47 | console.error(error); 48 | toast.error("Error fetching category"); 49 | } 50 | }; 51 | 52 | fetchCategory(); 53 | }, [searchTerm]); 54 | return ( 55 |
56 |
57 |

{name}

58 |
59 | {loading ? ( 60 | 61 | ) : ( 62 |
63 | {products && 64 | products.map((product, index) => ( 65 |
66 | 67 |
68 | ))} 69 |
70 | )} 71 |
72 | 73 |
74 |
75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /client/src/app/offer/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Loader from "@/components/Loader"; 3 | import Pagination from "@/components/Pagination"; 4 | import ProductCard from "@/components/ProductCard"; 5 | import Search from "@/components/Search"; 6 | import { useGlobalContext } from "@/context/GlobalProvider"; 7 | import { axiosInstance } from "@/utils/axiosConfig"; 8 | import { useEffect, useState } from "react"; 9 | import { toast } from "react-toastify"; 10 | 11 | export default function Page() { 12 | const [products, setProducts] = useState([]); 13 | 14 | const [PaginatedValue, setPaginatedValue] = useState(1); 15 | 16 | const [loading, setLoading] = useState(true); 17 | 18 | const { searchTerm, setSearchTerm, pagination, setPagination } = 19 | useGlobalContext(); 20 | 21 | useEffect(() => { 22 | setPagination({ ...pagination, currentPage: 1 }); 23 | setSearchTerm(""); 24 | }, []); 25 | 26 | useEffect(() => { 27 | if (PaginatedValue == pagination.totalPages) { 28 | } else { 29 | setPagination({ ...pagination, totalPages: PaginatedValue }); 30 | } 31 | }, [PaginatedValue]); 32 | 33 | useEffect(() => { 34 | setLoading(true); 35 | const fetchCategory = async () => { 36 | const query = new URLSearchParams({ 37 | search: searchTerm, 38 | pagination: pagination.currentPage, 39 | }).toString(); 40 | try { 41 | const res = await axiosInstance.get(`/product/filter/offer?${query}`); 42 | // console.log(res.data.products); 43 | setProducts(res.data.products); 44 | setPaginatedValue(res.data.totalPages); 45 | setLoading(false); 46 | } catch (error) { 47 | console.error(error); 48 | toast.error("Error fetching category"); 49 | } 50 | }; 51 | 52 | fetchCategory(); 53 | }, [searchTerm]); 54 | 55 | return ( 56 |
57 |
58 |

59 | {" "} 60 | Offer Products 61 |

62 |
63 |
64 | 65 |
66 | {loading ? ( 67 | 68 | ) : ( 69 |
70 | {products && 71 | products.map((product, index) => ( 72 |
73 | 74 |
75 | ))} 76 |
77 | )} 78 |
79 | 80 |
81 |
82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /client/src/app/trend/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Loader from "@/components/Loader"; 3 | import Pagination from "@/components/Pagination"; 4 | import ProductCard from "@/components/ProductCard"; 5 | import Search from "@/components/Search"; 6 | import { useGlobalContext } from "@/context/GlobalProvider"; 7 | import { axiosInstance } from "@/utils/axiosConfig"; 8 | import { useEffect, useState } from "react"; 9 | import { toast } from "react-toastify"; 10 | 11 | export default function Page() { 12 | const [products, setProducts] = useState([]); 13 | 14 | const [PaginatedValue, setPaginatedValue] = useState(1); 15 | 16 | const [loading, setLoading] = useState(true); 17 | 18 | const { searchTerm, setSearchTerm, pagination, setPagination } = 19 | useGlobalContext(); 20 | 21 | useEffect(() => { 22 | setPagination({ ...pagination, currentPage: 1 }); 23 | setSearchTerm(""); 24 | }, []); 25 | 26 | useEffect(() => { 27 | if (PaginatedValue == pagination.totalPages) { 28 | } else { 29 | setPagination({ ...pagination, totalPages: PaginatedValue }); 30 | } 31 | }, [PaginatedValue]); 32 | 33 | useEffect(() => { 34 | setLoading(true); 35 | const fetchCategory = async () => { 36 | const query = new URLSearchParams({ 37 | search: searchTerm, 38 | pagination: pagination.currentPage, 39 | }).toString(); 40 | try { 41 | const res = await axiosInstance.get(`/product/filter/trend?${query}`); 42 | // console.log(res.data.products); 43 | setProducts(res.data.products); 44 | setPaginatedValue(res.data.totalPages); 45 | setLoading(false); 46 | } catch (error) { 47 | console.error(error); 48 | toast.error("Error fetching category"); 49 | } 50 | }; 51 | 52 | fetchCategory(); 53 | }, [searchTerm]); 54 | 55 | return ( 56 |
57 |
58 |

59 | {" "} 60 | Trending Products 61 |

62 |
63 |
64 | 65 |
66 | {loading ? ( 67 | 68 | ) : ( 69 |
70 | {products && 71 | products.map((product, index) => ( 72 |
73 | 74 |
75 | ))} 76 |
77 | )} 78 |
79 | 80 |
81 |
82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /server/controllers/cartController.js: -------------------------------------------------------------------------------- 1 | // controllers/cartController.js 2 | 3 | const Users = require("../models/userSchema"); 4 | const Products = require("../models/productSchema"); 5 | require("dotenv").config(); 6 | // Add a product to the cart 7 | const addToCart = async (req, res) => { 8 | const { productId, quantity } = req.body; 9 | console.log(productId, quantity); 10 | const userId = req.userId; // Assuming you have a middleware to attach the authenticated user 11 | 12 | try { 13 | const user = await Users.findById(userId); 14 | 15 | // Check if the product already exists in the cart 16 | const cartItemIndex = user.cart.findIndex( 17 | (item) => item.product.toString() === productId 18 | ); 19 | if (cartItemIndex > -1) { 20 | // If product exists, update the quantity 21 | user.cart[cartItemIndex].quantity += quantity; 22 | } else { 23 | // Otherwise, add the product to the cart 24 | user.cart.push({ product: productId, quantity }); 25 | } 26 | 27 | await user.save(); 28 | res.status(200).json(user.cart); 29 | } catch (error) { 30 | res.status(500).json({ message: "Server Error", error: error.message }); 31 | } 32 | }; 33 | 34 | // Remove a product from the cart 35 | const removeFromCart = async (req, res) => { 36 | const { id } = req.params; 37 | const userId = req.userId; 38 | 39 | try { 40 | const user = await Users.findById(userId); 41 | 42 | // Remove the product from the cart 43 | // user.cart = user.cart.filter((item) => item.product.toString() !== id); 44 | // console.log(user.cart); 45 | 46 | const productIndex = user.cart.findIndex((item) => item.product.toString() === id); 47 | 48 | if (productIndex === -1) { 49 | return res.status(404).json({ message: 'Product not found in cart' }); 50 | } 51 | 52 | // Decrease the quantity of the product 53 | user.cart[productIndex].quantity -= 1; 54 | 55 | // If the quantity reaches 0, remove the product from the cart 56 | if (user.cart[productIndex].quantity <= 0) { 57 | user.cart.splice(productIndex, 1); 58 | } 59 | await user.save(); 60 | res.status(200).json(user.cart); 61 | } catch (error) { 62 | res.status(500).json({ message: "Server Error", error: error.message }); 63 | } 64 | }; 65 | 66 | // Get the user's cart 67 | const getCart = async (req, res) => { 68 | const userId = req.userId; 69 | 70 | try { 71 | const user = await Users.findById(userId).populate("cart.product"); // Populate product details 72 | res.status(200).json(user.cart); 73 | } catch (error) { 74 | res.status(500).json({ message: "Server Error", error: error.message }); 75 | } 76 | }; 77 | 78 | module.exports = { 79 | addToCart, 80 | removeFromCart, 81 | getCart, 82 | }; 83 | -------------------------------------------------------------------------------- /client/src/components/CartProduct.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { GiShoppingCart } from "react-icons/gi"; 3 | import Link from "next/link"; 4 | import { axiosInstance } from "@/utils/axiosConfig"; 5 | import { toast } from "react-toastify"; 6 | import { useGlobalContext } from "@/context/GlobalProvider"; 7 | import Image from "next/image"; 8 | 9 | 10 | const CartProduct = ({ product, quantity }) => { 11 | const { userData, setUserData,setCartModified,cartModified } = useGlobalContext(); 12 | 13 | console.log("userdata",userData.cart) 14 | 15 | const handleRemoveProduct = async (id) => { 16 | try { 17 | const res = await axiosInstance.delete(`/cart/${id}`); 18 | console.log(res.data) 19 | // setUserData({ ...userData, cart: res.data }); 20 | setCartModified(!cartModified) 21 | toast.success("Product removed to Cart"); 22 | // location.reload(); 23 | } catch (error) { 24 | console.log(error); 25 | toast.error("Error adding to Cart"); 26 | } 27 | }; 28 | return ( 29 |
30 | 31 |
32 |
33 | product image 40 |
41 |
42 |
43 |
44 |
45 |

₹ {product.sellingPrice}

46 |
47 |
48 |

₹ {product.MRPprice}

49 |
50 |
51 |

{product.name}

52 |

★★★★★

53 |

54 | {product.category} 55 |

56 |
57 | 58 |
handleRemoveProduct(product._id)} 60 | className="flex cursor-pointer justify-center items-center gap-1 px-3 py-1 rounded-md border hover:bg-black hover:text-white duration-200 text-sm mt-2" 61 | > 62 | 63 |

Remove

64 |
65 |

Nos : {quantity}

66 |
67 | ); 68 | }; 69 | 70 | export default CartProduct; 71 | -------------------------------------------------------------------------------- /client/src/app/product/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Filter from "@/components/Filter"; 4 | import Loader from "@/components/Loader"; 5 | import Pagination from "@/components/Pagination"; 6 | import ProductCard from "@/components/ProductCard"; 7 | import Search from "@/components/Search"; 8 | import { useGlobalContext } from "@/context/GlobalProvider"; 9 | import { axiosInstance } from "@/utils/axiosConfig"; 10 | import { useEffect, useState } from "react"; 11 | import { toast } from "react-toastify"; 12 | 13 | export default function Page() { 14 | const { 15 | products, 16 | setProducts, 17 | searchTerm, 18 | setSearchTerm, 19 | filters, 20 | setFilters, 21 | pagination, 22 | setPagination, 23 | userData, 24 | setUserData, 25 | } = useGlobalContext(); 26 | // console.log(searchTerm, "in p"); 27 | // console.log(pagination, "in p"); 28 | // console.log(filters, "in p"); 29 | const [PaginatedValue, setPaginatedValue] = useState(1); 30 | 31 | const [loading, setLoading] = useState(true); 32 | 33 | useEffect(() => { 34 | setPagination({ ...pagination, currentPage: 1 }); 35 | }, []); 36 | 37 | useEffect(() => { 38 | if (PaginatedValue == pagination.totalPages) { 39 | } else { 40 | setPagination({ ...pagination, totalPages: PaginatedValue }); 41 | } 42 | }, [PaginatedValue]); 43 | useEffect(() => { 44 | setLoading(true); 45 | const fetchProducts = async () => { 46 | const query = new URLSearchParams({ 47 | search: searchTerm, 48 | category: filters.category, 49 | minPrice: filters.minPrice, 50 | maxPrice: filters.maxPrice, 51 | page: pagination.currentPage, 52 | }).toString(); 53 | try { 54 | const res = await axiosInstance.get(`/product?${query}`); 55 | // console.log(res.data); 56 | setProducts(res.data.products); 57 | setPaginatedValue(res.data.totalPages); 58 | setLoading(false); 59 | } catch (error) { 60 | toast.error("Error fetching Product"); 61 | console.error(error); 62 | } 63 | }; 64 | fetchProducts(); 65 | }, [searchTerm, filters, pagination]); 66 | 67 | return ( 68 |
69 | {loading ? ( 70 | 71 | ) : ( 72 |
73 |
74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 | {products && 84 | products.map((product, index) => ( 85 |
86 | 87 |
88 | ))} 89 |
90 |
91 | 92 |
93 |
94 | )} 95 |
96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /client/src/components/ProductCard.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { GiShoppingCart } from "react-icons/gi"; 3 | import { FaIndianRupeeSign } from "react-icons/fa6"; 4 | import Link from "next/link"; 5 | import { axiosInstance } from "@/utils/axiosConfig"; 6 | import { toast } from "react-toastify"; 7 | import Image from "next/image"; 8 | import { useGlobalContext } from "@/context/GlobalProvider"; 9 | 10 | const ProductCard = ({ product }) => { 11 | 12 | const {userData} = useGlobalContext(); 13 | 14 | const handleCart = async (id) => { 15 | if(userData){ 16 | try { 17 | const res = await axiosInstance.post("/cart", { 18 | productId: id, 19 | quantity: 1, 20 | }); 21 | // console.log(res.data); 22 | toast.success("Product added to Cart"); 23 | } catch (error) { 24 | console.log(error); 25 | toast.error("Error adding to Cart"); 26 | } 27 | } 28 | else{ 29 | toast.error("Login required"); 30 | } 31 | }; 32 | return ( 33 |
34 | 35 |
36 |
37 | {/* */} 43 | 44 | product image 54 |
55 |
56 |
57 |
58 |
59 |

₹ {product.sellingPrice}

60 |
61 |
62 |

₹ {product.MRPprice}

63 |
64 |
65 |

{product.name}

66 |

★★★★★

67 |

68 | {product.category} 69 |

70 |
71 | 72 |
handleCart(product._id)} 74 | className="flex cursor-pointer justify-center items-center gap-1 px-3 py-1 rounded-md border hover:bg-black hover:text-white duration-200 text-sm mt-2" 75 | > 76 | 77 |

Add to Cart

78 |
79 |

For Demo purpose only - GenRio !

80 |
81 | ); 82 | }; 83 | 84 | export default ProductCard; 85 | -------------------------------------------------------------------------------- /client/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | import { FaGithub, FaLinkedin } from 'react-icons/fa' 4 | const Footer = () => { 5 | return ( 6 |
7 |
8 |
9 |

Genkart

10 |

11 | Product of Genrio . Shop the best casuals and t-shirts at Genkart. 12 | Latest fashion trends in comfortable clothing. It's a own 13 | project of{" "} 14 | 15 | Sebe 16 | {" "} 17 | to showcase his skills throughout the world 18 |

19 |
20 |
21 |

Navigation

22 |
23 | Home 24 | Explore 25 | Casuals 26 | T shirts 27 | Trends 28 | Offers 29 |
30 |
31 |
32 |

Codebase

33 |

I provide entire codebase in github . Feel free to use and provide suggestions and issues on github . I provide contact information on the bootom of the page

34 |
35 | Codebase Link 36 |
37 |
38 |
39 |
40 |
41 |
42 |

43 | A product by{" "} 44 | 45 | 50 | {" "} 51 | Sebe{" "} 52 | 53 | {" "} 54 | | GenRio 55 |

56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 |
64 |
65 |
66 |
67 |
68 | ); 69 | }; 70 | 71 | export default Footer; 72 | -------------------------------------------------------------------------------- /client/src/app/categories/page.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | 4 | export default function Page() { 5 | return ( 6 |
7 |
8 |

All Categories

9 |
10 |
11 |
12 | casuals 19 |
20 |
21 |

Casual shirts

22 |

23 | Casual shirts are a versatile wardrobe essential, offering a perfect 24 | balance between comfort and style. Typically made from breathable 25 | fabrics like cotton or linen, they come in a wide range of colors, 26 | patterns, and fits, making them ideal for various occasions. Whether 27 | it's a laid-back outing or a semi-formal event, casual shirts can be 28 | easily dressed up with chinos or dressed down with jeans. Their 29 | relaxed design, featuring button-down fronts and collar variations, 30 | provides a smart yet effortless look, making them a go-to choice for 31 | modern, everyday wear. 32 |

33 |
34 | 38 | Explore 39 | 40 |
{" "} 41 |
42 |
43 |
44 |
45 | tshirt 52 |
53 |
54 |

Tshirts

55 |

56 | Casual shirts are a versatile wardrobe essential, offering a perfect 57 | balance between comfort and style. Typically made from breathable 58 | fabrics like cotton or linen, they come in a wide range of colors, 59 | patterns, and fits, making them ideal for various occasions. Whether 60 | it's a laid-back outing or a semi-formal event, casual shirts can be 61 | easily dressed up with chinos or dressed down with jeans. Their 62 | relaxed design, featuring button-down fronts and collar variations, 63 | provides a smart yet effortless look, making them a go-to choice for 64 | modern, everyday wear. 65 |

66 |
67 | 71 | Explore 72 | 73 |
{" "} 74 |
75 |
76 |
77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /client/src/app/admin/secure/users/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Link from "next/link"; 4 | import { useEffect, useMemo, useState } from "react"; 5 | 6 | import { IoIosArrowBack } from "react-icons/io"; 7 | import { toast } from "react-toastify"; 8 | 9 | import { axiosInstance } from "@/utils/axiosConfig"; 10 | import { useGlobalContext } from "@/context/GlobalProvider"; 11 | 12 | import Pagination from "@/components/Pagination"; 13 | import Loader from "@/components/Loader"; 14 | 15 | export default function Page() { 16 | const [PaginatedValue, SetPaginatedValue] = useState(1); 17 | 18 | const [loading, setLoading] = useState(true); 19 | 20 | const [users, setUsers] = useState([]); 21 | const { pagination, setPagination } = useGlobalContext(); 22 | const [totalUsers, setTotalUsers] = useState(0); 23 | 24 | useEffect(() => { 25 | if (PaginatedValue == pagination.totalPages) { 26 | } else { 27 | setPagination({ ...pagination, totalPages: PaginatedValue }); 28 | } 29 | }, [PaginatedValue]); 30 | 31 | const memoizedData = useMemo(() => { 32 | const fetchUsers = async () => { 33 | const query = new URLSearchParams({ 34 | page: pagination.currentPage, 35 | }).toString(); 36 | try { 37 | const res = await axiosInstance.get(`/user/all?${query}`); 38 | // console.log(res.data); 39 | 40 | setUsers(res.data.users); 41 | SetPaginatedValue(res.data.totalPages); 42 | setLoading(false); 43 | } catch (error) { 44 | toast.error("Error fetching Product"); 45 | console.error(error); 46 | } 47 | }; 48 | return async () => { 49 | setLoading(true); 50 | await fetchUsers(); 51 | }; 52 | }, [pagination]); 53 | useEffect(() => { 54 | memoizedData(); 55 | }, [memoizedData]); 56 | return ( 57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 |

All Users

65 |
66 |
67 | 68 | {loading ? ( 69 | 70 | ) : ( 71 |
72 | {users && ( 73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | {users.map((user) => ( 86 | 87 | 94 | 95 | 98 | 99 | 100 | ))} 101 | 102 |
ImageNameCart countEmail
88 | {user.name} 93 | {user.name} 96 | {user.cart.length} 97 | {user.email}
103 |
104 | )} 105 |
106 | )} 107 |
108 | 109 |
110 |
111 | ); 112 | } 113 | -------------------------------------------------------------------------------- /client/src/app/profile/edit/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Loader from "@/components/Loader"; 3 | import { useGlobalContext } from "@/context/GlobalProvider"; 4 | import { axiosInstance } from "@/utils/axiosConfig"; 5 | import Image from "next/image"; 6 | import { useRouter } from "next/navigation"; 7 | import { useState } from "react"; 8 | import { toast } from "react-toastify"; 9 | 10 | export default function Page() { 11 | const router = useRouter(); 12 | const [name, setName] = useState(""); 13 | const [image, setImage] = useState(null); 14 | const { userData, setUserData } = useGlobalContext(); 15 | const [imageSrc, setImageSrc] = useState(null); 16 | 17 | 18 | const handleFileChange = (e) => { 19 | const file = e.target.files[0]; // Get the first file 20 | setImage(file) 21 | if (file) { 22 | // console.log(file, "infile"); 23 | 24 | const imageUrl = URL.createObjectURL(file); // Create a temporary URL for the image 25 | setImageSrc(imageUrl); // Set the image source 26 | } 27 | }; 28 | 29 | console.log(userData); 30 | 31 | const handleSubmit = async (e) => { 32 | 33 | const formData = new FormData(); 34 | 35 | formData.append("name", name); 36 | formData.append("image", image); 37 | e.preventDefault(); 38 | try { 39 | const res = await axiosInstance.put("/user/me",formData,{ 40 | headers: { 41 | 'Content-Type': 'multipart/form-data', 42 | }, 43 | }); 44 | // console.log(res.data); 45 | toast.success("Updated Successfully"); 46 | router.push("/profile"); 47 | } catch (error) { 48 | console.error(error); 49 | toast.error("Unable to update Data"); 50 | } 51 | }; 52 | return ( 53 |
54 |
55 |

Edit Profile

56 |
57 | {userData ? ( 58 |
59 | {/*
60 |

Image

61 |
*/} 62 |
63 | {/* */} 64 | 69 |
70 |
71 |
72 |
73 | 76 | 77 | 86 |
87 |
88 | 91 | 98 |
99 |
100 | 103 | setName(e.target.value)} 108 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 109 | /> 110 |
111 | 112 |
113 | 119 |
120 |
121 |
122 |
123 | ) : } 124 |
125 | ); 126 | } 127 | -------------------------------------------------------------------------------- /client/src/app/(auth)/forgot-password/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { axiosInstance } from "@/utils/axiosConfig"; 3 | import Image from "next/image"; 4 | import Link from "next/link"; 5 | import { useRouter } from "next/navigation"; 6 | import { useState } from "react"; 7 | import { FaOpencart, FaRegEye, FaRegEyeSlash } from "react-icons/fa"; 8 | import { IoIosArrowBack } from "react-icons/io"; 9 | import { toast } from "react-toastify"; 10 | 11 | export default function Page() { 12 | const router = useRouter(); 13 | const [email, setEmail] = useState(""); 14 | const [show, setShow] = useState(false); 15 | 16 | const [mailStatus,setMailStatus] = useState(false); 17 | const handleShow = () => { 18 | show === true ? setShow(false) : setShow(true); 19 | }; 20 | 21 | const handleForgot = async (e) => { 22 | e.preventDefault(); 23 | 24 | try { 25 | const res = await axiosInstance.post("/auth/forgot-password", { 26 | email, 27 | }); 28 | // console.log(res.data.message); 29 | toast.success("Mail Sent Successfully"); 30 | setMailStatus(true); 31 | } catch (error) { 32 | console.error(error); 33 | toast.error('Error sending Mail') 34 | toast.error(error.response.data.message); 35 | 36 | } 37 | }; 38 | return ( 39 |
40 |
41 |
router.push("/")} 43 | className="flex gap-1 items-center hover:font-semibold cursor-pointer hover:underline underline-offset-4" 44 | > 45 | 46 |

Back to Home

47 |
48 |
49 |
50 |
51 | 58 |
59 |
60 |
61 |
62 |
63 |

64 | Forgot Password 65 |

66 |
67 |

68 | Enter the email address which is given to the account , We 69 | will send a mail to reset your password !{" "} 70 |

71 |
72 | { 73 | mailStatus ? ( 74 |
75 |

Mail Sent Successfully !

76 |

Check your mail

77 |
78 | ) : ( 79 |
84 |
85 | 88 | setEmail(e.target.value)} 93 | required 94 | className="px-5 py-2 border border-black/20 rounded-xl focus:outline-none " 95 | /> 96 |
97 | 98 |
99 | 104 |
105 |
106 |

107 | Back to{" "} 108 | 109 | Login ? 110 | 111 |

112 |
113 |
114 | ) 115 | } 116 |
117 |
118 |
119 |
120 | 121 |

122 | GK 123 |

124 |
125 |
126 |
127 |
128 |
129 | ); 130 | } 131 | -------------------------------------------------------------------------------- /client/src/app/admin/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { setCookie } from "@/actions/setCookie"; 3 | import { axiosInstance } from "@/utils/axiosConfig"; 4 | 5 | import axios from "axios"; 6 | import Image from "next/image"; 7 | import Link from "next/link"; 8 | import { useRouter } from "next/navigation"; 9 | import { useEffect, useState } from "react"; 10 | import { FaOpencart, FaRegEye, FaRegEyeSlash } from "react-icons/fa"; 11 | import { toast } from "react-toastify"; 12 | 13 | export default function Page() { 14 | const router = useRouter(); 15 | const [show, setShow] = useState(false); 16 | const handleShow = () => { 17 | show === true ? setShow(false) : setShow(true); 18 | }; 19 | 20 | const [email, setEmail] = useState(""); 21 | const [password, setPassword] = useState(""); 22 | 23 | const [pop, setPop] = useState(false); 24 | 25 | const handleSubmit = async (e) => { 26 | e.preventDefault(); 27 | 28 | try { 29 | const res = await axiosInstance.post("/auth/admin/login", { 30 | email, 31 | password, 32 | }); 33 | // console.log(res.data); 34 | await setCookie("adminToken", res.data.adminToken); 35 | router.push("/admin/secure/home"); 36 | toast.success("Logged In Successfully"); 37 | } catch (error) { 38 | console.error(error); 39 | // console.log(error.response.data.message); 40 | toast.error("Issue on Login"); 41 | } 42 | }; 43 | 44 | return ( 45 |
46 |
47 |
48 |
49 |
50 |
51 |

52 | Welcome Sebe 53 |

54 |

Admin

55 |
60 |
61 | 64 | setEmail(e.target.value)} 69 | className="px-5 py-2 border border-black/20 rounded-xl focus:outline-none " 70 | /> 71 |
72 |
73 | 76 |
77 | setPassword(e.target.value)} 82 | className="px-3 py-2 w-full focus:outline-none " 83 | /> 84 |
85 | {show ? ( 86 | 90 | ) : ( 91 | 95 | )} 96 |
97 |
98 |
99 | 100 |
101 | 106 |
107 |
108 |

109 | Not a Admin ?{" "} 110 | 111 | Go to Home 112 | 113 |

114 |
115 |
116 |
117 |
118 |
119 |
120 | 121 |

122 | GK 123 |

124 |
125 |
126 |
127 |
128 | 135 |
136 |
137 |
138 | ); 139 | } 140 | -------------------------------------------------------------------------------- /client/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useEffect, useState } from "react"; 3 | import { FaFilter } from "react-icons/fa"; 4 | import { Select, Option, Spinner } from "@material-tailwind/react"; 5 | 6 | import { 7 | Button, 8 | Dialog, 9 | DialogHeader, 10 | DialogBody, 11 | DialogFooter, 12 | } from "@material-tailwind/react"; 13 | import { useGlobalContext } from "@/context/GlobalProvider"; 14 | import { axiosInstance } from "@/utils/axiosConfig"; 15 | const Filter = () => { 16 | const [openFilter, setOpenFilter] = useState(false); 17 | const [category, setCategory] = useState(""); 18 | const [minPrice, setMinPrice] = useState(0); 19 | const [maxPrice, setMaxPrice] = useState(1000000); 20 | const handleOpenFilter = (data) => { 21 | setOpenFilter(!openFilter); 22 | }; 23 | 24 | const { filters, setFilters, setProducts } = useGlobalContext(); 25 | const categories = [ 26 | { 27 | label: "Casuals", 28 | value: "casuals", 29 | }, 30 | { 31 | label: "T shirts", 32 | value: "tshirts", 33 | }, 34 | { 35 | label: "None", 36 | value: "", 37 | }, 38 | ]; 39 | const handleFilter = () => { 40 | setFilters({ 41 | ...filters, 42 | category, 43 | minPrice, 44 | maxPrice, 45 | }); 46 | setOpenFilter(false); 47 | // axiosInstance 48 | // .get(`/products?category=${filters.category}&minPrice=${filters.minPrice}&maxPrice=${filters.maxPrice}`) 49 | // .then((response) => { 50 | // setProducts(response.data); 51 | // }) 52 | // .catch((error) => { 53 | // console.error("Error fetching products: ", error); 54 | // }); 55 | }; 56 | 57 | useEffect(() => { 58 | setCategory(filters.category); 59 | }, [filters]); 60 | 61 | return ( 62 |
63 | 68 | 69 |
70 |

Filters

71 |
75 |

Close

76 |
77 |
78 |
79 | 80 |
81 |
82 | 85 | 102 |
103 |
104 | 107 | setMinPrice(e.target.value)} 113 | className="capitalize w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 114 | /> 115 |
116 |
117 | 120 | setMaxPrice(e.target.value)} 126 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 127 | /> 128 |
129 |
130 |
131 | 132 |
133 |
137 | 138 |

Apply Filters

139 |
140 |
141 |
142 |
143 |
147 | 148 |

Apply Filters

149 |
150 |
151 | ); 152 | }; 153 | 154 | export default Filter; 155 | -------------------------------------------------------------------------------- /server/controllers/authController.js: -------------------------------------------------------------------------------- 1 | const Users = require("../models/userSchema"); 2 | const Admin = require("../models/adminSchema"); 3 | const crypto = require("crypto"); 4 | const bcrypt = require("bcrypt"); 5 | const sendEmail = require("../utils/sendEmail"); 6 | const jwt = require("jsonwebtoken"); 7 | const cookie = require("cookie"); 8 | require("dotenv").config(); 9 | 10 | const generateToken = (userId, secret, expiresIn) => { 11 | return jwt.sign({ userId }, secret, { expiresIn }); 12 | }; 13 | 14 | const login = async (req, res) => { 15 | const { email, password } = req.body; 16 | 17 | try { 18 | const user = await Users.findOne({ email }); 19 | if (!user) { 20 | return res.status(400).json({ message: "Invalid credentials" }); 21 | } 22 | 23 | const isMatch = await user.matchPassword(password); 24 | if (!isMatch) { 25 | return res.status(400).json({ message: "Invalid credentials" }); 26 | } 27 | 28 | const token = generateToken( 29 | user.id, 30 | process.env.JWT_USER_SECRET, 31 | process.env.JWT_EXPIRES_IN 32 | ); 33 | 34 | res.cookie("userToken", token, { 35 | // domain:process.env.DOMAIN_NAME, 36 | httpOnly: true, 37 | path: "/", 38 | secure: process.env.NODE_ENV === "production", 39 | sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax", 40 | maxAge: 24 * 60 * 60 * 1000, 41 | }); 42 | // res.setHeader('Authorization', `Bearer ${token}`); 43 | res.status(200).json({ message: "Login successful",token }); 44 | } catch (error) { 45 | res.status(500).json({ message: "Login Error", error }); 46 | } 47 | }; 48 | 49 | const logout = (req, res) => { 50 | res.clearCookie("userToken"); 51 | res.json({ message: "Logout successful" }); 52 | }; 53 | 54 | const signup = async (req, res) => { 55 | const { name, email, password } = req.body; 56 | 57 | try { 58 | const hashedPassword = await bcrypt.hash(password, 10); 59 | const user = new Users({ name, email, password: hashedPassword }); 60 | await user.save(); 61 | 62 | res.status(201).json({ message: "User created", user }); 63 | } catch (error) { 64 | res.status(500).json({ error: "Signup failed" }); 65 | } 66 | }; 67 | 68 | const forgotPassword = async (req, res) => { 69 | const { email } = req.body; 70 | console.log(email) 71 | try { 72 | const user = await Users.findOne({ email }); 73 | if (!user) { 74 | return res.status(404).json({ message: "User not found" }); 75 | } 76 | 77 | const resetToken = crypto.randomBytes(32).toString("hex"); 78 | user.resetPasswordToken = resetToken; 79 | user.resetPasswordExpires = Date.now() + 3600000; // 1 hour 80 | await user.save(); 81 | 82 | await sendEmail( 83 | user.email, 84 | "Password Reset", 85 | `Reset your password here: ${process.env.CLIENT_URL}/reset-password?token=${resetToken}` 86 | ); 87 | // await transporter.sendMail(mailOptions); 88 | res.status(200).json({ message: "Password reset email sent" }); 89 | } catch (error) { 90 | res.status(500).json({ message: "Failed to send reset email" }); 91 | } 92 | }; 93 | 94 | const resetPassword = async (req, res) => { 95 | const { token, newPassword } = req.body; 96 | 97 | console.log(token, newPassword) 98 | 99 | try { 100 | const user = await Users.findOne({ 101 | resetPasswordToken: token, 102 | resetPasswordExpires: { $gt: Date.now() }, 103 | }); 104 | 105 | if (!user) 106 | return res.status(400).json({ error: "Invalid or expired token" }); 107 | 108 | user.password = await bcrypt.hash(newPassword, 10); 109 | user.resetPasswordToken = undefined; 110 | user.resetPasswordExpires = undefined; 111 | await user.save(); 112 | 113 | res.status(200).json({ message: "Password reset successful" }); 114 | } catch (error) { 115 | res.status(500).json({ error: "Failed to reset password" }); 116 | } 117 | }; 118 | 119 | const adminLogin = async (req, res) => { 120 | const { email, password } = req.body; 121 | console.log(email, password); 122 | const admin = await Admin.findOne({ email: email }); 123 | console.log("adminone", admin); 124 | if (!admin) { 125 | return res.status(401).json({ message: "Invalid credentials" }); 126 | } 127 | 128 | console.log("admin", admin.email); 129 | 130 | const isMatch = await bcrypt.compare(password, admin.password); 131 | console.log("ismatch", isMatch); 132 | if (!isMatch) { 133 | return res.status(400).json({ message: "Invalid credentials" }); 134 | } 135 | 136 | const adminToken = generateToken( 137 | admin.id, 138 | process.env.JWT_SECRET, 139 | process.env.JWT_EXPIRES_IN 140 | ); 141 | 142 | res.cookie("adminUserToken", adminToken, { 143 | httpOnly: true, 144 | path: "/", 145 | secure: process.env.NODE_ENV === "production", 146 | sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax", 147 | maxAge: 24 * 60 * 60 * 1000, // 1 day 148 | }); 149 | 150 | res.status(200).json({ message: "Login successful",adminToken }); 151 | }; 152 | 153 | const adminLogout = (req, res) => { 154 | res.clearCookie("adminUserToken"); 155 | res.json({ message: "Logout successful" }); 156 | }; 157 | 158 | const adminSignup = async (req, res) => { 159 | const { email, password } = req.body; 160 | console.log(email, password); 161 | try { 162 | const hashedPassword = await bcrypt.hash(password, 10); 163 | const admin = new Admin({ email, password: hashedPassword }); 164 | console.log("admin created", admin); 165 | await admin.save(); 166 | 167 | res.status(201).json({ message: "admin created", admin }); 168 | } catch (error) { 169 | res.status(500).json({ message: error.message }); 170 | } 171 | }; 172 | 173 | module.exports = { 174 | login, 175 | logout, 176 | signup, 177 | forgotPassword, 178 | resetPassword, 179 | adminLogin, 180 | adminSignup, 181 | adminLogout, 182 | }; 183 | -------------------------------------------------------------------------------- /client/src/app/(auth)/login/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { setCookie } from "@/actions/setCookie"; 3 | import { useGlobalContext } from "@/context/GlobalProvider"; 4 | import { axiosInstance } from "@/utils/axiosConfig"; 5 | import Image from "next/image"; 6 | import Link from "next/link"; 7 | import { useRouter } from "next/navigation"; 8 | import { useState } from "react"; 9 | import { FaOpencart, FaRegEye, FaRegEyeSlash } from "react-icons/fa"; 10 | import { IoIosArrowBack } from "react-icons/io"; 11 | import { toast } from "react-toastify"; 12 | 13 | export default function Page() { 14 | const router = useRouter(); 15 | const [show, setShow] = useState(false); 16 | const [email, setEmail] = useState(""); 17 | const [password, setPassword] = useState(""); 18 | const handleShow = () => { 19 | show === true ? setShow(false) : setShow(true); 20 | }; 21 | const { setUserAuth } = useGlobalContext(); 22 | const handleLogin = async (e) => { 23 | 24 | e.preventDefault(); 25 | try { 26 | const res = await axiosInstance.post("/auth/login", { email, password }); 27 | // console.log(res.data); 28 | await setCookie("token", res.data.token); 29 | setUserAuth(true); 30 | router.push("/"); 31 | // location.reload(); 32 | 33 | 34 | toast.success("Logged In Successfully"); 35 | } catch (error) { 36 | console.error(error); 37 | toast.error(error.response.data.message); 38 | } 39 | }; 40 | 41 | return ( 42 |
43 |
44 |
router.push("/")} 46 | className="flex gap-1 items-center hover:font-semibold cursor-pointer hover:underline underline-offset-4" 47 | > 48 | 49 |

Back to Home

50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |

Welcome

58 |
63 |
64 | 67 | setEmail(e.target.value)} 72 | value={email} 73 | className="px-5 py-2 border border-black/20 rounded-xl focus:outline-none " 74 | /> 75 |
76 |
77 | 80 |
81 | setPassword(e.target.value)} 87 | className="px-3 py-2 w-full focus:outline-none " 88 | /> 89 |
90 | {show ? ( 91 | 95 | ) : ( 96 | 100 | )} 101 |
102 |
103 |
104 |
105 | 109 | Forgot your password ? 110 | 111 |
112 |
113 | 118 |
119 |
120 |

121 | Not a User ?{" "} 122 | 123 | SignUp 124 | 125 |

126 |
127 |
128 |
129 |
130 |
131 |
132 | 133 |

134 | GK 135 |

136 |
137 |
138 |
139 |
140 | 147 |
148 |
149 |
150 | ); 151 | } 152 | -------------------------------------------------------------------------------- /client/src/app/(auth)/reset-password/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { axiosInstance } from "@/utils/axiosConfig"; 3 | import Image from "next/image"; 4 | import Link from "next/link"; 5 | import { useRouter, useSearchParams } from "next/navigation"; 6 | import { Suspense, useEffect, useState } from "react"; 7 | import { FaOpencart, FaRegEye, FaRegEyeSlash } from "react-icons/fa"; 8 | import { IoIosArrowBack } from "react-icons/io"; 9 | import { toast } from "react-toastify"; 10 | 11 | export default function Page() { 12 | 13 | return ( 14 | Loading...

}> 15 | 16 |
17 | ); 18 | } 19 | 20 | const ResetPassword = () => { 21 | const router = useRouter(); 22 | const searchParams = useSearchParams(); 23 | const [token, setToken] = useState(""); 24 | 25 | const [show, setShow] = useState(false); 26 | const handleShow = () => { 27 | show === true ? setShow(false) : setShow(true); 28 | }; 29 | const [password, setPassword] = useState(""); 30 | const [confirmPassword, setConfirmPassword] = useState(""); 31 | 32 | useEffect(() => { 33 | setToken(searchParams.get("token")); 34 | }, []); 35 | console.log(password); 36 | const handleReset = async (e) => { 37 | e.preventDefault(); 38 | if (password === confirmPassword) { 39 | try { 40 | const res = await axiosInstance.put("/auth/reset-password", { 41 | newPassword: password, 42 | token, 43 | }); 44 | // console.log(res.data); 45 | toast.success("Password reset successfully"); 46 | router.push("/login"); 47 | } catch (error) { 48 | console.error(error); 49 | toast.error("Error on password reset"); 50 | } 51 | } else { 52 | toast.error("Password must be same"); 53 | } 54 | }; 55 | return ( 56 |
57 |
58 |
router.push("/")} 60 | className="flex gap-1 items-center hover:font-semibold cursor-pointer hover:underline underline-offset-4" 61 | > 62 | 63 |

Back to Home

64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |

72 | Reset Password 73 |

74 |
75 |

76 | Enter your password to gain access to your account , Please 77 | remember your password from now on ! 78 |

79 |
80 |
85 |
86 | 89 |
90 | setPassword(e.target.value)} 96 | className="px-3 py-2 w-full focus:outline-none " 97 | /> 98 |
99 | {show ? ( 100 | 104 | ) : ( 105 | 109 | )} 110 |
111 |
112 |
113 |
114 | 117 |
118 | setConfirmPassword(e.target.value)} 124 | className="px-3 py-2 w-full focus:outline-none " 125 | /> 126 |
127 | {show ? ( 128 | 132 | ) : ( 133 | 137 | )} 138 |
139 |
140 |
141 | 142 |
143 | 148 |
149 |
150 |

151 | Back to{" "} 152 | 153 | Login ? 154 | 155 |

156 |
157 |
158 |
159 |
160 |
161 |
162 | 163 |

164 | GK 165 |

166 |
167 |
168 |
169 |
170 | 177 |
178 |
179 |
180 | ); 181 | }; 182 | -------------------------------------------------------------------------------- /client/src/app/(auth)/signup/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { axiosInstance } from "@/utils/axiosConfig"; 3 | import axios from "axios"; 4 | import Image from "next/image"; 5 | import Link from "next/link"; 6 | import { useRouter } from "next/navigation"; 7 | import { useState } from "react"; 8 | import { FaOpencart, FaRegEye, FaRegEyeSlash } from "react-icons/fa"; 9 | import { IoIosArrowBack } from "react-icons/io"; 10 | import { toast } from "react-toastify"; 11 | 12 | export default function Page() { 13 | const router = useRouter(); 14 | const [show, setShow] = useState(false); 15 | 16 | const [name, setName] = useState(""); 17 | const [email, setEmail] = useState(""); 18 | const [password, setPassword] = useState(""); 19 | const [confirmPassword, setConfirmPassword] = useState(""); 20 | 21 | const handleShow = () => { 22 | show === true ? setShow(false) : setShow(true); 23 | }; 24 | 25 | const handleCheck = (e) => { 26 | e.preventDefault(); 27 | if (password === confirmPassword) { 28 | handleSubmit(); 29 | } else { 30 | toast.warn("Password must be same", { 31 | autoClose: 3000, // Closes after 3 seconds 32 | }); 33 | } 34 | }; 35 | 36 | const handleSubmit = async () => { 37 | 38 | try { 39 | 40 | 41 | const res = await axiosInstance.post("/auth/signup", { 42 | name, 43 | email, 44 | password, 45 | }); 46 | // console.log(res); 47 | toast.success("Account created", { 48 | autoClose: 3000, // Closes after 3 seconds 49 | }); 50 | router.push('/login') 51 | } catch (error) { 52 | console.error(error); 53 | } 54 | }; 55 | 56 | return ( 57 |
58 |
59 |
router.push("/")} 61 | className="flex gap-1 items-center hover:font-semibold cursor-pointer hover:underline underline-offset-4" 62 | > 63 | 64 |

Back to Home

65 |
66 |
67 |
68 |
69 | 76 |
77 |
78 |
79 |
80 |
81 |

Welcome

82 |
83 |
84 | 87 | setName(e.target.value)} 94 | className="px-5 py-2 border border-black/20 rounded-xl focus:outline-none " 95 | /> 96 |
97 |
98 | 101 | setEmail(e.target.value)} 108 | className="px-5 py-2 border border-black/20 rounded-xl focus:outline-none " 109 | /> 110 |
111 |
112 | 115 |
116 | setPassword(e.target.value)} 123 | className="px-3 py-2 w-full focus:outline-none " 124 | /> 125 |
126 | {show ? ( 127 | 131 | ) : ( 132 | 136 | )} 137 |
138 |
139 |
140 |
141 | 144 |
145 | setConfirmPassword(e.target.value)} 151 | className="px-3 py-2 w-full focus:outline-none " 152 | /> 153 |
154 | {show ? ( 155 | 159 | ) : ( 160 | 164 | )} 165 |
166 |
167 |
168 |
169 | 175 |
176 |
177 |

178 | Already a User ?{" "} 179 | 180 | Login 181 | 182 |

183 |
184 |
185 |
186 |
187 |
188 |
189 | 190 |

191 | GK 192 |

193 |
194 |
195 |
196 |
197 |
198 | ); 199 | } 200 | -------------------------------------------------------------------------------- /client/src/components/Admin/AdminNavbar.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import { GoHomeFill } from "react-icons/go"; 4 | import { SiGoogleanalytics } from "react-icons/si"; 5 | import { AiOutlineProduct } from "react-icons/ai"; 6 | import { BiSolidCategory } from "react-icons/bi"; 7 | import { FaBox } from "react-icons/fa6"; 8 | import { FaOpencart, FaUser } from "react-icons/fa"; 9 | import { useParams, usePathname } from "next/navigation"; 10 | import Link from "next/link"; 11 | import { HiOutlineMenuAlt4 } from "react-icons/hi"; 12 | import { Drawer } from "@mui/material"; 13 | import { IoMdClose } from "react-icons/io"; 14 | import { IoLogOutOutline } from "react-icons/io5"; 15 | import { Spinner } from "@material-tailwind/react"; 16 | import { toast } from "react-toastify"; 17 | import { useRouter } from "next/navigation"; 18 | import { axiosInstance } from "@/utils/axiosConfig"; 19 | import { removeCookie } from "@/actions/removeCookie"; 20 | 21 | const AdminNavbar = () => { 22 | const path = usePathname(); 23 | const [open, setOpen] = useState(false); 24 | 25 | const router = useRouter(); 26 | const toggleDrawer = (newOpen) => () => { 27 | setOpen(newOpen); 28 | }; 29 | const routes = [ 30 | { 31 | path: "/admin/secure/home", 32 | icon: , 33 | label: "Home", 34 | }, 35 | { 36 | path: "/admin/secure/dashboard", 37 | icon: , 38 | label: "Analytics", 39 | }, 40 | { 41 | path: "/admin/secure/products", 42 | icon: , 43 | label: "Products", 44 | }, 45 | { 46 | path: "/admin/secure/users", 47 | icon: , 48 | label: "Users", 49 | }, 50 | { 51 | path: "/admin/secure/categories", 52 | icon: , 53 | label: "Categories", 54 | }, 55 | // { 56 | // path: "/admin/secure/logout", 57 | // icon: , 58 | // label: "Logout", 59 | // }, 60 | ]; 61 | 62 | // console.log(path); 63 | 64 | const handleLogout = async () => { 65 | try { 66 | const res = await axiosInstance.post("/auth/admin/logout"); 67 | console.log(res.data); 68 | removeCookie("adminToken"); 69 | toast.success("Logout successfully"); 70 | router.push("/admin"); 71 | } catch (error) { 72 | console.error(error); 73 | toast.error("Unable to Logout"); 74 | } 75 | }; 76 | 77 | return ( 78 |
79 |
80 |
81 |
82 | 100 |
101 |
102 | 103 |
104 |
105 |
106 | 107 |
108 |
109 |

Menu

110 |
111 | {/*
112 | 119 | 120 |
*/} 121 |
122 | {routes.map((route, index) => { 123 | return ( 124 | 133 |
{route.icon}
134 |

{route.label}

135 | 136 | ); 137 | })} 138 |
142 | 143 |

Logout

144 |
145 |
146 |
147 |
148 |
149 |
150 | 151 |

152 | G K 153 |

154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | {routes.map((route, index) => { 166 | return ( 167 | 176 |
{route.icon}
177 |

{route.label}

178 | 179 | ); 180 | })} 181 |
185 | 186 |

Logout

187 |
188 |
189 |
190 | 191 |

192 | GK 193 |

194 |
195 |
196 |
197 |
198 |
199 |
200 | ); 201 | }; 202 | 203 | export default AdminNavbar; 204 | -------------------------------------------------------------------------------- /client/src/app/product/[id]/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { axiosInstance } from "@/utils/axiosConfig"; 4 | import { useParams, useRouter } from "next/navigation"; 5 | import { useEffect, useState } from "react"; 6 | import { toast } from "react-toastify"; 7 | import { SiHomeassistantcommunitystore } from "react-icons/si"; 8 | import { GiShoppingCart } from "react-icons/gi"; 9 | import { BiSolidOffer } from "react-icons/bi"; 10 | import { IoIosArrowBack } from "react-icons/io"; 11 | import Link from "next/link"; 12 | import Loader from "@/components/Loader"; 13 | import Image from "next/image"; 14 | import SmallFooter from "@/components/SmallFooter"; 15 | import { useGlobalContext } from "@/context/GlobalProvider"; 16 | 17 | export default function Page() { 18 | const { id } = useParams(); 19 | const [product, setProduct] = useState(null); 20 | const router = useRouter(); 21 | const [currentImage, setCurrentImage] = useState(""); 22 | const {userData} = useGlobalContext(); 23 | 24 | const handleCart = async (pid) => { 25 | if(userData){ 26 | try { 27 | const res = await axiosInstance.post("/cart", { 28 | productId: pid, 29 | quantity: 1, 30 | }); 31 | // console.log(res.data); 32 | toast.success("Product added to Cart"); 33 | } catch (error) { 34 | console.log(error); 35 | toast.error("Error adding to Cart"); 36 | } 37 | } 38 | else{ 39 | toast.error("Login required"); 40 | } 41 | }; 42 | 43 | useEffect(() => { 44 | const fetchProduct = async () => { 45 | try { 46 | const res = await axiosInstance.get(`/product/${id}`); 47 | // console.log(res.data); 48 | setProduct(res.data); 49 | setCurrentImage(res.data.image); 50 | } catch (error) { 51 | console.error(error); 52 | toast.error("Error fetching product"); 53 | } 54 | }; 55 | fetchProduct(); 56 | }, []); 57 | 58 | return ( 59 |
60 |
61 |
router.back()} 63 | className="flex gap-1 items-center hover:font-semibold cursor-pointer hover:underline underline-offset-4" 64 | > 65 | 66 |

Back

67 |
68 |
69 | {product ? ( 70 |
71 |
72 |
73 |
74 | {product?.additionalImages.map((image, index) => ( 75 |
setCurrentImage(image)} 77 | key={index} 78 | className="mx-auto max-w-[100px] max-h-[100px] relative" 79 | > 80 | product image 90 |
91 | ))} 92 | {/* setCurrentImage(product.image)} 95 | alt="" 96 | className="rounded-md mx-auto object-cover max-h-[100px] max-w-[100px] aspect-square" 97 | /> */} 98 | setCurrentImage(product.image)} 103 | alt="product image" 104 | priority={false} 105 | placeholder="blur" 106 | blurDataURL={product.image} 107 | className="rounded-md mx-auto object-cover max-h-[100px] max-w-[100px] aspect-square" 108 | /> 109 |
110 |
111 | {/* */} 116 | 117 | product image 127 |
128 |
129 |
130 |
131 | 132 |

{product.vendor}

133 |
134 |
135 |

136 | {product.name} 137 |

138 |

139 | {product.category} 140 |

141 |

★★★★★

142 |
143 |
144 |

145 | ₹ {product.sellingPrice} 146 |

147 |
148 |

₹ {product.MRPprice}

149 |
150 |
151 |
152 |
153 |

154 | Available Stock : {product.quantity} nos 155 |

156 |
157 |
158 |

159 | Product Code : {product._id} 160 |

161 |
162 |
163 |
164 |

171 | Trending Product 172 |

173 |
180 | 181 |

Offers available

182 |
183 |
184 |
handleCart(product._id)} className="flex cursor-pointer justify-center items-center gap-1 px-3 py-2 rounded-md border bg-black text-white duration-200 text-sm mt-2"> 185 | 186 |

Add to Cart

187 |
188 |
189 |
190 |
191 |
192 |

Description

193 |

{product.description}

194 |
195 |
196 |
197 | ) : ( 198 | 199 | )} 200 | 201 |
202 | ); 203 | } 204 | -------------------------------------------------------------------------------- /server/controllers/productController.js: -------------------------------------------------------------------------------- 1 | const Products = require("../models/productSchema"); 2 | const cloudinary = require("../config/cloudinary"); 3 | require("dotenv").config(); 4 | const createProduct = async (req, res) => { 5 | const { 6 | name, 7 | vendor, 8 | quantity, 9 | description, 10 | MRPprice, 11 | sellingPrice, 12 | category, 13 | trend, 14 | offer, 15 | } = req.body; 16 | 17 | console.log(req.files); 18 | try { 19 | const imageResult = await cloudinary.uploader.upload( 20 | req.files.image[0].path, 21 | { 22 | folder: process.env.CLOUDINARY_FOLDER_NAME, 23 | } 24 | ); 25 | 26 | // Upload additional images if any 27 | let additionalImages = []; 28 | if (req.files.additionalImages) { 29 | const additionalImagesUploadPromises = req.files.additionalImages.map( 30 | (img) => 31 | cloudinary.uploader.upload(img.path, { 32 | folder: process.env.CLOUDINARY_FOLDER_NAME, 33 | }) 34 | ); 35 | const additionalImagesResults = await Promise.all( 36 | additionalImagesUploadPromises 37 | ); 38 | additionalImages = additionalImagesResults.map( 39 | (result) => result.secure_url 40 | ); 41 | } 42 | 43 | const newProduct = new Products({ 44 | name, 45 | description, 46 | quantity, 47 | vendor, 48 | MRPprice, 49 | sellingPrice, 50 | category, 51 | trend, 52 | offer, 53 | image: imageResult.secure_url, 54 | additionalImages: additionalImages, 55 | }); 56 | console.log(newProduct); 57 | const savedProduct = await newProduct.save(); 58 | res.status(201).json(savedProduct); 59 | } catch (error) { 60 | res.status(500).json({ message: "Error creating product", error }); 61 | } 62 | }; 63 | 64 | const updateProduct = async (req, res) => { 65 | try { 66 | const { 67 | name, 68 | vendor, 69 | quantity, 70 | description, 71 | MRPprice, 72 | sellingPrice, 73 | category, 74 | trend, 75 | offer, 76 | } = req.body; 77 | 78 | const product = await Products.findById(req.params.id); 79 | if (!product) { 80 | return res.status(404).json({ message: "Product not found" }); 81 | } 82 | console.log(product.image); 83 | 84 | // let imageUrl = product.imageUrl; 85 | // if (req.file) { 86 | // const publicId = product.imageUrl.split("/").pop().split(".")[0]; 87 | // await cloudinary.uploader.destroy(publicId); 88 | 89 | // const result = await cloudinary.uploader.upload(req.file.path, { 90 | // folder: process.env.CLOUDINARY_FOLDER_NAME, 91 | // }); 92 | // imageUrl = result.secure_url; 93 | // } 94 | 95 | console.log(req.files.image); 96 | if (req.files.image) { 97 | // Delete the old image from Cloudinary (optional, based on your use case) 98 | if (product.image) { 99 | const publicId = product.image.split("/").pop().split(".")[0]; 100 | await cloudinary.uploader.destroy(publicId); 101 | } 102 | const imageResult = await cloudinary.uploader.upload( 103 | req.files.image[0].path 104 | ); 105 | product.image = imageResult.secure_url; 106 | } 107 | 108 | if (req.files.additionalImages) { 109 | // Delete old additional images from Cloudinary (optional) 110 | if (product.additionalImages.length > 0) { 111 | for (let image of product.additionalImages) { 112 | console.log(image); 113 | const publicId = image.split("/").pop().split(".")[0]; 114 | await cloudinary.uploader.destroy(publicId); 115 | } 116 | } 117 | const additionalImagesUploadPromises = req.files.additionalImages.map( 118 | (img) => cloudinary.uploader.upload(img.path) 119 | ); 120 | const additionalImagesResults = await Promise.all( 121 | additionalImagesUploadPromises 122 | ); 123 | product.additionalImages = additionalImagesResults.map( 124 | (result) => result.secure_url 125 | ); 126 | } 127 | 128 | product.name = name || product.name; 129 | product.description = description || product.description; 130 | product.MRPprice = MRPprice || product.MRPprice; 131 | product.sellingPrice = sellingPrice || product.sellingPrice; 132 | product.category = category || product.category; 133 | product.trend = trend || product.trend; 134 | product.offer = offer || product.offer; 135 | product.vendor = vendor || product.vendor; 136 | product.quantity = quantity || product.quantity; 137 | 138 | const updatedProduct = await product.save(); 139 | res.json(updatedProduct); 140 | } catch (error) { 141 | res.status(500).json({ message: "Server Error", error: error.message }); 142 | } 143 | }; 144 | 145 | const deleteProduct = async (req, res) => { 146 | try { 147 | const product = await Products.findByIdAndDelete({ _id: req.params.id }); 148 | console.log(product); 149 | if (!product) { 150 | return res.status(404).json({ message: "Product not found" }); 151 | } 152 | res.json({ message: "Product removed" }); 153 | } catch (error) { 154 | res.status(500).json({ message: "Server Error" }); 155 | } 156 | }; 157 | 158 | const getProducts = async (req, res) => { 159 | const { 160 | search, 161 | category, 162 | minPrice, 163 | maxPrice, 164 | page = 1, 165 | limit = 8, 166 | } = req.query; 167 | try { 168 | console.log( 169 | search, 170 | "s", 171 | category, 172 | "c", 173 | minPrice, 174 | "m", 175 | maxPrice, 176 | "m", 177 | page, 178 | limit 179 | ); 180 | const query = {}; 181 | if (search) { 182 | console.log("s"); 183 | query.name = { $regex: search, $options: "i" }; 184 | } 185 | if (category) { 186 | console.log("c"); 187 | 188 | query.category = category; 189 | } 190 | // if (minPrice > 0 && maxPrice > 0) { 191 | // console.log("m", minPrice, maxPrice); 192 | // query.price = { $gte: Number(minPrice), $lte: Number(maxPrice) }; 193 | // } 194 | if (minPrice > 0) { 195 | query.sellingPrice = { ...query.sellingPrice, $gte: Number(minPrice) }; 196 | } 197 | if (maxPrice > 0) { 198 | query.sellingPrice = { ...query.sellingPrice, $lte: Number(maxPrice) }; 199 | } 200 | 201 | const products = await Products.find(query) 202 | .skip((page - 1) * limit) 203 | .limit(Number(limit)); 204 | const totalProducts = await Products.countDocuments(query); 205 | const totalPages = Math.ceil(totalProducts / limit); 206 | res.status(200).json({ products, totalPages }); 207 | } catch (error) { 208 | res.status(500).json({ message: "Error fetching products", error }); 209 | } 210 | }; 211 | 212 | const getProductById = async (req, res) => { 213 | try { 214 | const product = await Products.findById(req.params.id); 215 | 216 | if (!product) { 217 | return res.status(404).json({ message: "Product not found" }); 218 | } 219 | res.json(product); 220 | } catch (error) { 221 | res.status(500).json({ message: "Server Error" }); 222 | } 223 | }; 224 | 225 | const trendProducts = async (req, res) => { 226 | const { search, page = 1, limit = 8 } = req.query; 227 | console.log("trend"); 228 | const query = {}; 229 | 230 | try { 231 | if (search) { 232 | console.log("s"); 233 | query.name = { $regex: search, $options: "i" }; 234 | } 235 | query.trend = true; 236 | const products = await Products.find(query) 237 | .skip((page - 1) * limit) 238 | .limit(Number(limit)); 239 | const totalProducts = await Products.countDocuments(query); 240 | const totalPages = Math.ceil(totalProducts / limit); 241 | res.status(200).json({ products, totalPages }); 242 | } catch (error) { 243 | res.status(500).json({ message: "Error fetching products", error }); 244 | } 245 | }; 246 | 247 | const offerProducts = async (req, res) => { 248 | const { search, page = 1, limit = 8 } = req.query; 249 | const query = {}; 250 | 251 | try { 252 | console.log("offer"); 253 | 254 | if (search) { 255 | console.log("s"); 256 | query.name = { $regex: search, $options: "i" }; 257 | } 258 | query.offer = true; 259 | 260 | console.log(query,"query"); 261 | const products = await Products.find(query) 262 | .skip((page - 1) * limit) 263 | .limit(Number(limit)); 264 | console.log(products); 265 | const totalProducts = await Products.countDocuments(query); 266 | const totalPages = Math.ceil(totalProducts / limit); 267 | res.status(200).json({ products, totalPages }); 268 | } catch (error) { 269 | res.status(500).json({ message: "Error fetching Offer products", error }); 270 | } 271 | }; 272 | 273 | module.exports = { 274 | createProduct, 275 | updateProduct, 276 | deleteProduct, 277 | getProducts, 278 | getProductById, 279 | trendProducts, 280 | offerProducts, 281 | }; 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Genkart-Ecommerce 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Genkart is a sophisticated e-commerce platform designed to demonstrate a wide range of web development skills. Unlike traditional e-commerce sites, Genkart intentionally omits payment and "Buy Now" options, focusing instead on the user experience, product management, and robust backend functionality. The project is built using cutting-edge technologies and frameworks to ensure scalability, performance, and maintainability. 10 | 11 | ### Tech Stack Overview: 12 | 13 | Frontend: Built with Next.js, a powerful React-based framework that supports server-side rendering and static site generation, enhancing both performance and SEO. 14 | 15 | Backend: Powered by Node.js and Express.js, providing a flexible and efficient environment for handling API requests and managing server-side logic. 16 | 17 | Database: Utilizes MongoDB, a NoSQL database, for storing product information, user data, and other critical information. The schema-less nature of MongoDB allows for rapid development and flexibility in data management. 18 | 19 | Image Management: Cloudinary is integrated for efficient image storage and delivery, offering responsive images and optimized media assets across the platform. 20 | 21 | Styling: The user interface is styled with Tailwind CSS and Material UI, combining the utility-first approach of Tailwind with the component-rich Material UI to create a visually appealing and responsive design. Material Tailwind further enhances the design with additional UI components. 22 | 23 | ### Key Features: 24 | 25 | Authentication & Authorization: 26 | 27 | Secure user authentication and role-based authorization are implemented using JWT (JSON Web Tokens). This ensures that only authenticated users can access certain features, with secure token exchange managed via cookies. 28 | The platform supports user roles such as admin and customer, each with specific access levels and permissions. 29 | 30 | Product Management: 31 | 32 | Product Pages: Each product category, such as T-shirts and casual shirts, has dedicated pages. These pages dynamically display products pulled from the database, with detailed information and images stored via Cloudinary. 33 | Category Pages: Users can browse products by categories, which are organized to provide a seamless shopping experience. 34 | User Profile: 35 | 36 | The profile page allows users to manage their personal information, view their browsing history, and update account settings. It is a hub for user-specific interactions, designed to be intuitive and user-friendly. 37 | 38 | Admin Panel: 39 | 40 | The admin dashboard is a critical feature of Genkart, empowering administrators to perform CRUD (Create, Read, Update, Delete) operations on products, categories, and user accounts. This panel is designed with security in mind, ensuring that only authorized admins can make changes to the platform’s content. 41 | 42 | ### Project Objectives: 43 | 44 | Genkart is not just a showcase of products; it is a demonstration of full-stack web development skills. By integrating modern technologies and best practices, this project serves as a portfolio piece, illustrating the ability to build scalable, maintainable, and user-friendly applications. The project also highlights proficiency in handling complex authentication mechanisms, managing media assets efficiently, and designing responsive and interactive user interfaces. 45 | 46 | ### Potential Future Enhancements: 47 | 48 | Although the current version of Genkart does not include payment and "Buy Now" options, it is architected to easily integrate these features in the future. Possible enhancements include: 49 | 50 | Payment Gateway Integration: Adding secure payment processing with popular gateways like Razorpay or Stripe. 51 | Shopping Cart: Implementing a fully functional shopping cart to manage user selections. 52 | Order Management: Developing an order management system to track purchases and handle user orders efficiently. 53 | 54 | ## Technologies used ... 55 | 56 | this project is developed by using 57 | 58 | - Next js 59 | - Node js 60 | - Express js 61 | - MongoDB 62 | - Tailwind css 63 | - Cloudinary 64 | - Material UI 65 | - Material Tailwind 66 | etc... 67 | 68 | ## How to use 69 | 70 | ### requirements 71 | 72 | create a mongodb atlas account : https://www.mongodb.com/products/platform/atlas-database 73 | 74 | create a cloudinary account : https://cloudinary.com/ 75 | 76 | use visual studio code editor : https://code.visualstudio.com/download 77 | 78 | intall git 79 | for windows : https://git-scm.com/download/win 80 | for mac : https://git-scm.com/download/mac 81 | 82 | install node js : https://nodejs.org/en/download/package-manager 83 | 84 | ### get code from github 85 | 86 | create a folder and open in visual studio code 87 | 88 | open new terminal in vs code then run below command 89 | 90 | ```bash 91 | git clone https://github.com/Sebe2k04/Genkart-Next-Node-Ecommerce-v2.git ./ 92 | ``` 93 | 94 | then provide env files provided below 95 | 96 | ### Environment Variables 97 | 98 | initially create a env file in root folder of next js - location (/client/.env) 99 | 100 | important note : you can provide jwt secret based on you wish but provide same secret value for client and server 101 | 102 | ```bash 103 | 104 | # backend url 105 | NEXT_PUBLIC_API='http://localhost:5000/api' 106 | # frontend url 107 | NEXT_PUBLIC_CLIENT_URL="http://localhost:5000/" 108 | # jwt secret for verify user - replace as per you wish -same as backend 109 | NEXT_PUBLIC_JWT_SECRET="adminfksnkzv" 110 | # jwt secret value for verify admin- replace as per you wish -same as backend 111 | NEXT_PUBLIC_JWT_USER_SECRET="usernsdbdskvn" 112 | NEXT_PUBLIC_NODE_ENV="development" 113 | 114 | ``` 115 | 116 | after that create a env file in root folder of server - location (/server/.env) 117 | 118 | ```bash 119 | # your mongodb uri - replace username and password and provide yours 120 | 121 | MONGO_DB_URI="mongodb+srv://username:password@project.wvpqroq.mongodb.net/genkartv2?retryWrites=true&w=majority&appName=project" 122 | # gmail to send mail to users for reset password 123 | EMAIL_USER="genriotesting@gmail.com" 124 | # gmail app password to provide access to send emails --for info search how to send mail use nodemailer in node js 125 | EMAIL_PASS="vivh ztpd snny zjda" 126 | # client url 127 | CLIENT_URL="http://localhost:3000" 128 | # node environment 129 | NODE_ENV="production" 130 | # cloudinary name 131 | CLOUDINARY_CLOUD_NAME="" 132 | # cloudinary api key 133 | CLOUDINARY_API_KEY="" 134 | # cloudinary secret key 135 | CLOUDINARY_API_SECRET="" 136 | # cloudinary folder name to store files in specific folder 137 | CLOUDINARY_FOLDER_NAME="Genkartv2" 138 | # jwt secret to encode and decode admin token between client and server -provide same value as frontend 139 | JWT_SECRET="adminfksnkzv" 140 | # jwt secret to encode and decode user token between client and server -provide same value as frontend 141 | JWT_USER_SECRET="usernsdbdskvn" 142 | # jwt expiration 143 | JWT_EXPIRES_IN="1d" 144 | ``` 145 | 146 | ### how to run it 147 | 148 | note : initially the website will be blank because no user , admin or products are not present in your database.. 149 | 150 | create two terminals in vs code 151 | 152 | in first one 153 | 154 | ```bash 155 | cd server 156 | npm install 157 | npm start 158 | ``` 159 | 160 | in second one 161 | 162 | ```bash 163 | cd client 164 | npm install 165 | npm run dev 166 | ``` 167 | 168 | Now you have running your frontend and backend 169 | all the running url will be displayed on respective terminal 170 | 171 | ### create admin user 172 | 173 | Important note : 174 | 175 | initially go to below file 176 | 177 | /server/routes/authRoutes.js 178 | 179 | then uncomment the admin signup route 180 | 181 | -- 182 | 183 | note i didn't provide admin signup ui , due to secure concerns . after create admin comment the respective route in server auth route - (admin signup) 184 | 185 | open postman 186 | 187 | then create new workspace 188 | 189 | then provide url backend url with respective route 190 | for example : if you running in localhost 5000 191 | 192 | http://localhost:5000/api/auth/admin/signup 193 | 194 | 195 | 196 | after that you got a response similar like above image 197 | 198 | then in frontend url login with respective email and password to gain access to admin dashboard in , 199 | 200 | http://localhost:3000/admin 201 | 202 | then you can add and remove products in admin dashboard 203 | 204 | # project authority 205 | 206 | this project is developed only by @sebe2k04 , if you have any queries contact me on , 207 | 208 | github : 209 | 210 | https://github.com/Sebe2k04 211 | 212 | linked in : 213 | 214 | https://www.linkedin.com/in/sebe2k04/ 215 | 216 | gmail : 217 | 218 | sebe2k04@gmail.com 219 | 220 | website: 221 | 222 | https://sebe2k04.vercel.app/ 223 | 224 | this project is developed only by myself - sebe , to showcase my developing skills , im a fresher and im currently looking full time job oppurtunities , thank you all... 225 | -------------------------------------------------------------------------------- /client/src/app/admin/secure/products/create/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Search from "@/components/Search"; 3 | import Link from "next/link"; 4 | import { FaCirclePlus } from "react-icons/fa6"; 5 | import { IoIosArrowBack } from "react-icons/io"; 6 | import { FaBox } from "react-icons/fa6"; 7 | import { Select, Option, Spinner } from "@material-tailwind/react"; 8 | import { Switch } from "@material-tailwind/react"; 9 | import { useRef, useState } from "react"; 10 | import { axiosConfig } from "@/utils/axiosConfig"; 11 | import { toast } from "react-toastify"; 12 | import axios from "axios"; 13 | import { axiosInstance } from "@/utils/axiosConfig"; 14 | 15 | export default function Page() { 16 | const categories = [ 17 | { 18 | label: "Casuals", 19 | value: "casuals", 20 | }, 21 | { 22 | label: "T shirts", 23 | value: "tshirts", 24 | }, 25 | ]; 26 | 27 | const [name, setName] = useState(""); 28 | const [category, setCategory] = useState(""); 29 | const [vendor, setVendor] = useState("GenRio"); 30 | const [MRPprice, setMRPprice] = useState(""); 31 | const [sellingprice, setSellingprice] = useState(""); 32 | const [quantity, setQuantity] = useState(10); 33 | const [description, setDescription] = useState(""); 34 | const [trend, setTrend] = useState(false); 35 | const [offer, setOffer] = useState(false); 36 | const [image, setImage] = useState(null); 37 | const [additionalImages, setAdditionalImages] = useState([]); 38 | const [loading, setLoading] = useState(false); 39 | const inputFile = useRef(null); 40 | 41 | console.log(image); 42 | console.log(additionalImages); 43 | const MAX_LENGTH = 2; 44 | const handleReset = () => { 45 | if (inputFile.current) { 46 | inputFile.current.value = ""; 47 | inputFile.current.type = "text"; 48 | inputFile.current.type = "file"; 49 | } 50 | }; 51 | const handleAdditionalImages = (e) => { 52 | if (Array.from(e.target.files).length > MAX_LENGTH) { 53 | e.preventDefault(); 54 | toast.error(`Cannot upload files more than ${MAX_LENGTH}`); 55 | // e.target.files = null; 56 | handleReset(); 57 | return; 58 | } else { 59 | setAdditionalImages(e.target.files); 60 | } 61 | }; 62 | 63 | const handleSubmit = async (e) => { 64 | e.preventDefault(); 65 | setLoading(true); 66 | 67 | const formdata = new FormData(); 68 | formdata.append("name", name); 69 | formdata.append("description", description); 70 | formdata.append("vendor", vendor); 71 | formdata.append("MRPprice", MRPprice); 72 | formdata.append("sellingPrice", sellingprice); 73 | formdata.append("quantity", quantity); 74 | formdata.append("category", category); 75 | formdata.append("trend", trend); 76 | formdata.append("offer", offer); 77 | formdata.append("image", image); 78 | // let additionalImagesData = JSON.parse(additionalImages) 79 | // additionalImagesData.forEach((img) => formdata.append('additionalImages', img)) 80 | 81 | for (let i = 0; i < additionalImages.length; i++) { 82 | formdata.append("additionalImages", additionalImages[i]); 83 | } 84 | 85 | try { 86 | const res = await axiosInstance.post(`/product`, formdata, { 87 | headers: { 88 | "Content-type": "multipart/form-data", 89 | }, 90 | }); 91 | console.log(res.data); 92 | setLoading(false); 93 | toast.success("Product added successfully"); 94 | } catch (error) { 95 | toast.error("Error creating product"); 96 | setLoading(false); 97 | console.error(error); 98 | } 99 | }; 100 | 101 | return ( 102 |
103 |
108 |
109 |
110 | 111 |
112 |
113 |
114 |
115 |
116 | 117 | 118 | 119 | 120 |

Create Product

121 |
122 |
123 | 127 | 128 |

All Products

129 | 130 |
131 |
132 |
133 |
134 |
135 | 138 | setName(e.target.value)} 142 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 143 | /> 144 |
145 |
146 | 149 | 164 |
165 |
166 | 167 | setVendor(e.target.value)} 171 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 172 | /> 173 |
174 |
175 | 178 | setMRPprice(e.target.value)} 182 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 183 | /> 184 |
185 |
186 | 189 | setSellingprice(e.target.value)} 193 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 194 | /> 195 |
196 |
197 | 200 | setQuantity(e.target.value)} 205 | className=" w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-gray-600 sm:text-sm" 206 | /> 207 |
208 |
209 | 212 | {/* */} 217 | setImage(e.target.files[0])} 224 | className="file:bg-gray-50 file:px-5 file:py-2 file:rounded-md file:border file:border-gray-200 lg:file:mr-10 file:mr-5" 225 | /> 226 |
227 |
228 | 231 | {/* */} 236 | 247 |
248 | 249 |
250 | 253 |