├── .gitignore ├── server ├── constants │ └── index.js ├── nodemon.json ├── public │ └── images │ │ ├── avatar.png │ │ └── products │ │ ├── cap.webp │ │ ├── doll.webp │ │ ├── table.webp │ │ ├── watch.webp │ │ ├── basket.webp │ │ ├── clockBlack.webp │ │ ├── eyeGlass.webp │ │ ├── flowerBase.webp │ │ ├── headPhone.webp │ │ ├── roundBowl.webp │ │ ├── backPackGray.webp │ │ ├── backPackBlack.webp │ │ └── productOfTheYear.webp ├── vercel.json ├── routes │ ├── checkout.mjs │ ├── dashboardRoute.mjs │ ├── paymentRoute.js │ ├── brandRoute.mjs │ ├── categoryRoute.mjs │ ├── contactRoute.js │ ├── orderRoute.mjs │ ├── productRoute.mjs │ └── userRoute.mjs ├── .env ├── config │ ├── mongodb.js │ └── cloudinary.js ├── models │ ├── categoryModel.js │ ├── brandModel.js │ ├── productModel.js │ ├── contactModel.js │ ├── userModel.js │ └── orderModel.js ├── package.json ├── middleware │ ├── avatarUpload.mjs │ ├── userAuth.js │ ├── adminAuth.js │ └── multer.mjs ├── createAdmin.mjs ├── checkDB.mjs ├── index.mjs └── controllers │ └── checkoutController.mjs ├── client ├── src │ ├── components │ │ ├── Sale.jsx │ │ ├── ui │ │ │ ├── cn.js │ │ │ ├── title.jsx │ │ │ ├── text-design.jsx │ │ │ └── button.jsx │ │ ├── MobileNavigation.jsx │ │ ├── Container.jsx │ │ ├── Badge.jsx │ │ ├── skeletons │ │ │ ├── ProductSkeleton.jsx │ │ │ └── ProductGridSkeleton.jsx │ │ ├── Logout.jsx │ │ ├── PreviousArrow.jsx │ │ ├── NextArrow.jsx │ │ ├── products │ │ │ ├── NavTitle.jsx │ │ │ ├── Price.jsx │ │ │ ├── Brand.jsx │ │ │ ├── Category.jsx │ │ │ ├── Color.jsx │ │ │ ├── ProductBanner.jsx │ │ │ └── PaginationProductList.jsx │ │ ├── PriceFormat.jsx │ │ ├── MainLoader.jsx │ │ ├── Breadcrumbs.jsx │ │ ├── SocialLinks.jsx │ │ ├── homeProducts │ │ │ ├── ProductOfTheYear.jsx │ │ │ ├── ProductInfo.jsx │ │ │ ├── ProductsOnSale.jsx │ │ │ ├── NewArrivals.jsx │ │ │ ├── BestSellers.jsx │ │ │ └── SpecialOffers.jsx │ │ ├── CardProduct.jsx │ │ ├── ScrollToTop.jsx │ │ ├── RootLayout.jsx │ │ ├── PriceContainer.jsx │ │ ├── AddToCartButton.jsx │ │ ├── PremiumModal.jsx │ │ └── InteractiveMap.jsx │ ├── constants │ │ ├── index.js │ │ └── navigation.js │ ├── fonts │ │ ├── suse.woff2 │ │ └── open_sans.woff2 │ ├── assets │ │ ├── pdf │ │ │ └── pdf1.pdf │ │ ├── images │ │ │ ├── payment.png │ │ │ ├── contactUs.jpg │ │ │ ├── emptyCart.png │ │ │ ├── logoLight.png │ │ │ ├── orebiLogo.png │ │ │ ├── orebiLogoooo.png │ │ │ ├── products │ │ │ │ ├── cap.webp │ │ │ │ ├── avatar.png │ │ │ │ ├── basket.webp │ │ │ │ ├── doll.webp │ │ │ │ ├── table.webp │ │ │ │ ├── watch.webp │ │ │ │ ├── eyeGlass.webp │ │ │ │ ├── clockBlack.webp │ │ │ │ ├── flowerBase.webp │ │ │ │ ├── headPhone.webp │ │ │ │ ├── roundBowl.webp │ │ │ │ ├── backPackBlack.webp │ │ │ │ ├── backPackGray.webp │ │ │ │ ├── productOfTheYear.webp │ │ │ │ ├── newArrival │ │ │ │ │ ├── newArrFour.webp │ │ │ │ │ ├── newArrOne.webp │ │ │ │ │ ├── newArrTwo.webp │ │ │ │ │ └── newArrThree.webp │ │ │ │ ├── specialOffer │ │ │ │ │ ├── spfFour.webp │ │ │ │ │ ├── spfOne.webp │ │ │ │ │ ├── spfThree.webp │ │ │ │ │ └── spfTwo.webp │ │ │ │ └── bestSeller │ │ │ │ │ ├── bestSellerFour.webp │ │ │ │ │ ├── bestSellerOne.webp │ │ │ │ │ ├── bestSellerThree.webp │ │ │ │ │ └── bestSellerTwo.webp │ │ │ ├── banner │ │ │ │ ├── slideFour.png │ │ │ │ ├── slideOne.png │ │ │ │ ├── slideTwo.png │ │ │ │ ├── slideThree.png │ │ │ │ ├── bannerImgOne.jpg │ │ │ │ ├── bannerImgThree.jpg │ │ │ │ ├── bannerImgTwo.jpg │ │ │ │ ├── bannerImgTwo.webp │ │ │ │ └── bannerImgThree.webp │ │ │ ├── slideOne-removebg-preview.png │ │ │ └── index.js │ │ └── products │ │ │ ├── flower_base.jpg │ │ │ ├── table_lamp.jpg │ │ │ ├── wall_clock.jpg │ │ │ ├── pottery_base.jpg │ │ │ ├── deco_accessories.jpg │ │ │ └── basket_with_handles.jpg │ ├── pages │ │ ├── Offers.jsx │ │ ├── Product.jsx │ │ ├── Contact.jsx │ │ ├── Wishlist.jsx │ │ └── NotFound.jsx │ ├── styles │ │ ├── index.css │ │ └── base.css │ ├── helpers │ │ ├── index.js │ │ ├── firebase.js │ │ └── stockManager.js │ ├── redux │ │ ├── store.js │ │ └── orebiSlice.js │ ├── App.jsx │ └── main.jsx ├── .env ├── vercel.json ├── public │ ├── favicon.ico │ ├── robots.txt │ ├── manifest.json │ ├── vite.svg │ └── sitemap.xml ├── postcss.config.js ├── vite.config.js ├── .gitignore ├── README.md ├── config.js ├── tailwind.config.js ├── eslint.config.js ├── package.json └── index.html ├── admin ├── .env ├── config.js ├── vercel.json ├── public │ ├── favicon.ico │ └── vite.svg ├── postcss.config.js ├── src │ ├── assets │ │ ├── images │ │ │ ├── payment.png │ │ │ ├── logoLight.png │ │ │ ├── orebiLogo.png │ │ │ ├── sale │ │ │ │ ├── saleImgOne.webp │ │ │ │ ├── saleImgTwo.webp │ │ │ │ └── saleImgThree.webp │ │ │ └── index.js │ │ └── react.svg │ ├── index.css │ ├── components │ │ ├── ui │ │ │ ├── cn.js │ │ │ ├── title.jsx │ │ │ └── input.tsx │ │ ├── Container.jsx │ │ ├── ScrollToTop.jsx │ │ ├── PriceFormat.jsx │ │ ├── ProtectedRoute.jsx │ │ ├── SmallLoader.jsx │ │ ├── Loader.jsx │ │ └── PremiumModal.jsx │ ├── redux │ │ ├── store.js │ │ └── authSlice.js │ ├── pages │ │ ├── ApiDocumentation.jsx │ │ ├── Contacts.jsx │ │ ├── Analytics.jsx │ │ ├── Inventory.jsx │ │ └── Invoice.jsx │ ├── main.jsx │ ├── services │ │ └── authService.js │ ├── config │ │ └── index.js │ └── App.jsx ├── tailwind.config.js ├── vite.config.js ├── .gitignore ├── index.html ├── eslint.config.js └── package.json └── public └── thumbnail.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /server/constants/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/Sale.jsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/constants/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /admin/.env: -------------------------------------------------------------------------------- 1 | VITE_BACKEND_URL=http://localhost:8000 -------------------------------------------------------------------------------- /server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": ["public/images/*"] 3 | } 4 | -------------------------------------------------------------------------------- /admin/config.js: -------------------------------------------------------------------------------- 1 | export const serverUrl = import.meta.env.VITE_BACKEND_URL; 2 | -------------------------------------------------------------------------------- /client/.env: -------------------------------------------------------------------------------- 1 | VITE_BACKEND_URL=http://localhost:8000 2 | VITE_STRIPE_PUBLISHABLE_KEY= 3 | -------------------------------------------------------------------------------- /admin/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] 3 | } 4 | -------------------------------------------------------------------------------- /client/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] 3 | } 4 | -------------------------------------------------------------------------------- /public/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/public/thumbnail.png -------------------------------------------------------------------------------- /admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/public/favicon.ico -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/fonts/suse.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/fonts/suse.woff2 -------------------------------------------------------------------------------- /client/src/assets/pdf/pdf1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/pdf/pdf1.pdf -------------------------------------------------------------------------------- /admin/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/src/fonts/open_sans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/fonts/open_sans.woff2 -------------------------------------------------------------------------------- /server/public/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/avatar.png -------------------------------------------------------------------------------- /admin/src/assets/images/payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/src/assets/images/payment.png -------------------------------------------------------------------------------- /admin/src/assets/images/logoLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/src/assets/images/logoLight.png -------------------------------------------------------------------------------- /admin/src/assets/images/orebiLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/src/assets/images/orebiLogo.png -------------------------------------------------------------------------------- /client/src/assets/images/payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/payment.png -------------------------------------------------------------------------------- /client/src/assets/images/contactUs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/contactUs.jpg -------------------------------------------------------------------------------- /client/src/assets/images/emptyCart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/emptyCart.png -------------------------------------------------------------------------------- /client/src/assets/images/logoLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/logoLight.png -------------------------------------------------------------------------------- /client/src/assets/images/orebiLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/orebiLogo.png -------------------------------------------------------------------------------- /server/public/images/products/cap.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/cap.webp -------------------------------------------------------------------------------- /server/public/images/products/doll.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/doll.webp -------------------------------------------------------------------------------- /server/public/images/products/table.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/table.webp -------------------------------------------------------------------------------- /server/public/images/products/watch.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/watch.webp -------------------------------------------------------------------------------- /client/src/assets/images/orebiLogoooo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/orebiLogoooo.png -------------------------------------------------------------------------------- /client/src/assets/images/products/cap.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/cap.webp -------------------------------------------------------------------------------- /client/src/assets/products/flower_base.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/products/flower_base.jpg -------------------------------------------------------------------------------- /client/src/assets/products/table_lamp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/products/table_lamp.jpg -------------------------------------------------------------------------------- /client/src/assets/products/wall_clock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/products/wall_clock.jpg -------------------------------------------------------------------------------- /server/public/images/products/basket.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/basket.webp -------------------------------------------------------------------------------- /admin/src/assets/images/sale/saleImgOne.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/src/assets/images/sale/saleImgOne.webp -------------------------------------------------------------------------------- /admin/src/assets/images/sale/saleImgTwo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/src/assets/images/sale/saleImgTwo.webp -------------------------------------------------------------------------------- /client/src/assets/images/banner/slideFour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/slideFour.png -------------------------------------------------------------------------------- /client/src/assets/images/banner/slideOne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/slideOne.png -------------------------------------------------------------------------------- /client/src/assets/images/banner/slideTwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/slideTwo.png -------------------------------------------------------------------------------- /client/src/assets/images/products/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/avatar.png -------------------------------------------------------------------------------- /client/src/assets/images/products/basket.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/basket.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/doll.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/doll.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/table.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/table.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/watch.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/watch.webp -------------------------------------------------------------------------------- /client/src/assets/products/pottery_base.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/products/pottery_base.jpg -------------------------------------------------------------------------------- /server/public/images/products/clockBlack.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/clockBlack.webp -------------------------------------------------------------------------------- /server/public/images/products/eyeGlass.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/eyeGlass.webp -------------------------------------------------------------------------------- /server/public/images/products/flowerBase.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/flowerBase.webp -------------------------------------------------------------------------------- /server/public/images/products/headPhone.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/headPhone.webp -------------------------------------------------------------------------------- /server/public/images/products/roundBowl.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/roundBowl.webp -------------------------------------------------------------------------------- /admin/src/assets/images/index.js: -------------------------------------------------------------------------------- 1 | import logo from "./orebiLogo.png"; 2 | import logoLight from "./logoLight.png"; 3 | 4 | export { logo, logoLight }; 5 | -------------------------------------------------------------------------------- /admin/src/assets/images/sale/saleImgThree.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/admin/src/assets/images/sale/saleImgThree.webp -------------------------------------------------------------------------------- /client/src/assets/images/banner/slideThree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/slideThree.png -------------------------------------------------------------------------------- /client/src/assets/images/products/eyeGlass.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/eyeGlass.webp -------------------------------------------------------------------------------- /client/src/assets/products/deco_accessories.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/products/deco_accessories.jpg -------------------------------------------------------------------------------- /server/public/images/products/backPackGray.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/backPackGray.webp -------------------------------------------------------------------------------- /client/src/assets/images/banner/bannerImgOne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/bannerImgOne.jpg -------------------------------------------------------------------------------- /client/src/assets/images/banner/bannerImgThree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/bannerImgThree.jpg -------------------------------------------------------------------------------- /client/src/assets/images/banner/bannerImgTwo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/bannerImgTwo.jpg -------------------------------------------------------------------------------- /client/src/assets/images/banner/bannerImgTwo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/bannerImgTwo.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/clockBlack.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/clockBlack.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/flowerBase.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/flowerBase.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/headPhone.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/headPhone.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/roundBowl.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/roundBowl.webp -------------------------------------------------------------------------------- /client/src/assets/products/basket_with_handles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/products/basket_with_handles.jpg -------------------------------------------------------------------------------- /client/src/pages/Offers.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Offers = () => { 4 | return
Offers
; 5 | }; 6 | 7 | export default Offers; 8 | -------------------------------------------------------------------------------- /server/public/images/products/backPackBlack.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/backPackBlack.webp -------------------------------------------------------------------------------- /admin/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .active { 6 | background-color: black; 7 | color: white; 8 | } 9 | -------------------------------------------------------------------------------- /client/src/assets/images/banner/bannerImgThree.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/banner/bannerImgThree.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/backPackBlack.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/backPackBlack.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/backPackGray.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/backPackGray.webp -------------------------------------------------------------------------------- /client/src/pages/Product.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Product = () => { 4 | return
Product
; 5 | }; 6 | 7 | export default Product; 8 | -------------------------------------------------------------------------------- /server/public/images/products/productOfTheYear.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/server/public/images/products/productOfTheYear.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/productOfTheYear.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/productOfTheYear.webp -------------------------------------------------------------------------------- /client/src/assets/images/slideOne-removebg-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/slideOne-removebg-preview.png -------------------------------------------------------------------------------- /client/src/styles/index.css: -------------------------------------------------------------------------------- 1 | @import "./base.css"; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .scrollbar-hide::-webkit-scrollbar { 6 | display: none; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/assets/images/products/newArrival/newArrFour.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/newArrival/newArrFour.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/newArrival/newArrOne.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/newArrival/newArrOne.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/newArrival/newArrTwo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/newArrival/newArrTwo.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/specialOffer/spfFour.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/specialOffer/spfFour.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/specialOffer/spfOne.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/specialOffer/spfOne.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/specialOffer/spfThree.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/specialOffer/spfThree.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/specialOffer/spfTwo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/specialOffer/spfTwo.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/newArrival/newArrThree.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/newArrival/newArrThree.webp -------------------------------------------------------------------------------- /admin/src/components/ui/cn.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | export const cn = (...inputs) => { 4 | return twMerge(clsx(inputs)); 5 | }; 6 | -------------------------------------------------------------------------------- /client/src/assets/images/products/bestSeller/bestSellerFour.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/bestSeller/bestSellerFour.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/bestSeller/bestSellerOne.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/bestSeller/bestSellerOne.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/bestSeller/bestSellerThree.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/bestSeller/bestSellerThree.webp -------------------------------------------------------------------------------- /client/src/assets/images/products/bestSeller/bestSellerTwo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noorjsdivs/orebishopping-yt/HEAD/client/src/assets/images/products/bestSeller/bestSellerTwo.webp -------------------------------------------------------------------------------- /client/src/components/ui/cn.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | export const cn = (...inputs) => { 4 | return twMerge(clsx(inputs)); 5 | }; 6 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /cart 3 | Disallow: /orders 4 | Disallow: /signin 5 | Disallow: /signup 6 | Disallow: /profile 7 | Sitemap: https://orebiclient.reactbd.com/sitemap.xml -------------------------------------------------------------------------------- /client/src/components/MobileNavigation.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const MobileNavigation = () => { 4 | return
MobileNavigation
; 5 | }; 6 | 7 | export default MobileNavigation; 8 | -------------------------------------------------------------------------------- /admin/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /admin/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { port: 5174 }, 8 | }); 9 | -------------------------------------------------------------------------------- /client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { port: 5173 }, 8 | }); 9 | -------------------------------------------------------------------------------- /admin/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import authReducer from "./authSlice"; 3 | 4 | export const store = configureStore({ 5 | reducer: { 6 | auth: authReducer, 7 | }, 8 | }); 9 | 10 | export default store; 11 | -------------------------------------------------------------------------------- /client/src/components/ui/title.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { cn } from "./cn"; 3 | 4 | const Title = ({ children, className }) => { 5 | return

{children}

; 6 | }; 7 | 8 | export default Title; 9 | -------------------------------------------------------------------------------- /server/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "index.mjs", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "/index.mjs" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /admin/src/components/Container.jsx: -------------------------------------------------------------------------------- 1 | import { cn } from "./ui/cn"; 2 | 3 | const Container = ({ children, className }) => { 4 | return ( 5 |
6 | {children} 7 |
8 | ); 9 | }; 10 | 11 | export default Container; 12 | -------------------------------------------------------------------------------- /client/src/components/ui/text-design.jsx: -------------------------------------------------------------------------------- 1 | import { cn } from "./cn"; 2 | 3 | const Title = ({ children, className }) => { 4 | return ( 5 |

6 | {children} 7 |

8 | ); 9 | }; 10 | 11 | export default Title; 12 | -------------------------------------------------------------------------------- /client/src/components/Container.jsx: -------------------------------------------------------------------------------- 1 | import { cn } from "./ui/cn"; 2 | 3 | const Container = ({ children, className }) => { 4 | return ( 5 |
6 | {children} 7 |
8 | ); 9 | }; 10 | 11 | export default Container; 12 | -------------------------------------------------------------------------------- /server/routes/checkout.mjs: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { processCheckout } from "../controllers/checkoutController.mjs"; 3 | 4 | const router = Router(); 5 | 6 | // Process checkout and update product stock 7 | router.post("/api/checkout", processCheckout); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /admin/src/components/ui/title.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { cn } from "./cn"; 3 | 4 | const Title = ({ children, className }) => { 5 | return ( 6 |

7 | {children} 8 |

9 | ); 10 | }; 11 | 12 | export default Title; 13 | -------------------------------------------------------------------------------- /admin/src/components/ScrollToTop.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | 4 | function ScrollToTop() { 5 | const { pathname } = useLocation(); 6 | 7 | useEffect(() => { 8 | window.scrollTo(0, 0); 9 | }, [pathname]); 10 | 11 | return null; 12 | } 13 | 14 | export default ScrollToTop; 15 | -------------------------------------------------------------------------------- /client/src/components/Badge.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Badge = ({ text }) => { 4 | return ( 5 |
6 | {text} 7 |
8 | ); 9 | }; 10 | 11 | export default Badge; 12 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /server/.env: -------------------------------------------------------------------------------- 1 | PORT=8000 2 | NODE_ENV=development 3 | MONGO_URI="" 4 | # bpimern@gmail.com 5 | CLOUDINARY_NAME= 6 | CLOUDINARY_API_KEY= 7 | CLOUDINARY_SECRET_KEY= 8 | ADMIN_URL=http://localhost:5174 9 | CLIENT_URL=http://localhost:5173 10 | JWT_SECRET=your_jwt_secret 11 | ADMIN_EMAIL= 12 | ADMIN_PASSWORD= 13 | STRIPE_SECRET_KEY= 14 | STRIPE_PUBLISHABLE_KEY= 15 | STRIPE_WEBHOOK_SECRET= -------------------------------------------------------------------------------- /server/config/mongodb.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const dbConnect = async () => { 4 | try { 5 | const conn = await mongoose.connect(process.env.MONGO_URI); 6 | console.log(`MongoDB Connected: ${conn.connection.host}`); 7 | } catch (error) { 8 | console.error(`Error: ${error.message}`); 9 | process.exit(1); 10 | } 11 | }; 12 | 13 | export default dbConnect; 14 | -------------------------------------------------------------------------------- /client/src/pages/Contact.jsx: -------------------------------------------------------------------------------- 1 | import PremiumMessage from "../components/PremiumMessage"; 2 | 3 | const Contact = () => { 4 | return ( 5 | 10 | ); 11 | }; 12 | 13 | export default Contact; 14 | -------------------------------------------------------------------------------- /admin/src/components/PriceFormat.jsx: -------------------------------------------------------------------------------- 1 | import { cn } from "./ui/cn"; 2 | 3 | const PriceFormat = ({ amount, className }) => { 4 | const formattedAmount = new Number(amount).toLocaleString("en-US", { 5 | style: "currency", 6 | currency: "USD", 7 | minimumFractionDigits: 2, 8 | }); 9 | return {formattedAmount}; 10 | }; 11 | 12 | export default PriceFormat; 13 | -------------------------------------------------------------------------------- /client/src/constants/navigation.js: -------------------------------------------------------------------------------- 1 | export const headerNavigation = [ 2 | { 3 | title: "Home", 4 | link: "/", 5 | }, 6 | { 7 | title: "Shop", 8 | link: "/shop", 9 | }, 10 | { 11 | title: "About", 12 | link: "/about", 13 | }, 14 | { 15 | title: "Contact", 16 | link: "/contact", 17 | }, 18 | { 19 | title: "Orders", 20 | link: "/orders", 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /client/src/helpers/index.js: -------------------------------------------------------------------------------- 1 | export const getData = async (endpoint) => { 2 | try { 3 | const response = await fetch(endpoint, { 4 | method: "GET", 5 | headers: { 6 | "Content-Type": "application/json", 7 | }, 8 | }); 9 | const data = await response.json(); 10 | return data; 11 | } catch (error) { 12 | console.error("Error fetching products:", error); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Orebi Admin 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/styles/base.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @font-face { 4 | font-family: "SUSE"; 5 | font-style: normal; 6 | font-weight: 100 800; 7 | font-display: swap; 8 | src: url("../fonts/suse.woff2"); 9 | } 10 | 11 | @font-face { 12 | font-family: "Open Sans"; 13 | font-style: normal; 14 | font-weight: 300 800; 15 | font-stretch: 100%; 16 | font-display: swap; 17 | src: url("../fonts/open_sans.woff2"); 18 | } 19 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Orebi Shopping", 3 | "short_name": "Orebi", 4 | "description": "Premium e-commerce shopping experience", 5 | "start_url": "/", 6 | "display": "standalone", 7 | "background_color": "#ffffff", 8 | "theme_color": "#000000", 9 | "icons": [ 10 | { 11 | "src": "favicon.ico", 12 | "sizes": "64x64 32x32 24x24 16x16", 13 | "type": "image/x-icon" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /client/src/components/skeletons/ProductSkeleton.jsx: -------------------------------------------------------------------------------- 1 | const ProductSkeleton = () => ( 2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | ); 11 | 12 | export default ProductSkeleton; 13 | -------------------------------------------------------------------------------- /client/config.js: -------------------------------------------------------------------------------- 1 | export const serverUrl = import.meta.env.VITE_BACKEND_URL; 2 | 3 | const checkConfig = (server) => { 4 | let config = {}; 5 | switch (server) { 6 | case "production": 7 | config = { 8 | baseUrl: "http://localhost:8000", 9 | }; 10 | break; 11 | case "local": 12 | config = { 13 | baseUrl: "http://localhost:8000", 14 | }; 15 | break; 16 | default: 17 | break; 18 | } 19 | return config; 20 | }; 21 | 22 | export const selectServer = "local"; 23 | export const config = checkConfig(selectServer); 24 | -------------------------------------------------------------------------------- /client/src/components/Logout.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from "./ui/button"; 3 | import toast from "react-hot-toast"; 4 | import { useNavigate } from "react-router-dom"; 5 | 6 | const Logout = () => { 7 | const navigate = useNavigate(); 8 | 9 | const handleLogout = async () => { 10 | localStorage.removeItem("token"); 11 | toast.success("log out successfully"); 12 | navigate("/"); 13 | }; 14 | return ( 15 | 18 | ); 19 | }; 20 | 21 | export default Logout; 22 | -------------------------------------------------------------------------------- /client/src/components/PreviousArrow.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FaLongArrowAltLeft } from "react-icons/fa"; 3 | 4 | const PreviousArrow = (props) => { 5 | const { onClick } = props; 6 | return ( 7 |
11 | 12 | 13 | 14 |
15 | ); 16 | }; 17 | 18 | export default PreviousArrow; 19 | -------------------------------------------------------------------------------- /server/routes/dashboardRoute.mjs: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { 3 | getDashboardStats, 4 | getAnalytics, 5 | getQuickStats, 6 | } from "../controllers/dashboardController.mjs"; 7 | import adminAuth from "../middleware/adminAuth.js"; 8 | 9 | const router = Router(); 10 | 11 | const routeValue = "/api/dashboard/"; 12 | 13 | // Admin dashboard routes 14 | router.get(`${routeValue}stats`, adminAuth, getDashboardStats); 15 | router.get(`${routeValue}analytics`, adminAuth, getAnalytics); 16 | router.get(`${routeValue}quick-stats`, adminAuth, getQuickStats); 17 | 18 | export default router; 19 | -------------------------------------------------------------------------------- /client/src/components/NextArrow.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FaLongArrowAltRight } from "react-icons/fa"; 3 | 4 | const NextArrow = (props) => { 5 | const { onClick } = props; 6 | return ( 7 |
11 | 12 | 13 | 14 |
15 | ); 16 | }; 17 | 18 | export default NextArrow; 19 | -------------------------------------------------------------------------------- /client/src/components/products/NavTitle.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BiCaretDown } from "react-icons/bi"; 3 | 4 | const NavTitle = ({ children, icons }) => { 5 | return ( 6 |
7 | {icons ? ( 8 | <> 9 |

{children}

10 | {icons && } 11 | 12 | ) : ( 13 | <> 14 |

{children}

15 | 16 | )} 17 |
18 | ); 19 | }; 20 | 21 | export default NavTitle; 22 | -------------------------------------------------------------------------------- /client/src/helpers/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | import { getAuth } from "firebase/auth"; 4 | import { getFirestore } from "firebase/firestore"; 5 | import { getStorage } from "firebase/storage"; 6 | 7 | const firebaseConfig = { 8 | apiKey: "", 9 | authDomain: "", 10 | projectId: "", 11 | storageBucket: "", 12 | messagingSenderId: "", 13 | appId: "", 14 | measurementId: "", 15 | }; 16 | 17 | const app = initializeApp(firebaseConfig); 18 | const auth = getAuth(); 19 | const db = getFirestore(); 20 | const storage = getStorage(); 21 | export { app, auth, db, storage }; 22 | -------------------------------------------------------------------------------- /admin/src/components/ProtectedRoute.jsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { Navigate, useLocation } from "react-router-dom"; 3 | import PropTypes from "prop-types"; 4 | 5 | const ProtectedRoute = ({ children }) => { 6 | const { isAuthenticated } = useSelector((state) => state.auth); 7 | const location = useLocation(); 8 | 9 | if (!isAuthenticated) { 10 | // Redirect to login page with return url 11 | return ; 12 | } 13 | 14 | return children; 15 | }; 16 | 17 | ProtectedRoute.propTypes = { 18 | children: PropTypes.node.isRequired, 19 | }; 20 | 21 | export default ProtectedRoute; 22 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from "tailwindcss/defaultTheme"; 2 | /** @type {import('tailwindcss').Config} */ 3 | export default { 4 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 5 | theme: { 6 | extend: { 7 | colors: { 8 | primary: "#262626", 9 | lightText: "#6D6D6D", 10 | destructive: "#b91c1c", 11 | }, 12 | boxShadow: { 13 | testShadow: "0px 0px 54px -13px rgba(0,0,0,0.7)", 14 | }, 15 | fontFamily: { 16 | sans: ["Open Sans", ...defaultTheme.fontFamily.sans], 17 | titleFont: ["SUSE", "sans-serif"], 18 | }, 19 | }, 20 | }, 21 | plugins: [], 22 | }; 23 | -------------------------------------------------------------------------------- /server/models/categoryModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const categorySchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | unique: true, 8 | trim: true, 9 | }, 10 | image: { 11 | type: String, 12 | required: true, 13 | }, 14 | description: { 15 | type: String, 16 | trim: true, 17 | }, 18 | isActive: { 19 | type: Boolean, 20 | default: true, 21 | }, 22 | createdAt: { 23 | type: Date, 24 | default: Date.now, 25 | }, 26 | }); 27 | 28 | const categoryModel = 29 | mongoose.models.category || mongoose.model("category", categorySchema); 30 | 31 | export default categoryModel; 32 | -------------------------------------------------------------------------------- /client/src/components/skeletons/ProductGridSkeleton.jsx: -------------------------------------------------------------------------------- 1 | import ProductSkeleton from "./ProductSkeleton"; 2 | 3 | const ProductGridSkeleton = ({ title = "Loading...", count = 8 }) => { 4 | return ( 5 |
6 |
7 |
{title}
8 |
9 |
10 | {Array.from({ length: count }).map((_, index) => ( 11 | 12 | ))} 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default ProductGridSkeleton; 19 | -------------------------------------------------------------------------------- /client/src/components/PriceFormat.jsx: -------------------------------------------------------------------------------- 1 | import { cn } from "./ui/cn"; 2 | import PropTypes from "prop-types"; 3 | 4 | const PriceFormat = ({ amount, className }) => { 5 | // Handle undefined, null, or NaN values 6 | const numericAmount = 7 | typeof amount === "number" && !isNaN(amount) ? amount : 0; 8 | 9 | const formattedAmount = new Number(numericAmount).toLocaleString("en-US", { 10 | style: "currency", 11 | currency: "USD", 12 | minimumFractionDigits: 2, 13 | }); 14 | return {formattedAmount}; 15 | }; 16 | 17 | PriceFormat.propTypes = { 18 | amount: PropTypes.number, 19 | className: PropTypes.string, 20 | }; 21 | 22 | export default PriceFormat; 23 | -------------------------------------------------------------------------------- /admin/src/pages/ApiDocumentation.jsx: -------------------------------------------------------------------------------- 1 | import PremiumMessage from "../components/PremiumMessage"; 2 | 3 | const ApiDocumentation = () => { 4 | const apiFeatures = [ 5 | "Complete REST API documentation", 6 | "Interactive API testing interface", 7 | "Real-time API monitoring", 8 | "Authentication endpoints", 9 | "Webhook integrations", 10 | "Developer support and resources", 11 | ]; 12 | 13 | return ( 14 | 19 | ); 20 | }; 21 | 22 | export default ApiDocumentation; 23 | -------------------------------------------------------------------------------- /server/models/brandModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const brandSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | unique: true, 8 | trim: true, 9 | }, 10 | image: { 11 | type: String, 12 | required: true, 13 | }, 14 | description: { 15 | type: String, 16 | trim: true, 17 | }, 18 | website: { 19 | type: String, 20 | trim: true, 21 | }, 22 | isActive: { 23 | type: Boolean, 24 | default: true, 25 | }, 26 | createdAt: { 27 | type: Date, 28 | default: Date.now, 29 | }, 30 | }); 31 | 32 | const brandModel = 33 | mongoose.models.brand || mongoose.model("brand", brandSchema); 34 | 35 | export default brandModel; 36 | -------------------------------------------------------------------------------- /admin/src/pages/Contacts.jsx: -------------------------------------------------------------------------------- 1 | import PremiumMessage from "../components/PremiumMessage"; 2 | 3 | const Contacts = () => { 4 | const contactFeatures = [ 5 | "Advanced contact management system", 6 | "Automated email response templates", 7 | "Contact analytics and reporting", 8 | "Bulk contact operations", 9 | "Contact segmentation and filtering", 10 | "Priority support ticket system", 11 | ]; 12 | 13 | return ( 14 | 19 | ); 20 | }; 21 | 22 | export default Contacts; 23 | -------------------------------------------------------------------------------- /admin/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { cn } from "./cn"; 3 | 4 | export const Label = ({ htmlFor, children, className }) => { 5 | return ( 6 | 12 | ); 13 | }; 14 | 15 | const Input = ({ type, className, placeholder, id, name, onChange, value }) => { 16 | return ( 17 | 29 | ); 30 | }; 31 | 32 | export default Input; 33 | -------------------------------------------------------------------------------- /admin/src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import { Provider } from "react-redux"; 5 | import { Toaster } from "react-hot-toast"; 6 | import App from "./App.jsx"; 7 | import { store } from "./redux/store.js"; 8 | import "./index.css"; 9 | 10 | createRoot(document.getElementById("root")).render( 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 27 | ); 28 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.mjs", 6 | "type": "module", 7 | "scripts": { 8 | "start": "NODE_ENV=production node index.mjs", 9 | "dev": "NODE_ENV=development nodemon index.mjs", 10 | "server": "NODE_ENV=development nodemon index.mjs", 11 | "prod": "NODE_ENV=production node index.mjs" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "bcrypt": "^5.1.1", 18 | "cloudinary": "^2.5.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.4.5", 21 | "express": "^4.21.0", 22 | "jsonwebtoken": "^9.0.2", 23 | "mongoose": "^8.9.5", 24 | "multer": "1.4.5-lts.1", 25 | "nodemon": "^3.1.4", 26 | "stripe": "^18.4.0", 27 | "validator": "^13.12.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import { 3 | persistStore, 4 | persistReducer, 5 | FLUSH, 6 | REHYDRATE, 7 | PAUSE, 8 | PERSIST, 9 | PURGE, 10 | REGISTER, 11 | } from "redux-persist"; 12 | import storage from "redux-persist/lib/storage"; 13 | import orebiReducer from "./orebiSlice"; 14 | 15 | const persistConfig = { 16 | key: "root", 17 | version: 1, 18 | storage, 19 | }; 20 | 21 | const persistedReducer = persistReducer(persistConfig, orebiReducer); 22 | 23 | export const store = configureStore({ 24 | reducer: { orebiReducer: persistedReducer }, 25 | middleware: (getDefaultMiddleware) => 26 | getDefaultMiddleware({ 27 | serializableCheck: { 28 | ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], 29 | }, 30 | }), 31 | }); 32 | 33 | export let persistor = persistStore(store); 34 | -------------------------------------------------------------------------------- /server/routes/paymentRoute.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | createPaymentIntent, 4 | confirmPayment, 5 | handleStripeWebhook, 6 | createOrder, 7 | } from "../controllers/paymentController.js"; 8 | import userAuth from "../middleware/userAuth.js"; 9 | 10 | const router = express.Router(); 11 | 12 | const routeValue = "/api/payment/"; 13 | 14 | // Create order 15 | router.post("/api/order/create", userAuth, createOrder); 16 | 17 | // Stripe payment routes 18 | router.post( 19 | `${routeValue}stripe/create-payment-intent`, 20 | userAuth, 21 | createPaymentIntent 22 | ); 23 | router.post(`${routeValue}stripe/confirm-payment`, userAuth, confirmPayment); 24 | 25 | // Stripe webhook (no auth required) 26 | router.post( 27 | `${routeValue}stripe/webhook`, 28 | express.raw({ type: "application/json" }), 29 | handleStripeWebhook 30 | ); 31 | 32 | export default router; 33 | -------------------------------------------------------------------------------- /server/routes/brandRoute.mjs: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | createBrand, 4 | getBrands, 5 | getBrand, 6 | updateBrand, 7 | deleteBrand, 8 | } from "../controllers/brandController.mjs"; 9 | import upload from "../middleware/multer.mjs"; 10 | import adminAuth from "../middleware/adminAuth.js"; 11 | 12 | const brandRouter = express.Router(); 13 | 14 | const routeValue = "/api/brand"; 15 | 16 | // Public routes 17 | brandRouter.get(`${routeValue}`, getBrands); 18 | brandRouter.get(`${routeValue}/:id`, getBrand); 19 | 20 | // Admin only routes 21 | brandRouter.post( 22 | `${routeValue}`, 23 | adminAuth, 24 | upload.single("image"), 25 | createBrand 26 | ); 27 | brandRouter.put( 28 | `${routeValue}/:id`, 29 | adminAuth, 30 | upload.single("image"), 31 | updateBrand 32 | ); 33 | brandRouter.delete(`${routeValue}/:id`, adminAuth, deleteBrand); 34 | 35 | export default brandRouter; 36 | -------------------------------------------------------------------------------- /client/src/components/MainLoader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { logo } from "../assets/images"; 3 | 4 | const MainLoader = () => { 5 | return ( 6 |
7 |
8 | Logo 9 |
10 | 11 | 12 | 13 | 14 |

15 | Orebi shopping is loading... 16 |

17 |
18 | ); 19 | }; 20 | 21 | export default MainLoader; 22 | -------------------------------------------------------------------------------- /server/models/productModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const productSchema = new mongoose.Schema( 4 | { 5 | _type: { type: String }, 6 | name: { type: String, required: true }, 7 | images: { type: Array, required: true }, 8 | price: { type: Number, required: true }, 9 | discountedPercentage: { type: Number, required: true, default: 10 }, 10 | stock: { type: Number, required: true, default: 0 }, 11 | soldQuantity: { type: Number, default: 0 }, 12 | category: { type: String, required: true }, 13 | brand: { type: String }, 14 | badge: { type: Boolean }, 15 | isAvailable: { type: Boolean }, 16 | offer: { type: Boolean }, 17 | description: { type: String, required: true }, 18 | tags: { type: Array }, 19 | }, 20 | { 21 | timestamps: true, 22 | } 23 | ); 24 | 25 | const productModel = 26 | mongoose.models.product || mongoose.model("product", productSchema); 27 | 28 | export default productModel; 29 | -------------------------------------------------------------------------------- /server/routes/categoryRoute.mjs: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | createCategory, 4 | getCategories, 5 | getCategory, 6 | updateCategory, 7 | deleteCategory, 8 | } from "../controllers/categoryController.mjs"; 9 | import upload from "../middleware/multer.mjs"; 10 | import adminAuth from "../middleware/adminAuth.js"; 11 | 12 | const categoryRouter = express.Router(); 13 | 14 | const routeValue = "/api/category"; 15 | 16 | // Public routes 17 | categoryRouter.get(`${routeValue}`, getCategories); 18 | categoryRouter.get(`${routeValue}/:id`, getCategory); 19 | 20 | // Admin only routes 21 | categoryRouter.post( 22 | `${routeValue}`, 23 | adminAuth, 24 | upload.single("image"), 25 | createCategory 26 | ); 27 | categoryRouter.put( 28 | `${routeValue}/:id`, 29 | adminAuth, 30 | upload.single("image"), 31 | updateCategory 32 | ); 33 | categoryRouter.delete(`${routeValue}/:id`, adminAuth, deleteCategory); 34 | 35 | export default categoryRouter; 36 | -------------------------------------------------------------------------------- /server/routes/contactRoute.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | createContact, 4 | getAllContacts, 5 | getContactById, 6 | updateContactStatus, 7 | deleteContact, 8 | getUserContacts, 9 | } from "../controllers/contactController.js"; 10 | import userAuth from "../middleware/userAuth.js"; 11 | import adminAuth from "../middleware/adminAuth.js"; 12 | 13 | const router = express.Router(); 14 | 15 | const routeValue = "/api/contact"; 16 | 17 | // User routes (require authentication) 18 | router.post(`${routeValue}`, userAuth, createContact); 19 | router.get(`${routeValue}/my-contacts`, userAuth, getUserContacts); 20 | 21 | // Admin routes (require admin authentication) 22 | router.get(`${routeValue}/admin/all`, adminAuth, getAllContacts); 23 | router.get(`${routeValue}/admin/:id`, adminAuth, getContactById); 24 | router.put(`${routeValue}/admin/:id/status`, adminAuth, updateContactStatus); 25 | router.delete(`${routeValue}/admin/:id`, adminAuth, deleteContact); 26 | 27 | export default router; 28 | -------------------------------------------------------------------------------- /server/routes/orderRoute.mjs: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { 3 | createOrder, 4 | getAllOrders, 5 | getUserOrders, 6 | getUserOrderById, 7 | updateOrderStatus, 8 | getOrderStats, 9 | deleteOrder, 10 | } from "../controllers/orderController.mjs"; 11 | import adminAuth from "../middleware/adminAuth.js"; 12 | import userAuth from "../middleware/userAuth.js"; 13 | 14 | const router = Router(); 15 | 16 | const routeValue = "/api/order/"; 17 | 18 | // User routes (require authentication) 19 | router.post(`${routeValue}create`, userAuth, createOrder); 20 | router.get(`${routeValue}my-orders`, userAuth, getUserOrders); 21 | router.get(`${routeValue}user/:orderId`, userAuth, getUserOrderById); 22 | 23 | // Admin routes 24 | router.get(`${routeValue}admin/user/:userId`, adminAuth, getUserOrders); 25 | router.get(`${routeValue}list`, adminAuth, getAllOrders); 26 | router.get(`${routeValue}stats`, adminAuth, getOrderStats); 27 | router.post(`${routeValue}update-status`, adminAuth, updateOrderStatus); 28 | router.post(`${routeValue}delete`, adminAuth, deleteOrder); 29 | 30 | export default router; 31 | -------------------------------------------------------------------------------- /server/middleware/avatarUpload.mjs: -------------------------------------------------------------------------------- 1 | import multer from "multer"; 2 | import path from "path"; 3 | 4 | // Configure multer for avatar uploads (storing temporarily for Cloudinary upload) 5 | const avatarStorage = multer.diskStorage({ 6 | destination: function (req, file, cb) { 7 | cb(null, "./public/temp/"); // Temporary storage before Cloudinary upload 8 | }, 9 | filename: function (req, file, cb) { 10 | // Generate unique filename with timestamp 11 | const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9); 12 | const ext = path.extname(file.originalname); 13 | cb(null, "temp-avatar-" + uniqueSuffix + ext); 14 | }, 15 | }); 16 | 17 | // File filter for images only 18 | const imageFilter = (req, file, cb) => { 19 | if (file.mimetype.startsWith("image/")) { 20 | cb(null, true); 21 | } else { 22 | cb(new Error("Only image files are allowed!"), false); 23 | } 24 | }; 25 | 26 | const avatarUpload = multer({ 27 | storage: avatarStorage, 28 | limits: { 29 | fileSize: 5 * 1024 * 1024, // 5MB limit 30 | }, 31 | fileFilter: imageFilter, 32 | }); 33 | 34 | export { avatarUpload }; 35 | -------------------------------------------------------------------------------- /admin/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /client/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /server/middleware/userAuth.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | import userModel from "../models/userModel.js"; 3 | 4 | const userAuth = async (req, res, next) => { 5 | try { 6 | const authHeader = req.headers.authorization; 7 | const token = 8 | authHeader && authHeader.startsWith("Bearer ") 9 | ? authHeader.slice(7) 10 | : req.headers.token; 11 | 12 | if (!token) { 13 | return res.json({ 14 | success: false, 15 | message: "Not Authorized, login required", 16 | }); 17 | } 18 | 19 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 20 | 21 | // Find user by ID from token 22 | const user = await userModel.findById(decoded.id); 23 | 24 | if (!user) { 25 | return res.json({ success: false, message: "User not found" }); 26 | } 27 | 28 | if (!user.isActive) { 29 | return res.json({ success: false, message: "Account is deactivated" }); 30 | } 31 | 32 | // Add user info to request object 33 | req.user = user; 34 | next(); 35 | } catch (error) { 36 | console.log(error); 37 | res.json({ success: false, message: "Invalid token" }); 38 | } 39 | }; 40 | 41 | export default userAuth; 42 | -------------------------------------------------------------------------------- /server/models/contactModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const contactSchema = new mongoose.Schema( 4 | { 5 | name: { 6 | type: String, 7 | required: true, 8 | trim: true, 9 | }, 10 | email: { 11 | type: String, 12 | required: true, 13 | trim: true, 14 | lowercase: true, 15 | }, 16 | subject: { 17 | type: String, 18 | required: true, 19 | trim: true, 20 | }, 21 | message: { 22 | type: String, 23 | required: true, 24 | trim: true, 25 | }, 26 | userId: { 27 | type: mongoose.Schema.Types.ObjectId, 28 | ref: "user", 29 | required: true, 30 | }, 31 | status: { 32 | type: String, 33 | enum: ["unread", "read", "replied"], 34 | default: "unread", 35 | }, 36 | adminNotes: { 37 | type: String, 38 | trim: true, 39 | default: "", 40 | }, 41 | }, 42 | { 43 | timestamps: true, 44 | } 45 | ); 46 | 47 | // Index for better query performance 48 | contactSchema.index({ userId: 1 }); 49 | contactSchema.index({ status: 1 }); 50 | contactSchema.index({ createdAt: -1 }); 51 | 52 | export default mongoose.model("Contact", contactSchema); 53 | -------------------------------------------------------------------------------- /client/src/components/Breadcrumbs.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { HiOutlineChevronRight } from "react-icons/hi"; 3 | import { useLocation } from "react-router-dom"; 4 | import { cn } from "./ui/cn"; 5 | 6 | const Breadcrumbs = ({ prevLocation, title, className }) => { 7 | const location = useLocation(); 8 | const [locationPath, setLocationPath] = useState(""); 9 | useEffect(() => { 10 | setLocationPath(location.pathname.split("/")[1]); 11 | }, [location]); 12 | 13 | return ( 14 |
15 |

21 | {title} 22 |

23 |

24 | {prevLocation === "" ? "Home" : prevLocation} 25 | 26 | 27 | 28 | 29 | 30 | {locationPath} 31 | 32 |

33 |
34 | ); 35 | }; 36 | 37 | export default Breadcrumbs; 38 | -------------------------------------------------------------------------------- /server/middleware/adminAuth.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | import userModel from "../models/userModel.js"; 3 | 4 | const adminAuth = async (req, res, next) => { 5 | try { 6 | const authHeader = req.headers.authorization; 7 | const token = 8 | authHeader && authHeader.startsWith("Bearer ") 9 | ? authHeader.slice(7) 10 | : req.headers.token; 11 | 12 | if (!token) { 13 | return res.json({ success: false, message: "Not Authorized, try again" }); 14 | } 15 | 16 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 17 | 18 | // Find user by ID from token 19 | const user = await userModel.findById(decoded.id); 20 | 21 | if (!user) { 22 | return res.json({ success: false, message: "User not found" }); 23 | } 24 | 25 | if (user.role !== "admin") { 26 | return res.json({ success: false, message: "Admin access required" }); 27 | } 28 | 29 | if (!user.isActive) { 30 | return res.json({ success: false, message: "Account is deactivated" }); 31 | } 32 | 33 | // Add user info to request object 34 | req.user = user; 35 | next(); 36 | } catch (error) { 37 | console.log(error); 38 | res.json({ success: false, message: "Invalid token" }); 39 | } 40 | }; 41 | 42 | export default adminAuth; 43 | -------------------------------------------------------------------------------- /client/src/components/SocialLinks.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | FaEnvelope, 4 | FaFacebook, 5 | FaGithub, 6 | FaLinkedin, 7 | FaYoutube, 8 | } from "react-icons/fa"; 9 | import { twMerge } from "tailwind-merge"; 10 | 11 | const linkData = [ 12 | { icon: , href: "https://github.com/" }, 13 | { icon: , href: "https://www.youtube.com/@reactjsBD" }, 14 | { 15 | icon: , 16 | href: "https://www.linkedin.com/in/noor-mohammad-ab2245193/", 17 | }, 18 | { icon: , href: "https://www.youtube.com/@reactjsBD" }, 19 | { icon: , href: "https://www.youtube.com/@reactjsBD" }, 20 | ]; 21 | 22 | const SocialLinks = ({ className, iconStyle }) => { 23 | return ( 24 |
30 | {linkData?.map((item, index) => ( 31 | 39 | {item?.icon} 40 | 41 | ))} 42 |
43 | ); 44 | }; 45 | 46 | export default SocialLinks; 47 | -------------------------------------------------------------------------------- /admin/src/services/authService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { serverUrl } from "../../config"; 3 | 4 | // Create axios instance with base configuration 5 | const api = axios.create({ 6 | baseURL: serverUrl, 7 | headers: { 8 | "Content-Type": "application/json", 9 | }, 10 | }); 11 | 12 | // Add token to requests if available 13 | api.interceptors.request.use((config) => { 14 | const token = localStorage.getItem("token"); 15 | if (token) { 16 | config.headers.Authorization = `Bearer ${token}`; 17 | } 18 | return config; 19 | }); 20 | 21 | // Authentication service 22 | export const authService = { 23 | // Admin login 24 | adminLogin: async (credentials) => { 25 | const response = await api.post("/api/user/admin", credentials); 26 | return response.data; 27 | }, 28 | 29 | // User login 30 | userLogin: async (credentials) => { 31 | const response = await api.post("/api/user/login", credentials); 32 | return response.data; 33 | }, 34 | 35 | // User registration 36 | userRegister: async (userData) => { 37 | const response = await api.post("/api/user/register", userData); 38 | return response.data; 39 | }, 40 | 41 | // Get user profile (if needed) 42 | getUserProfile: async () => { 43 | const response = await api.get("/api/user/profile"); 44 | return response.data; 45 | }, 46 | }; 47 | 48 | export default authService; 49 | -------------------------------------------------------------------------------- /admin/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/products/Price.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import NavTitle from "./NavTitle"; 3 | 4 | const Price = () => { 5 | const priceList = [ 6 | { 7 | _id: 950, 8 | priceOne: 0.0, 9 | priceTwo: 49.99, 10 | }, 11 | { 12 | _id: 951, 13 | priceOne: 50.0, 14 | priceTwo: 99.99, 15 | }, 16 | { 17 | _id: 952, 18 | priceOne: 100.0, 19 | priceTwo: 199.99, 20 | }, 21 | { 22 | _id: 953, 23 | priceOne: 200.0, 24 | priceTwo: 399.99, 25 | }, 26 | { 27 | _id: 954, 28 | priceOne: 400.0, 29 | priceTwo: 599.99, 30 | }, 31 | { 32 | _id: 955, 33 | priceOne: 600.0, 34 | priceTwo: 1000.0, 35 | }, 36 | ]; 37 | return ( 38 |
39 | Shop by Price 40 |
41 |
    42 | {priceList.map((item) => ( 43 |
  • 47 | ${item.priceOne.toFixed(2)} - ${item.priceTwo.toFixed(2)} 48 |
  • 49 | ))} 50 |
51 |
52 |
53 | ); 54 | }; 55 | 56 | export default Price; 57 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --mode development", 8 | "dev:prod": "vite --mode production", 9 | "build": "vite build --mode production", 10 | "build:dev": "vite build --mode development", 11 | "lint": "eslint .", 12 | "preview": "vite preview --mode production", 13 | "preview:dev": "vite preview --mode development" 14 | }, 15 | "dependencies": { 16 | "@headlessui/react": "^2.1.10", 17 | "@reduxjs/toolkit": "^2.8.2", 18 | "axios": "^1.7.7", 19 | "clsx": "^2.1.1", 20 | "framer-motion": "^12.23.12", 21 | "prop-types": "^15.8.1", 22 | "react": "^18.3.1", 23 | "react-dom": "^18.3.1", 24 | "react-hot-toast": "^2.4.1", 25 | "react-icons": "^5.3.0", 26 | "react-redux": "^9.2.0", 27 | "react-router-dom": "^6.26.2", 28 | "react-toastify": "^11.0.5", 29 | "tailwind-merge": "^2.5.3" 30 | }, 31 | "devDependencies": { 32 | "@eslint/js": "^9.11.1", 33 | "@types/react": "^18.3.10", 34 | "@types/react-dom": "^18.3.0", 35 | "@vitejs/plugin-react": "^4.3.2", 36 | "autoprefixer": "^10.4.20", 37 | "eslint": "^9.11.1", 38 | "eslint-plugin-react": "^7.37.0", 39 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 40 | "eslint-plugin-react-refresh": "^0.4.12", 41 | "globals": "^15.9.0", 42 | "postcss": "^8.4.47", 43 | "tailwindcss": "^3.4.13", 44 | "vite": "^5.4.8" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/routes/productRoute.mjs: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { 3 | addProduct, 4 | listProducts, 5 | removeProduct, 6 | singleProducts, 7 | updateStock, 8 | updateProduct, 9 | } from "../controllers/productController.mjs"; 10 | import upload from "../middleware/multer.mjs"; 11 | import adminAuth from "../middleware/adminAuth.js"; 12 | 13 | const router = Router(); 14 | 15 | const routeValue = "/api/product/"; 16 | 17 | // Admin routes for product management 18 | router.post( 19 | `${routeValue}add`, 20 | upload.fields([ 21 | { name: "image1", maxCount: 1 }, 22 | { name: "image2", maxCount: 1 }, 23 | { name: "image3", maxCount: 1 }, 24 | { name: "image4", maxCount: 1 }, 25 | ]), 26 | adminAuth, 27 | addProduct 28 | ); 29 | router.post(`${routeValue}remove`, adminAuth, removeProduct); 30 | router.put( 31 | `${routeValue}update/:id`, 32 | upload.fields([ 33 | { name: "image1", maxCount: 1 }, 34 | { name: "image2", maxCount: 1 }, 35 | { name: "image3", maxCount: 1 }, 36 | { name: "image4", maxCount: 1 }, 37 | ]), 38 | adminAuth, 39 | updateProduct 40 | ); 41 | router.post(`${routeValue}update-stock`, updateStock); 42 | router.get(`${routeValue}single`, singleProducts); 43 | router.get(`${routeValue}list`, listProducts); 44 | 45 | // Public routes for frontend 46 | router.get("/api/products", listProducts); 47 | router.get("/api/products/:type", (req, res, next) => { 48 | req.query._type = req.params.type; 49 | listProducts(req, res, next); 50 | }); 51 | 52 | export default router; 53 | -------------------------------------------------------------------------------- /server/models/userModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const userSchema = new mongoose.Schema( 4 | { 5 | name: { type: String, required: true }, 6 | email: { type: String, required: true, unique: true }, 7 | password: { type: String, required: true }, 8 | role: { 9 | type: String, 10 | enum: ["admin", "user"], 11 | default: "user", 12 | }, 13 | userCart: { 14 | type: Object, 15 | default: {}, 16 | }, 17 | orders: [ 18 | { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: "order", 21 | }, 22 | ], 23 | addresses: [ 24 | { 25 | label: { type: String, required: true }, // e.g., 'Home', 'Work', 'Billing' 26 | street: { type: String, required: true }, 27 | city: { type: String, required: true }, 28 | state: { type: String, required: true }, 29 | zipCode: { type: String, required: true }, 30 | country: { type: String, required: true }, 31 | phone: { type: String, default: "" }, 32 | isDefault: { type: Boolean, default: false }, 33 | _id: { type: mongoose.Schema.Types.ObjectId, auto: true }, 34 | }, 35 | ], 36 | isActive: { type: Boolean, default: true }, 37 | lastLogin: { type: Date }, 38 | avatar: { type: String, default: "" }, 39 | }, 40 | { 41 | minimize: false, 42 | timestamps: true, 43 | } 44 | ); 45 | 46 | // Index for better query performance 47 | userSchema.index({ role: 1 }); 48 | 49 | const userModel = mongoose.models.user || mongoose.model("user", userSchema); 50 | 51 | export default userModel; 52 | -------------------------------------------------------------------------------- /client/src/components/products/Brand.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { motion } from "framer-motion"; 3 | import NavTitle from "./NavTitle"; 4 | 5 | const Brand = () => { 6 | const [showBrands, setShowBrands] = useState(true); 7 | const brands = [ 8 | { 9 | _id: 9006, 10 | title: "Apple", 11 | }, 12 | { 13 | _id: 9007, 14 | title: "Ultron", 15 | }, 16 | { 17 | _id: 9008, 18 | title: "Unknown", 19 | }, 20 | { 21 | _id: 9009, 22 | title: "Shoppers Home", 23 | }, 24 | { 25 | _id: 9010, 26 | title: "Hoichoi", 27 | }, 28 | ]; 29 | 30 | return ( 31 |
32 |
setShowBrands(!showBrands)} 34 | className="cursor-pointer" 35 | > 36 | Shop by Brand 37 |
38 | {showBrands && ( 39 | 44 |
    45 | {brands.map((item) => ( 46 |
  • 50 | {item.title} 51 |
  • 52 | ))} 53 |
54 |
55 | )} 56 |
57 | ); 58 | }; 59 | 60 | export default Brand; 61 | -------------------------------------------------------------------------------- /client/src/components/products/Category.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | import { ImPlus } from "react-icons/im"; 4 | import NavTitle from "./NavTitle"; 5 | 6 | const Category = () => { 7 | const [showSubCatOne, setShowSubCatOne] = useState(false); 8 | const items = [ 9 | { 10 | _id: 990, 11 | title: "New Arrivals", 12 | icons: true, 13 | }, 14 | { 15 | _id: 991, 16 | title: "Gudgets", 17 | }, 18 | { 19 | _id: 992, 20 | title: "Accessories", 21 | icons: true, 22 | }, 23 | { 24 | _id: 993, 25 | title: "Electronics", 26 | }, 27 | { 28 | _id: 994, 29 | title: "Others", 30 | }, 31 | ]; 32 | return ( 33 |
34 | Shop by Category 35 |
36 |
    37 | {items.map(({ _id, title, icons }) => ( 38 |
  • 42 | {title} 43 | {icons && ( 44 | setShowSubCatOne(!showSubCatOne)} 46 | className="text-[10px] lg:text-xs cursor-pointer text-gray-400 hover:text-primeColor duration-300" 47 | > 48 | 49 | 50 | )} 51 |
  • 52 | ))} 53 |
54 |
55 |
56 | ); 57 | }; 58 | 59 | export default Category; 60 | -------------------------------------------------------------------------------- /server/createAdmin.mjs: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import bcrypt from "bcrypt"; 3 | import userModel from "./models/userModel.js"; 4 | import "dotenv/config"; 5 | 6 | const createInitialAdmin = async () => { 7 | try { 8 | // Connect to MongoDB 9 | await mongoose.connect(process.env.MONGO_URI); 10 | console.log("Connected to MongoDB"); 11 | 12 | // Check if admin already exists 13 | const existingAdmin = await userModel.findOne({ role: "admin" }); 14 | 15 | if (existingAdmin) { 16 | console.log("Admin user already exists:", existingAdmin.email); 17 | process.exit(0); 18 | } 19 | 20 | // Create admin user 21 | const adminEmail = process.env.ADMIN_EMAIL || "admin@orebi.com"; 22 | const adminPassword = process.env.ADMIN_PASSWORD || "admin123456"; 23 | const adminName = process.env.ADMIN_NAME || "Admin User"; 24 | 25 | // Hash password 26 | const salt = await bcrypt.genSalt(10); 27 | const hashedPassword = await bcrypt.hash(adminPassword, salt); 28 | 29 | const adminUser = new userModel({ 30 | name: adminName, 31 | email: adminEmail, 32 | password: hashedPassword, 33 | role: "admin", 34 | isActive: true, 35 | }); 36 | 37 | await adminUser.save(); 38 | 39 | console.log("✅ Initial admin user created successfully!"); 40 | console.log("Email:", adminEmail); 41 | console.log("Password:", adminPassword); 42 | console.log("⚠️ Please change the default password after first login"); 43 | } catch (error) { 44 | console.error("Error creating admin user:", error); 45 | } finally { 46 | await mongoose.disconnect(); 47 | console.log("Disconnected from MongoDB"); 48 | process.exit(0); 49 | } 50 | }; 51 | 52 | createInitialAdmin(); 53 | -------------------------------------------------------------------------------- /client/src/components/ui/button.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { cva } from "class-variance-authority"; 3 | import { cn } from "./cn"; 4 | 5 | const buttonVariants = cva( 6 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 7 | { 8 | variants: { 9 | variant: { 10 | default: 11 | "bg-primary/90 hover:bg-primary text-white/80 hover:text-white", 12 | destructive: 13 | "bg-destructive/90 text-white/90 hover:bg-destructive hover:text-white", 14 | outline: 15 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 16 | secondary: 17 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 18 | ghost: "hover:bg-accent hover:text-accent-foreground", 19 | link: "text-primary underline-offset-4 hover:underline", 20 | }, 21 | size: { 22 | default: "h-10 px-4 py-2", 23 | sm: "h-9 rounded-md px-3", 24 | lg: "h-11 rounded-md px-8", 25 | icon: "h-10 w-10", 26 | }, 27 | }, 28 | defaultVariants: { 29 | variant: "default", 30 | size: "default", 31 | }, 32 | } 33 | ); 34 | const Button = React.forwardRef( 35 | ({ className, variant, size, asChild = false, ...props }, ref) => { 36 | const Comp = asChild ? Slot : "button"; 37 | return ( 38 | 43 | ); 44 | } 45 | ); 46 | Button.displayName = "Button"; 47 | 48 | export { Button, buttonVariants }; 49 | -------------------------------------------------------------------------------- /client/src/components/homeProducts/ProductOfTheYear.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | import { productOfTheYear } from "../../assets/images"; 3 | import { Button } from "../ui/button"; 4 | 5 | const ProductOfTheYear = () => { 6 | return ( 7 | 8 |
9 | productImage 14 |
15 |
16 |

17 | Product of The Year 18 |

19 |

20 | Discover our most innovative and popular product that has captured 21 | hearts worldwide. Experience excellence in every detail. 22 |

23 |
24 | 27 |
28 |
29 | 30 | ); 31 | }; 32 | 33 | export default ProductOfTheYear; 34 | -------------------------------------------------------------------------------- /client/src/assets/images/index.js: -------------------------------------------------------------------------------- 1 | import logo from "./orebiLogo.png"; 2 | import logoLight from "./logoLight.png"; 3 | import bannerImgOne from "./banner/slideOne.png"; 4 | import bannerImgTwo from "./banner/slideTwo.png"; 5 | import bannerImgThree from "./banner/slideThree.png"; 6 | // ============== Products Start here ==================== 7 | 8 | // Year Product 9 | import productOfTheYear from "./products/productOfTheYear.webp"; 10 | // ============== Products End here ====================== 11 | import paymentCard from "./payment.png"; 12 | import emptyCart from "../images/emptyCart.png"; 13 | // ProductList 14 | import backPackGray from "./products/backPackGray.webp"; 15 | import backPackBlack from "./products/backPackBlack.webp"; 16 | import basket from "./products/basket.webp"; 17 | import cap from "./products/cap.webp"; 18 | import clockBlack from "./products/clockBlack.webp"; 19 | import doll from "./products/doll.webp"; 20 | import eyeGlass from "./products/eyeGlass.webp"; 21 | import flowerBase from "./products/flowerBase.webp"; 22 | import headPhone from "./products/headPhone.webp"; 23 | import roundBowl from "./products/roundBowl.webp"; 24 | import table from "./products/table.webp"; 25 | import watch from "./products/watch.webp"; 26 | import avatar from "./products/avatar.png"; 27 | import contactUs from "./contactUs.jpg"; 28 | 29 | export { 30 | logo, 31 | logoLight, 32 | bannerImgOne, 33 | bannerImgTwo, 34 | bannerImgThree, 35 | 36 | // Year Product 37 | productOfTheYear, 38 | // ===================== Products End here ============== 39 | paymentCard, 40 | emptyCart, 41 | // productList 42 | backPackGray, 43 | backPackBlack, 44 | basket, 45 | cap, 46 | clockBlack, 47 | doll, 48 | eyeGlass, 49 | flowerBase, 50 | headPhone, 51 | roundBowl, 52 | table, 53 | watch, 54 | avatar, 55 | contactUs, 56 | }; 57 | -------------------------------------------------------------------------------- /server/middleware/multer.mjs: -------------------------------------------------------------------------------- 1 | import multer from "multer"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | import { fileURLToPath } from "url"; 5 | 6 | // Get the directory name of the current module 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | 10 | // Ensure temp directory exists 11 | const ensureTempDir = () => { 12 | const tempDir = path.join(__dirname, "../public/temp/"); 13 | if (!fs.existsSync(tempDir)) { 14 | fs.mkdirSync(tempDir, { recursive: true }); 15 | console.log("Created temp directory:", tempDir); 16 | } 17 | return tempDir; 18 | }; 19 | 20 | const storage = multer.diskStorage({ 21 | destination: function (req, file, callback) { 22 | // Ensure temp directory exists before using it 23 | try { 24 | const tempDir = ensureTempDir(); 25 | callback(null, tempDir); 26 | } catch (error) { 27 | console.error("Error creating temp directory:", error); 28 | callback(error, null); 29 | } 30 | }, 31 | filename: function (req, file, callback) { 32 | // Generate unique filename with timestamp 33 | const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9); 34 | const extension = path.extname(file.originalname); 35 | const nameWithoutExt = path.basename(file.originalname, extension); 36 | callback(null, uniqueSuffix + "-" + nameWithoutExt + extension); 37 | }, 38 | }); 39 | 40 | const upload = multer({ 41 | storage, 42 | limits: { 43 | fileSize: 10 * 1024 * 1024, // 10MB limit 44 | }, 45 | fileFilter: (req, file, callback) => { 46 | // Check if file is an image 47 | if (file.mimetype.startsWith("image/")) { 48 | callback(null, true); 49 | } else { 50 | callback(new Error("Only image files are allowed"), false); 51 | } 52 | }, 53 | }); 54 | 55 | export default upload; 56 | -------------------------------------------------------------------------------- /admin/src/config/index.js: -------------------------------------------------------------------------------- 1 | // Admin Configuration 2 | const ENV = import.meta.env.MODE || "development"; 3 | 4 | const config = { 5 | development: { 6 | API_BASE_URL: "http://localhost:3000", 7 | CLIENT_BASE_URL: "http://localhost:5173", 8 | ADMIN_BASE_URL: "http://localhost:5174", 9 | NODE_ENV: "development", 10 | DEBUG: true, 11 | LOG_LEVEL: "debug", 12 | }, 13 | production: { 14 | API_BASE_URL: 15 | import.meta.env.VITE_API_BASE_URL || "https://your-api-domain.com", 16 | CLIENT_BASE_URL: 17 | import.meta.env.VITE_CLIENT_BASE_URL || "https://orebiclient.reactbd.com", 18 | ADMIN_BASE_URL: 19 | import.meta.env.VITE_ADMIN_BASE_URL || "https://orebiadmin.reactbd.com", 20 | NODE_ENV: "production", 21 | DEBUG: false, 22 | LOG_LEVEL: "error", 23 | }, 24 | }; 25 | 26 | // Export the configuration based on current environment 27 | const currentConfig = config[ENV] || config.development; 28 | 29 | export const { 30 | API_BASE_URL, 31 | CLIENT_BASE_URL, 32 | ADMIN_BASE_URL, 33 | NODE_ENV, 34 | DEBUG, 35 | LOG_LEVEL, 36 | } = currentConfig; 37 | 38 | // Legacy support for existing serverUrl import 39 | export const serverUrl = API_BASE_URL; 40 | 41 | // Environment check utilities 42 | export const isDevelopment = ENV === "development"; 43 | export const isProduction = ENV === "production"; 44 | 45 | // Logger utility 46 | export const logger = { 47 | debug: (...args) => { 48 | if (DEBUG) { 49 | console.log("[DEBUG]", ...args); 50 | } 51 | }, 52 | info: (...args) => { 53 | if (DEBUG || LOG_LEVEL === "info") { 54 | console.info("[INFO]", ...args); 55 | } 56 | }, 57 | warn: (...args) => { 58 | console.warn("[WARN]", ...args); 59 | }, 60 | error: (...args) => { 61 | console.error("[ERROR]", ...args); 62 | }, 63 | }; 64 | 65 | export default currentConfig; 66 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --mode development", 8 | "dev:prod": "vite --mode production", 9 | "build": "vite build --mode production", 10 | "build:dev": "vite build --mode development", 11 | "lint": "eslint .", 12 | "preview": "vite preview --mode production", 13 | "preview:dev": "vite preview --mode development" 14 | }, 15 | "dependencies": { 16 | "@headlessui/react": "^2.1.8", 17 | "@reduxjs/toolkit": "^2.2.7", 18 | "@stripe/react-stripe-js": "^3.8.1", 19 | "@stripe/stripe-js": "^7.7.0", 20 | "@tailwindcss/line-clamp": "^0.4.4", 21 | "axios": "^1.7.7", 22 | "class-variance-authority": "^0.7.0", 23 | "clsx": "^2.1.1", 24 | "firebase": "^10.13.2", 25 | "framer-motion": "^11.5.4", 26 | "jwt-decode": "^4.0.0", 27 | "leaflet": "^1.9.4", 28 | "react": "^18.3.1", 29 | "react-dom": "^18.3.1", 30 | "react-hot-toast": "^2.4.1", 31 | "react-icons": "^5.3.0", 32 | "react-leaflet": "^5.0.0", 33 | "react-paginate": "^8.2.0", 34 | "react-redux": "^9.1.2", 35 | "react-router-dom": "^6.26.2", 36 | "react-slick": "^0.30.2", 37 | "redux-persist": "^6.0.0", 38 | "slick-carousel": "^1.8.1", 39 | "tailwind-merge": "^2.5.2" 40 | }, 41 | "devDependencies": { 42 | "@eslint/js": "^9.9.0", 43 | "@types/react": "^18.3.3", 44 | "@types/react-dom": "^18.3.0", 45 | "@vitejs/plugin-react": "^4.3.1", 46 | "autoprefixer": "^10.4.20", 47 | "eslint": "^9.9.0", 48 | "eslint-plugin-react": "^7.35.0", 49 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 50 | "eslint-plugin-react-refresh": "^0.4.9", 51 | "globals": "^15.9.0", 52 | "postcss": "^8.4.45", 53 | "tailwindcss": "^3.4.11", 54 | "vite": "^5.4.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /client/src/components/CardProduct.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ImCross } from "react-icons/im"; 3 | import { useDispatch } from "react-redux"; 4 | import { deleteItem } from "../redux/orebiSlice"; 5 | import AddToCartButton from "./AddToCartButton"; 6 | import PriceFormat from "./PriceFormat"; 7 | import PriceContainer from "./PriceContainer"; 8 | import toast from "react-hot-toast"; 9 | 10 | const CartProduct = ({ item }) => { 11 | const dispatch = useDispatch(); 12 | return ( 13 |
14 |
15 | { 17 | dispatch(deleteItem(item._id)); 18 | toast.success( 19 | `${item?.name.substring(0, 10)}... is deleted successfully!` 20 | ); 21 | }} 22 | className="text-primeColor hover:text-red-500 duration-300 cursor-pointer" 23 | /> 24 | productImage 25 |

{item.name}

26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 | 35 |
36 | 37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | export default CartProduct; 44 | -------------------------------------------------------------------------------- /client/src/components/products/Color.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { motion } from "framer-motion"; 3 | import NavTitle from "./NavTitle"; 4 | 5 | const Color = () => { 6 | const [showColors, setShowColors] = useState(true); 7 | const colors = [ 8 | { 9 | _id: 9001, 10 | title: "Green", 11 | base: "#22c55e", 12 | }, 13 | { 14 | _id: 9002, 15 | title: "Gray", 16 | base: "#a3a3a3", 17 | }, 18 | { 19 | _id: 9003, 20 | title: "Red", 21 | base: "#dc2626", 22 | }, 23 | { 24 | _id: 9004, 25 | title: "Yellow", 26 | base: "#f59e0b", 27 | }, 28 | { 29 | _id: 9005, 30 | title: "Blue", 31 | base: "#3b82f6", 32 | }, 33 | ]; 34 | 35 | return ( 36 |
37 |
setShowColors(!showColors)} 39 | className="cursor-pointer" 40 | > 41 | Shop by Color 42 |
43 | {showColors && ( 44 | 49 |
    50 | {colors.map((item) => ( 51 |
  • 55 | 59 | {item.title} 60 |
  • 61 | ))} 62 |
63 |
64 | )} 65 |
66 | ); 67 | }; 68 | 69 | export default Color; 70 | -------------------------------------------------------------------------------- /client/src/components/ScrollToTop.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | const ScrollToTop = () => { 4 | const [isVisible, setIsVisible] = useState(false); 5 | 6 | // Show button when page is scrolled down more than 800px 7 | const toggleVisibility = () => { 8 | if (window.pageYOffset > 800) { 9 | setIsVisible(true); 10 | } else { 11 | setIsVisible(false); 12 | } 13 | }; 14 | 15 | // Scroll to top smoothly 16 | const scrollToTop = () => { 17 | window.scrollTo({ 18 | top: 0, 19 | behavior: "smooth", 20 | }); 21 | }; 22 | 23 | useEffect(() => { 24 | window.addEventListener("scroll", toggleVisibility); 25 | return () => { 26 | window.removeEventListener("scroll", toggleVisibility); 27 | }; 28 | }, []); 29 | 30 | // Arrow up icon as SVG 31 | const ArrowUpIcon = () => ( 32 | 38 | 44 | 45 | ); 46 | 47 | return ( 48 | <> 49 | {isVisible && ( 50 |
51 | 58 |
59 | )} 60 | 61 | ); 62 | }; 63 | 64 | export default ScrollToTop; 65 | -------------------------------------------------------------------------------- /server/checkDB.mjs: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import productModel from "./models/productModel.js"; 3 | import userModel from "./models/userModel.js"; 4 | import orderModel from "./models/orderModel.js"; 5 | import "dotenv/config"; 6 | 7 | const checkDatabase = async () => { 8 | try { 9 | await mongoose.connect(process.env.MONGO_URI); 10 | console.log("✅ Connected to database"); 11 | 12 | // Check products 13 | const products = await productModel.find({}); 14 | console.log(`\n📦 Products in database: ${products.length}`); 15 | 16 | if (products.length > 0) { 17 | console.log("\nExisting products:"); 18 | products.slice(0, 10).forEach((p, index) => { 19 | console.log( 20 | `${index + 1}. ${p.name} - $${p.price} (Category: ${ 21 | p.category 22 | }) - Type: ${p._type || "N/A"}` 23 | ); 24 | }); 25 | if (products.length > 10) { 26 | console.log(`... and ${products.length - 10} more products`); 27 | } 28 | } else { 29 | console.log("No products found in database"); 30 | } 31 | 32 | // Check users 33 | const users = await userModel.find({}); 34 | console.log(`\n👥 Users in database: ${users.length}`); 35 | const adminUsers = users.filter((u) => u.role === "admin"); 36 | const regularUsers = users.filter((u) => u.role === "user"); 37 | console.log(` - Admin users: ${adminUsers.length}`); 38 | console.log(` - Regular users: ${regularUsers.length}`); 39 | 40 | // Check orders 41 | const orders = await orderModel.find({}); 42 | console.log(`\n🛍️ Orders in database: ${orders.length}`); 43 | 44 | console.log("\n🎯 Database check completed!"); 45 | 46 | await mongoose.disconnect(); 47 | process.exit(0); 48 | } catch (error) { 49 | console.error("❌ Error:", error); 50 | process.exit(1); 51 | } 52 | }; 53 | 54 | checkDatabase(); 55 | -------------------------------------------------------------------------------- /client/src/components/homeProducts/ProductInfo.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AddToCartButton from "../AddToCartButton"; 3 | import { MdStar } from "react-icons/md"; 4 | import PriceContainer from "../PriceContainer"; 5 | 6 | const ProductInfo = ({ productInfo }) => { 7 | return ( 8 |
9 |

{productInfo.name}

10 | 11 |
12 |
13 | {Array?.from({ length: 5 })?.map((_, index) => { 14 | const filled = index + 1 <= Math.floor(productInfo?.ratings); 15 | const halfFilled = 16 | index + 1 > Math.floor(productInfo?.ratings) && 17 | index < Math.ceil(productInfo?.ratings); 18 | 19 | return ( 20 | 30 | ); 31 | })} 32 |
33 |

{`(${productInfo?.ratings?.toFixed( 34 | 1 35 | )} reviews)`}

36 |
37 |

{productInfo.description}

38 |

Be the first to leave a review.

39 | 40 | 41 | 42 |

43 | Categories:{" "} 44 | 45 | {productInfo.category} 46 | 47 |

48 |
49 | ); 50 | }; 51 | 52 | export default ProductInfo; 53 | -------------------------------------------------------------------------------- /client/src/components/RootLayout.jsx: -------------------------------------------------------------------------------- 1 | import Header from "./Header"; 2 | import { Outlet, ScrollRestoration } from "react-router-dom"; 3 | import Footer from "./Footer"; 4 | import ScrollToTop from "./ScrollToTop"; 5 | import "slick-carousel/slick/slick.css"; 6 | import { Provider } from "react-redux"; 7 | import { persistor, store } from "../redux/store"; 8 | import { PersistGate } from "redux-persist/integration/react"; 9 | import { Toaster } from "react-hot-toast"; 10 | import MainLoader from "./MainLoader"; 11 | import ServicesTag from "./ServicesTag"; 12 | 13 | const RootLayout = () => { 14 | return ( 15 | 16 | } persistor={persistor}> 17 | {/* Premium Support Badge */} 18 |
19 |
20 | 💖 21 | Support this project & get the premium source code! 22 | 28 | ☕ Buy Me a Coffee 29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 |