Repository: sangammukherjee/NextJS-Ecommerce-2023
Branch: master
Commit: 82c64f9440d9
Files: 76
Total size: 148.9 KB
Directory structure:
gitextract_v8_ctk5n/
├── .gitignore
├── README.md
├── jsconfig.json
├── next.config.js
├── package.json
├── postcss.config.js
├── src/
│ ├── app/
│ │ ├── account/
│ │ │ └── page.js
│ │ ├── admin-view/
│ │ │ ├── add-product/
│ │ │ │ └── page.js
│ │ │ ├── all-products/
│ │ │ │ └── page.js
│ │ │ └── page.js
│ │ ├── api/
│ │ │ ├── address/
│ │ │ │ ├── add-new-address/
│ │ │ │ │ └── route.js
│ │ │ │ ├── delete-address/
│ │ │ │ │ └── route.js
│ │ │ │ ├── get-all-address/
│ │ │ │ │ └── route.js
│ │ │ │ └── update-address/
│ │ │ │ └── route.js
│ │ │ ├── admin/
│ │ │ │ ├── add-product/
│ │ │ │ │ └── route.js
│ │ │ │ ├── all-products/
│ │ │ │ │ └── route.js
│ │ │ │ ├── delete-product/
│ │ │ │ │ └── route.js
│ │ │ │ ├── orders/
│ │ │ │ │ ├── get-all-orders/
│ │ │ │ │ │ └── route.js
│ │ │ │ │ └── update-order/
│ │ │ │ │ └── route.js
│ │ │ │ ├── product-by-category/
│ │ │ │ │ └── route.js
│ │ │ │ ├── product-by-id/
│ │ │ │ │ └── route.js
│ │ │ │ └── update-product/
│ │ │ │ └── route.js
│ │ │ ├── cart/
│ │ │ │ ├── add-to-cart/
│ │ │ │ │ └── route.js
│ │ │ │ ├── all-cart-items/
│ │ │ │ │ └── route.js
│ │ │ │ └── delete-from-cart/
│ │ │ │ └── route.js
│ │ │ ├── login/
│ │ │ │ └── route.js
│ │ │ ├── order/
│ │ │ │ ├── create-order/
│ │ │ │ │ └── route.js
│ │ │ │ ├── get-all-orders/
│ │ │ │ │ └── route.js
│ │ │ │ └── order-details/
│ │ │ │ └── route.js
│ │ │ ├── register/
│ │ │ │ └── route.js
│ │ │ └── stripe/
│ │ │ └── route.js
│ │ ├── cart/
│ │ │ └── page.js
│ │ ├── checkout/
│ │ │ └── page.js
│ │ ├── globals.css
│ │ ├── layout.js
│ │ ├── login/
│ │ │ └── page.js
│ │ ├── orders/
│ │ │ ├── [order-details]/
│ │ │ │ └── page.js
│ │ │ └── page.js
│ │ ├── page.js
│ │ ├── product/
│ │ │ ├── [details]/
│ │ │ │ └── page.js
│ │ │ └── listing/
│ │ │ ├── all-products/
│ │ │ │ └── page.js
│ │ │ ├── kids/
│ │ │ │ └── page.js
│ │ │ ├── men/
│ │ │ │ └── page.js
│ │ │ └── women/
│ │ │ └── page.js
│ │ ├── register/
│ │ │ └── page.js
│ │ └── unauthorized-page/
│ │ └── page.js
│ ├── components/
│ │ ├── CartModal/
│ │ │ └── index.js
│ │ ├── CommonCart/
│ │ │ └── index.js
│ │ ├── CommonDetails/
│ │ │ └── index.js
│ │ ├── CommonListing/
│ │ │ ├── ProductButtons/
│ │ │ │ └── index.js
│ │ │ ├── ProductTile/
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── CommonModal/
│ │ │ └── index.js
│ │ ├── FormElements/
│ │ │ ├── InputComponent/
│ │ │ │ └── index.js
│ │ │ ├── SelectComponent/
│ │ │ │ └── index.js
│ │ │ └── TileComponent/
│ │ │ └── index.js
│ │ ├── Loader/
│ │ │ └── componentlevel/
│ │ │ └── index.js
│ │ ├── Navbar/
│ │ │ └── index.js
│ │ └── Notification/
│ │ └── index.js
│ ├── context/
│ │ └── index.js
│ ├── database/
│ │ └── index.js
│ ├── middleware/
│ │ └── AuthUser.js
│ ├── models/
│ │ ├── address.js
│ │ ├── cart.js
│ │ ├── order.js
│ │ ├── product.js
│ │ └── user.js
│ ├── services/
│ │ ├── address/
│ │ │ └── index.js
│ │ ├── cart/
│ │ │ └── index.js
│ │ ├── login/
│ │ │ └── index.js
│ │ ├── order/
│ │ │ └── index.js
│ │ ├── product/
│ │ │ └── index.js
│ │ ├── register/
│ │ │ └── index.js
│ │ └── stripe/
│ │ └── index.js
│ └── utils/
│ └── index.js
└── tailwind.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
================================================
FILE: README.md
================================================
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).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
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.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
================================================
FILE: jsconfig.json
================================================
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
================================================
FILE: next.config.js
================================================
/** @type {import('next').NextConfig} */
const nextConfig = {}
module.exports = nextConfig
================================================
FILE: package.json
================================================
{
"name": "ecommerce",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@headlessui/react": "^1.7.15",
"@stripe/stripe-js": "^1.54.1",
"autoprefixer": "10.4.14",
"bcryptjs": "^2.4.3",
"firebase": "^10.0.0",
"joi": "^17.9.2",
"js-cookie": "^3.0.5",
"jsonwebtoken": "^9.0.1",
"mongoose": "^7.3.2",
"next": "13.4.9",
"postcss": "8.4.25",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-spinners": "^0.13.8",
"react-toastify": "^9.1.3",
"stripe": "^12.12.0",
"tailwindcss": "3.3.2"
},
"devDependencies": {
"encoding": "^0.1.13"
}
}
================================================
FILE: postcss.config.js
================================================
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
================================================
FILE: src/app/account/page.js
================================================
"use client";
import InputComponent from "@/components/FormElements/InputComponent";
import ComponentLevelLoader from "@/components/Loader/componentlevel";
import Notification from "@/components/Notification";
import { GlobalContext } from "@/context";
import {
addNewAddress,
deleteAddress,
fetchAllAddresses,
updateAddress,
} from "@/services/address";
import { addNewAddressFormControls } from "@/utils";
import { useRouter } from "next/navigation";
import { useContext, useEffect, useState } from "react";
import { PulseLoader } from "react-spinners";
import { toast } from "react-toastify";
export default function Account() {
const {
user,
addresses,
setAddresses,
addressFormData,
setAddressFormData,
componentLevelLoader,
setComponentLevelLoader,
pageLevelLoader,
setPageLevelLoader,
} = useContext(GlobalContext);
const [showAddressForm, setShowAddressForm] = useState(false);
const [currentEditedAddressId, setCurrentEditedAddressId] = useState(null);
const router = useRouter()
async function extractAllAddresses() {
setPageLevelLoader(true);
const res = await fetchAllAddresses(user?._id);
if (res.success) {
setPageLevelLoader(false);
setAddresses(res.data);
}
}
async function handleAddOrUpdateAddress() {
setComponentLevelLoader({ loading: true, id: "" });
const res =
currentEditedAddressId !== null
? await updateAddress({
...addressFormData,
_id: currentEditedAddressId,
})
: await addNewAddress({ ...addressFormData, userID: user?._id });
console.log(res);
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setAddressFormData({
fullName: "",
city: "",
country: "",
postalCode: "",
address: "",
});
extractAllAddresses();
setCurrentEditedAddressId(null);
} else {
setComponentLevelLoader({ loading: false, id: "" });
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setAddressFormData({
fullName: "",
city: "",
country: "",
postalCode: "",
address: "",
});
}
}
function handleUpdateAddress(getCurrentAddress) {
setShowAddressForm(true);
setAddressFormData({
fullName: getCurrentAddress.fullName,
city: getCurrentAddress.city,
country: getCurrentAddress.country,
postalCode: getCurrentAddress.postalCode,
address: getCurrentAddress.address,
});
setCurrentEditedAddressId(getCurrentAddress._id);
}
async function handleDelete(getCurrentAddressID) {
setComponentLevelLoader({ loading: true, id: getCurrentAddressID });
const res = await deleteAddress(getCurrentAddressID);
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
extractAllAddresses();
} else {
setComponentLevelLoader({ loading: false, id: "" });
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
}
}
useEffect(() => {
if (user !== null) extractAllAddresses();
}, [user]);
return (
<section>
<div className="mx-auto bg-gray-100 px-4 sm:px-6 lg:px-8">
<div className="bg-white shadow">
<div className="p-6 sm:p-12">
<div className="flex flex-col space-y-4 md:space-y-0 md:space-x-6 md:flex-row">
{/* we have render random user image here */}
</div>
<div className="flex flex-col flex-1">
<h4 className="text-lg font-semibold text-center md:text-left">
{user?.name}
</h4>
<p>{user?.email}</p>
<p>{user?.role}</p>
</div>
<button onClick={()=>router.push('/orders')} className="mt-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide">
View Your Orders
</button>
<div className="mt-6">
<h1 className="font-bold text-lg">Your Addresses :</h1>
{pageLevelLoader ? (
<PulseLoader
color={"#000000"}
loading={pageLevelLoader}
size={15}
data-testid="loader"
/>
) : (
<div className="mt-4 flex flex-col gap-4">
{addresses && addresses.length ? (
addresses.map((item) => (
<div className="border p-6" key={item._id}>
<p>Name : {item.fullName}</p>
<p>Address : {item.address}</p>
<p>City : {item.city}</p>
<p>Country : {item.country}</p>
<p>PostalCode : {item.postalCode}</p>
<button
onClick={() => handleUpdateAddress(item)}
className="mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
Update
</button>
<button
onClick={() => handleDelete(item._id)}
className="mt-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
{componentLevelLoader &&
componentLevelLoader.loading &&
componentLevelLoader.id === item._id ? (
<ComponentLevelLoader
text={"Deleting"}
color={"#ffffff"}
loading={
componentLevelLoader &&
componentLevelLoader.loading
}
/>
) : (
"Delete"
)}
</button>
</div>
))
) : (
<p>No address found ! Please add a new address below</p>
)}
</div>
)}
</div>
<div className="mt-4">
<button
onClick={() => setShowAddressForm(!showAddressForm)}
className="mt-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
{showAddressForm ? "Hide Address Form" : "Add New Address"}
</button>
</div>
{showAddressForm ? (
<div className="flex flex-col mt-5 justify-center pt-4 items-center">
<div className="w-full mt-6 mr-0 mb-0 ml-0 space-y-8">
{addNewAddressFormControls.map((controlItem) => (
<InputComponent
type={controlItem.type}
placeholder={controlItem.placeholder}
label={controlItem.label}
value={addressFormData[controlItem.id]}
onChange={(event) =>
setAddressFormData({
...addressFormData,
[controlItem.id]: event.target.value,
})
}
/>
))}
</div>
<button
onClick={handleAddOrUpdateAddress}
className="mt-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
{componentLevelLoader && componentLevelLoader.loading ? (
<ComponentLevelLoader
text={"Saving"}
color={"#ffffff"}
loading={
componentLevelLoader && componentLevelLoader.loading
}
/>
) : (
"Save"
)}
</button>
</div>
) : null}
</div>
</div>
</div>
<Notification />
</section>
);
}
================================================
FILE: src/app/admin-view/add-product/page.js
================================================
"use client";
import InputComponent from "@/components/FormElements/InputComponent";
import SelectComponent from "@/components/FormElements/SelectComponent";
import TileComponent from "@/components/FormElements/TileComponent";
import ComponentLevelLoader from "@/components/Loader/componentlevel";
import Notification from "@/components/Notification";
import { GlobalContext } from "@/context";
import { addNewProduct, updateAProduct } from "@/services/product";
import {
AvailableSizes,
adminAddProductformControls,
firebaseConfig,
firebaseStroageURL,
} from "@/utils";
import { initializeApp } from "firebase/app";
import {
getDownloadURL,
getStorage,
ref,
uploadBytesResumable,
} from "firebase/storage";
import { useRouter } from "next/navigation";
import { useContext, useEffect, useState } from "react";
import { toast } from "react-toastify";
import { resolve } from "styled-jsx/css";
const app = initializeApp(firebaseConfig);
const storage = getStorage(app, firebaseStroageURL);
const createUniqueFileName = (getFile) => {
const timeStamp = Date.now();
const randomStringValue = Math.random().toString(36).substring(2, 12);
return `${getFile.name}-${timeStamp}-${randomStringValue}`;
};
async function helperForUPloadingImageToFirebase(file) {
const getFileName = createUniqueFileName(file);
const storageReference = ref(storage, `ecommerce/${getFileName}`);
const uploadImage = uploadBytesResumable(storageReference, file);
return new Promise((resolve, reject) => {
uploadImage.on(
"state_changed",
(snapshot) => {},
(error) => {
console.log(error);
reject(error);
},
() => {
getDownloadURL(uploadImage.snapshot.ref)
.then((downloadUrl) => resolve(downloadUrl))
.catch((error) => reject(error));
}
);
});
}
const initialFormData = {
name: "",
price: 0,
description: "",
category: "men",
sizes: [],
deliveryInfo: "",
onSale: "no",
imageUrl: "",
priceDrop: 0,
};
export default function AdminAddNewProduct() {
const [formData, setFormData] = useState(initialFormData);
const {
componentLevelLoader,
setComponentLevelLoader,
currentUpdatedProduct,
setCurrentUpdatedProduct,
} = useContext(GlobalContext);
console.log(currentUpdatedProduct);
const router = useRouter();
useEffect(() => {
if (currentUpdatedProduct !== null) setFormData(currentUpdatedProduct);
}, [currentUpdatedProduct]);
async function handleImage(event) {
const extractImageUrl = await helperForUPloadingImageToFirebase(
event.target.files[0]
);
if (extractImageUrl !== "") {
setFormData({
...formData,
imageUrl: extractImageUrl,
});
}
}
function handleTileClick(getCurrentItem) {
let cpySizes = [...formData.sizes];
const index = cpySizes.findIndex((item) => item.id === getCurrentItem.id);
if (index === -1) {
cpySizes.push(getCurrentItem);
} else {
cpySizes = cpySizes.filter((item) => item.id !== getCurrentItem.id);
}
setFormData({
...formData,
sizes: cpySizes,
});
}
async function handleAddProduct() {
setComponentLevelLoader({ loading: true, id: "" });
const res =
currentUpdatedProduct !== null
? await updateAProduct(formData)
: await addNewProduct(formData);
console.log(res);
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setFormData(initialFormData);
setCurrentUpdatedProduct(null)
setTimeout(() => {
router.push("/admin-view/all-products");
}, 1000);
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: "" });
setFormData(initialFormData);
}
}
console.log(formData);
return (
<div className="w-full mt-5 mr-0 mb-0 ml-0 relative">
<div className="flex flex-col items-start justify-start p-10 bg-white shadow-2xl rounded-xl relative">
<div className="w-full mt-6 mr-0 mb-0 ml-0 space-y-8">
<input
accept="image/*"
max="1000000"
type="file"
onChange={handleImage}
/>
<div className="flex gap-2 flex-col">
<label>Available sizes</label>
<TileComponent
selected={formData.sizes}
onClick={handleTileClick}
data={AvailableSizes}
/>
</div>
{adminAddProductformControls.map((controlItem) =>
controlItem.componentType === "input" ? (
<InputComponent
type={controlItem.type}
placeholder={controlItem.placeholder}
label={controlItem.label}
value={formData[controlItem.id]}
onChange={(event) => {
setFormData({
...formData,
[controlItem.id]: event.target.value,
});
}}
/>
) : controlItem.componentType === "select" ? (
<SelectComponent
label={controlItem.label}
options={controlItem.options}
value={formData[controlItem.id]}
onChange={(event) => {
setFormData({
...formData,
[controlItem.id]: event.target.value,
});
}}
/>
) : null
)}
<button
onClick={handleAddProduct}
className="inline-flex w-full items-center justify-center bg-black px-6 py-4 text-lg text-white font-medium uppercase tracking-wide"
>
{componentLevelLoader && componentLevelLoader.loading ? (
<ComponentLevelLoader
text={currentUpdatedProduct !== null ? 'Updating Product' : "Adding Product"}
color={"#ffffff"}
loading={componentLevelLoader && componentLevelLoader.loading}
/>
) : currentUpdatedProduct !== null ? (
"Update Product"
) : (
"Add Product"
)}
</button>
</div>
</div>
<Notification />
</div>
);
}
================================================
FILE: src/app/admin-view/all-products/page.js
================================================
import CommonListing from "@/components/CommonListing";
import { getAllAdminProducts } from "@/services/product";
export default async function AdminAllProducts() {
const allAdminProducts = await getAllAdminProducts()
return <CommonListing data={allAdminProducts && allAdminProducts.data}/>
}
================================================
FILE: src/app/admin-view/page.js
================================================
"use client";
import ComponentLevelLoader from "@/components/Loader/componentlevel";
import { GlobalContext } from "@/context";
import { getAllOrdersForAllUsers, updateStatusOfOrder } from "@/services/order";
import { useContext, useEffect } from "react";
import { PulseLoader } from "react-spinners";
export default function AdminView() {
const {
allOrdersForAllUsers,
setAllOrdersForAllUsers,
user,
pageLevelLoader,
setPageLevelLoader,
componentLevelLoader,
setComponentLevelLoader,
} = useContext(GlobalContext);
async function extractAllOrdersForAllUsers() {
setPageLevelLoader(true);
const res = await getAllOrdersForAllUsers();
console.log(res);
if (res.success) {
setPageLevelLoader(false);
setAllOrdersForAllUsers(
res.data && res.data.length
? res.data.filter((item) => item.user._id !== user._id)
: []
);
} else {
setPageLevelLoader(false);
}
}
useEffect(() => {
if (user !== null) extractAllOrdersForAllUsers();
}, [user]);
console.log(allOrdersForAllUsers);
async function handleUpdateOrderStatus(getItem) {
setComponentLevelLoader({ loading: true, id: getItem._id });
const res = await updateStatusOfOrder({
...getItem,
isProcessing: false,
});
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
extractAllOrdersForAllUsers();
} else {
setComponentLevelLoader({ loading: true, id: "" });
}
}
if (pageLevelLoader) {
return (
<div className="w-full min-h-screen flex justify-center items-center">
<PulseLoader
color={"#000000"}
loading={pageLevelLoader}
size={30}
data-testid="loader"
/>
</div>
);
}
return (
<section>
<div className="mx-auto px-4 sm:px-6 lg:px-8">
<div>
<div className="px-4 py-6 sm:px-8 sm:py-10">
<div className="flow-root">
{allOrdersForAllUsers && allOrdersForAllUsers.length ? (
<ul className="flex flex-col gap-4">
{allOrdersForAllUsers.map((item) => (
<li
key={item._id}
className="bg-gray-200 shadow p-5 flex flex-col space-y-3 py-6 text-left"
>
<div className="flex">
<h1 className="font-bold text-lg mb-3 flex-1">
#order: {item._id}
</h1>
<div className="flex flex-col gap-2">
<div className="flex items-center">
<p className="mr-3 text-sm font-medium text-gray-900">
User Name :
</p>
<p className="text-sm font-semibold text-gray-900">
{item?.user?.name}
</p>
</div>
<div className="flex items-center">
<p className="mr-3 text-sm font-medium text-gray-900">
User Email :
</p>
<p className="text-sm font-semibold text-gray-900">
{item?.user?.email}
</p>
</div>
<div className="flex items-center">
<p className="mr-3 text-sm font-medium text-gray-900">
Total Paid Amount :
</p>
<p className="text-sm font-semibold text-gray-900">
${item?.totalPrice}
</p>
</div>
</div>
</div>
<div className="flex gap-2">
{item.orderItems.map((orderItem, index) => (
<div key={index} className="shrink-0">
<img
alt="Order Item"
className="h-24 w-24 max-w-full rounded-lg object-cover"
src={
orderItem &&
orderItem.product &&
orderItem.product.imageUrl
}
/>
</div>
))}
</div>
<div className="flex gap-5">
<button className="disabled:opacity-50 mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide">
{item.isProcessing
? "Order is Processing"
: "Order is delivered"}
</button>
<button
onClick={() => handleUpdateOrderStatus(item)}
disabled={!item.isProcessing}
className="disabled:opacity-50 mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
{componentLevelLoader &&
componentLevelLoader.loading &&
componentLevelLoader.id === item._id ? (
<ComponentLevelLoader
text={"Updating Order Status"}
color={"#ffffff"}
loading={
componentLevelLoader &&
componentLevelLoader.loading
}
/>
) : (
"Update Order Status"
)}
</button>
</div>
</li>
))}
</ul>
) : null}
</div>
</div>
</div>
</div>
</section>
);
}
================================================
FILE: src/app/api/address/add-new-address/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Address from "@/models/address";
import Joi from "joi";
import { NextResponse } from "next/server";
const AddNewAddress = Joi.object({
fullName: Joi.string().required(),
address: Joi.string().required(),
city: Joi.string().required(),
country: Joi.string().required(),
postalCode: Joi.string().required(),
userID: Joi.string().required(),
});
export const dynamic = "force-dynamic";
export async function POST(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const data = await req.json();
const { fullName, address, city, country, postalCode, userID } = data;
const { error } = AddNewAddress.validate({
fullName,
address,
city,
country,
postalCode,
userID,
});
if (error) {
return NextResponse.json({
success: false,
message: error.details[0].message,
});
}
const newlyAddedAddress = await Address.create(data);
if (newlyAddedAddress) {
return NextResponse.json({
success: true,
message: "Address added successfully",
});
} else {
return NextResponse.json({
success: false,
message: "failed to add an address ! Please try again later",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/address/delete-address/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Address from "@/models/address";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function DELETE(req) {
try {
await connectToDB();
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({
success: false,
message: "Address ID is required",
});
}
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const deletedAddress = await Address.findByIdAndDelete(id);
if (deletedAddress) {
return NextResponse.json({
success: true,
message: "Address is deleted successfully",
});
} else {
return NextResponse.json({
success: false,
message: "failed to delete address ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/address/get-all-address/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Address from "@/models/address";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({
success: false,
message: "You are not logged In",
});
}
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const getAllAddresses = await Address.find({ userID: id });
if (getAllAddresses) {
return NextResponse.json({
success: true,
data: getAllAddresses,
});
} else {
return NextResponse.json({
success: false,
message: "failed to get addresses ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/address/update-address/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Address from "@/models/address";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function PUT(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const data = await req.json();
const { _id, fullName, city, address, country, postalCode } = data;
const updateAddress = await Address.findOneAndUpdate(
{
_id: _id,
},
{ fullName, city, address, country, postalCode },
{ new: true }
);
if (updateAddress) {
return NextResponse.json({
success: true,
message: "Address updated successfully!",
});
} else {
return NextResponse.json({
success: false,
message: "failed to update address ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/add-product/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Product from "@/models/product";
import Joi from "joi";
import { NextResponse } from "next/server";
const AddNewProductSchema = Joi.object({
name: Joi.string().required(),
description: Joi.string().required(),
price: Joi.number().required(),
category: Joi.string().required(),
sizes: Joi.array().required(),
deliveryInfo: Joi.string().required(),
onSale: Joi.string().required(),
priceDrop: Joi.number().required(),
imageUrl: Joi.string().required(),
});
export const dynamic = "force-dynamic";
export async function POST(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req)
console.log(isAuthUser , 'sangam');
if (isAuthUser?.role === "admin") {
const extractData = await req.json();
const {
name,
description,
price,
imageUrl,
category,
sizes,
deliveryInfo,
onSale,
priceDrop,
} = extractData;
const { error } = AddNewProductSchema.validate({
name,
description,
price,
imageUrl,
category,
sizes,
deliveryInfo,
onSale,
priceDrop,
});
if (error) {
return NextResponse.json({
success: false,
message: error.details[0].message,
});
}
const newlyCreatedProduct = await Product.create(extractData);
if (newlyCreatedProduct) {
return NextResponse.json({
success: true,
message: "Product added successfully",
});
} else {
return NextResponse.json({
success: false,
message: "Failed to add the product ! please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not autorized !",
});
}
} catch (error) {
console.log(error);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/all-products/route.js
================================================
import connectToDB from "@/database";
import Product from "@/models/product";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const extractAllproducts = await Product.find({});
if (extractAllproducts) {
return NextResponse.json({
success: true,
data: extractAllproducts,
});
} else {
return NextResponse.json({
success: false,
status: 204,
message: "No Products found",
});
}
} catch (error) {
console.log(error);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/delete-product/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Product from "@/models/product";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function DELETE(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser?.role === "admin") {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id)
return NextResponse.json({
success: false,
message: "Product ID is required",
});
const deletedProduct = await Product.findByIdAndDelete(id);
if (deletedProduct) {
return NextResponse.json({
success: true,
message: "Product deleted successfully",
});
} else {
return NextResponse.json({
success: false,
message: "Failed to delete the product ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(error);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/orders/get-all-orders/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Order from "@/models/order";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser?.role === "admin") {
const getAllOrders = await Order.find({})
.populate("orderItems.product")
.populate("user");
if (getAllOrders) {
return NextResponse.json({
success: true,
data: getAllOrders,
});
} else {
return NextResponse.json({
success: false,
message:
"failed to fetch the orders ! Please try again after some time.",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not autorized !",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/orders/update-order/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Order from "@/models/order";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function PUT(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
const data = await req.json();
if (isAuthUser?.role === "admin") {
const {
_id,
shippingAddress,
orderItems,
paymentMethod,
isPaid,
paidAt,
isProcessing,
} = data;
const updateOrder = await Order.findOneAndUpdate(
{ _id: _id },
{
shippingAddress,
orderItems,
paymentMethod,
isPaid,
paidAt,
isProcessing,
},
{ new: true }
);
if (updateOrder) {
return NextResponse.json({
success: true,
message: "Order status updated successfully! ",
});
} else {
return NextResponse.json({
success: true,
message: "failed to update the status of order",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not autorized !",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/product-by-category/route.js
================================================
import connectToDB from "@/database";
import Product from "@/models/product";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
const getData = await Product.find({ category: id });
if (getData) {
return NextResponse.json({
success: true,
data: getData,
});
} else {
return NextResponse.json({
success: false,
status: 204,
message: "No Products found !",
});
}
} catch (e) {
console.log(error);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/product-by-id/route.js
================================================
import connectToDB from "@/database";
import Product from "@/models/product";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const { searchParams } = new URL(req.url);
const productId = searchParams.get("id");
if (!productId) {
return NextResponse.json({
success: false,
status: 400,
message: "Product id is required",
});
}
const getData = await Product.find({ _id: productId });
if (getData && getData.length) {
return NextResponse.json({ success: true, data: getData[0] });
} else {
return NextResponse.json({
success: false,
status: 204,
message: "No Product found",
});
}
} catch (error) {
console.log(error);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/admin/update-product/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Product from "@/models/product";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function PUT(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser?.role === "admin") {
const extractData = await req.json();
const {
_id,
name,
price,
description,
category,
sizes,
deliveryInfo,
onSale,
priceDrop,
imageUrl,
} = extractData;
const updatedProduct = await Product.findOneAndUpdate(
{
_id: _id,
},
{
name,
price,
description,
category,
sizes,
deliveryInfo,
onSale,
priceDrop,
imageUrl,
},
{ new: true }
);
if (updatedProduct) {
return NextResponse.json({
success: true,
message: "Product updated successfully",
});
} else {
return NextResponse.json({
success: false,
message: "Failed to update the product ! Please try again later",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(error);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/cart/add-to-cart/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Cart from "@/models/cart";
import Joi from "joi";
import { NextResponse } from "next/server";
const AddToCart = Joi.object({
userID: Joi.string().required(),
productID: Joi.string().required(),
});
export const dynamic = "force-dynamic";
export async function POST(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const data = await req.json();
const {productID , userID} = data;
const { error } = AddToCart.validate({ userID, productID });
if (error) {
return NextResponse.json({
success: false,
message: error.details[0].message,
});
}
console.log(productID, userID);
const isCurrentCartItemAlreadyExists = await Cart.find({
productID: productID,
userID: userID,
});
console.log(isCurrentCartItemAlreadyExists);
if (isCurrentCartItemAlreadyExists?.length > 0) {
return NextResponse.json({
success: false,
message:
"Product is already added in cart! Please add different product",
});
}
const saveProductToCart = await Cart.create(data);
console.log(saveProductToCart);
if (saveProductToCart) {
return NextResponse.json({
success: true,
message: "Product is added to cart !",
});
} else {
return NextResponse.json({
success: false,
message: "failed to add the product to cart ! Please try again.",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/cart/all-cart-items/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Cart from "@/models/cart";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id)
return NextResponse.json({
success: false,
message: "Please login in!",
});
const extractAllCartItems = await Cart.find({ userID: id }).populate(
"productID"
);
if (extractAllCartItems) {
return NextResponse.json({ success: true, data: extractAllCartItems });
} else {
return NextResponse.json({
success: false,
message: "No Cart items are found !",
status: 204,
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (e) {
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again",
});
}
}
================================================
FILE: src/app/api/cart/delete-from-cart/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Cart from "@/models/cart";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function DELETE(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id)
return NextResponse.json({
success: false,
message: "Cart Item ID is required",
});
const deleteCartItem = await Cart.findByIdAndDelete(id);
if (deleteCartItem) {
return NextResponse.json({
success: true,
message: "Cart Item deleted successfully",
});
} else {
return NextResponse.json({
success: false,
message: "Failed to delete Cart item ! Please try again.",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authenticated",
});
}
} catch (error) {
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again",
});
}
}
================================================
FILE: src/app/api/login/route.js
================================================
import connectToDB from "@/database";
import User from "@/models/user";
import { compare } from "bcryptjs";
import Joi from "joi";
import jwt from "jsonwebtoken";
import { NextResponse } from "next/server";
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().required(),
});
export const dynamic = "force-dynamic";
export async function POST(req) {
await connectToDB();
const { email, password } = await req.json();
const { error } = schema.validate({ email, password });
if (error) {
return NextResponse.json({
success: false,
message: error.details[0].message,
});
}
try {
const checkUser = await User.findOne({ email });
if (!checkUser) {
return NextResponse.json({
success: false,
message: "Account not found with this email",
});
}
const checkPassword = await compare(password, checkUser.password);
if (!checkPassword) {
return NextResponse.json({
success: false,
message: "Incorrect password. Please try again !",
});
}
const token = jwt.sign(
{
id: checkUser._id,
email: checkUser?.email,
role: checkUser?.role,
},
"default_secret_key",
{ expiresIn: "1d" }
);
const finalData = {
token,
user: {
email: checkUser.email,
name: checkUser.name,
_id: checkUser._id,
role: checkUser.role,
},
};
return NextResponse.json({
success: true,
message: "Login successfull!",
finalData,
});
} catch (e) {
console.log("Error while logging In. Please try again");
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/order/create-order/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Cart from "@/models/cart";
import Order from "@/models/order";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function POST(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const data = await req.json();
const { user } = data;
const saveNewOrder = await Order.create(data);
if (saveNewOrder) {
await Cart.deleteMany({ userID: user });
return NextResponse.json({
success: true,
message: "Products are on the way !",
});
} else {
return NextResponse.json({
success: false,
message: "Failed to create a order ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authticated",
});
}
} catch (e) {
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/order/get-all-orders/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Order from "@/models/order";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
const extractAllOrders = await Order.find({ user: id }).populate(
"orderItems.product"
);
if (extractAllOrders) {
return NextResponse.json({
success: true,
data: extractAllOrders,
});
} else {
return NextResponse.json({
success: false,
message: "Failed to get all orders ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authticated",
});
}
} catch (e) {
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/order/order-details/route.js
================================================
import connectToDB from "@/database";
import AuthUser from "@/middleware/AuthUser";
import Order from "@/models/order";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(req) {
try {
await connectToDB();
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id)
return NextResponse.json({
success: false,
message: "Product ID is required",
});
const extractOrderDetails = await Order.findById(id).populate(
"orderItems.product"
);
if (extractOrderDetails) {
return NextResponse.json({
success: true,
data: extractOrderDetails,
});
} else {
return NextResponse.json({
success: false,
message: "Failed to get order details ! Please try again",
});
}
} else {
return NextResponse.json({
success: false,
message: "You are not authticated",
});
}
} catch (e) {
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/register/route.js
================================================
import connectToDB from "@/database";
import User from "@/models/user";
import { hash } from "bcryptjs";
import Joi from "joi";
import { NextResponse } from "next/server";
const schema = Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
role: Joi.string().required(),
});
export const dynamic = "force-dynamic";
export async function POST(req) {
await connectToDB();
const { name, email, password, role } = await req.json();
//validate the schema
const { error } = schema.validate({ name, email, password, role });
if (error) {
console.log(error);
return NextResponse.json({
success: false,
message: error.details[0].message,
});
}
try {
//check if the user is exists or not
const isUserAlreadyExists = await User.findOne({ email });
if (isUserAlreadyExists) {
return NextResponse.json({
success: false,
message: "User is already exists. Please try with different email.",
});
} else {
const hashPassword = await hash(password, 12);
const newlyCreatedUser = await User.create({
name,
email,
password: hashPassword,
role,
});
if (newlyCreatedUser) {
return NextResponse.json({
success: true,
message: "Account created successfully.",
});
}
}
} catch (error) {
console.log("Error while new user registration. Please try again");
return NextResponse.json({
success: false,
message: "Something went wrong ! Please try again later",
});
}
}
================================================
FILE: src/app/api/stripe/route.js
================================================
import AuthUser from "@/middleware/AuthUser";
import { NextResponse } from "next/server";
const stripe = require("stripe")(
"sk_test_51NMv6ZSC6E6fnyMeTYV3h3Xge6Tot3xYQVEO6KMpiB5A6bKIrRS9YymIBEupAFqF0XM274IwwU2Zq7EXx1Pn8LiA00SyPEZqk9"
);
export const dynamic = "force-dynamic";
export async function POST(req) {
try {
const isAuthUser = await AuthUser(req);
if (isAuthUser) {
const res = await req.json();
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: res,
mode: "payment",
success_url: "http://localhost:3000/checkout" + "?status=success",
cancel_url: "http://localhost:3000/checkout" + "?status=cancel",
});
return NextResponse.json({
success: true,
id: session.id,
});
} else {
return NextResponse.json({
success: true,
message: "You are not authenticated",
});
}
} catch (e) {
console.log(e);
return NextResponse.json({
status: 500,
success: false,
message: "Something went wrong ! Please try again",
});
}
}
================================================
FILE: src/app/cart/page.js
================================================
"use client";
import CommonCart from "@/components/CommonCart";
import { GlobalContext } from "@/context";
import { deleteFromCart, getAllCartItems } from "@/services/cart";
import { useContext, useEffect } from "react";
import { PulseLoader } from "react-spinners";
import { toast } from "react-toastify";
export default function Cart() {
const {
user,
setCartItems,
cartItems,
pageLevelLoader,
setPageLevelLoader,
setComponentLevelLoader,
componentLevelLoader,
} = useContext(GlobalContext);
async function extractAllCartItems() {
setPageLevelLoader(true);
const res = await getAllCartItems(user?._id);
if (res.success) {
const updatedData =
res.data && res.data.length
? res.data.map((item) => ({
...item,
productID: {
...item.productID,
price:
item.productID.onSale === "yes"
? parseInt(
(
item.productID.price -
item.productID.price * (item.productID.priceDrop / 100)
).toFixed(2)
)
: item.productID.price,
},
}))
: [];
setCartItems(updatedData);
setPageLevelLoader(false);
localStorage.setItem("cartItems", JSON.stringify(updatedData));
}
console.log(res);
}
useEffect(() => {
if (user !== null) extractAllCartItems();
}, [user]);
async function handleDeleteCartItem(getCartItemID) {
setComponentLevelLoader({ loading: true, id: getCartItemID });
const res = await deleteFromCart(getCartItemID);
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
extractAllCartItems();
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: getCartItemID });
}
}
if (pageLevelLoader) {
return (
<div className="w-full min-h-screen flex justify-center items-center">
<PulseLoader
color={"#000000"}
loading={pageLevelLoader}
size={30}
data-testid="loader"
/>
</div>
);
}
return (
<CommonCart
componentLevelLoader={componentLevelLoader}
handleDeleteCartItem={handleDeleteCartItem}
cartItems={cartItems}
/>
);
}
================================================
FILE: src/app/checkout/page.js
================================================
"use client";
import Notification from "@/components/Notification";
import { GlobalContext } from "@/context";
import { fetchAllAddresses } from "@/services/address";
import { createNewOrder } from "@/services/order";
import { callStripeSession } from "@/services/stripe";
import { loadStripe } from "@stripe/stripe-js";
import { useRouter, useSearchParams } from "next/navigation";
import { useContext, useEffect, useState } from "react";
import { PulseLoader } from "react-spinners";
import { toast } from "react-toastify";
export default function Checkout() {
const {
cartItems,
user,
addresses,
setAddresses,
checkoutFormData,
setCheckoutFormData,
} = useContext(GlobalContext);
const [selectedAddress, setSelectedAddress] = useState(null);
const [isOrderProcessing, setIsOrderProcessing] = useState(false);
const [orderSuccess, setOrderSuccess] = useState(false);
const router = useRouter();
const params = useSearchParams();
const publishableKey =
"pk_test_51NMv6ZSC6E6fnyMeRIEb9oEXdGRCC9yrBTT4xWHgcjWOuFcqFiAHErvaS50K1hl5t5WJXVGfLLWxvb705IWJhA3300yCcrMnlM";
const stripePromise = loadStripe(publishableKey);
console.log(cartItems);
async function getAllAddresses() {
const res = await fetchAllAddresses(user?._id);
if (res.success) {
setAddresses(res.data);
}
}
useEffect(() => {
if (user !== null) getAllAddresses();
}, [user]);
useEffect(() => {
async function createFinalOrder() {
const isStripe = JSON.parse(localStorage.getItem("stripe"));
if (
isStripe &&
params.get("status") === "success" &&
cartItems &&
cartItems.length > 0
) {
setIsOrderProcessing(true);
const getCheckoutFormData = JSON.parse(
localStorage.getItem("checkoutFormData")
);
const createFinalCheckoutFormData = {
user: user?._id,
shippingAddress: getCheckoutFormData.shippingAddress,
orderItems: cartItems.map((item) => ({
qty: 1,
product: item.productID,
})),
paymentMethod: "Stripe",
totalPrice: cartItems.reduce(
(total, item) => item.productID.price + total,
0
),
isPaid: true,
isProcessing: true,
paidAt: new Date(),
};
const res = await createNewOrder(createFinalCheckoutFormData);
if (res.success) {
setIsOrderProcessing(false);
setOrderSuccess(true);
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
} else {
setIsOrderProcessing(false);
setOrderSuccess(false);
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
}
}
}
createFinalOrder();
}, [params.get("status"), cartItems]);
function handleSelectedAddress(getAddress) {
if (getAddress._id === selectedAddress) {
setSelectedAddress(null);
setCheckoutFormData({
...checkoutFormData,
shippingAddress: {},
});
return;
}
setSelectedAddress(getAddress._id);
setCheckoutFormData({
...checkoutFormData,
shippingAddress: {
...checkoutFormData.shippingAddress,
fullName: getAddress.fullName,
city: getAddress.city,
country: getAddress.country,
postalCode: getAddress.postalCode,
address: getAddress.address,
},
});
}
async function handleCheckout() {
const stripe = await stripePromise;
const createLineItems = cartItems.map((item) => ({
price_data: {
currency: "usd",
product_data: {
images: [item.productID.imageUrl],
name: item.productID.name,
},
unit_amount: item.productID.price * 100,
},
quantity: 1,
}));
const res = await callStripeSession(createLineItems);
setIsOrderProcessing(true);
localStorage.setItem("stripe", true);
localStorage.setItem("checkoutFormData", JSON.stringify(checkoutFormData));
const { error } = await stripe.redirectToCheckout({
sessionId: res.id,
});
console.log(error);
}
console.log(checkoutFormData);
useEffect(() => {
if (orderSuccess) {
setTimeout(() => {
// setOrderSuccess(false);
router.push("/orders");
}, [2000]);
}
}, [orderSuccess]);
if (orderSuccess) {
return (
<section className="h-screen bg-gray-200">
<div className="mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto mt-8 max-w-screen-xl px-4 sm:px-6 lg:px-8 ">
<div className="bg-white shadow">
<div className="px-4 py-6 sm:px-8 sm:py-10 flex flex-col gap-5">
<h1 className="font-bold text-lg">
Your payment is successfull and you will be redirected to
orders page in 2 seconds !
</h1>
</div>
</div>
</div>
</div>
</section>
);
}
if (isOrderProcessing) {
return (
<div className="w-full min-h-screen flex justify-center items-center">
<PulseLoader
color={"#000000"}
loading={isOrderProcessing}
size={30}
data-testid="loader"
/>
</div>
);
}
return (
<div>
<div className="grid sm:px-10 lg:grid-cols-2 lg:px-20 xl:px-32">
<div className="px-4 pt-8">
<p className="font-medium text-xl">Cart Summary</p>
<div className="mt-8 space-y-3 rounded-lg border bg-white px-2 py-4 sm:px-5">
{cartItems && cartItems.length ? (
cartItems.map((item) => (
<div
className="flex flex-col rounded-lg bg-white sm:flex-row"
key={item._id}
>
<img
src={item && item.productID && item.productID.imageUrl}
alt="Cart Item"
className="m-2 h-24 w-28 rounded-md border object-cover object-center"
/>
<div className="flex w-full flex-col px-4 py-4">
<span className="font-bold">
{item && item.productID && item.productID.name}
</span>
<span className="font-semibold">
{item && item.productID && item.productID.price}
</span>
</div>
</div>
))
) : (
<div>Your cart is empty</div>
)}
</div>
</div>
<div className="mt-10 bg-gray-50 px-4 pt-8 lg:mt-0">
<p className="text-xl font-medium">Shipping address details</p>
<p className="text-gray-400 font-bold">
Complete your order by selecting address below
</p>
<div className="w-full mt-6 mr-0 mb-0 ml-0 space-y-6">
{addresses && addresses.length ? (
addresses.map((item) => (
<div
onClick={() => handleSelectedAddress(item)}
key={item._id}
className={`border p-6 ${
item._id === selectedAddress ? "border-red-900" : ""
}`}
>
<p>Name : {item.fullName}</p>
<p>Address : {item.address}</p>
<p>City : {item.city}</p>
<p>Country : {item.country}</p>
<p>PostalCode : {item.postalCode}</p>
<button className="mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide">
{item._id === selectedAddress
? "Selected Address"
: "Select Address"}
</button>
</div>
))
) : (
<p>No addresses added</p>
)}
</div>
<button
onClick={() => router.push("/account")}
className="mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
Add new address
</button>
<div className="mt-6 border-t border-b py-2">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-900">Subtotal</p>
<p className="text-lg font-bold text-gray-900">
$
{cartItems && cartItems.length
? cartItems.reduce(
(total, item) => item.productID.price + total,
0
)
: "0"}
</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-900">Shipping</p>
<p className="text-lg font-bold text-gray-900">Free</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-900">Total</p>
<p className="text-lg font-bold text-gray-900">
$
{cartItems && cartItems.length
? cartItems.reduce(
(total, item) => item.productID.price + total,
0
)
: "0"}
</p>
</div>
<div className="pb-10">
<button
disabled={
(cartItems && cartItems.length === 0) ||
Object.keys(checkoutFormData.shippingAddress).length === 0
}
onClick={handleCheckout}
className="disabled:opacity-50 mt-5 mr-5 w-full inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
Checkout
</button>
</div>
</div>
</div>
</div>
<Notification />
</div>
);
}
================================================
FILE: src/app/globals.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
min-height: 100vh;
height: 100%;
}
body::-webkit-scrollbar {
width: 5px;
}
body::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}
body::-webkit-scrollbar-thumb {
background-color: darkgrey;
outline: 1px solid slategrey;
}
================================================
FILE: src/app/layout.js
================================================
import GlobalState from '@/context'
import './globals.css'
import { Inter } from 'next/font/google'
import Navbar from '@/components/Navbar'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<GlobalState>
<Navbar/>
<main className='flex min-h-screen flex-col mt-[80px]'>{children}</main>
</GlobalState>
</body>
</html>
)
}
================================================
FILE: src/app/login/page.js
================================================
"use client";
import InputComponent from "@/components/FormElements/InputComponent";
import ComponentLevelLoader from "@/components/Loader/componentlevel";
import Notification from "@/components/Notification";
import { GlobalContext } from "@/context";
import { login } from "@/services/login";
import { loginFormControls } from "@/utils";
import Cookies from "js-cookie";
import { useRouter } from "next/navigation";
import { useContext, useEffect, useState } from "react";
import { toast } from "react-toastify";
const initialFormdata = {
email: "",
password: "",
};
export default function Login() {
const [formData, setFormData] = useState(initialFormdata);
const {
isAuthUser,
setIsAuthUser,
user,
setUser,
componentLevelLoader,
setComponentLevelLoader,
} = useContext(GlobalContext);
const router = useRouter();
console.log(formData);
function isValidForm() {
return formData &&
formData.email &&
formData.email.trim() !== "" &&
formData.password &&
formData.password.trim() !== ""
? true
: false;
}
async function handleLogin() {
setComponentLevelLoader({ loading: true, id: "" });
const res = await login(formData);
console.log(res);
if (res.success) {
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setIsAuthUser(true);
setUser(res?.finalData?.user);
setFormData(initialFormdata);
Cookies.set("token", res?.finalData?.token);
localStorage.setItem("user", JSON.stringify(res?.finalData?.user));
setComponentLevelLoader({ loading: false, id: "" });
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setIsAuthUser(false);
setComponentLevelLoader({ loading: false, id: "" });
}
}
console.log(isAuthUser, user);
useEffect(() => {
if (isAuthUser) router.push("/");
}, [isAuthUser]);
return (
<div className="bg-white relative">
<div className="flex flex-col items-center justify-between pt-0 pr-10 pb-0 pl-10 mt-8 mr-auto xl:px-5 lg:flex-row">
<div className="flex flex-col justify-center items-center w-full pr-10 pl-10 lg:flex-row">
<div className="w-full mt-10 mr-0 mb-0 ml-0 relative max-w-2xl lg:mt-0 lg:w-5/12">
<div className="flex flex-col items-center justify-start pt-10 pr-10 pb-10 pl-10 bg-white shadow-2xl rounded-xl relative z-10">
<p className="w-full text-4xl font-medium text-center font-serif">
Login
</p>
<div className="w-full mt-6 mr-0 mb-0 ml-0 relative space-y-8">
{loginFormControls.map((controlItem) =>
controlItem.componentType === "input" ? (
<InputComponent
type={controlItem.type}
placeholder={controlItem.placeholder}
label={controlItem.label}
value={formData[controlItem.id]}
onChange={(event) => {
setFormData({
...formData,
[controlItem.id]: event.target.value,
});
}}
/>
) : null
)}
<button
className="disabled:opacity-50 inline-flex w-full items-center justify-center bg-black px-6 py-4 text-lg
text-white transition-all duration-200 ease-in-out focus:shadow font-medium uppercase tracking-wide
"
disabled={!isValidForm()}
onClick={handleLogin}
>
{componentLevelLoader && componentLevelLoader.loading ? (
<ComponentLevelLoader
text={"Logging In"}
color={"#ffffff"}
loading={
componentLevelLoader && componentLevelLoader.loading
}
/>
) : (
"Login"
)}
</button>
<div className="flex flex-col gap-2">
<p>New to website ?</p>
<button
className="inline-flex w-full items-center justify-center bg-black px-6 py-4 text-lg
text-white transition-all duration-200 ease-in-out focus:shadow font-medium uppercase tracking-wide
"
onClick={() => router.push("/register")}
>
Register
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<Notification />
</div>
);
}
================================================
FILE: src/app/orders/[order-details]/page.js
================================================
"use client";
import { GlobalContext } from "@/context";
import { getOrderDetails } from "@/services/order";
import { useParams, useRouter } from "next/navigation";
import { useContext, useEffect } from "react";
import { PulseLoader } from "react-spinners";
export default function OrderDetails() {
const {
pageLevelLoader,
setPageLevelLoader,
orderDetails,
setOrderDetails,
user,
} = useContext(GlobalContext);
const params = useParams();
const router = useRouter()
async function extractOrderDetails() {
setPageLevelLoader(true);
const res = await getOrderDetails(params["order-details"]);
if (res.success) {
setPageLevelLoader(false);
setOrderDetails(res.data);
} else {
setPageLevelLoader(false);
}
console.log(res);
}
useEffect(() => {
extractOrderDetails();
}, []);
if (pageLevelLoader) {
return (
<div className="w-full min-h-screen flex justify-center items-center">
<PulseLoader
color={"#000000"}
loading={pageLevelLoader}
size={30}
data-testid="loader"
/>
</div>
);
}
return (
<div className="py-14 px-4 md:px-6">
<div className="flex justify-start items-start space-y-2 flex-col">
<h1 className="text-3xl lg:text-4xl font-bold leading-7 lg:leading-9 text-gray-900">
Order #{orderDetails && orderDetails._id}
</h1>
<p className="text-base font-medium leadong-6 text-gray-600">
{orderDetails &&
orderDetails.createdAt &&
orderDetails.createdAt.split("T")[0]}{" "}
|{" "}
{orderDetails &&
orderDetails.createdAt &&
orderDetails.createdAt.split("T")[1].split(".")[0]}
</p>
</div>
<div className="mt-10 flex flex-col justify-center xl:flex-row items-stretch w-full xl:space-x-8 md:space-y-6 xl:space-y-0">
<div className="flex flex-col justify-start items-start w-full space-y-4 md:space-y-6 xl:space-y-8">
<div className="flex flex-col justify-start items-start bg-gray-50 px-4 py-4 md:p-6 xl:p-8 w-full">
<p className="font-bol text-lg ">Your order summary</p>
{orderDetails &&
orderDetails.orderItems &&
orderDetails.orderItems.length
? orderDetails.orderItems.map((item) => (
<div
key={item._id}
className="mt-4 md:mt-6 flex flex-col md:flex-row justify-start items-start md:items-center md:space-x-6 xl:space-x-8 w-full"
>
<div className="pb-4 md:pb-8 w-full md:w-40">
<img
src={item && item.product && item.product.imageUrl}
className="w-full hidden md:block"
/>
</div>
<div className="border-b border-gray-300 md:flex-row flex-col flex justify-between items-start w-full pb-8 space-y-4 md:space-y-0">
<div className="w-full flex flex-col justify-start items-start space-y-8">
<h3 className="text-xl font-semibold leading-6 text-gray-900">
{item && item.product && item.product.name}
</h3>
</div>
<div className="w-full flex justify-between items-start space-x-8">
<h3 className="text-xl font-semibold leading-6 text-gray-900">
${item && item.product && item.product.price}
</h3>
</div>
</div>
</div>
))
: null}
</div>
<div className="flex justify-center flex-col md:flex-row items-stretch w-full space-y-4 md:space-y-0 md:space-x-5 xl:space-x-8">
<div className="flex flex-col px-4 py-6 md:p-6 xl:p-8 w-full bg-gray-50 space-y-6">
<h3 className="text-xl font-semibold leading-6 text-gray-900">
Summary
</h3>
<div className="flex justify-center items-center w-full space-y-4 flex-col border-gray-200 border-b pb-4">
<div className="flex justify-between w-full">
<p className="text-base leading-5 text-gray-800">Subtotal</p>
<p className="text-base leading-5 text-gray-900">
${orderDetails && orderDetails.totalPrice}
</p>
</div>
<div className="flex justify-between w-full">
<p className="text-base leading-5 text-gray-800">Shipping</p>
<p className="text-base leading-5 text-gray-900">Free</p>
</div>
<div className="flex justify-between w-full">
<p className="text-base leading-5 text-gray-800">Subtotal</p>
<p className="text-base leading-5 text-gray-900">
${orderDetails && orderDetails.totalPrice}
</p>
</div>
</div>
</div>
</div>
</div>
<div className="flex flex-col gap-5">
<div className="bg-gray-50 w-full xl:w-96 flex items-center md:items-start px-4 py-6 flex-col">
<h3 className="text-xl font-semibold leading-6 text-gray-900">
Customer Details
</h3>
<div className="flex flex-col justify-start items-start flex-shrink-0">
<div className="flex gap-4 justify-center flex-col w-full md:justify-start py-8 border-b border-gray-200">
<p className="text-base font-semibold leading-4 text-left text-gray-950">
Name: {user?.name}
</p>
<p className="text-base font-semibold leading-4 text-left text-gray-950">
Email: {user?.email}
</p>
</div>
</div>
</div>
<div className="flex justify-between xl:h-full items-stretch w-full flex-col mt-6 md:mt-0">
<div className="flex justify-center md:justify-start xl:flex-col flex-col md:space-x-6 lg:space-x-8 xl:space-x-0 space-y-4 md:space-y-0 xl:space-y-12 md:flex-row items-center md:items-start ">
<div className="flex justify-center md:justify-start items-center md:items-start flex-col space-y-4 xl:mt-8">
<p>Shipping Address</p>
<p>
Address :{" "}
{orderDetails && orderDetails.shippingAddress.address}
</p>
<p>City :{orderDetails && orderDetails.shippingAddress.city}</p>
<p>
Country :{" "}
{orderDetails && orderDetails.shippingAddress.country}
</p>
<p>
Postal Code :{" "}
{orderDetails && orderDetails.shippingAddress.postalCode}
</p>
</div>
</div>
</div>
<button
onClick={() => router.push(`/`)}
className="mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
Shop Again
</button>
</div>
</div>
</div>
);
}
================================================
FILE: src/app/orders/page.js
================================================
"use client";
import Notification from "@/components/Notification";
import { GlobalContext } from "@/context";
import { getAllOrdersForUser } from "@/services/order";
import { useRouter } from "next/navigation";
import { useContext, useEffect } from "react";
import { PulseLoader } from "react-spinners";
import { toast } from "react-toastify";
export default function Orders() {
const {
user,
pageLevelLoader,
setPageLevelLoader,
allOrdersForUser,
setAllOrdersForUser,
} = useContext(GlobalContext);
const router = useRouter();
async function extractAllOrders() {
setPageLevelLoader(true);
const res = await getAllOrdersForUser(user?._id);
if (res.success) {
setPageLevelLoader(false);
setAllOrdersForUser(res.data);
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
} else {
setPageLevelLoader(false);
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
}
}
useEffect(() => {
if (user !== null) extractAllOrders();
}, [user]);
console.log(allOrdersForUser);
if (pageLevelLoader) {
return (
<div className="w-full min-h-screen flex justify-center items-center">
<PulseLoader
color={"#000000"}
loading={pageLevelLoader}
size={30}
data-testid="loader"
/>
</div>
);
}
return (
<section>
<div className="mx-auto px-4 sm:px-6 lg:px-8">
<div className="mt-8 mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8">
<div>
<div className="px-4 py-6 sm:px-8 sm:py-10">
<div className="flow-root">
{allOrdersForUser && allOrdersForUser.length ? (
<ul className="flex flex-col gap-4">
{allOrdersForUser.map((item) => (
<li
key={item._id}
className="bg-gray-200 shadow p-5 flex flex-col space-y-3 py-6 text-left"
>
<div className="flex">
<h1 className="font-bold text-lg mb-3 flex-1">
#order: {item._id}
</h1>
<div className="flex items-center">
<p className="mr-3 text-sm font-medium text-gray-900">
Total paid amount
</p>
<p className="mr-3 text-2xl font-semibold text-gray-900">
${item.totalPrice}
</p>
</div>
</div>
<div className="flex gap-2">
{item.orderItems.map((orderItem, index) => (
<div key={index} className="shrink-0">
<img
alt="Order Item"
className="h-24 w-24 max-w-full rounded-lg object-cover"
src={
orderItem &&
orderItem.product &&
orderItem.product.imageUrl
}
/>
</div>
))}
</div>
<div className="flex gap-5">
<button className="disabled:opacity-50 mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide">
{item.isProcessing
? "Order is Processing"
: "Order is delivered"}
</button>
<button
onClick={() => router.push(`/orders/${item._id}`)}
className=" mt-5 mr-5 inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
View Order Details
</button>
</div>
</li>
))}
</ul>
) : null}
</div>
</div>
</div>
</div>
</div>
<Notification />
</section>
);
}
================================================
FILE: src/app/page.js
================================================
"use client";
import { GlobalContext } from "@/context";
import { getAllAdminProducts } from "@/services/product";
import { useRouter } from "next/navigation";
import { useContext, useEffect, useState } from "react";
export default function Home() {
const { isAuthUser } = useContext(GlobalContext);
const [products, setProducts] = useState([]);
const router = useRouter();
async function getListOfProducts() {
const res = await getAllAdminProducts();
if (res.success) {
setProducts(res.data);
}
}
useEffect(() => {
getListOfProducts();
}, []);
console.log(products);
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<section className="">
<div className="grid max-w-screen-xl px-4 py-8 mx-suto lg:gap-8 xl:gap-0 lg:py-16 lg:grid-cols-12">
<div className="mr-auto place-self-center lg:col-span-7">
<h1 className="max-w-2xl mb-4 text-4xl font-extrabold tracking-tight leading-none md:text-5xl xl:text-6xl">
Best Fashion Collection
</h1>
<p className="max-w-2xl mb-6 font-light text-gray-500 lg:mb-8 md:text-lg lg:text-xl">
Quisquemos sodales suscipit tortor ditaemcos condimentum de cosmo
lacus meleifend menean diverra loremous.
</p>
<button
type="button"
onClick={() => router.push("/product/listing/all-products")}
className="mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
Explore Shop Collection
</button>
</div>
<div className="hidden lg:mt-0 lg:col-span-5 lg:flex">
<img
src="https://images.unsplash.com/photo-1483985988355-763728e1935b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
alt="Explore Shop Collection"
/>
</div>
</div>
<div className="max-w-screen-xl px-4 py-8 mx-auto sm:py-12 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3 lg:items-stretch">
<div className="grid p-6 bg-gray-100 rounded place-content-center sm:p-8">
<div className="max-w-md mx-auto text-center lg:text-left">
<div>
<h2 className="text-xl font-bold text-gray-900 sm:text-3xl">
Summer Sale Collection
</h2>
</div>
<button
onClick={() => router.push("/product/listing/all-products")}
className="mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
Shop ALL
</button>
</div>
</div>
<div className="lg:col-span-2 lg:py-8">
<ul className="grid grid-cols-2 gap-4">
{products && products.length
? products
.filter((item) => item.onSale === "yes")
.splice(0, 2)
.map((productItem) => (
<li
onClick={() =>
router.push(`/product/${productItem._id}`)
}
className="cursor-pointer"
key={productItem._id}
>
<div>
<img
src={productItem.imageUrl}
alt="Sale Product Item"
className="object-cover w-full rounded aspect-square"
/>
</div>
<div className="mt-3">
<h3 className="font-medium text-gray-900">
{productItem.name}
</h3>
<p className="mt-1 text-sm text-gray-800">
${productItem.price}{" "}
<span className="text-red-700">{`(-${productItem.priceDrop}%) Off`}</span>
</p>
</div>
</li>
))
: null}
</ul>
</div>
</div>
</div>
<div className="max-w-screen-xl px-4 py-8 mx-auto sm:px-6 sm:py-12 lg:px-8">
<div className="text-center">
<h2 className="text-xl font-bold text-gray-950 sm:text-3xl">
SHOP BY CATEGORY
</h2>
</div>
<ul className="grid grid-cols-1 gap-4 mt-8 lg:grid-cols-3">
<li>
<div className="relative block group">
<img
src="https://images.unsplash.com/photo-1618898909019-010e4e234c55?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80"
className="object-cover w-full aspect-square"
/>
<div className="absolute inset-0 flex flex-col items-start justify-end p-6">
<h3 className="text-xl font-medium text-white">KIDS</h3>
<button
onClick={() => router.push("/product/listing/kids")}
className="mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
Shop Now
</button>
</div>
</div>
</li>
<li>
<div className="relative block group">
<img
src="https://images.unsplash.com/photo-1624623278313-a930126a11c3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80"
className="object-cover w-full aspect-square"
/>
<div className="absolute inset-0 flex flex-col items-start justify-end p-6">
<h3 className="text-xl font-medium text-white">WOMEN</h3>
<button
onClick={() => router.push("/product/listing/women")}
className="mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
Shop Now
</button>
</div>
</div>
</li>
<li className="lg:col-span-2 lg:col-start-2 lg:row-span-2 lg:row-start-1">
<div className="relative block group">
<img
src="https://images.unsplash.com/photo-1593795899768-947c4929449d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2672&q=80"
className="object-cover w-full aspect-square"
/>
<div className="absolute inset-0 flex flex-col items-start justify-end p-6">
<h3 className="text-xl font-medium text-white">MEN</h3>
<button
onClick={() => router.push("/product/listing/men")}
className="mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
Shop Now
</button>
</div>
</div>
</li>
</ul>
</div>
</section>
</main>
);
}
================================================
FILE: src/app/product/[details]/page.js
================================================
import CommonDetails from "@/components/CommonDetails";
import { productById } from "@/services/product";
export default async function ProductDetails({ params }) {
const productDetailsData = await productById(params.details);
console.log(productDetailsData, "sangam");
return <CommonDetails item={productDetailsData && productDetailsData.data} />;
}
================================================
FILE: src/app/product/listing/all-products/page.js
================================================
import CommonListing from "@/components/CommonListing";
import { getAllAdminProducts } from "@/services/product";
export default async function AllProducts() {
const getAllProducts = await getAllAdminProducts();
return <CommonListing data={getAllProducts && getAllProducts.data} />;
}
================================================
FILE: src/app/product/listing/kids/page.js
================================================
import CommonListing from "@/components/CommonListing";
import { productByCategory } from "@/services/product";
export default async function KidsAllProducts() {
const getAllProducts = await productByCategory("kids");
return <CommonListing data={getAllProducts && getAllProducts.data} />;
}
================================================
FILE: src/app/product/listing/men/page.js
================================================
import CommonListing from "@/components/CommonListing";
import { productByCategory } from "@/services/product";
export default async function MenAllProducts() {
const getAllProducts = await productByCategory("men");
return <CommonListing data={getAllProducts && getAllProducts.data} />;
}
================================================
FILE: src/app/product/listing/women/page.js
================================================
import CommonListing from "@/components/CommonListing";
import { productByCategory } from "@/services/product";
export default async function WomenAllProducts() {
const getAllProducts = await productByCategory("women");
return <CommonListing data={getAllProducts && getAllProducts.data} />;
}
================================================
FILE: src/app/register/page.js
================================================
"use client";
import InputComponent from "@/components/FormElements/InputComponent";
import SelectComponent from "@/components/FormElements/SelectComponent";
import ComponentLevelLoader from "@/components/Loader/componentlevel";
import Notification from "@/components/Notification";
import { GlobalContext } from "@/context";
import { registerNewUser } from "@/services/register";
import { registrationFormControls } from "@/utils";
import { useRouter } from "next/navigation";
import { useContext, useEffect, useState } from "react";
import { toast } from "react-toastify";
const initialFormData = {
name: "",
email: "",
password: "",
role: "customer",
};
export default function Register() {
const [formData, setFormData] = useState(initialFormData);
const [isRegistered, setIsRegistered] = useState(false);
const { pageLevelLoader, setPageLevelLoader , isAuthUser } = useContext(GlobalContext);
const router = useRouter()
console.log(formData);
function isFormValid() {
return formData &&
formData.name &&
formData.name.trim() !== "" &&
formData.email &&
formData.email.trim() !== "" &&
formData.password &&
formData.password.trim() !== ""
? true
: false;
}
console.log(isFormValid());
async function handleRegisterOnSubmit() {
setPageLevelLoader(true);
const data = await registerNewUser(formData);
if (data.success) {
toast.success(data.message, {
position: toast.POSITION.TOP_RIGHT,
});
setIsRegistered(true);
setPageLevelLoader(false);
setFormData(initialFormData);
} else {
toast.error(data.message, {
position: toast.POSITION.TOP_RIGHT,
});
setPageLevelLoader(false);
setFormData(initialFormData);
}
console.log(data);
}
useEffect(() => {
if (isAuthUser) router.push("/");
}, [isAuthUser]);
return (
<div className="bg-white relative">
<div className="flex flex-col items-center justify-between pt-0 pr-10 pb-0 pl-10 mt-8 mr-auto xl:px-5 lg:flex-row">
<div className="flex flex-col justify-center items-center w-full pr-10 pl-10 lg:flex-row">
<div className="w-full mt-10 mr-0 mb-0 ml-0 relative max-w-2xl lg:mt-0 lg:w-5/12">
<div className="flex flex-col items-center justify-start pt-10 pr-10 pb-10 pl-10 bg-white shadow-2xl rounded-xl relative z-10">
<p className="w-full text-4xl font-medium text-center font-serif">
{isRegistered
? "Registration Successfull !"
: "Sign up for an account"}
</p>
{isRegistered ? (
<button
className="inline-flex w-full items-center justify-center bg-black px-6 py-4 text-lg
text-white transition-all duration-200 ease-in-out focus:shadow font-medium uppercase tracking-wide
"
onClick={()=>router.push('/login')}
>
Login
</button>
) : (
<div className="w-full mt-6 mr-0 mb-0 ml-0 relative space-y-8">
{registrationFormControls.map((controlItem) =>
controlItem.componentType === "input" ? (
<InputComponent
type={controlItem.type}
placeholder={controlItem.placeholder}
label={controlItem.label}
onChange={(event) => {
setFormData({
...formData,
[controlItem.id]: event.target.value,
});
}}
value={formData[controlItem.id]}
/>
) : controlItem.componentType === "select" ? (
<SelectComponent
options={controlItem.options}
label={controlItem.label}
onChange={(event) => {
setFormData({
...formData,
[controlItem.id]: event.target.value,
});
}}
value={formData[controlItem.id]}
/>
) : null
)}
<button
className=" disabled:opacity-50 inline-flex w-full items-center justify-center bg-black px-6 py-4 text-lg
text-white transition-all duration-200 ease-in-out focus:shadow font-medium uppercase tracking-wide
"
disabled={!isFormValid()}
onClick={handleRegisterOnSubmit}
>
{pageLevelLoader ? (
<ComponentLevelLoader
text={"Registering"}
color={"#ffffff"}
loading={pageLevelLoader}
/>
) : (
"Register"
)}
</button>
</div>
)}
</div>
</div>
</div>
</div>
<Notification />
</div>
);
}
================================================
FILE: src/app/unauthorized-page/page.js
================================================
"use client";
export default function Unauthorized() {
return (
<section className="h-screen bg-gray-200">
<div className="mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto mt-8 max-w-screen-xl px-4 sm:px-6 lg:px-8 ">
<div className="bg-white shadow">
<div className="px-4 py-6 sm:px-8 sm:py-10 flex flex-col gap-5">
<h1 className="font-bold text-lg">
You don't have access to view this page!
</h1>
</div>
</div>
</div>
</div>
</section>
);
}
================================================
FILE: src/components/CartModal/index.js
================================================
"use client";
import { Fragment, useContext, useEffect } from "react";
import CommonModal from "../CommonModal";
import { GlobalContext } from "@/context";
import { deleteFromCart, getAllCartItems } from "@/services/cart";
import { toast } from "react-toastify";
import ComponentLevelLoader from "../Loader/componentlevel";
import { useRouter } from "next/navigation";
export default function CartModal() {
const {
showCartModal,
setShowCartModal,
cartItems,
setCartItems,
user,
setComponentLevelLoader,
componentLevelLoader,
} = useContext(GlobalContext);
const router = useRouter();
async function extractAllCartItems() {
const res = await getAllCartItems(user?._id);
if (res.success) {
const updatedData =
res.data && res.data.length
? res.data.map((item) => ({
...item,
productID: {
...item.productID,
price:
item.productID.onSale === "yes"
? parseInt(
(
item.productID.price -
item.productID.price * (item.productID.priceDrop / 100)
).toFixed(2)
)
: item.productID.price,
},
}))
: [];
setCartItems(updatedData);
localStorage.setItem("cartItems", JSON.stringify(updatedData));
}
console.log(res);
}
useEffect(() => {
if (user !== null) extractAllCartItems();
}, [user]);
async function handleDeleteCartItem(getCartItemID) {
setComponentLevelLoader({ loading: true, id: getCartItemID });
const res = await deleteFromCart(getCartItemID);
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
extractAllCartItems();
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: getCartItemID });
}
}
return (
<CommonModal
showButtons={true}
show={showCartModal}
setShow={setShowCartModal}
mainContent={
cartItems && cartItems.length ? (
<ul role="list" className="-my-6 divide-y divide-gray-300">
{cartItems.map((cartItem) => (
<li key={cartItem.id} className="flex py-6">
<div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200">
<img
src={
cartItem &&
cartItem.productID &&
cartItem.productID.imageUrl
}
alt="Cart Item"
className="h-full w-full object-cover object-center"
/>
</div>
<div className="ml-4 flex flex-1 flex-col">
<div>
<div className="flex justify-between text-base font-medium text-gray-900">
<h3>
<a>
{cartItem &&
cartItem.productID &&
cartItem.productID.name}
</a>
</h3>
</div>
<p className="mt-1 text-sm text-gray-600">
$
{cartItem &&
cartItem.productID &&
cartItem.productID.price}
</p>
</div>
<div className="flex flex-1 items-end justify-between text-sm">
<button
type="button"
className="font-medium text-yellow-600 sm:order-2"
onClick={() => handleDeleteCartItem(cartItem._id)}
>
{componentLevelLoader &&
componentLevelLoader.loading &&
componentLevelLoader.id === cartItem._id ? (
<ComponentLevelLoader
text={"Removing"}
color={"#000000"}
loading={
componentLevelLoader && componentLevelLoader.loading
}
/>
) : (
"Remove"
)}
</button>
</div>
</div>
</li>
))}
</ul>
) : null
}
buttonComponent={
<Fragment>
<button
type="button"
onClick={() => {
router.push("/cart");
setShowCartModal(false);
}}
className="mt-1.5 w-full inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide"
>
Go To Cart
</button>
<button
disabled={cartItems && cartItems.length === 0}
type="button"
onClick={() => {
router.push("/checkout");
setShowCartModal(false);
}}
className="mt-1.5 w-full inline-block bg-black text-white px-5 py-3 text-xs font-medium uppercase tracking-wide disabled:opacity-50"
>
Checkout
</button>
<div className="mt-6 flex justify-center text-center text-sm text-gray-600">
<button type="button" className="font-medium text-grey">
Continue Shopping
<span aria-hidden="true"> →</span>
</button>
</div>
</Fragment>
}
/>
);
}
================================================
FILE: src/components/CommonCart/index.js
================================================
"use client";
import { useRouter } from "next/navigation";
import ComponentLevelLoader from "../Loader/componentlevel";
export default function CommonCart({
cartItems = [],
handleDeleteCartItem,
componentLevelLoader,
}) {
const router = useRouter()
return (
<section className="h-screen bg-gray-100">
<div className="mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto mt-8 max-w-screen-xl px-4 sm:px-6 lg:px-8">
<div className="bg-white shadow">
<div className="px-4 py-6 sm:px-8 sm:py-10">
<div className="flow-root">
{cartItems && cartItems.length ? (
<ul className="-my-8">
{cartItems.map((cartItem) => (
<li
className="flex-col flex space-y-3 py-6 text-left sm:flex-row sm:space-x-5 sm:space-y-0"
key={cartItem.id}
>
<div className="shrink-0">
<img
src={
cartItem &&
cartItem.productID &&
cartItem.productID.imageUrl
}
alt="Product image"
className="h-24 w-25 max-w-full rounded-lg object-cover"
/>
</div>
<div className="flex flex-1 flex-col justify-between">
<div className="sm:col-gap-5 sm:grid sm:grid-cols-2">
<div className="pr-8 sm:pr-4">
<p className="text-base font-semibold text-gray-900">
{cartItem &&
cartItem.productID &&
cartItem.productID.name}
</p>
</div>
<div className="mt-4 flex gap-3 items-end justify-between sm:mt-0 sm:items-start sm:justify-end">
<p className="shrink-0 w-20 text-base font-semibold text-gray-950 sm:order-1 sm:ml-8 sm:text-right">
$
{cartItem &&
cartItem.productID &&
cartItem.productID.price}
</p>
<button
type="button"
className="font-medium text-yellow-700 sm:order-2"
onClick={() =>
handleDeleteCartItem(cartItem._id)
}
>
{componentLevelLoader &&
componentLevelLoader.loading &&
componentLevelLoader.id === cartItem._id ? (
<ComponentLevelLoader
text={"Removing"}
color={"#0000000"}
loading={
componentLevelLoader &&
componentLevelLoader.loading
}
/>
) : (
"Remove"
)}
</button>
</div>
</div>
</div>
</li>
))}
</ul>
) : (
<h1 className="font-bold text-lg">Your cart is Empty !</h1>
)}
</div>
<div className="mt-6 border-t border-b py-2">
<div className="flex items-center justify-between">
<p className="text-sm text-gray-400">Subtotal</p>
<p className="text-lg text-black font-semibold">
$
{cartItems && cartItems.length
? cartItems.reduce(
(total, item) => item.productID.price + total,
0
)
: "0"}
</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm text-gray-400">Shipping</p>
<p className="text-lg text-black font-semibold">$0</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm text-gray-400">Total</p>
<p className="text-lg text-black font-semibold">
$
{cartItems && cartItems.length
? cartItems.reduce(
(total, item) => item.productID.price + total,
0
)
: "0"}
</p>
</div>
<div className="mt-5 text-center">
<button
onClick={()=>router.push('/checkout')}
disabled={cartItems && cartItems.length === 0}
className="disabled:opacity-50 group inline-flex w-full items-center justify-center bg-black px-6 py-4 text-lg text-white font-medium uppercase tracking-wide"
>
Checkout
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
================================================
FILE: src/components/CommonDetails/index.js
================================================
"use client";
import { GlobalContext } from "@/context";
import { useContext } from "react";
import { toast } from "react-toastify";
import ComponentLevelLoader from "../Loader/componentlevel";
import { addToCart } from "@/services/cart";
import Notification from "../Notification";
export default function CommonDetails({ item }) {
const {
setComponentLevelLoader,
componentLevelLoader,
user,
setShowCartModal,
} = useContext(GlobalContext);
async function handleAddToCart(getItem) {
setComponentLevelLoader({ loading: true, id: "" });
const res = await addToCart({ productID: getItem._id, userID: user._id });
if (res.success) {
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: "" });
setShowCartModal(true);
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: "" });
setShowCartModal(true);
}
}
return (
<section className="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8">
<div className="container mx-auto px-4">
<div className="lg:col-gap-12 xl:col-gap-16 mt-8 grid grid-cols-1 gap-12 lg:mt-12 lg:grid-cols-5 lg:gap-16">
<div className="lg:col-span-3 lg:row-end-1">
<div className="lg:flex lg:items-start">
<div className="lg:order-2 lg:ml-5">
<div className="max-w-xl overflow-hidden rounded-lg">
<img
src={item.imageUrl}
className="h-full w-full max-w-full object-cover"
alt="Product Details"
/>
</div>
</div>
<div className="mt-2 w-full lg:order-1 lg:w-32 lg:flex-shrink-0">
<div className="flex flex-row items-start lg:flex-col">
<button
type="button"
className="flex-0 aspect-square mb-3 h-20 overflow-hidden rounded-lg border-2 border-gray-100 text-center"
>
<img
src={item.imageUrl}
className="h-full w-full object-cover"
alt="Product Details"
/>
</button>
<button
type="button"
className="flex-0 aspect-square mb-3 h-20 overflow-hidden rounded-lg border-2 border-gray-100 text-center"
>
<img
src={item.imageUrl}
className="h-full w-full object-cover"
alt="Product Details"
/>
</button>
</div>
</div>
</div>
</div>
<div className="lg:col-span-2 lg:row-span-2 lg:row-end-2">
<h1 className="text-2xl font-bold text-gray-900">
{item && item.name}
</h1>
<div className="mt-10 flex flex-col items-center justify-between space-y-4 botder-t border-b py-4 sm:flex-row sm:space-y-0">
<div className="flex items-end">
<h1
className={`text-3xl font-bold mr-2 ${
item.onSale === "yes" ? "line-through" : ""
}`}
>
${item && item.price}
</h1>
{item.onSale === "yes" ? (
<h1 className="text-3xl font-bold text-red-700">{`$${(
item.price -
item.price * (item.priceDrop / 100)
).toFixed(2)}`}</h1>
) : null}
</div>
<button
type="button"
onClick={() => handleAddToCart(item)}
className="mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium tracking-wide uppercase text-white"
>
{componentLevelLoader && componentLevelLoader.loading ? (
<ComponentLevelLoader
text={"Adding to Cart"}
color={"#ffffff"}
loading={
componentLevelLoader && componentLevelLoader.loading
}
/>
) : (
"Add to Cart"
)}
</button>
</div>
<ul className="mt-8 space-y-2">
<li className="flex items-center text-left text-sm font-medium text-gray-600">
{item && item.deliveryInfo}
</li>
<li className="flex items-center text-left text-sm font-medium text-gray-600">
{"Cancel anytime"}
</li>
</ul>
<div className="lg:col-span-3">
<div className="border-b border-gray-400">
<nav className="flex gap-4">
<a
href="#"
className="border-b-2 border-gray-900 py-4 text-sm font-medium text-gray-900"
>
Description
</a>
</nav>
</div>
<div className="mt-8 flow-root sm:mt-12">
{item && item.description}
</div>
</div>
</div>
</div>
</div>
<Notification/>
</section>
);
}
================================================
FILE: src/components/CommonListing/ProductButtons/index.js
================================================
"use client";
import ComponentLevelLoader from "@/components/Loader/componentlevel";
import { GlobalContext } from "@/context";
import { addToCart } from "@/services/cart";
import { deleteAProduct } from "@/services/product";
import { usePathname, useRouter } from "next/navigation";
import { useContext } from "react";
import { toast } from "react-toastify";
export default function ProductButton({ item }) {
const pathName = usePathname();
const {
setCurrentUpdatedProduct,
setComponentLevelLoader,
componentLevelLoader,
user,
showCartModal, setShowCartModal
} = useContext(GlobalContext);
const router = useRouter();
const isAdminView = pathName.includes("admin-view");
async function handleDeleteProduct(item) {
setComponentLevelLoader({ loading: true, id: item._id });
const res = await deleteAProduct(item._id);
if (res.success) {
setComponentLevelLoader({ loading: false, id: "" });
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
router.refresh();
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: "" });
}
}
async function handleAddToCart(getItem) {
setComponentLevelLoader({ loading: true, id: getItem._id });
const res = await addToCart({ productID: getItem._id, userID: user._id });
if (res.success) {
toast.success(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: "" });
setShowCartModal(true);
} else {
toast.error(res.message, {
position: toast.POSITION.TOP_RIGHT,
});
setComponentLevelLoader({ loading: false, id: "" });
setShowCartModal(true)
}
console.log(res);
}
return isAdminView ? (
<>
<button
onClick={() => {
setCurrentUpdatedProduct(item);
router.push("/admin-view/add-product");
}}
className="mt-1.5 flex w-full justify-center bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
Update
</button>
<button
onClick={() => handleDeleteProduct(item)}
className="mt-1.5 flex w-full justify-center bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
{componentLevelLoader &&
componentLevelLoader.loading &&
item._id === componentLevelLoader.id ? (
<ComponentLevelLoader
text={"Deleting Product"}
color={"#ffffff"}
loading={componentLevelLoader && componentLevelLoader.loading}
/>
) : (
"DELETE"
)}
</button>
</>
) : (
<>
<button
onClick={() => handleAddToCart(item)}
className="mt-1.5 flex w-full justify-center bg-black px-5 py-3 text-xs font-medium uppercase tracking-wide text-white"
>
{componentLevelLoader &&
componentLevelLoader.loading &&
componentLevelLoader.id === item._id ? (
<ComponentLevelLoader
text={"Adding to cart"}
color={"#ffffff"}
loading={componentLevelLoader && componentLevelLoader.loading}
/>
) : (
"Add To Cart"
)}
</button>
</>
);
}
================================================
FILE: src/components/CommonListing/ProductTile/index.js
================================================
"use client";
import { useRouter } from "next/navigation";
export default function ProductTile({ item }) {
const router = useRouter();
return (
<div onClick={()=> router.push(`/product/${item._id}`)}>
<div className="overflow-hideen aspect-w-1 aspect-h-1 h-52">
<img
src={item.imageUrl}
alt="Product image"
className="h-full w-full object-cover transition-all duration-300 group-hover:scale-125"
/>
</div>
{item.onSale === "yes" ? (
<div className="absolute top-0 m-2 rounded-full bg-black">
<p className="rounded-full p-1 text-[8px] font-bold uppercase tracking-wide text-white sm:py-1 sm:px-3">
Sale
</p>
</div>
) : null}
<div className="my-4 mx-auto flex w-10/12 flex-col items-start justify-between">
<div className="mb-2 flex">
<p
className={`mr-3 text-sm font-semibold ${
item.onSale === "yes" ? "line-through" : ""
}`}
>{`$ ${item.price}`}</p>
{item.onSale === "yes" ? (
<p className="mr-3 text-sm font-semibold text-red-700">{`$ ${(
item.price -
item.price * (item.priceDrop / 100)
).toFixed(2)}`}</p>
) : null}
{item.onSale === "yes" ? (
<p className="mr-3 text-sm font-semibold">{`-(${item.priceDrop}%)off`}</p>
) : null}
</div>
<h3 className="md-2 text-gray-400 text-sm">{item.name}</h3>
</div>
</div>
);
}
================================================
FILE: src/components/CommonListing/index.js
================================================
"use client";
import { useRouter } from "next/navigation";
import ProductButton from "./ProductButtons";
import ProductTile from "./ProductTile";
import { useEffect } from "react";
import Notification from "../Notification";
export default function CommonListing({ data }) {
const router = useRouter();
useEffect(() => {
router.refresh();
}, []);
return (
<section className="bg-white py-12 sm:py-16">
<div className="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8">
<div className="mt-10 grid grid-cols-2 gap-6 sm:grid-cols-4 sm:gap-4 lg:mt-16">
{data && data.length
? data.map((item) => (
<article
className="relative flex flex-col overflow-hidden border cursor-pointer"
key={item._id}
>
<ProductTile item={item} />
<ProductButton item={item} />
</article>
))
: null}
</div>
</div>
<Notification/>
</section>
);
}
================================================
FILE: src/components/CommonModal/index.js
================================================
"use client";
import { Dialog, Transition } from "@headlessui/react";
import { Fragment } from "react";
export default function CommonModal({
modalTitle,
mainContent,
showButtons,
buttonComponent,
show,
setShow,
showModalTitle
}) {
return (
<Transition.Root show={show} as={Fragment}>
<Dialog as="div" className={"relative z-10"} onClose={setShow}>
<Transition.Child
as={Fragment}
enter="ease-in-out duration-900"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 overflow-hidden">
<div className="absolute inset-0 overflow-hidden">
<div className="fixed inset-y-0 right-0 flex max-w-full pl-10">
<Transition.Child
as={Fragment}
enter="ease-in-out duration-900"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Panel className={"w-screen max-w-md"}>
<div className="flex h-full flex-col overflow-y-scroll bg-white shadow-xl">
<div className="flex-1 overflow-y-auto px-4 py-6 sm:px-6">
{
showModalTitle ? <div className="flex items-start justify-between">
<Dialog.Title>{modalTitle}</Dialog.Title>
</div> : null
}
<div className="mt-20">{mainContent}</div>
</div>
{showButtons ? (
<div className="border-none px-4 py-6 sm:px-6">
{buttonComponent}
</div>
) : null}
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
);
}
================================================
FILE: src/components/FormElements/InputComponent/index.js
================================================
export default function InputComponent({
label,
placeholder,
onChange,
value,
type,
}) {
return (
<div className="relative">
<p className=" pt-0 pr-2 pb-0 pl-2 absolute -mt-3 mr-0 mb-0 ml-2 font-medium text-gray-600 bg-white">
{label}
</p>
<input
placeholder={placeholder}
type={type || "text"}
value={value}
onChange={onChange}
className="border placeholder-gray-400 focus:outline-none focus:border-black w-full pt-4 pr-4 pb-4 pl-4 mr-0 mt-0 ml-0 text-base block bg-white border-gray-300 rounded-md"
/>
</div>
);
}
================================================
FILE: src/components/FormElements/SelectComponent/index.js
================================================
export default function SelectComponent({
label,
value,
onChange,
options = [],
}) {
return (
<div className="relative">
<p className=" pt-0 pr-2 absolute pb-0 pl-2 -mt-3 mr-0 mb-0 ml-2 font-medium text-gray-600 bg-white">
{label}
</p>
<select
value={value}
onChange={onChange}
className="border placeholder-gray-400 focus:outline-none focus:border-black w-full pt-4 pr-4 pb-4 pl-4 mr-0 mt-0 ml-0 text-base block bg-white border-gray-300 rounded-md"
>
{options && options.length ? (
options.map((optionItem) => (
<option
id={optionItem.id}
value={optionItem.id}
key={optionItem.id}
>
{optionItem.label}
</option>
))
) : (
<option id="" value={""}>
Select
</option>
)}
</select>
</div>
);
}
================================================
FILE: src/components/FormElements/TileComponent/index.js
================================================
export default function TileComponent({ data, selected = [], onClick }) {
return data && data.length ? (
<div className="mt-3 flex flex-wrap items-center gap-1">
{data.map((dataItem) => (
<label
onClick={() => onClick(dataItem)}
className={`cursor-pointer ${
selected &&
selected.length &&
selected.map((item) => item.id).indexOf(dataItem.id) !== -1
? "bg-black"
: ""
}`}
key={dataItem.id}
>
<span
className={`rounded-lg border border-black px-6 py-2 font-bold ${
selected &&
selected.length &&
selected.map((item) => item.id).indexOf(dataItem.id) !== -1
? "text-white"
: ""
}`}
>
{dataItem.label}
</span>
</label>
))}
</div>
) : null;
}
================================================
FILE: src/components/Loader/componentlevel/index.js
================================================
"use client";
import { PulseLoader } from "react-spinners";
export default function ComponentLevelLoader({ text, color, loading, size }) {
return (
<span className="flex gap-1 items-center">
{text}
<PulseLoader
color={color}
loading={loading}
size={size || 10}
data-testid="loader"
/>
</span>
);
}
================================================
FILE: src/components/Navbar/index.js
================================================
"use client";
import { GlobalContext } from "@/context";
import { adminNavOptions, navOptions } from "@/utils";
import { Fragment, useContext, useEffect } from "react";
import CommonModal from "../CommonModal";
import Cookies from "js-cookie";
import { usePathname, useRouter } from "next/navigation";
import CartModal from "../CartModal";
function NavItems({ isModalView = false, isAdminView, router }) {
return (
<div
className={`items-center justify-between w-full md:flex md:w-auto ${
isModalView ? "" : "hidden"
}`}
id="nav-items"
>
<ul
className={`flex flex-col p-4 md:p-0 mt-4 font-medium rounded-lg md:flex-row md:space-x-8 md:mt-0 md:border-0 bg-white ${
isModalView ? "border-none" : "border border-gray-100"
}`}
>
{isAdminView
? adminNavOptions.map((item) => (
<li
className="cursor-pointer block py-2 pl-3 pr-4 text-gray-900 rounded md:p-0"
key={item.id}
onClick={() => router.push(item.path)}
>
{item.label}
</li>
))
: navOptions.map((item) => (
<li
className="cursor-pointer block py-2 pl-3 pr-4 text-gray-900 rounded md:p-0"
key={item.id}
onClick={() => router.push(item.path)}
>
{item.label}
</li>
))}
</ul>
</div>
);
}
export default function Navbar() {
const { showNavModal, setShowNavModal } = useContext(GlobalContext);
const {
user,
isAuthUser,
setIsAuthUser,
setUser,
currentUpdatedProduct,
setCurrentUpdatedProduct,
showCartModal,
setShowCartModal
} = useContext(GlobalContext);
const pathName = usePathname();
const router = useRouter();
console.log(currentUpdatedProduct, "navbar");
useEffect(() => {
if (
pathName !== "/admin-view/add-product" &&
currentUpdatedProduct !== null
)
setCurrentUpdatedProduct(null);
}, [pathName]);
function handleLogout() {
setIsAuthUser(false);
setUser(null);
Cookies.remove("token");
localStorage.clear();
router.push("/");
}
const isAdminView = pathName.includes("admin-view");
return (
<>
<nav className="bg-white fixed w-full z-20 top-0 left-0 border-b border-gray-200">
<div className="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<div
onClick={() => router.push("/")}
className="flex items-center cursor-pointer"
>
<span className="slef-center text-2xl font-semibold whitespace-nowrap">
Ecommercery
</span>
</div>
<div className="flex md:order-2 gap-2">
{!isAdminView && isAuthUser ? (
<Fragment>
<button
className={
"mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium upprcase tracking-wide text-white"
}
onClick={()=>router.push('/account')}
>
Account
</button>
<button
className={
"mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium upprcase tracking-wide text-white"
}
onClick={()=> setShowCartModal(true)}
>
Cart
</button>
</Fragment>
) : null}
{user?.role === "admin" ? (
isAdminView ? (
<button
className={
"mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium upprcase tracking-wide text-white"
}
onClick={() => router.push("/")}
>
Client View
</button>
) : (
<button
onClick={() => router.push("/admin-view")}
className={
"mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium upprcase tracking-wide text-white"
}
>
Admin View
</button>
)
) : null}
{isAuthUser ? (
<button
onClick={handleLogout}
className={
"mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium upprcase tracking-wide text-white"
}
>
Logout
</button>
) : (
<button
onClick={() => router.push("/login")}
className={
"mt-1.5 inline-block bg-black px-5 py-3 text-xs font-medium upprcase tracking-wide text-white"
}
>
Login
</button>
)}
<button
data-collapse-toggle="navbar-sticky"
type="button"
className="inline-flex items-center p-2 text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
aria-controls="navbar-sticky"
aria-expanded="false"
onClick={() => setShowNavModal(true)}
>
<span className="sr-only">Open main menu</span>
<svg
className="w-6 h-6"
aria-hidden="true"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clip-rule="evenodd"
></path>
</svg>
</button>
</div>
<NavItems router={router} isAdminView={isAdminView} />
</div>
</nav>
<CommonModal
showModalTitle={false}
mainContent={
<NavItems
router={router}
isModalView={true}
isAdminView={isAdminView}
/>
}
show={showNavModal}
setShow={setShowNavModal}
/>
{showCartModal && <CartModal />}
</>
);
}
================================================
FILE: src/components/Notification/index.js
================================================
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
export default function Notification() {
return (
<ToastContainer
position="top-right"
autoClose={4000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
pauseOnFocusLoss
draggable
pauseOnHover
theme="light"
rtl={false}
/>
);
}
================================================
FILE: src/context/index.js
================================================
"use client";
import Cookies from "js-cookie";
import { usePathname, useRouter } from "next/navigation";
import { createContext, useEffect, useState } from "react";
export const GlobalContext = createContext(null);
export const initialCheckoutFormData = {
shippingAddress: {},
paymentMethod: "",
totalPrice: 0,
isPaid: false,
paidAt: new Date(),
isProcessing: true,
};
const protectedRoutes = ["cart", "checkout", "account", "orders", "admin-view"];
const protectedAdminRoutes = [
"/admin-view",
"/admin-view/add-product",
"/admin-view/all-products",
];
export default function GlobalState({ children }) {
const [showNavModal, setShowNavModal] = useState(false);
const [pageLevelLoader, setPageLevelLoader] = useState(true);
const [componentLevelLoader, setComponentLevelLoader] = useState({
loading: false,
id: "",
});
const [isAuthUser, setIsAuthUser] = useState(null);
const [user, setUser] = useState(null);
const [currentUpdatedProduct, setCurrentUpdatedProduct] = useState(null);
const [showCartModal, setShowCartModal] = useState(false);
const [cartItems, setCartItems] = useState([]);
const [addresses, setAddresses] = useState([]);
const [addressFormData, setAddressFormData] = useState({
fullName: "",
city: "",
country: "",
postalCode: "",
address: "",
});
const [checkoutFormData, setCheckoutFormData] = useState(
initialCheckoutFormData
);
const [allOrdersForUser, setAllOrdersForUser] = useState([]);
const [orderDetails, setOrderDetails] = useState(null);
const [allOrdersForAllUsers, setAllOrdersForAllUsers] = useState([]);
const router = useRouter();
const pathName = usePathname();
useEffect(() => {
if (Cookies.get("token") !== undefined) {
setIsAuthUser(true);
const userData = JSON.parse(localStorage.getItem("user")) || {};
const getCartItems = JSON.parse(localStorage.getItem("cartItems")) || [];
setUser(userData);
setCartItems(getCartItems);
} else {
setIsAuthUser(false);
setUser({}); //unauthenticated user
}
}, [Cookies]);
useEffect(() => {
if (
pathName !== "/register" &&
!pathName.includes("product") &&
pathName !== "/" &&
user &&
Object.keys(user).length === 0 &&
protectedRoutes.includes(pathName) > -1
)
router.push("/login");
}, [user, pathName]);
useEffect(() => {
if (
user !== null &&
user &&
Object.keys(user).length > 0 &&
user?.role !== "admin" &&
protectedAdminRoutes.indexOf(pathName) > -1
)
router.push("/unauthorized-page");
}, [user, pathName]);
return (
<GlobalContext.Provider
value={{
showNavModal,
setShowNavModal,
pageLevelLoader,
setPageLevelLoader,
isAuthUser,
setIsAuthUser,
user,
setUser,
componentLevelLoader,
setComponentLevelLoader,
currentUpdatedProduct,
setCurrentUpdatedProduct,
showCartModal,
setShowCartModal,
cartItems,
setCartItems,
addresses,
setAddresses,
addressFormData,
setAddressFormData,
checkoutFormData,
setCheckoutFormData,
allOrdersForUser,
setAllOrdersForUser,
orderDetails,
setOrderDetails,
allOrdersForAllUsers,
setAllOrdersForAllUsers,
}}
>
{children}
</GlobalContext.Provider>
);
}
================================================
FILE: src/database/index.js
================================================
import mongoose from "mongoose";
const configOptions = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
const connectToDB = async () => {
const connectionUrl =
"your-mongodb-url";
mongoose
.connect(connectionUrl, configOptions)
.then(() => console.log("Ecommerce database connected successfully!"))
.catch((err) =>
console.log(`Getting Error from DB connection ${err.message}`)
);
};
export default connectToDB;
================================================
FILE: src/middleware/AuthUser.js
================================================
import jwt from "jsonwebtoken";
export const dynamic = "force-dynamic";
const AuthUser = async (req) => {
const token = req.headers.get("Authorization")?.split(" ")[1];
if (!token) return false;
try {
const extractAuthUserInfo = jwt.verify(token, "default_secret_key");
if (extractAuthUserInfo) return extractAuthUserInfo;
} catch (e) {
console.log(e);
return false;
}
};
export default AuthUser;
================================================
FILE: src/models/address.js
================================================
import mongoose from "mongoose";
const NewAddressSchema = new mongoose.Schema(
{
userID: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
fullName: String,
address: String,
city: String,
country: String,
postalCode: String,
},
{ timestamps: true }
);
const Address =
mongoose.models.Address || mongoose.model("Address", NewAddressSchema);
export default Address;
================================================
FILE: src/models/cart.js
================================================
import mongoose from "mongoose";
const CartSchema = new mongoose.Schema(
{
userID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
productID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Products',
},
quantity: {
type: Number,
required: true,
default: 1,
},
},
{ timestamps: true }
);
const Cart = mongoose.models.Cart || mongoose.model("Cart", CartSchema);
export default Cart;
================================================
FILE: src/models/order.js
================================================
import mongoose from "mongoose";
import Product from "./product";
import User from "./user";
const OrderSchema = new mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
orderItems: [
{
qty: { type: Number, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "Products",
},
},
],
shippingAddress: {
fullName: { type: String, required: true },
address: { type: String, required: true },
city: { type: String, required: true },
country: { type: String, required: true },
postalCode: { type: String, required: true },
},
paymentMethod: { type: String, required: true, default: "Stripe" },
totalPrice: { type: Number, required: true },
isPaid: { type: Boolean, required: true },
paidAt: { type: Date, required: true },
isProcessing: { type: Boolean, required: true },
},
{ timestamps: true }
);
const Order = mongoose.models.Order || mongoose.model("Order", OrderSchema);
export default Order;
================================================
FILE: src/models/product.js
================================================
import mongoose from "mongoose";
const ProductSchema = new mongoose.Schema(
{
name: String,
description: String,
price: Number,
category: String,
sizes: Array,
deliveryInfo: String,
onSale: String,
priceDrop: Number,
imageUrl: String,
},
{ timestamps: true }
);
const Product =
mongoose.models.Products || mongoose.model("Products", ProductSchema);
export default Product;
================================================
FILE: src/models/user.js
================================================
import mongoose from "mongoose";
const UserSchema = new mongoose.Schema({
name: String,
email: String,
password: String,
role: String,
});
const User = mongoose.models.User || mongoose.model("User", UserSchema);
export default User;
================================================
FILE: src/services/address/index.js
================================================
import Cookies from "js-cookie";
export const addNewAddress = async (formData) => {
try {
const res = await fetch("/api/address/add-new-address", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const fetchAllAddresses = async (id) => {
try {
const res = await fetch(`/api/address/get-all-address?id=${id}`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const updateAddress = async (formData) => {
try {
const res = await fetch("/api/address/update-address", {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const deleteAddress = async (id) => {
try {
const res = await fetch(`/api/address/delete-address?id=${id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
================================================
FILE: src/services/cart/index.js
================================================
import Cookies from "js-cookie";
export const addToCart = async (formData) => {
try {
const res = await fetch("/api/cart/add-to-cart", {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const getAllCartItems = async (id) => {
try {
const res = await fetch(`http://localhost:3000/api/cart/all-cart-items?id=${id}`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const deleteFromCart = async (id) => {
try {
const res = await fetch(`/api/cart/delete-from-cart?id=${id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
================================================
FILE: src/services/login/index.js
================================================
export const login = async (formData) => {
try {
const response = await fetch("/api/login", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(formData),
});
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
================================================
FILE: src/services/order/index.js
================================================
import Cookies from "js-cookie";
export const createNewOrder = async (formData) => {
try {
const res = await fetch("/api/order/create-order", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const getAllOrdersForUser = async (id) => {
try {
const res = await fetch(`/api/order/get-all-orders?id=${id}`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const getOrderDetails = async (id) => {
try {
const res = await fetch(`/api/order/order-details?id=${id}`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const getAllOrdersForAllUsers = async () => {
try {
const res = await fetch(`/api/admin/orders/get-all-orders`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const updateStatusOfOrder = async (formData) => {
try {
const res = await fetch(`/api/admin/orders/update-order`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
================================================
FILE: src/services/product/index.js
================================================
//add a new product service
import Cookies from "js-cookie";
export const addNewProduct = async (formData) => {
try {
const response = await fetch("/api/admin/add-product", {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
export const getAllAdminProducts = async () => {
try {
const res = await fetch("http://localhost:3000/api/admin/all-products", {
method: "GET",
cache: "no-store",
});
const data = await res.json();
return data;
} catch (error) {
console.log(error);
}
};
export const updateAProduct = async (formData) => {
try {
const res = await fetch("/api/admin/update-product", {
method: "PUT",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
cache: "no-store",
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const deleteAProduct = async (id) => {
try {
const res = await fetch(`/api/admin/delete-product?id=${id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${Cookies.get("token")}`,
},
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const productByCategory = async (id) => {
try {
const res = await fetch(
`http://localhost:3000/api/admin/product-by-category?id=${id}`,
{
method: "GET",
cache: "no-store",
}
);
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
export const productById = async (id) => {
try {
const res = await fetch(
`http://localhost:3000/api/admin/product-by-id?id=${id}`,
{
method: "GET",
cache: "no-store",
}
);
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
================================================
FILE: src/services/register/index.js
================================================
export const registerNewUser = async (formData) => {
try {
const response = await fetch("/api/register", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(formData),
});
const finalData = await response.json();
return finalData;
} catch (e) {
console.log("error", e);
}
};
================================================
FILE: src/services/stripe/index.js
================================================
import Cookies from "js-cookie";
export const callStripeSession = async (formData) => {
try {
const res = await fetch("/api/stripe", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${Cookies.get("token")}`,
},
body: JSON.stringify(formData),
});
const data = await res.json();
return data;
} catch (e) {
console.log(e);
}
};
================================================
FILE: src/utils/index.js
================================================
export const navOptions = [
{
id: "home",
label: "Home",
path: "/",
},
{
id: "listing",
label: "All Products",
path: "/product/listing/all-products",
},
{
id: "listingMen",
label: "Men",
path: "/product/listing/men",
},
{
id: "listingWomen",
label: "Women",
path: "/product/listing/women",
},
{
id: "listingKids",
label: "kids",
path: "/product/listing/kids",
},
];
export const adminNavOptions = [
{
id: "adminListing",
label: "Manage All Products",
path: "/admin-view/all-products",
},
{
id: "adminNewProduct",
label: "Add New Product",
path: "/admin-view/add-product",
},
];
export const registrationFormControls = [
{
id: "name",
type: "text",
placeholder: "Enter your name",
label: "Name",
componentType: "input",
},
{
id: "email",
type: "email",
placeholder: "Enter your email",
label: "Email",
componentType: "input",
},
{
id: "password",
type: "password",
placeholder: "Enter your password",
label: "Password",
componentType: "input",
},
{
id: "role",
type: "",
placeholder: "",
label: "Role",
componentType: "select",
options: [
{
id: "admin",
label: "Admin",
},
{
id: "customer",
label: "customer",
},
],
},
];
export const loginFormControls = [
{
id: "email",
type: "email",
placeholder: "Enter your email",
label: "Email",
componentType: "input",
},
{
id: "password",
type: "password",
placeholder: "Enter your password",
label: "Password",
componentType: "input",
},
];
export const adminAddProductformControls = [
{
id: "name",
type: "text",
placeholder: "Enter name",
label: "Name",
componentType: "input",
},
{
id: "price",
type: "number",
placeholder: "Enter price",
label: "Price",
componentType: "input",
},
{
id: "description",
type: "text",
placeholder: "Enter description",
label: "Description",
componentType: "input",
},
{
id: "category",
type: "",
placeholder: "",
label: "Category",
componentType: "select",
options: [
{
id: "men",
label: "Men",
},
{
id: "women",
label: "Women",
},
{
id: "kids",
label: "Kids",
},
],
},
{
id: "deliveryInfo",
type: "text",
placeholder: "Enter deliveryInfo",
label: "Delivery Info",
componentType: "input",
},
{
id: "onSale",
type: "",
placeholder: "",
label: "On Sale",
componentType: "select",
options: [
{
id: "yes",
label: "Yes",
},
{
id: "no",
label: "No",
},
],
},
{
id: "priceDrop",
type: "number",
placeholder: "Enter Price Drop",
label: "Price Drop",
componentType: "input",
},
];
export const AvailableSizes = [
{
id: "s",
label: "S",
},
{
id: "m",
label: "M",
},
{
id: "l",
label: "L",
},
];
export const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
projectId: "PROJECT_ID",
storageBucket: "STROAGE_BUCKET",
messagingSenderId: "MESSAGING_SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID",
};
export const firebaseStroageURL =
"YOUR_FIREBASE_STROAGE_URL";
export const addNewAddressFormControls = [
{
id: "fullName",
type: "input",
placeholder: "Enter your full name",
label: "Full Name",
componentType: "input",
},
{
id: "address",
type: "input",
placeholder: "Enter your full address",
label: "Address",
componentType: "input",
},
{
id: "city",
type: "input",
placeholder: "Enter your city",
label: "City",
componentType: "input",
},
{
id: "country",
type: "input",
placeholder: "Enter your country",
label: "Country",
componentType: "input",
},
{
id: "postalCode",
type: "input",
placeholder: "Enter your postal code",
label: "Postal Code",
componentType: "input",
},
];
================================================
FILE: tailwind.config.js
================================================
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},
plugins: [],
}
gitextract_v8_ctk5n/ ├── .gitignore ├── README.md ├── jsconfig.json ├── next.config.js ├── package.json ├── postcss.config.js ├── src/ │ ├── app/ │ │ ├── account/ │ │ │ └── page.js │ │ ├── admin-view/ │ │ │ ├── add-product/ │ │ │ │ └── page.js │ │ │ ├── all-products/ │ │ │ │ └── page.js │ │ │ └── page.js │ │ ├── api/ │ │ │ ├── address/ │ │ │ │ ├── add-new-address/ │ │ │ │ │ └── route.js │ │ │ │ ├── delete-address/ │ │ │ │ │ └── route.js │ │ │ │ ├── get-all-address/ │ │ │ │ │ └── route.js │ │ │ │ └── update-address/ │ │ │ │ └── route.js │ │ │ ├── admin/ │ │ │ │ ├── add-product/ │ │ │ │ │ └── route.js │ │ │ │ ├── all-products/ │ │ │ │ │ └── route.js │ │ │ │ ├── delete-product/ │ │ │ │ │ └── route.js │ │ │ │ ├── orders/ │ │ │ │ │ ├── get-all-orders/ │ │ │ │ │ │ └── route.js │ │ │ │ │ └── update-order/ │ │ │ │ │ └── route.js │ │ │ │ ├── product-by-category/ │ │ │ │ │ └── route.js │ │ │ │ ├── product-by-id/ │ │ │ │ │ └── route.js │ │ │ │ └── update-product/ │ │ │ │ └── route.js │ │ │ ├── cart/ │ │ │ │ ├── add-to-cart/ │ │ │ │ │ └── route.js │ │ │ │ ├── all-cart-items/ │ │ │ │ │ └── route.js │ │ │ │ └── delete-from-cart/ │ │ │ │ └── route.js │ │ │ ├── login/ │ │ │ │ └── route.js │ │ │ ├── order/ │ │ │ │ ├── create-order/ │ │ │ │ │ └── route.js │ │ │ │ ├── get-all-orders/ │ │ │ │ │ └── route.js │ │ │ │ └── order-details/ │ │ │ │ └── route.js │ │ │ ├── register/ │ │ │ │ └── route.js │ │ │ └── stripe/ │ │ │ └── route.js │ │ ├── cart/ │ │ │ └── page.js │ │ ├── checkout/ │ │ │ └── page.js │ │ ├── globals.css │ │ ├── layout.js │ │ ├── login/ │ │ │ └── page.js │ │ ├── orders/ │ │ │ ├── [order-details]/ │ │ │ │ └── page.js │ │ │ └── page.js │ │ ├── page.js │ │ ├── product/ │ │ │ ├── [details]/ │ │ │ │ └── page.js │ │ │ └── listing/ │ │ │ ├── all-products/ │ │ │ │ └── page.js │ │ │ ├── kids/ │ │ │ │ └── page.js │ │ │ ├── men/ │ │ │ │ └── page.js │ │ │ └── women/ │ │ │ └── page.js │ │ ├── register/ │ │ │ └── page.js │ │ └── unauthorized-page/ │ │ └── page.js │ ├── components/ │ │ ├── CartModal/ │ │ │ └── index.js │ │ ├── CommonCart/ │ │ │ └── index.js │ │ ├── CommonDetails/ │ │ │ └── index.js │ │ ├── CommonListing/ │ │ │ ├── ProductButtons/ │ │ │ │ └── index.js │ │ │ ├── ProductTile/ │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── CommonModal/ │ │ │ └── index.js │ │ ├── FormElements/ │ │ │ ├── InputComponent/ │ │ │ │ └── index.js │ │ │ ├── SelectComponent/ │ │ │ │ └── index.js │ │ │ └── TileComponent/ │ │ │ └── index.js │ │ ├── Loader/ │ │ │ └── componentlevel/ │ │ │ └── index.js │ │ ├── Navbar/ │ │ │ └── index.js │ │ └── Notification/ │ │ └── index.js │ ├── context/ │ │ └── index.js │ ├── database/ │ │ └── index.js │ ├── middleware/ │ │ └── AuthUser.js │ ├── models/ │ │ ├── address.js │ │ ├── cart.js │ │ ├── order.js │ │ ├── product.js │ │ └── user.js │ ├── services/ │ │ ├── address/ │ │ │ └── index.js │ │ ├── cart/ │ │ │ └── index.js │ │ ├── login/ │ │ │ └── index.js │ │ ├── order/ │ │ │ └── index.js │ │ ├── product/ │ │ │ └── index.js │ │ ├── register/ │ │ │ └── index.js │ │ └── stripe/ │ │ └── index.js │ └── utils/ │ └── index.js └── tailwind.config.js
SYMBOL INDEX (55 symbols across 53 files)
FILE: src/app/account/page.js
function Account (line 19) | function Account() {
FILE: src/app/admin-view/add-product/page.js
function helperForUPloadingImageToFirebase (line 38) | async function helperForUPloadingImageToFirebase(file) {
function AdminAddNewProduct (line 72) | function AdminAddNewProduct() {
FILE: src/app/admin-view/all-products/page.js
function AdminAllProducts (line 6) | async function AdminAllProducts() {
FILE: src/app/admin-view/page.js
function AdminView (line 9) | function AdminView() {
FILE: src/app/api/address/add-new-address/route.js
function POST (line 18) | async function POST(req) {
FILE: src/app/api/address/delete-address/route.js
function DELETE (line 8) | async function DELETE(req) {
FILE: src/app/api/address/get-all-address/route.js
function GET (line 8) | async function GET(req) {
FILE: src/app/api/address/update-address/route.js
function PUT (line 8) | async function PUT(req) {
FILE: src/app/api/admin/add-product/route.js
function POST (line 21) | async function POST(req) {
FILE: src/app/api/admin/all-products/route.js
function GET (line 7) | async function GET(req) {
FILE: src/app/api/admin/delete-product/route.js
function DELETE (line 8) | async function DELETE(req) {
FILE: src/app/api/admin/orders/get-all-orders/route.js
function GET (line 8) | async function GET(req) {
FILE: src/app/api/admin/orders/update-order/route.js
function PUT (line 8) | async function PUT(req) {
FILE: src/app/api/admin/product-by-category/route.js
function GET (line 7) | async function GET(req) {
FILE: src/app/api/admin/product-by-id/route.js
function GET (line 7) | async function GET(req) {
FILE: src/app/api/admin/update-product/route.js
function PUT (line 8) | async function PUT(req) {
FILE: src/app/api/cart/add-to-cart/route.js
function POST (line 14) | async function POST(req) {
FILE: src/app/api/cart/all-cart-items/route.js
function GET (line 8) | async function GET(req) {
FILE: src/app/api/cart/delete-from-cart/route.js
function DELETE (line 8) | async function DELETE(req) {
FILE: src/app/api/login/route.js
function POST (line 15) | async function POST(req) {
FILE: src/app/api/order/create-order/route.js
function POST (line 9) | async function POST(req) {
FILE: src/app/api/order/get-all-orders/route.js
function GET (line 8) | async function GET(req) {
FILE: src/app/api/order/order-details/route.js
function GET (line 8) | async function GET(req) {
FILE: src/app/api/register/route.js
function POST (line 16) | async function POST(req) {
FILE: src/app/api/stripe/route.js
function POST (line 10) | async function POST(req) {
FILE: src/app/cart/page.js
function Cart (line 10) | function Cart() {
FILE: src/app/checkout/page.js
function Checkout (line 14) | function Checkout() {
FILE: src/app/layout.js
function RootLayout (line 13) | function RootLayout({ children }) {
FILE: src/app/login/page.js
function Login (line 19) | function Login() {
FILE: src/app/orders/[order-details]/page.js
function OrderDetails (line 9) | function OrderDetails() {
FILE: src/app/orders/page.js
function Orders (line 11) | function Orders() {
FILE: src/app/page.js
function Home (line 8) | function Home() {
FILE: src/app/product/[details]/page.js
function ProductDetails (line 4) | async function ProductDetails({ params }) {
FILE: src/app/product/listing/all-products/page.js
function AllProducts (line 4) | async function AllProducts() {
FILE: src/app/product/listing/kids/page.js
function KidsAllProducts (line 4) | async function KidsAllProducts() {
FILE: src/app/product/listing/men/page.js
function MenAllProducts (line 4) | async function MenAllProducts() {
FILE: src/app/product/listing/women/page.js
function WomenAllProducts (line 4) | async function WomenAllProducts() {
FILE: src/app/register/page.js
function Register (line 21) | function Register() {
FILE: src/app/unauthorized-page/page.js
function Unauthorized (line 3) | function Unauthorized() {
FILE: src/components/CartModal/index.js
function CartModal (line 11) | function CartModal() {
FILE: src/components/CommonCart/index.js
function CommonCart (line 6) | function CommonCart({
FILE: src/components/CommonDetails/index.js
function CommonDetails (line 10) | function CommonDetails({ item }) {
FILE: src/components/CommonListing/ProductButtons/index.js
function ProductButton (line 11) | function ProductButton({ item }) {
FILE: src/components/CommonListing/ProductTile/index.js
function ProductTile (line 5) | function ProductTile({ item }) {
FILE: src/components/CommonListing/index.js
function CommonListing (line 9) | function CommonListing({ data }) {
FILE: src/components/CommonModal/index.js
function CommonModal (line 6) | function CommonModal({
FILE: src/components/FormElements/InputComponent/index.js
function InputComponent (line 1) | function InputComponent({
FILE: src/components/FormElements/SelectComponent/index.js
function SelectComponent (line 1) | function SelectComponent({
FILE: src/components/FormElements/TileComponent/index.js
function TileComponent (line 1) | function TileComponent({ data, selected = [], onClick }) {
FILE: src/components/Loader/componentlevel/index.js
function ComponentLevelLoader (line 5) | function ComponentLevelLoader({ text, color, loading, size }) {
FILE: src/components/Navbar/index.js
function NavItems (line 11) | function NavItems({ isModalView = false, isAdminView, router }) {
function Navbar (line 48) | function Navbar() {
FILE: src/components/Notification/index.js
function Notification (line 4) | function Notification() {
FILE: src/context/index.js
function GlobalState (line 26) | function GlobalState({ children }) {
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (165K chars).
[
{
"path": ".gitignore",
"chars": 368,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "README.md",
"chars": 1369,
"preview": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js"
},
{
"path": "jsconfig.json",
"chars": 77,
"preview": "{\n \"compilerOptions\": {\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n }\n}\n"
},
{
"path": "next.config.js",
"chars": 92,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {}\n\nmodule.exports = nextConfig\n"
},
{
"path": "package.json",
"chars": 743,
"preview": "{\n \"name\": \"ecommerce\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"ne"
},
{
"path": "postcss.config.js",
"chars": 82,
"preview": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n}\n"
},
{
"path": "src/app/account/page.js",
"chars": 8540,
"preview": "\"use client\";\n\nimport InputComponent from \"@/components/FormElements/InputComponent\";\nimport ComponentLevelLoader from \""
},
{
"path": "src/app/admin-view/add-product/page.js",
"chars": 6426,
"preview": "\"use client\";\n\nimport InputComponent from \"@/components/FormElements/InputComponent\";\nimport SelectComponent from \"@/com"
},
{
"path": "src/app/admin-view/all-products/page.js",
"chars": 302,
"preview": "import CommonListing from \"@/components/CommonListing\";\nimport { getAllAdminProducts } from \"@/services/product\";\n\n\n\nexp"
},
{
"path": "src/app/admin-view/page.js",
"chars": 6267,
"preview": "\"use client\";\n\nimport ComponentLevelLoader from \"@/components/Loader/componentlevel\";\nimport { GlobalContext } from \"@/c"
},
{
"path": "src/app/api/address/add-new-address/route.js",
"chars": 1728,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Address from \"@/models/addres"
},
{
"path": "src/app/api/address/delete-address/route.js",
"chars": 1247,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Address from \"@/models/addres"
},
{
"path": "src/app/api/address/get-all-address/route.js",
"chars": 1223,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Address from \"@/models/addres"
},
{
"path": "src/app/api/address/update-address/route.js",
"chars": 1258,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Address from \"@/models/addres"
},
{
"path": "src/app/api/admin/add-product/route.js",
"chars": 2103,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Product from \"@/models/produc"
},
{
"path": "src/app/api/admin/all-products/route.js",
"chars": 763,
"preview": "import connectToDB from \"@/database\";\nimport Product from \"@/models/product\";\nimport { NextResponse } from \"next/server\""
},
{
"path": "src/app/api/admin/delete-product/route.js",
"chars": 1276,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Product from \"@/models/produc"
},
{
"path": "src/app/api/admin/orders/get-all-orders/route.js",
"chars": 1091,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Order from \"@/models/order\";\n"
},
{
"path": "src/app/api/admin/orders/update-order/route.js",
"chars": 1429,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Order from \"@/models/order\";\n"
},
{
"path": "src/app/api/admin/product-by-category/route.js",
"chars": 801,
"preview": "import connectToDB from \"@/database\";\nimport Product from \"@/models/product\";\nimport { NextResponse } from \"next/server\""
},
{
"path": "src/app/api/admin/product-by-id/route.js",
"chars": 969,
"preview": "import connectToDB from \"@/database\";\nimport Product from \"@/models/product\";\nimport { NextResponse } from \"next/server\""
},
{
"path": "src/app/api/admin/update-product/route.js",
"chars": 1572,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Product from \"@/models/produc"
},
{
"path": "src/app/api/cart/add-to-cart/route.js",
"chars": 1944,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Cart from \"@/models/cart\";\nim"
},
{
"path": "src/app/api/cart/all-cart-items/route.js",
"chars": 1215,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Cart from \"@/models/cart\";\nim"
},
{
"path": "src/app/api/cart/delete-from-cart/route.js",
"chars": 1224,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Cart from \"@/models/cart\";\nim"
},
{
"path": "src/app/api/login/route.js",
"chars": 1799,
"preview": "import connectToDB from \"@/database\";\nimport User from \"@/models/user\";\nimport { compare } from \"bcryptjs\";\nimport Joi f"
},
{
"path": "src/app/api/order/create-order/route.js",
"chars": 1130,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Cart from \"@/models/cart\";\nim"
},
{
"path": "src/app/api/order/get-all-orders/route.js",
"chars": 1116,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Order from \"@/models/order\";\n"
},
{
"path": "src/app/api/order/order-details/route.js",
"chars": 1256,
"preview": "import connectToDB from \"@/database\";\nimport AuthUser from \"@/middleware/AuthUser\";\nimport Order from \"@/models/order\";\n"
},
{
"path": "src/app/api/register/route.js",
"chars": 1644,
"preview": "import connectToDB from \"@/database\";\nimport User from \"@/models/user\";\nimport { hash } from \"bcryptjs\";\nimport Joi from"
},
{
"path": "src/app/api/stripe/route.js",
"chars": 1139,
"preview": "import AuthUser from \"@/middleware/AuthUser\";\nimport { NextResponse } from \"next/server\";\n\nconst stripe = require(\"strip"
},
{
"path": "src/app/cart/page.js",
"chars": 2540,
"preview": "\"use client\";\n\nimport CommonCart from \"@/components/CommonCart\";\nimport { GlobalContext } from \"@/context\";\nimport { del"
},
{
"path": "src/app/checkout/page.js",
"chars": 10141,
"preview": "\"use client\";\n\nimport Notification from \"@/components/Notification\";\nimport { GlobalContext } from \"@/context\";\nimport {"
},
{
"path": "src/app/globals.css",
"chars": 652,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n:root {\n --foreground-rgb: 0, 0, 0;\n --background-start-rg"
},
{
"path": "src/app/layout.js",
"chars": 595,
"preview": "import GlobalState from '@/context'\nimport './globals.css'\nimport { Inter } from 'next/font/google'\nimport Navbar from '"
},
{
"path": "src/app/login/page.js",
"chars": 4836,
"preview": "\"use client\";\n\nimport InputComponent from \"@/components/FormElements/InputComponent\";\nimport ComponentLevelLoader from \""
},
{
"path": "src/app/orders/[order-details]/page.js",
"chars": 7379,
"preview": "\"use client\";\n\nimport { GlobalContext } from \"@/context\";\nimport { getOrderDetails } from \"@/services/order\";\nimport { u"
},
{
"path": "src/app/orders/page.js",
"chars": 4459,
"preview": "\"use client\";\n\nimport Notification from \"@/components/Notification\";\nimport { GlobalContext } from \"@/context\";\nimport {"
},
{
"path": "src/app/page.js",
"chars": 7627,
"preview": "\"use client\";\n\nimport { GlobalContext } from \"@/context\";\nimport { getAllAdminProducts } from \"@/services/product\";\nimpo"
},
{
"path": "src/app/product/[details]/page.js",
"chars": 360,
"preview": "import CommonDetails from \"@/components/CommonDetails\";\nimport { productById } from \"@/services/product\";\n\nexport defaul"
},
{
"path": "src/app/product/listing/all-products/page.js",
"chars": 291,
"preview": "import CommonListing from \"@/components/CommonListing\";\nimport { getAllAdminProducts } from \"@/services/product\";\n\nexpor"
},
{
"path": "src/app/product/listing/kids/page.js",
"chars": 297,
"preview": "import CommonListing from \"@/components/CommonListing\";\nimport { productByCategory } from \"@/services/product\";\n\nexport "
},
{
"path": "src/app/product/listing/men/page.js",
"chars": 295,
"preview": "import CommonListing from \"@/components/CommonListing\";\nimport { productByCategory } from \"@/services/product\";\n\nexport "
},
{
"path": "src/app/product/listing/women/page.js",
"chars": 299,
"preview": "import CommonListing from \"@/components/CommonListing\";\nimport { productByCategory } from \"@/services/product\";\n\nexport "
},
{
"path": "src/app/register/page.js",
"chars": 5303,
"preview": "\"use client\";\n\nimport InputComponent from \"@/components/FormElements/InputComponent\";\nimport SelectComponent from \"@/com"
},
{
"path": "src/app/unauthorized-page/page.js",
"chars": 577,
"preview": "\"use client\";\n\nexport default function Unauthorized() {\n return (\n <section className=\"h-screen bg-gray-200\">\n "
},
{
"path": "src/components/CartModal/index.js",
"chars": 5812,
"preview": "\"use client\";\n\nimport { Fragment, useContext, useEffect } from \"react\";\nimport CommonModal from \"../CommonModal\";\nimport"
},
{
"path": "src/components/CommonCart/index.js",
"chars": 5798,
"preview": "\"use client\";\n\nimport { useRouter } from \"next/navigation\";\nimport ComponentLevelLoader from \"../Loader/componentlevel\";"
},
{
"path": "src/components/CommonDetails/index.js",
"chars": 5468,
"preview": "\"use client\";\n\nimport { GlobalContext } from \"@/context\";\nimport { useContext } from \"react\";\nimport { toast } from \"rea"
},
{
"path": "src/components/CommonListing/ProductButtons/index.js",
"chars": 3360,
"preview": "\"use client\";\n\nimport ComponentLevelLoader from \"@/components/Loader/componentlevel\";\nimport { GlobalContext } from \"@/c"
},
{
"path": "src/components/CommonListing/ProductTile/index.js",
"chars": 1555,
"preview": "\"use client\";\n\nimport { useRouter } from \"next/navigation\";\n\nexport default function ProductTile({ item }) {\n const rou"
},
{
"path": "src/components/CommonListing/index.js",
"chars": 1042,
"preview": "\"use client\";\n\nimport { useRouter } from \"next/navigation\";\nimport ProductButton from \"./ProductButtons\";\nimport Product"
},
{
"path": "src/components/CommonModal/index.js",
"chars": 2256,
"preview": "\"use client\";\n\nimport { Dialog, Transition } from \"@headlessui/react\";\nimport { Fragment } from \"react\";\n\nexport default"
},
{
"path": "src/components/FormElements/InputComponent/index.js",
"chars": 610,
"preview": "export default function InputComponent({\n label,\n placeholder,\n onChange,\n value,\n type,\n}) {\n return (\n <div c"
},
{
"path": "src/components/FormElements/SelectComponent/index.js",
"chars": 940,
"preview": "export default function SelectComponent({\n label,\n value,\n onChange,\n options = [],\n}) {\n return (\n <div classNa"
},
{
"path": "src/components/FormElements/TileComponent/index.js",
"chars": 930,
"preview": "export default function TileComponent({ data, selected = [], onClick }) {\n return data && data.length ? (\n <div clas"
},
{
"path": "src/components/Loader/componentlevel/index.js",
"chars": 362,
"preview": "\"use client\";\n\nimport { PulseLoader } from \"react-spinners\";\n\nexport default function ComponentLevelLoader({ text, color"
},
{
"path": "src/components/Navbar/index.js",
"chars": 6528,
"preview": "\"use client\";\n\nimport { GlobalContext } from \"@/context\";\nimport { adminNavOptions, navOptions } from \"@/utils\";\nimport "
},
{
"path": "src/components/Notification/index.js",
"chars": 405,
"preview": "import { ToastContainer } from \"react-toastify\";\nimport \"react-toastify/dist/ReactToastify.css\";\n\nexport default functio"
},
{
"path": "src/context/index.js",
"chars": 3484,
"preview": "\"use client\";\n\nimport Cookies from \"js-cookie\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport { creat"
},
{
"path": "src/database/index.js",
"chars": 455,
"preview": "import mongoose from \"mongoose\";\n\nconst configOptions = {\n useNewUrlParser: true,\n useUnifiedTopology: true,\n};\n\nconst"
},
{
"path": "src/middleware/AuthUser.js",
"chars": 428,
"preview": "import jwt from \"jsonwebtoken\";\n\nexport const dynamic = \"force-dynamic\";\n\nconst AuthUser = async (req) => {\n const toke"
},
{
"path": "src/models/address.js",
"chars": 421,
"preview": "import mongoose from \"mongoose\";\n\nconst NewAddressSchema = new mongoose.Schema(\n {\n userID: {\n type: mongoose.S"
},
{
"path": "src/models/cart.js",
"chars": 476,
"preview": "import mongoose from \"mongoose\";\n\nconst CartSchema = new mongoose.Schema(\n {\n userID: {\n type: mongoose.Schem"
},
{
"path": "src/models/order.js",
"chars": 1111,
"preview": "import mongoose from \"mongoose\";\nimport Product from \"./product\";\nimport User from \"./user\";\n\nconst OrderSchema = new mo"
},
{
"path": "src/models/product.js",
"chars": 420,
"preview": "import mongoose from \"mongoose\";\n\nconst ProductSchema = new mongoose.Schema(\n {\n name: String,\n description: Stri"
},
{
"path": "src/models/user.js",
"chars": 244,
"preview": "import mongoose from \"mongoose\";\n\nconst UserSchema = new mongoose.Schema({\n name: String,\n email: String,\n password: "
},
{
"path": "src/services/address/index.js",
"chars": 1533,
"preview": "import Cookies from \"js-cookie\";\n\nexport const addNewAddress = async (formData) => {\n try {\n const res = await fetch"
},
{
"path": "src/services/cart/index.js",
"chars": 1125,
"preview": "import Cookies from \"js-cookie\";\n\nexport const addToCart = async (formData) => {\n try {\n const res = await fetch(\"/a"
},
{
"path": "src/services/login/index.js",
"chars": 348,
"preview": "export const login = async (formData) => {\n try {\n const response = await fetch(\"/api/login\", {\n method: \"POST\""
},
{
"path": "src/services/order/index.js",
"chars": 1870,
"preview": "import Cookies from \"js-cookie\";\n\nexport const createNewOrder = async (formData) => {\n try {\n const res = await fetc"
},
{
"path": "src/services/product/index.js",
"chars": 2196,
"preview": "//add a new product service\n\nimport Cookies from \"js-cookie\";\n\nexport const addNewProduct = async (formData) => {\n try "
},
{
"path": "src/services/register/index.js",
"chars": 372,
"preview": "export const registerNewUser = async (formData) => {\n try {\n const response = await fetch(\"/api/register\", {\n m"
},
{
"path": "src/services/stripe/index.js",
"chars": 434,
"preview": "import Cookies from \"js-cookie\";\n\nexport const callStripeSession = async (formData) => {\n try {\n const res = await f"
},
{
"path": "src/utils/index.js",
"chars": 4198,
"preview": "export const navOptions = [\n {\n id: \"home\",\n label: \"Home\",\n path: \"/\",\n },\n {\n id: \"listing\",\n label:"
},
{
"path": "tailwind.config.js",
"chars": 480,
"preview": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n './src/pages/**/*.{js,ts,jsx,tsx,mdx}',\n"
}
]
About this extraction
This page contains the full source code of the sangammukherjee/NextJS-Ecommerce-2023 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (148.9 KB), approximately 37.1k tokens, and a symbol index with 55 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.