├── .eslintrc.json
├── README.md
├── public
├── favicon.ico
├── vercel.svg
├── img
│ ├── social
│ │ ├── linkedin.svg
│ │ ├── email.svg
│ │ └── github.svg
│ ├── profile_pic.svg
│ └── hero.svg
└── next.svg
├── jsconfig.json
├── postcss.config.js
├── src
├── middleware.js
├── utils
│ └── capitalizeFirstLetter.js
├── pages
│ ├── api
│ │ ├── hello.js
│ │ └── auth
│ │ │ └── [...nextauth].js
│ ├── auth
│ │ ├── view-all-user.js
│ │ └── profile.js
│ ├── _document.js
│ ├── index.js
│ ├── products
│ │ ├── order
│ │ │ └── index.js
│ │ ├── printer
│ │ │ └── index.jsx
│ │ ├── vr
│ │ │ └── index.jsx
│ │ ├── view-all-products
│ │ │ └── index.jsx
│ │ ├── laptop-battery
│ │ │ └── index.jsx
│ │ ├── smart-watch
│ │ │ └── index.jsx
│ │ ├── television-startech
│ │ │ └── index.jsx
│ │ ├── mobile-phone
│ │ │ └── index.jsx
│ │ ├── portable-power-station
│ │ │ └── index.jsx
│ │ ├── laptop
│ │ │ └── index.jsx
│ │ ├── earbuds
│ │ │ └── index.jsx
│ │ ├── gimbal
│ │ │ └── index.jsx
│ │ ├── drone
│ │ │ └── index.jsx
│ │ ├── headphone
│ │ │ └── index.jsx
│ │ ├── cc-camera
│ │ │ └── index.jsx
│ │ ├── graphics-card
│ │ │ └── index.jsx
│ │ ├── action-camera
│ │ │ └── index.jsx
│ │ ├── gaming-console
│ │ │ └── index.jsx
│ │ ├── bluetooth-speakers
│ │ │ └── index.jsx
│ │ └── pc-builder
│ │ │ └── index.jsx
│ ├── _app.js
│ ├── add-to-cart
│ │ └── index.js
│ ├── 404.js
│ ├── product
│ │ └── [productId].js
│ ├── categories
│ │ └── [categoryName].js
│ ├── product-details
│ │ └── [productId].js
│ ├── profile
│ │ └── index.js
│ ├── add-featehr.js
│ ├── build-pc.js
│ ├── sign-in.js
│ ├── sign-up.js
│ └── add-product.js
├── components
│ ├── Shared
│ │ ├── Card
│ │ │ ├── KeyFeatures.jsx
│ │ │ ├── HomePageCard.jsx
│ │ │ ├── ViewCart.jsx
│ │ │ └── Card.jsx
│ │ ├── SingleCard
│ │ │ ├── KeyFeatures.jsx
│ │ │ └── Card.jsx
│ │ ├── Navbar
│ │ │ ├── SubNavbar.js
│ │ │ ├── Navbar.js
│ │ │ ├── MobileNavbar.js
│ │ │ └── MainNavbar.js
│ │ ├── Loading
│ │ │ └── LoadingSpinner.jsx
│ │ ├── Loader
│ │ │ └── Loader.jsx
│ │ ├── Footer
│ │ │ └── Footer.js
│ │ ├── SidebarMenu
│ │ │ └── SidebarMenu.js
│ │ └── AddToCart
│ │ │ └── Card.js
│ ├── ImageBB
│ │ └── imageUpload.jsx
│ ├── FeedbackForm
│ │ ├── FeedbackForm.js
│ │ └── FeedFrom.js
│ ├── Categories
│ │ ├── CategoriesProduct.js
│ │ └── Categories.js
│ ├── UI
│ │ ├── Dropdown.js
│ │ └── Search.js
│ ├── Product
│ │ ├── ViewOrderProduct.js
│ │ ├── ProductFeed.js
│ │ ├── Order.js
│ │ ├── Product.js
│ │ └── ProductDetails.js
│ ├── Layout
│ │ └── Layout.js
│ ├── Banner
│ │ └── Banner.js
│ ├── BuildProduct
│ │ └── BuildProduct.js
│ ├── User
│ │ ├── DeleteUser.jsx
│ │ ├── RoleChangeModal.jsx
│ │ └── ViewAllUser.jsx
│ └── ChooseUs
│ │ └── ChooseUs.js
├── redux
│ ├── features
│ │ ├── categories
│ │ │ └── categoriesApi.js
│ │ ├── product
│ │ │ └── productApi.js
│ │ ├── userFeedBack
│ │ │ └── FeedBack.js
│ │ ├── productFeather
│ │ │ ├── productFeather.js
│ │ │ └── products.js
│ │ ├── api
│ │ │ └── apiSlice.js
│ │ ├── addToCard
│ │ │ └── addToCard.js
│ │ ├── auth
│ │ │ └── userAuth.js
│ │ └── pcbuild
│ │ │ └── pcbuildSlice.js
│ ├── Slice
│ │ ├── userSlice.js
│ │ └── authSlice.js
│ ├── app
│ │ └── store.js
│ └── store
│ │ └── store.js
├── constants
│ └── constants.js
└── styles
│ └── globals.css
├── next.config.js
├── .gitignore
├── tailwind.config.js
└── package.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 | // eslintrc
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🐳 Hazrat Ali
2 |
3 | # 🍏 Programmer || Software Engineering
4 |
5 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hazrat-Ali9/Gadget-Galaxy-Frontend/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 | // js config json
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
8 | // postcss config
--------------------------------------------------------------------------------
/src/middleware.js:
--------------------------------------------------------------------------------
1 | export { default } from "next-auth/middleware";
2 |
3 | export const config = { matcher: ["/add-to-cartaaaa", "/check-out"] };
4 |
--------------------------------------------------------------------------------
/src/utils/capitalizeFirstLetter.js:
--------------------------------------------------------------------------------
1 | export const capitalizeFirstLetter = (string) => {
2 | return string.charAt(0).toUpperCase() + string.slice(1);
3 | };
4 |
5 |
--------------------------------------------------------------------------------
/src/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function handler(req, res) {
4 | res.status(200).json({ name: 'John Doe' })
5 | }
6 |
--------------------------------------------------------------------------------
/src/pages/api/auth/[...nextauth].js:
--------------------------------------------------------------------------------
1 | import NextAuth from "next-auth";
2 |
3 | export const authOptions = {
4 | providers: [
5 | ],
6 | // pages: {
7 | // signIn: "/sign-in"
8 | // }
9 | };
10 |
11 | export default NextAuth(authOptions);
12 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | images: {
5 | domains: ["avatars.githubusercontent.com", "www.startech.com.bd"],
6 | },
7 | };
8 | // next config
9 | module.exports = nextConfig;
10 |
--------------------------------------------------------------------------------
/src/pages/auth/view-all-user.js:
--------------------------------------------------------------------------------
1 | import ViewAllUser from '@/components/User/ViewAllUser';
2 | import React from 'react';
3 |
4 | const Index = () => {
5 | return (
6 |
7 | < ViewAllUser />
8 |
9 | );
10 | };
11 |
12 | export default Index;
--------------------------------------------------------------------------------
/src/components/Shared/Card/KeyFeatures.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const KeyFeatures = ({ item }) => {
4 | return (
5 |
6 | {!item.key ? `${item.feature}` : `${item.key} : ${item.feature}`}
7 |
8 | );
9 | };
10 |
11 | export default KeyFeatures;
12 |
--------------------------------------------------------------------------------
/src/pages/_document.js:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/Shared/SingleCard/KeyFeatures.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const KeyFeatures = ({ item }) => {
4 | return (
5 |
6 | {!item.key ? `${item.feature}` : `${item.key} : ${item.feature}`}
7 |
8 | );
9 | };
10 |
11 | export default KeyFeatures;
12 |
--------------------------------------------------------------------------------
/src/redux/features/categories/categoriesApi.js:
--------------------------------------------------------------------------------
1 | import { apiSlice } from "../api/apiSlice";
2 |
3 | const categoriesApi = apiSlice.injectEndpoints({
4 | endpoints: (builder) => ({
5 | getCategories: builder.query({
6 | query: () => "/categories",
7 | }),
8 | }),
9 | });
10 |
11 | export default categoriesApi;
12 | export const { useGetCategoriesQuery } = categoriesApi;
13 |
--------------------------------------------------------------------------------
/src/components/Shared/Navbar/SubNavbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const SubNavbar = () => {
4 | return (
5 |
6 |
9 |
10 | );
11 | };
12 |
13 | export default SubNavbar;
--------------------------------------------------------------------------------
/src/components/Shared/Loading/LoadingSpinner.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const LoadingSpinner = () => {
4 | return (
5 |
9 | );
10 | };
11 |
12 | export default LoadingSpinner;
13 |
--------------------------------------------------------------------------------
/src/redux/features/product/productApi.js:
--------------------------------------------------------------------------------
1 | import { apiSlice } from "../api/apiSlice";
2 |
3 | const productApi = apiSlice.injectEndpoints({
4 | endpoints: (builder) => ({
5 | getProducts: builder.query({
6 | query: () => "/products",
7 | }),
8 | getProductById: builder.query({
9 | query: (id) => `/products/${id}`,
10 | }),
11 | }),
12 | });
13 |
14 | export default productApi;
15 | export const { useGetProductsQuery } = productApi;
16 |
--------------------------------------------------------------------------------
/src/components/ImageBB/imageUpload.jsx:
--------------------------------------------------------------------------------
1 | export const PostImage =async (image) => {
2 | const formData = new FormData();
3 | formData.append("image", image);
4 |
5 | const url =
6 | `https://api.imgbb.com/1/upload?key=66a8afb5774f60c0f148db65634f7f62`;
7 | const res = await fetch(url, {
8 | method: "POST",
9 | body: formData,
10 | })
11 | const data = await res.json();
12 | // console.log(data);
13 | const imageUrl = data.data.display_url
14 | return imageUrl;
15 | }
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import Banner from "@/components/Banner/Banner";
2 | import ChooseUs from "@/components/ChooseUs/ChooseUs";
3 | import FeedbackForm from "@/components/FeedbackForm/FeedbackForm";
4 | import ProductFeed from "@/components/Product/ProductFeed";
5 |
6 | export default function Home({ products, categories }) {
7 |
8 | return (
9 | <>
10 |
11 |
12 |
13 |
14 |
15 | >
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/redux/Slice/userSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | user: null,
5 | };
6 |
7 | const userSlice = createSlice({
8 | name: "user",
9 | initialState,
10 | reducers: {
11 | setUser: (state, action) => {
12 | state.user = JSON.stringify(action.payload);
13 | },
14 | clearUser: (state) => {
15 | state.user = null;
16 | },
17 | },
18 | });
19 |
20 | export const { setUser, clearUser } = userSlice.actions;
21 | export default userSlice.reducer;
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 | # gitignore
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
37 | .vercel
38 |
--------------------------------------------------------------------------------
/src/redux/app/store.js:
--------------------------------------------------------------------------------
1 |
2 | import { configureStore } from "@reduxjs/toolkit";;
3 | import authReducer from "../Slice/authSlice";
4 | import { setupListeners } from "@reduxjs/toolkit/dist/query";
5 | import { apiSlice } from "../features/api/apiSlice";
6 |
7 | export const store = configureStore({
8 | reducer: {
9 | [apiSlice.reducerPath]: apiSlice.reducer,
10 | auth: authReducer,
11 | },
12 | middleware: (getDefaultMiddleware) =>
13 | getDefaultMiddleware().concat(apiSlice.middleware),
14 | devTools: true,
15 | });
16 | setupListeners(store.dispatch);
--------------------------------------------------------------------------------
/src/components/Shared/Loader/Loader.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Loader = () => {
4 | return (
5 |
9 |
10 | Loading...
11 |
12 |
13 | );
14 | };
15 |
16 | export default Loader;
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/redux/store/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import { apiSlice } from "../EndPoints/fetchbasequery";
3 | import { setupListeners } from "@reduxjs/toolkit/dist/query";
4 | import authReducer from "../Slice/authSlice";
5 | import useReducer from "../Slice/userSlice";
6 |
7 | export const store = configureStore({
8 | reducer: {
9 | [apiSlice.reducerPath]: apiSlice.reducer,
10 | auth: authReducer,
11 | user: useReducer
12 | },
13 | middleware: (getDefaultMiddleware) =>
14 | getDefaultMiddleware().concat(apiSlice.middleware),
15 | });
16 |
17 | setupListeners(store.dispatch);
18 |
19 |
--------------------------------------------------------------------------------
/public/img/social/linkedin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/img/social/email.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/pages/products/order/index.js:
--------------------------------------------------------------------------------
1 | import Order from '@/components/Product/Order';
2 | import { useGetOrderProductQuery } from '@/redux/features/addToCard/addToCard';
3 | import React from 'react';
4 |
5 | const Index = () => {
6 | const {data, isLoading, isError} = useGetOrderProductQuery()
7 | console.log(data);
8 | return (
9 |
10 |
11 | {
12 | data?.data.map((data, i)=> <>
13 |
14 | >)
15 | }
16 |
17 |
18 | );
19 | };
20 |
21 | export default Index;
--------------------------------------------------------------------------------
/src/pages/_app.js:
--------------------------------------------------------------------------------
1 | import Layout from "@/components/Layout/Layout";
2 | import { store, wrapper } from "@/redux/app/store";
3 | import { SessionProvider } from "next-auth/react";
4 | import { Provider } from "react-redux";
5 | import "@/styles/globals.css";
6 | import { Toaster } from "react-hot-toast";
7 |
8 | export default function App({
9 | Component,
10 | pageProps: { session, ...pageProps },
11 | }) {
12 | return (
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/redux/features/userFeedBack/FeedBack.js:
--------------------------------------------------------------------------------
1 | import { apiSlice } from "../api/apiSlice";
2 |
3 |
4 | const featherAPI = apiSlice.injectEndpoints({
5 | endpoints: (builder) => ({
6 | addFeedBack: builder.mutation({
7 | query: (formData) => {
8 | return {
9 | url: `/api/v1/feedback/send-feedback`,
10 | method: "POST",
11 | body: formData,
12 | };
13 | },
14 | }),
15 | getFeedBack: builder.query({
16 | query: () => {
17 | return {
18 | url: `/api/v1/feather/get-feather`,
19 | method: "GET",
20 | };
21 | },
22 | })
23 | }),
24 | });
25 |
26 | export const {
27 | useAddFeedBackMutation,
28 | useGetFeedBackQuery
29 | } = featherAPI;
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | fontFamily: {
10 | Poppins: ["Poppins", "sans-serif"],
11 | },
12 | extend: {
13 | colors: {
14 | blue: {
15 | light: "#0a81ab",
16 | dark: "#0c4271",
17 | },
18 | },
19 | },
20 | screens: {
21 | xxs: "375px",
22 | xs: "425px",
23 | sm: "640px",
24 | md: "780px",
25 | lg: "1024px",
26 | xl: "1280px",
27 | "2xl": "1536px",
28 | },
29 | },
30 | plugins: [require("@tailwindcss/line-clamp")],
31 | };
32 |
33 | // Tailwind
--------------------------------------------------------------------------------
/src/redux/features/productFeather/productFeather.js:
--------------------------------------------------------------------------------
1 | // import { apiSlice } from "../api/apiSlice";
2 |
3 | import { apiSlice } from "../api/apiSlice";
4 |
5 |
6 | const featherAPI = apiSlice.injectEndpoints({
7 | endpoints: (builder) => ({
8 | addFeather: builder.mutation({
9 | query: (formData) => {
10 | return {
11 | url: `/api/v1/feather/create-feater`,
12 | method: "POST",
13 | body: formData,
14 | };
15 | },
16 | }),
17 | getFeather: builder.query({
18 | query: () => {
19 | return {
20 | url: `/api/v1/feather/get-feather`,
21 | method: "GET",
22 | };
23 | },
24 | }),
25 | }),
26 | });
27 |
28 | export const {
29 | useAddFeatherMutation,
30 | useGetFeatherQuery
31 | } = featherAPI;
--------------------------------------------------------------------------------
/src/constants/constants.js:
--------------------------------------------------------------------------------
1 | export const NavbarDropdown = [
2 | {
3 | id: 5,
4 | name: "Logout",
5 | },
6 | ];
7 |
8 | export const NavbarCategories = [
9 | {
10 | id: 1,
11 | name: "CPU / Processor",
12 | path: "/categories/processor",
13 | },
14 | {
15 | id: 2,
16 | name: "Motherboard",
17 | path: "/categories/motherboard",
18 | },
19 | {
20 | id: 3,
21 | name: "RAM",
22 | path: "/categories/RAM",
23 | },
24 | {
25 | id: 4,
26 | name: "Power Supply Unit",
27 | path: "/categories/Power Supply Unit",
28 | },
29 | {
30 | id: 5,
31 | name: "Storage Device",
32 | path: "/categories/Storage Device",
33 | },
34 | {
35 | id: 6,
36 | name: "Monitor",
37 | path: "/categories/monitor",
38 | },
39 | {
40 | id: 7,
41 | name: "Others",
42 | path: "/categories/others",
43 | },
44 | ];
45 |
--------------------------------------------------------------------------------
/src/pages/add-to-cart/index.js:
--------------------------------------------------------------------------------
1 | import Card from '@/components/Shared/AddToCart/Card';
2 | import { useGetAddToCartProductsQuery } from '@/redux/features/addToCard/addToCard';
3 | import { useRouter } from 'next/router';
4 | import React, { useEffect } from 'react';
5 |
6 | const Index = () => {
7 | const router = useRouter()
8 | const {data, isLoading, isError, refetch} = useGetAddToCartProductsQuery()
9 | useEffect(() => {
10 | if (typeof window !== "undefined") {
11 | const accessToken = localStorage.getItem("accessToken");
12 | const refreshToken = localStorage.getItem("refreshToken");
13 | if (!accessToken && !refreshToken) {
14 | router.push('/sign-in');
15 | }
16 | }
17 | }, [router]);
18 | return (
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export default Index;
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | // pages/404.js
2 | import Head from "next/head";
3 | import Link from "next/link";
4 |
5 | export default function Custom404() {
6 | return (
7 | <>
8 |
9 | PC Builder | Page Not Found
10 |
11 |
12 |
13 |
14 |
15 |
16 | 404
17 |
18 |
19 | Page Not Found
20 |
21 |
22 |
23 | HomePage
24 |
25 |
26 |
27 | >
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Shared/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import { ChevronDownIcon } from "@heroicons/react/24/solid";
2 | import Image from "next/image";
3 | import { useRouter } from "next/router";
4 | import { useState } from "react";
5 | import Skeleton from "react-loading-skeleton";
6 | import Dropdown from "../../UI/Dropdown";
7 |
8 | import { NavbarCategories, NavbarDropdown } from "@/constants/constants";
9 | import { signIn, useSession } from "next-auth/react";
10 | import Link from "next/link";
11 | import MainNavbar from "./MainNavbar";
12 | import SubNavbar from "./SubNavbar";
13 |
14 | function Navbar() {
15 | const router = useRouter();
16 | const [dropDown, setDropDown] = useState(false);
17 | const [categoriesDropDown, setCategoriesDropDown] = useState(false);
18 | const { data: session, status } = useSession();
19 | const userImage = session?.user?.image || "/img/profile_pic.svg";
20 |
21 | return (
22 |
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | export default Navbar;
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pc-builder-assignment",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "react-scripts start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@heroicons/react": "^2.0.18",
13 | "@reduxjs/toolkit": "^1.9.5",
14 | "@tailwindcss/line-clamp": "^0.4.4",
15 | "autoprefixer": "10.4.14",
16 | "axios": "^1.5.1",
17 | "eslint": "8.45.0",
18 | "eslint-config-next": "13.4.12",
19 | "js-cookie": "^3.0.5",
20 | "next": "13.4.12",
21 | "next-auth": "^4.22.3",
22 | "next-redux-wrapper": "^8.1.0",
23 | "postcss": "8.4.27",
24 | "react": "18.2.0",
25 | "react-dom": "18.2.0",
26 | "react-hot-toast": "^2.4.1",
27 | "react-icon": "^1.0.0",
28 | "react-icons": "^4.11.0",
29 | "react-loader-spinner": "^5.3.4",
30 | "react-loading-skeleton": "^3.3.1",
31 | "react-onclickoutside": "^6.13.0",
32 | "react-redux": "^8.1.1",
33 | "tailwindcss": "3.3.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/pages/product/[productId].js:
--------------------------------------------------------------------------------
1 | import Card from "@/components/Shared/SingleCard/Card";
2 |
3 | const ProductDetails = ({ item }) => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default ProductDetails;
12 |
13 | export const getStaticPaths = async () => {
14 | const res = await fetch(`https://gadget-galaxy-server-seven.vercel.app/api/v1/product/get-product`)
15 |
16 |
17 | const datas = await res?.json()
18 | const data = datas?.data
19 | const paths = data?.map((pro) => ({
20 | params: { productId: pro._id }
21 |
22 | }))
23 | return { paths, fallback: false }
24 | }
25 |
26 | export const getStaticProps = async (context) => {
27 | const { params } = context
28 | const res = await fetch(`https://gadget-galaxy-server-seven.vercel.app/api/v1/product/get-product/${params.productId}`)
29 |
30 | const data = await res?.json()
31 | return {
32 | props: {
33 | item: data
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/FeedbackForm/FeedbackForm.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image';
2 | import React from 'react';
3 | import FeedFrom from './FeedFrom';
4 | // FeedbackFrom
5 | const FeedbackForm = () => {
6 | return (
7 |
8 |
9 | Feedback Form
10 |
Please provide a brief description of your feedback
11 |
12 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default FeedbackForm;
--------------------------------------------------------------------------------
/src/pages/products/printer/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 | return (
20 |
21 | {isLoading ?
:
22 | {
23 | data?.data?.map((item) => <>
24 |
25 | >)
26 | }
27 |
}
28 |
29 |
30 | );
31 | };
32 |
33 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/vr/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 |
20 | return (
21 |
22 | {isLoading ?
:
23 | {
24 | data?.data?.map((item) => <>
25 |
26 | >)
27 | }
28 |
}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/view-all-products/index.jsx:
--------------------------------------------------------------------------------
1 | import ViewCart from '@/components/Shared/Card/ViewCart';
2 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
3 | import { useDeleteToProductMutation, useGetAllProductQuery } from '@/redux/features/productFeather/products';
4 | import React from 'react';
5 | import toast from 'react-hot-toast';
6 |
7 | const Index = () => {
8 | const { data, isLoading, refetch } = useGetAllProductQuery();
9 | const [deleteToCart, resInfo] = useDeleteToProductMutation()
10 |
11 | const deleteToCartItem = async (id) => {
12 | await deleteToCart(id)
13 | await refetch()
14 | toast.error("Yah..! Delete successfully");
15 | }
16 | return (
17 |
18 | {isLoading ?
:
19 | {
20 | data.data?.map((item) => <>
21 |
22 | >)
23 | }
24 |
}
25 |
26 |
27 | );
28 | };
29 |
30 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/laptop-battery/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 |
20 | return (
21 |
22 | {isLoading ?
:
23 | {
24 | data?.data?.map((item) => <>
25 |
26 | >)
27 | }
28 |
}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/smart-watch/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 |
20 | return (
21 |
22 | {isLoading ?
:
23 | {
24 | data?.data?.map((item) => <>
25 |
26 | >)
27 | }
28 |
}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/television-startech/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 |
20 | return (
21 |
22 | {isLoading ?
:
23 | {
24 | data?.data?.map((item) => <>
25 |
26 | >)
27 | }
28 |
}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/mobile-phone/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 |
20 |
21 |
22 | return (
23 |
24 | {isLoading ?
:
25 | {
26 | data?.data?.map((item) => <>
27 |
28 | >)
29 | }
30 |
}
31 |
32 |
33 | );
34 | };
35 |
36 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/portable-power-station/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect, useState } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 |
6 | const Index = () => {
7 |
8 | const [routePath, setRoutePath] = useState()
9 |
10 | useEffect(() => {
11 | const pathSegments = window.location.pathname.split('/');
12 | const lastSegment = pathSegments[pathSegments.length - 1];
13 | setRoutePath(lastSegment);
14 | }, []);
15 |
16 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
17 | feather: routePath
18 | })
19 |
20 | return (
21 |
22 | {isLoading ?
:
23 | {
24 | data?.data?.map((item) => <>
25 |
26 | >)
27 | }
28 |
}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/laptop/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React, { useEffect } from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 |
7 | const Index = () => {
8 |
9 | const [routePath, setRoutePath] = useState()
10 |
11 | useEffect(() => {
12 | const pathSegments = window.location.pathname.split('/');
13 | const lastSegment = pathSegments[pathSegments.length - 1];
14 | setRoutePath(lastSegment);
15 | }, []);
16 |
17 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
18 | feather: routePath
19 | })
20 |
21 | return (
22 |
23 | {isLoading ?
:
24 | {
25 | data?.data?.map((item) => <>
26 |
27 | >)
28 | }
29 |
}
30 |
31 |
32 | );
33 | };
34 |
35 | export default Index;
--------------------------------------------------------------------------------
/src/components/Categories/CategoriesProduct.js:
--------------------------------------------------------------------------------
1 | import Product from "../Product/Product";
2 | // Catagories Product
3 | function CategoriesProduct({ products, categoryName }) {
4 | return (
5 |
6 |
7 | {categoryName}
8 |
9 |
10 | {products?.map(
11 | ({
12 | id,
13 | productName,
14 | price,
15 | description,
16 | category,
17 | image,
18 | status,
19 | reviews,
20 | }) => (
21 |
32 | )
33 | )}
34 |
35 |
36 | );
37 | }
38 |
39 | export default CategoriesProduct;
40 |
--------------------------------------------------------------------------------
/src/pages/products/earbuds/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 | const [routePath, setRoutePath] = useState()
10 |
11 | useEffect(() => {
12 | const pathSegments = window.location.pathname.split('/');
13 | const lastSegment = pathSegments[pathSegments.length - 1];
14 | setRoutePath(lastSegment);
15 | }, []);
16 |
17 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
18 | feather: routePath
19 | })
20 | return (
21 |
22 | {isLoading ?
:
23 | {
24 | data?.data?.map((item) => <>
25 |
26 | >)
27 | }
28 |
}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/gimbal/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useEffect } from 'react';
6 | import { useState } from 'react';
7 |
8 | const Index = () => {
9 | const [routePath, setRoutePath] = useState()
10 |
11 | useEffect(() => {
12 | const pathSegments = window.location.pathname.split('/');
13 | const lastSegment = pathSegments[pathSegments.length - 1];
14 | setRoutePath(lastSegment);
15 | }, []);
16 |
17 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
18 | feather: routePath
19 | })
20 |
21 | return (
22 |
23 | {isLoading ?
:
24 | {
25 | data?.data?.map((item) => <>
26 |
27 | >)
28 | }
29 |
}
30 |
31 |
32 | );
33 | };
34 |
35 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/drone/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: 'drone'
20 | })
21 |
22 | return (
23 |
24 | {isLoading ?
:
25 | {
26 | data?.data?.map((item) => <>
27 |
28 | >)
29 | }
30 |
}
31 |
32 |
33 | );
34 | };
35 |
36 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/headphone/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: routePath
20 | })
21 | return (
22 |
23 | {isLoading ?
:
24 | {
25 | data?.data?.map((item) => <>
26 |
27 | >)
28 | }
29 |
}
30 |
31 |
32 | );
33 | };
34 |
35 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/cc-camera/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: routePath
20 | })
21 |
22 | return (
23 |
24 | {isLoading ?
:
25 | {
26 | data?.data?.map((item) => <>
27 |
28 | >)
29 | }
30 |
}
31 |
32 |
33 | );
34 | };
35 |
36 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/graphics-card/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: routePath
20 | })
21 | return (
22 |
23 | {isLoading ?
:
24 | {
25 | data?.data?.map((item) => <>
26 |
27 | >)
28 | }
29 |
}
30 |
31 |
32 | );
33 | };
34 |
35 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/action-camera/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: routePath
20 | })
21 |
22 | return (
23 |
24 | {isLoading ?
:
25 | {
26 | data?.data?.map((item) => <>
27 |
28 | >)
29 | }
30 |
}
31 |
32 |
33 | );
34 | };
35 |
36 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/gaming-console/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: routePath
20 | })
21 |
22 | return (
23 |
24 | {isLoading ?
:
25 | {
26 | data?.data?.map((item) => <>
27 |
28 | >)
29 | }
30 |
}
31 |
32 |
33 | );
34 | };
35 |
36 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/bluetooth-speakers/index.jsx:
--------------------------------------------------------------------------------
1 | import { useGetFeatherProductQuery } from '@/redux/features/productFeather/products';
2 | import React from 'react';
3 | import Card from '@/components/Shared/Card/Card';
4 | import LoadingSpinner from '@/components/Shared/Loading/LoadingSpinner';
5 | import { useState } from 'react';
6 | import { useEffect } from 'react';
7 |
8 | const Index = () => {
9 |
10 | const [routePath, setRoutePath] = useState()
11 |
12 | useEffect(() => {
13 | const pathSegments = window.location.pathname.split('/');
14 | const lastSegment = pathSegments[pathSegments.length - 1];
15 | setRoutePath(lastSegment);
16 | }, []);
17 |
18 | const { data, isLoading, refetch } = useGetFeatherProductQuery({
19 | feather: routePath
20 | })
21 |
22 | return (
23 |
24 | {isLoading ?
:
25 | {
26 | data?.data?.map((item) => <>
27 |
28 | >)
29 | }
30 |
}
31 |
32 |
33 | );
34 | };
35 |
36 | export default Index;
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/UI/Dropdown.js:
--------------------------------------------------------------------------------
1 | import { signOut } from "next-auth/react";
2 | import { useRouter } from "next/router";
3 | import onClickOutside from "react-onclickoutside";
4 |
5 | function Dropdown({ hideDropDown, DropdownItem }) {
6 | const router = useRouter();
7 | Dropdown.handleClickOutside = hideDropDown;
8 |
9 | return (
10 |
11 | {DropdownItem.map((item) => {
12 | if (item.name === "Logout") {
13 | return (
14 |
{
18 | signOut();
19 | }}
20 | >
21 | Logout
22 |
23 | );
24 | }
25 |
26 | return (
27 |
router.push(`/${item.path}`)}
31 | >
32 | {item.name}
33 |
34 | );
35 | })}
36 |
37 | );
38 | }
39 |
40 | const clickOutsideConfig = {
41 | handleClickOutside: () => Dropdown.handleClickOutside,
42 | };
43 |
44 | export default onClickOutside(Dropdown, clickOutsideConfig);
45 |
--------------------------------------------------------------------------------
/src/redux/Slice/authSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 | import Cookies from "js-cookie";
3 | const isBrowser = typeof window !== "undefined";
4 | const accessToken = isBrowser ? localStorage.getItem("accessToken") : null;
5 | const refreshToken = isBrowser ? localStorage.getItem("refreshToken") : null;
6 | let initialState = {
7 | accessToken: null,
8 | refreshToken: null,
9 | };
10 | if (accessToken && refreshToken) {
11 | initialState.accessToken = accessToken;
12 | initialState.refreshToken = refreshToken;
13 | }
14 | const authSlice = createSlice({
15 | name: "auth",
16 | initialState,
17 | reducers: {
18 | setCredentials: (state, action) => {
19 | const { accessToken, refreshToken, user } = action.payload;
20 | state.accessToken = accessToken;
21 | state.refreshToken = refreshToken;
22 | state.user = refreshToken;
23 | localStorage.setItem("accessToken", accessToken);
24 | localStorage.setItem("refreshToken", refreshToken);
25 | localStorage.setItem("user", user);
26 | },
27 | logOut: (state, action) => {
28 | state.accessToken = null;
29 | state.refreshToken = null;
30 | localStorage.removeItem("accessToken");
31 | localStorage.removeItem("refreshToken");
32 | localStorage.removeItem("user", data.user);
33 | Cookies.remove("sessionid");
34 | },
35 | },
36 | });
37 | export default authSlice.reducer;
38 | export const { setCredentials, logOut } = authSlice.actions;
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/redux/features/productFeather/products.js:
--------------------------------------------------------------------------------
1 | import { apiSlice } from "../api/apiSlice";
2 |
3 |
4 | const featherAPI = apiSlice.injectEndpoints({
5 | endpoints: (builder) => ({
6 | addProducts: builder.mutation({
7 | query: (formData) => {
8 | return {
9 | url: `/api/v1/product/create-product`,
10 | method: "POST",
11 | body: formData,
12 | };
13 | },
14 | }),
15 | getProducts: builder.query({
16 | query: () => {
17 | return {
18 | url: `/api/v1/feather/get-feather`,
19 | method: "GET",
20 | };
21 | },
22 | }),
23 | getFeatherProduct: builder.query({
24 | query: ({feather}) => {
25 | return {
26 | url: `/api/v1/product/get-feather-product?feather=${feather}`,
27 | method: "GET",
28 | };
29 | },
30 | }),
31 | getAllProduct: builder.query({
32 | query: () => {
33 | return {
34 | url: `/api/v1/product/get-product`,
35 | method: "GET",
36 | };
37 | },
38 | }),
39 | deleteToProduct: builder.mutation({
40 | query: (id) => ({
41 | url: `/api/v1/product/delete-product/${id}`,
42 | method: "DELETE",
43 | }),
44 | }),
45 | }),
46 | });
47 |
48 | export const {
49 | useAddProductsMutation,
50 | useGetFeatherProductQuery,
51 | useGetAllProductQuery,
52 | useDeleteToProductMutation
53 | } = featherAPI;
--------------------------------------------------------------------------------
/src/components/Product/ViewOrderProduct.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ViewOrderProduct = ({ setViewOrderPage, item }) => {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
{item.name}
12 |
13 |
14 |
21 |
22 |
23 | setViewOrderPage(false)} className="button lg:px-10 lg:py-2 px-8 xl:text-xl lg:text-lg text-base flex items-center justify-center">Close
24 |
25 |
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default ViewOrderProduct;
--------------------------------------------------------------------------------
/src/components/Shared/Card/HomePageCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useRouter } from 'next/router';
3 |
4 | const HomePageCard = ({ item }) => {
5 | const router = useRouter();
6 | const productDetails = (id) => {
7 | router.push(`/product/${id}`);
8 | };
9 |
10 | return (
11 |
12 |
13 |
14 |
productDetails(item._id)}>
15 |
22 |
23 |
24 |
25 |
26 | productDetails(item._id)} className='bold text-center'>
27 | {item?.name}
28 |
29 |
30 |
31 |
32 |
33 | ${item?.price}
34 |
35 |
36 | );
37 | };
38 |
39 | export default HomePageCard;
40 |
--------------------------------------------------------------------------------
/src/components/Categories/Categories.js:
--------------------------------------------------------------------------------
1 | import { useGetFeatherQuery } from "@/redux/features/productFeather/productFeather";
2 | import { useRouter } from "next/router";
3 | import React from "react";
4 | // Catagoris
5 | const Categories = () => {
6 | const router = useRouter()
7 | const { data: productFeather, isLoading, isError } = useGetFeatherQuery()
8 | const opneProduct = (item)=>{
9 | router.push(`products/${item.dis}`);
10 | }
11 | return (
12 |
13 |
14 |
15 | Featured Category
16 |
17 |
Get Your Desired Product from Featured Category!
18 |
19 |
20 |
21 |
22 | {productFeather?.data?.map((item, i) => (
23 |
opneProduct(item)}
25 | key={`category-${i}`}
26 | className="w-[150px] flex flex-col px-5 py-5 gap-2 items-center m-2 bg-white rounded-xl shadow-2xl"
27 | >
28 |
29 |
30 |
31 |
32 |
{item.featherName}
33 |
34 |
35 | ))}
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Categories;
44 |
--------------------------------------------------------------------------------
/src/pages/categories/[categoryName].js:
--------------------------------------------------------------------------------
1 | // import CategoriesProduct from "@/components/Categories/CategoriesProduct";
2 | // import { capitalizeFirstLetter } from "@/utils/capitalizeFirstLetter";
3 | // import React from "react";
4 |
5 | // const Category = ({ products, categoryName }) => {
6 | // return (
7 | // 0 ? "" : "h-screen"}`}>
8 | //
9 | //
10 | // );
11 | // };
12 |
13 | // export default Category;
14 |
15 | // export const getStaticPaths = async () => {
16 | // const res = await fetch(
17 | // "https://pc-builder-assignment-server.vercel.app/products"
18 | // );
19 | // const products = await res.json();
20 | // const paths = products.map((product) => ({
21 | // params: { categoryName: product.category },
22 | // }));
23 | // return {
24 | // paths,
25 | // fallback: true,
26 | // };
27 | // };
28 |
29 | // export const getStaticProps = async (context) => {
30 |
31 |
32 | // try {
33 | // const res = await fetch(
34 | // `https://pc-builder-assignment-server.vercel.app/products?category=${capitalizeFirstLetter(
35 | // context.params.categoryName
36 | // )}`
37 | // );
38 | // const products = await res.json();
39 |
40 | // return {
41 | // props: {
42 | // products,
43 | // categoryName: context.params.categoryName,
44 | // },
45 | // };
46 | // } catch (error) {
47 | // console.log(error);
48 | // return {
49 | // notFound: true,
50 | // };
51 | // }
52 | // };
53 |
54 | import React from 'react';
55 |
56 | const Index = () => {
57 | return (
58 |
59 |
60 |
61 | );
62 | };
63 |
64 | export default Index;
--------------------------------------------------------------------------------
/src/pages/product-details/[productId].js:
--------------------------------------------------------------------------------
1 | import ProductDetails from "@/components/Product/ProductDetails";
2 | import Head from "next/head";
3 |
4 | const ProductInfo = ({ product }) => {
5 | return (
6 | <>
7 | {product?.productName && (
8 |
9 | PC Builder | {product?.productName}
10 |
11 | )}
12 |
25 | >
26 | );
27 | };
28 |
29 | export default ProductInfo;
30 |
31 | export const getStaticPaths = async () => {
32 | const res = await fetch(
33 | "https://pc-builder-assignment-server.vercel.app/products"
34 | );
35 | const products = await res.json();
36 | const paths = products.map((product) => ({
37 | params: { productId: product.id.toString() },
38 | }));
39 |
40 | return {
41 | paths,
42 | fallback: true,
43 | };
44 | };
45 |
46 | export const getStaticProps = async (context) => {
47 | try {
48 | const res = await fetch(
49 | `https://pc-builder-assignment-server.vercel.app/products/${context.params.productId}`
50 | );
51 | const product = await res.json();
52 |
53 | return {
54 | props: {
55 | product,
56 | },
57 | };
58 | } catch (error) {
59 | console.log(error);
60 | return {
61 | notFound: true,
62 | };
63 | }
64 | };
65 |
--------------------------------------------------------------------------------
/src/redux/features/api/apiSlice.js:
--------------------------------------------------------------------------------
1 | import { HYDRATE } from "next-redux-wrapper";
2 |
3 |
4 | import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
5 | import axios from "axios";
6 | import { logOut, setCredentials } from "../../Slice/authSlice";
7 | const URL = "https://gadget-galaxy-server-seven.vercel.app";
8 | // const URL = "http://localhost:5000/";
9 |
10 | const baseQuery = fetchBaseQuery({
11 | baseUrl: URL,
12 | prepareHeaders: (headers, { getState }) => {
13 | const token = getState().auth.accessToken;
14 | if (token) {
15 | headers.set("authorization", `Bearer ${token}`);
16 | }
17 | return headers;
18 | },
19 | });
20 |
21 | const baseQueryWithRefresh = async (args, api, extraOptions) => {
22 | let result = await baseQuery(args, api, extraOptions);
23 |
24 | if (result?.error?.status === 403) {
25 | const refreshToken = api.getState().auth.refreshToken;
26 | const data = { refreshToken: refreshToken };
27 | const refreshResult = await axios.post(`${URL}/`, data);
28 | if (refreshResult?.data) {
29 | api.dispatch(setCredentials());
30 | const accessToken = refreshResult.data.accessToken;
31 | console.log(accessToken);
32 | const refreshToken = refreshResult.data.refreshToken;
33 | console.log(refreshToken);
34 | console.log("with refresh");
35 | result = await baseQuery(args, api, extraOptions);
36 | } else {
37 | api.dispatch(logOut());
38 | }
39 | }
40 | return result;
41 | };
42 |
43 |
44 | export const apiSlice = createApi({
45 | reducerPath: "api",
46 | baseQuery: baseQueryWithRefresh,
47 | tagTypes: [
48 | 'cart-tags'
49 | ],
50 | extractRehydrationInfo(action, { reducerPath }) {
51 | if (action.type === HYDRATE) {
52 | return action.payload[reducerPath];
53 | }
54 | },
55 | endpoints: () => ({}),
56 | });
57 |
--------------------------------------------------------------------------------
/public/img/social/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/components/Shared/Card/ViewCart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useRouter } from 'next/router';
3 |
4 | const ViewCart = ({ item, deleteToCartItem }) => {
5 | const router = useRouter();
6 | const productDetails = (id) => {
7 | router.push(`/product/${id}`);
8 | };
9 |
10 | return (
11 |
12 |
13 |
14 |
productDetails(item._id)}>
15 |
22 |
23 |
24 |
25 |
26 | productDetails(item._id)} className='bold text-center'>
27 | {item?.name}
28 |
29 |
30 |
31 |
32 |
33 | ${item?.price}
34 |
35 |
36 | deleteToCartItem(item._id)} className="button lg:px-10 lg:py-2 px-8 xl:text-xl lg:text-lg text-base flex items-center justify-center">Delete
37 | {/* Update */}
38 |
39 |
40 | );
41 | };
42 |
43 | export default ViewCart;
44 |
--------------------------------------------------------------------------------
/src/redux/features/addToCard/addToCard.js:
--------------------------------------------------------------------------------
1 | // import { apiSlice } from "../api/apiSlice";
2 |
3 | import { apiSlice } from "../api/apiSlice";
4 |
5 |
6 | const featherAPI = apiSlice.injectEndpoints({
7 | endpoints: (builder) => ({
8 | addToCartProduct: builder.mutation({
9 | query: (data) => {
10 | const id = data.id;
11 | const body = data.body;
12 | return {
13 | url: `/api/v1/product/add-to-cart/${id}`,
14 | method: "POST",
15 | body,
16 | };
17 | },
18 | }),
19 | getAddToCartProducts: builder.query({
20 | query: () => {
21 | return {
22 | url: `/api/v1/product/get-to-cart`,
23 | method: "GET",
24 | };
25 | },
26 | invalidatesTags: ["cart-tags"],
27 | }),
28 | getOrderProduct: builder.query({
29 | query: () => {
30 | return {
31 | url: `/api/v1/product/get-all-order-products`,
32 | method: "GET",
33 | };
34 | },
35 | invalidatesTags: ["cart-tags"],
36 | }),
37 | deleteToCart: builder.mutation({
38 | query: (id) => ({
39 | url: `/api/v1/product/delete-to-cart/${id}`,
40 | method: "DELETE",
41 | }),
42 | invalidatesTags: ["cart-tags"],
43 | }),
44 | confirmProduct: builder.mutation({
45 | query: () => {
46 | return {
47 | url: `/api/v1/product/get-order-products`,
48 | method: "POST",
49 | };
50 | },
51 |
52 | }),
53 | }),
54 | });
55 |
56 | export const {
57 | useAddToCartProductMutation,
58 | useGetAddToCartProductsQuery,
59 | useDeleteToCartMutation,
60 | useConfirmProductMutation,
61 | useGetOrderProductQuery
62 | } = featherAPI;
--------------------------------------------------------------------------------
/src/components/Product/ProductFeed.js:
--------------------------------------------------------------------------------
1 | import { useGetAllProductQuery } from "@/redux/features/productFeather/products";
2 | import Categories from "../Categories/Categories";
3 | import LoadingSpinner from "../Shared/Loading/LoadingSpinner";
4 | import { useEffect } from "react";
5 | import { useState } from "react";
6 | import HomePageCard from "../Shared/Card/HomePageCard";
7 | function ProductFeed() {
8 | const { data, isLoading, refetch } = useGetAllProductQuery();
9 | const [randomProducts, setRandomProducts] = useState([]);
10 | function shuffleArray(array) {
11 | const shuffledArray = [...array];
12 | for (let i = shuffledArray.length - 1; i > 0; i--) {
13 | const j = Math.floor(Math.random() * (i + 1));
14 | [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
15 | }
16 | return shuffledArray;
17 | }
18 |
19 | useEffect(() => {
20 | if (data) {
21 | const shuffledProducts = shuffleArray(data.data).slice(0, 20);
22 | setRandomProducts(shuffledProducts);
23 | }
24 | }, [data]);
25 |
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Featured Products
35 |
36 |
37 | Check & Get Your Desired Product!
38 |
39 |
40 | {isLoading ?
:
41 | {
42 | randomProducts?.map((item) => <>
43 |
44 | >)
45 | }
46 |
}
47 |
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | export default ProductFeed;
56 |
--------------------------------------------------------------------------------
/src/components/Layout/Layout.js:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import Navbar from "../Shared/Navbar/Navbar";
3 | import Footer from "../Shared/Footer/Footer";
4 | import MobileNavbar from "../Shared/Navbar/MobileNavbar";
5 |
6 | const Layout = ({ children }) => {
7 |
8 | return (
9 | <>
10 |
11 |
12 |
13 |
17 | Gadget Galaxy
18 |
22 |
27 |
33 |
39 |
40 |
45 |
46 |
47 |
51 |
52 |
53 |
54 | <>
55 |
56 |
57 | {children}
58 |
59 | >
60 |
61 | >
62 | );
63 | }
64 |
65 | export default Layout;
--------------------------------------------------------------------------------
/src/components/Product/Order.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ViewOrderProduct from './ViewOrderProduct';
3 | import { useState } from 'react';
4 |
5 | const Order = ({ item, i }) => {
6 | const [viewOrderPage, setViewOrderPage] = useState(false)
7 | return (
8 |
9 |
10 |
11 |
12 |
Order# {i}
13 |
14 | Date Added: {item.created_at}
15 |
16 |
17 |
18 |
Status
19 |
20 |
21 |
22 |
23 |
30 |
{item?.name}
31 |
32 |
33 |
${item?.price}
34 | setViewOrderPage(true)} className="button lg:px-10 lg:py-2 px-8 xl:text-xl lg:text-lg text-base flex items-center justify-center">View
35 |
36 |
37 |
38 |
39 | {viewOrderPage && }
40 |
41 |
42 | );
43 | };
44 |
45 | export default Order;
--------------------------------------------------------------------------------
/src/redux/features/auth/userAuth.js:
--------------------------------------------------------------------------------
1 | import { apiSlice } from "../api/apiSlice";
2 |
3 |
4 | const featherAPI = apiSlice.injectEndpoints({
5 | endpoints: (builder) => ({
6 | createUser: builder.mutation({
7 | query: (data) => {
8 | return {
9 | url: `/api/v1/auth/register`,
10 | method: "POST",
11 | body: data,
12 | };
13 | },
14 | }),
15 | loginUser: builder.mutation({
16 | query: (data) => {
17 | return {
18 | url: `/api/v1/auth/login`,
19 | method: "POST",
20 | body: data,
21 | };
22 | },
23 | }),
24 | getUserProfile: builder.query({
25 | query: () => {
26 | return {
27 | url: `/api/v1/auth/get-user-profile`,
28 | method: "GET",
29 | };
30 | },
31 | }),
32 | getAllUser: builder.query({
33 | query: () => {
34 | return {
35 | url: `/api/v1/auth/get-user`,
36 | method: "GET",
37 | };
38 | },
39 | }),
40 | updateProfile: builder.mutation({
41 | query: (data) => {
42 | return {
43 | url: `/api/v1/auth/update-profile`,
44 | method: "PATCH",
45 | body: data,
46 | };
47 | },
48 | }),
49 | updateUserRole: builder.mutation({
50 | query: (data) => {
51 | const id = data.id
52 | return {
53 | url: `/api/v1/auth/update-user-role/${id}`,
54 | method: "PATCH",
55 | body: data,
56 | };
57 | },
58 | }),
59 | deleteUser: builder.mutation({
60 | query: (id) => ({
61 | url: `/api/v1/auth/delete-user/${id}`,
62 | method: "DELETE",
63 | }),
64 | }),
65 | }),
66 | });
67 |
68 | export const {
69 | useCreateUserMutation,
70 | useLoginUserMutation,
71 | useGetUserProfileQuery,
72 | useUpdateProfileMutation,
73 | useGetAllUserQuery,
74 | useUpdateUserRoleMutation,
75 | useDeleteUserMutation
76 | } = featherAPI;
--------------------------------------------------------------------------------
/public/img/profile_pic.svg:
--------------------------------------------------------------------------------
1 | profile pic
--------------------------------------------------------------------------------
/src/components/Banner/Banner.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import { ShoppingBagIcon } from "@heroicons/react/24/outline";
3 |
4 | function Banner() {
5 | const scrollHandler = () => {
6 | window.scrollTo({
7 | top: document.getElementById("products-feed").offsetTop - 90,
8 | behavior: "smooth",
9 | });
10 | //window.location.href='#products-feed'
11 | };
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | Stay Home
20 |
21 |
22 | Shop Online.
23 |
24 |
25 |
26 | Shop online from a wide range of genuine products whenever you
27 | want 24x7.
28 |
29 |
33 |
34 | Shop Now
35 |
36 |
37 |
38 |
46 |
47 |
48 |
49 | );
50 | }
51 |
52 | export default Banner;
53 |
--------------------------------------------------------------------------------
/src/components/BuildProduct/BuildProduct.js:
--------------------------------------------------------------------------------
1 | import { clearComponentByCategory } from "@/redux/features/pcbuild/pcbuildSlice";
2 | import { XMarkIcon } from "@heroicons/react/24/outline";
3 | import Image from "next/image";
4 | import Link from "next/link";
5 | import { useRouter } from "next/router";
6 | import { useDispatch } from "react-redux";
7 | // Build Product
8 | function BuildProduct({
9 | id,
10 | productName,
11 | price,
12 | category,
13 | image,
14 | border,
15 | }) {
16 | const router = useRouter();
17 | const dispatch = useDispatch();
18 | const removeItemFromBuild = () => {
19 | dispatch(clearComponentByCategory(category));
20 | };
21 |
22 | return (
23 |
28 |
29 | router.push(`/product-details/${id}`)}
37 | />
38 |
39 |
40 | {/* Middle */}
41 |
42 |
43 | {productName}
44 |
45 |
46 | {category}
47 |
48 |
49 |
50 | {/* Buttons on the right of the products */}
51 |
52 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 |
63 | export default BuildProduct;
64 |
--------------------------------------------------------------------------------
/src/components/Shared/Navbar/MobileNavbar.js:
--------------------------------------------------------------------------------
1 | // import Search from "@/components/UI/Search";
2 | import { Bars3Icon, ComputerDesktopIcon } from "@heroicons/react/24/outline";
3 | import { useRouter } from "next/router";
4 | import { useState } from "react";
5 | import SideBarMenu from "../SidebarMenu/SidebarMenu";
6 | import { useSelector } from "react-redux";
7 | import Link from "next/link";
8 |
9 | function MobileNavbar() {
10 | const router = useRouter();
11 | const [showSideBar, setShowBar] = useState(false);
12 | const pcbuild = useSelector((state) => state.pcbuild);
13 |
14 | return (
15 | <>
16 |
17 |
18 |
19 |
20 | setShowBar(true)} />
21 |
22 |
23 | Gadget Galaxy
24 |
25 |
26 |
router.push("/add-to-cart")}
29 | >
30 | {/*
*/}
31 |
shopping_basket
32 |
33 | {pcbuild?.qty}
34 |
35 |
36 |
37 | {/* */}
38 |
39 |
45 | setShowBar(false)} />
46 |
47 | >
48 | );
49 | }
50 |
51 | export default MobileNavbar;
52 |
--------------------------------------------------------------------------------
/src/components/User/DeleteUser.jsx:
--------------------------------------------------------------------------------
1 | import { useDeleteUserMutation } from '@/redux/features/auth/userAuth';
2 | import React from 'react';
3 | import { useEffect } from 'react';
4 |
5 | const DeleteUser = ({ setDeleteUser, user , refetch }) => {
6 | const [deleteUser, resInfo] = useDeleteUserMutation()
7 |
8 | const deleteUserFun = () =>{
9 | deleteUser(user?._id)
10 | }
11 |
12 | useEffect(()=> {
13 | if (resInfo?.isSuccess) {
14 | setDeleteUser(false)
15 | refetch()
16 | }
17 | },[resInfo, setDeleteUser, refetch])
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {
31 | setDeleteUser(false)
32 | }}
33 | className="font-bold p-2 text-xl btn btn-sm btn-circle absolute right-2 top-2"
34 | >
35 | ✕
36 |
37 |
38 | Are you sure! You Detele The user
39 |
40 |
41 |
42 | Delete User
43 |
44 |
45 |
46 |
47 |
48 |
49 | );
50 | };
51 |
52 | export default DeleteUser;
--------------------------------------------------------------------------------
/src/components/Product/Product.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import { ShoppingCartIcon } from "@heroicons/react/24/solid";
3 | import Link from "next/link";
4 | import { useRouter } from "next/router";
5 | import { StarIcon } from "@heroicons/react/24/solid";
6 | import { useSession } from "next-auth/react";
7 | import { useDispatch } from "react-redux";
8 |
9 | function Product({
10 | id,
11 | productName,
12 | price,
13 | description,
14 | category,
15 | status,
16 | image,
17 | reviews,
18 | }) {
19 | const router = useRouter();
20 | const dispatch = useDispatch();
21 | const { data: session } = useSession();
22 |
23 | const addItemToBuild = () => {
24 | if (!session?.user?.email) {
25 | alert("Please Sign In to add items to your build");
26 | router.push("/api/auth/signin");
27 | } else {
28 | dispatch(
29 | addComponent({
30 | id,
31 | name: productName,
32 | price,
33 | category,
34 | image,
35 | })
36 | );
37 | router.push("/pc-builder");
38 | }
39 | };
40 |
41 | return (
42 |
43 |
44 | {category}
45 |
46 |
router.push(`/product-details/${id}`)}
54 | />
55 |
56 | {productName}
57 |
58 |
59 | {description}
60 |
61 |
62 | $ {price}
63 | {status}
64 |
65 |
66 | {...Array(reviews)
67 | .fill()
68 | .map((_, i) => (
69 |
70 | ))}
71 |
72 |
76 |
77 | Add To Builder
78 |
79 |
80 | );
81 | }
82 |
83 | export default Product;
84 |
--------------------------------------------------------------------------------
/src/styles/globals.css:
--------------------------------------------------------------------------------
1 | /* ./styles/globals.css */
2 |
3 | /* Tailwindcss */
4 | @tailwind base;
5 | @tailwind components;
6 | @tailwind utilities;
7 |
8 | /* Fonts */
9 | @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400&display=swap");
10 |
11 | /* Custom tailwind class */
12 | @layer components {
13 | .link {
14 | @apply cursor-pointer hover:text-blue-light active:text-blue-light;
15 | }
16 | .button {
17 | @apply cursor-pointer rounded text-center p-2 text-sm text-white bg-gradient-to-b from-blue-light to-blue-dark border border-blue-light focus:outline-none focus:ring-2 focus:ring-blue-dark active:from-blue-dark;
18 | }
19 | .button-green {
20 | @apply cursor-pointer rounded text-center p-2 text-sm text-white bg-gradient-to-b from-green-500 to-green-800 border-green-500 focus:outline-none focus:ring-2 focus:ring-green-500 active:from-green-800;
21 | }
22 | .button-red {
23 | @apply cursor-pointer rounded text-center p-2 text-sm text-white bg-gradient-to-b from-red-500 to-red-800 border-red-500 focus:outline-none focus:ring-2 focus:ring-red-500 active:from-red-800;
24 | }
25 | .dropDownOption {
26 | @apply w-full cursor-pointer hover:bg-gray-100 py-2 px-3;
27 | }
28 | .dashboard-link {
29 | @apply link rounded border px-4 py-1 hover:bg-blue-light hover:text-white transition-all duration-200 text-sm;
30 | }
31 | }
32 |
33 | * {
34 | -webkit-tap-highlight-color: transparent;
35 | scroll-behavior: smooth;
36 | }
37 |
38 | html,
39 | body {
40 | font-family: "Poppins", sans-serif;
41 | -webkit-font-smoothing: antialiased;
42 | -moz-osx-font-smoothing: grayscale;
43 | color: #1f2937;
44 | scroll-behavior: smooth;
45 | background-color: white;
46 | }
47 |
48 | .heightFix,
49 | .heightFixAdmin {
50 | min-height: calc(100vh - 150px);
51 | height: 100%;
52 | }
53 |
54 | .loader svg {
55 | width: 150px;
56 | height: 150px;
57 | }
58 |
59 | /* Hide scrollbar for Chrome, Safari and Opera */
60 | .hideScrollBar::-webkit-scrollbar {
61 | display: none;
62 | }
63 | .table_col_img {
64 | min-width: 40px;
65 | }
66 |
67 | .table_col {
68 | min-width: 120px;
69 | }
70 |
71 | /* Hide scrollbar for IE, Edge and Firefox */
72 | .hideScrollBar {
73 | -ms-overflow-style: none; /* IE and Edge */
74 | scrollbar-width: none; /* Firefox */
75 | }
76 |
77 | .glassmorphism {
78 | background-color: white;
79 | background: rgba(255, 255, 255, 0.9);
80 | backdrop-filter: blur(6px);
81 | -webkit-backdrop-filter: blur(6px);
82 | }
83 |
84 | .layout {
85 | min-width: 320px;
86 | background-color: white;
87 | }
88 | .sideBarMenu {
89 | min-width: 250px;
90 | }
91 |
92 | .layout .Toastify__progress-bar--default {
93 | background: #10b981;
94 | background: linear-gradient(#10b981, #065f46) !important;
95 | }
96 |
97 | @media only screen and (max-width: 780px) {
98 | .loader svg {
99 | width: 120px;
100 | height: 120px;
101 | }
102 | }
103 |
104 | @media only screen and (max-width: 780px) {
105 | .heightFix {
106 | min-height: calc(100vh - 200px);
107 | height: 100%;
108 | }
109 | }
--------------------------------------------------------------------------------
/src/components/UI/Search.js:
--------------------------------------------------------------------------------
1 | // import { SearchIcon } from "@heroicons/react/24/solid";
2 | import { useRef, useState } from "react";
3 |
4 | const Search = () => {
5 | const [searchTerm, setSearchTerm] = useState("");
6 | const searchRef = useRef(null);
7 |
8 | // const closeSearch = () => {
9 | // setSearchTerm("");
10 | // };
11 |
12 | // useEffect(() => {
13 | // function handleClickOutside(e) {
14 | // let targetEl = e.target;
15 | // do {
16 | // if (targetEl === searchRef.current) {
17 | // return;
18 | // }
19 | // targetEl = targetEl.parentNode;
20 | // } while (targetEl);
21 | // closeSearch();
22 | // }
23 | // window.addEventListener("click", handleClickOutside);
24 | // return () => {
25 | // window.removeEventListener("click", handleClickOutside);
26 | // };
27 | // }, []);
28 |
29 |
30 | return (
31 |
32 |
33 | {/* */}
34 |
35 |
setSearchTerm(e.target.value)}
41 | // onChange={searchProduct}
42 | />
43 |
44 | {/* {searchTerm ? (
45 |
46 | {!isLoading || !loading ? (
47 | searchResults?.length ? (
48 | searchResults.map(({ item: { _id, title, image } }, i) =>
49 |
{
51 | closeSearch();
52 | router.push(`/product-details/${_id}`);
53 | }}
54 | className={`flex cursor-pointer items-center justify-between lg:px-5 py-2 px-4 ${
55 | i !== searchResults.length
56 | ? "border-b border-gray-200"
57 | : ""
58 | } bg-gray-50 hover:bg-gray-100`}
59 | >
60 |
61 | {title}
62 |
63 |
64 |
71 |
72 |
73 | ))
74 | ) : (
75 |
76 | No product found
77 |
78 | )
79 | ) : (
80 |
Loading...
81 | )}
82 |
83 | ) : (
84 | <>>
85 | )} */}
86 |
87 | );
88 | }
89 |
90 | export default Search;
91 |
--------------------------------------------------------------------------------
/src/components/Shared/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import Image from "next/image";
3 | import { useRouter } from "next/router";
4 | import { HeartIcon } from "@heroicons/react/24/outline";
5 |
6 | function Footer() {
7 | const router = useRouter();
8 | const gmailHandler = () => {
9 | window.open(
10 | "mailto:" +
11 | "hazrataliein@gmail.com" +
12 | "?subject=" +
13 | " " +
14 | "&body=" +
15 | " ",
16 | "_self"
17 | );
18 | };
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {/* {!admin ? (
30 |
31 |
Orders
32 |
33 | ) : (
34 | <>>
35 | )} */}
36 |
37 |
38 |
39 |
49 |
50 |
51 | {
60 | window.open("https://www.linkedin.com/in/hazratali9");
61 | }}
62 | />
63 |
64 |
65 | window.open("https://www.linkedin.com/in/hazratali9")}
74 | />
75 |
76 |
77 |
78 |
79 | Made With by
80 |
81 | Hazrat Ali
82 |
83 |
84 |
85 |
86 | );
87 | }
88 |
89 | export default Footer;
90 |
--------------------------------------------------------------------------------
/src/components/User/RoleChangeModal.jsx:
--------------------------------------------------------------------------------
1 | import { useUpdateUserRoleMutation } from '@/redux/features/auth/userAuth';
2 | import React, { useState } from 'react';
3 | import { useEffect } from 'react';
4 |
5 | const RoleChangeModal = ({ setUserRoleChange , user, refetch}) => {
6 | const [updateUserRole, resInfo] = useUpdateUserRoleMutation()
7 | const [selectedRole, setSelectedRole] = useState(user?.role);
8 |
9 | const handleRoleChange = (e) => {
10 | setSelectedRole(e.target.value);
11 | };
12 | const handleUserRoleChangeFn = ()=>{
13 | updateUserRole({
14 | role: selectedRole.toString(),
15 | id: user?._id
16 | })
17 |
18 | }
19 | useEffect(()=> {
20 | if (resInfo?.isSuccess) {
21 | setUserRoleChange(false)
22 | refetch()
23 | }
24 | },[resInfo, setUserRoleChange, refetch])
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {
37 | setUserRoleChange(false)
38 | }}
39 | className="font-bold p-2 text-xl btn btn-sm btn-circle absolute right-2 top-2"
40 | >
41 | ✕
42 |
43 |
44 | Are you sure! You Change the user Role
45 |
46 |
47 |
48 |
49 | Select Role:
50 |
55 | User
56 | Admin
57 |
58 |
59 |
Change User Role
60 |
61 |
62 |
63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default RoleChangeModal;
--------------------------------------------------------------------------------
/src/components/Shared/SingleCard/Card.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import KeyFeatures from './KeyFeatures';
3 | import { useRouter } from 'next/router';
4 | import { useState } from 'react';
5 | import { useEffect } from 'react';
6 | import { useAddToCartProductMutation } from '@/redux/features/addToCard/addToCard';
7 | import { toast } from 'react-hot-toast';
8 | import Loader from '../Loader/Loader';
9 |
10 | const Card = ({ item }) => {
11 | const router = useRouter();
12 | const [user, setUser] = useState(false);
13 | const [addToCartProduct, resInfo] = useAddToCartProductMutation()
14 | useEffect(() => {
15 | if (typeof window !== "undefined") {
16 | const user = localStorage.getItem("user");
17 | setUser(user)
18 | }
19 | }, [router]);
20 | const buyNowProduct = async () => {
21 | if (user) {
22 | const data = {
23 | id: item._id,
24 | quantity: 1,
25 | }
26 | await addToCartProduct(data)
27 | toast.success("The product add successfully in you cart..!");
28 | } else {
29 | router.push('/sign-in')
30 | }
31 | }
32 |
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
47 |
48 |
49 |
50 |
51 |
52 | {item?.name}
53 |
54 |
55 |
56 |
57 | {item?.keyFeatures?.map((feature, featureIndex) => (
58 |
59 | ))}
60 |
61 |
62 | ${item?.price}
63 |
64 |
65 | {resInfo.isLoading ?
66 |
67 | :
68 |
69 | shopping_cart Buy Now
70 | }
71 |
72 |
73 |
74 |
75 |
76 | );
77 | };
78 |
79 | export default Card;
--------------------------------------------------------------------------------
/src/redux/features/pcbuild/pcbuildSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | processor: {
5 | id: 0,
6 | name: "",
7 | price: 0,
8 | image: "",
9 | },
10 | gpu: {
11 | id: 0,
12 | name: "",
13 | price: 0,
14 | image: "",
15 | },
16 | ram: {
17 | id: 0,
18 | name: "",
19 | price: 0,
20 | image: "",
21 | },
22 | motherboard: {
23 | id: 0,
24 | name: "",
25 | price: 0,
26 | image: "",
27 | },
28 | storagedevice: {
29 | id: 0,
30 | name: "",
31 | price: 0,
32 | image: "",
33 | },
34 | powersupplyunit: {
35 | id: 0,
36 | name: "",
37 | price: 0,
38 | image: "",
39 | },
40 | monitor: {
41 | id: 0,
42 | name: "",
43 | price: 0,
44 | image: "",
45 | },
46 | others: {
47 | id: 0,
48 | name: "",
49 | price: 0,
50 | image: "",
51 | },
52 | total: 0,
53 | qty: 0,
54 | };
55 |
56 | const pcbuildSlice = createSlice({
57 | name: "pcbuild",
58 | initialState,
59 | reducers: {
60 | addComponent: (state, action) => {
61 | let { id, name, price, category, image } = action.payload;
62 | category = category?.replace(/\s+/g, "");
63 | category = category?.toLowerCase();
64 | // is id already in the state?
65 | if (state[category]?.id !== id) {
66 | if (state[category]?.name && state[category]?.price) {
67 | state.total -= state[category].price;
68 | state.qty -= 1;
69 | }
70 | state[category] = { id, name, price, image };
71 | state.total += price;
72 | state.qty += 1;
73 | //
74 | }
75 | },
76 | clearComponent: (state) => {
77 | state.processor = {
78 | id: 0,
79 | name: "",
80 | price: 0,
81 | image: "",
82 | };
83 | state.gpu = {
84 | id: 0,
85 | name: "",
86 | price: 0,
87 | image: "",
88 | };
89 | state.ram = {
90 | id: 0,
91 | name: "",
92 | price: 0,
93 | image: "",
94 | };
95 | state.motherboard = {
96 | id: 0,
97 | name: "",
98 | price: 0,
99 | image: "",
100 | };
101 | state.storagedevice = {
102 | id: 0,
103 | name: "",
104 | price: 0,
105 | image: "",
106 | };
107 | state.powersupplyunit = {
108 | id: 0,
109 | name: "",
110 | price: 0,
111 | image: "",
112 | };
113 | state.monitor = {
114 | id: 0,
115 | name: "",
116 | price: 0,
117 | image: "",
118 | };
119 | state.others = {
120 | id: 0,
121 | name: "",
122 | price: 0,
123 | image: "",
124 | };
125 | state.total = 0;
126 | state.qty = 0;
127 | },
128 | clearComponentByCategory: (state, action) => {
129 | let category = action.payload;
130 | category = category.replace(/\s+/g, "");
131 | category = category.toLowerCase();
132 | state[category] = {
133 | id: 0,
134 | name: "",
135 | price: 0,
136 | image: "",
137 | };
138 | state.total -= state[category].price;
139 | state.qty -= 1;
140 | },
141 | },
142 | });
143 |
144 | export const { addComponent, clearComponent, clearComponentByCategory } =
145 | pcbuildSlice.actions;
146 | export default pcbuildSlice.reducer;
147 |
--------------------------------------------------------------------------------
/src/components/Shared/Card/Card.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import KeyFeatures from './KeyFeatures';
3 | import { useRouter } from 'next/router';
4 | import { useAddToCartProductMutation } from '@/redux/features/addToCard/addToCard';
5 | import { toast } from 'react-hot-toast';
6 | import Loader from '../Loader/Loader';
7 |
8 | const Card = ({ item }) => {
9 | const router = useRouter();
10 | const [user, setUser] = useState(false);
11 | const [addToCartProduct, resInfo] = useAddToCartProductMutation()
12 | useEffect(() => {
13 | if (typeof window !== "undefined") {
14 | const user = localStorage.getItem("user");
15 | setUser(user)
16 | }
17 | }, [router]);
18 | const productDetails = (id) => {
19 | router.push(`/product/${id}`);
20 | };
21 | const buyNowProduct = async (item) => {
22 | if (user) {
23 | // console.log(item);
24 | const data = {
25 | id: item._id,
26 | quantity: 1,
27 | }
28 | await addToCartProduct(data)
29 | toast.success("The product add successfully in you cart..!");
30 | } else {
31 | router.push('/sign-in')
32 | }
33 | }
34 |
35 | return (
36 |
37 |
38 |
39 |
productDetails(item._id)}>
40 |
46 |
47 |
48 |
49 |
50 | productDetails(item._id)}
52 | className="font-bold text-center"
53 | >
54 | {item?.name}
55 |
56 |
57 |
58 | {item?.keyFeatures?.map((feature, featureIndex) => (
59 |
60 | ))}
61 |
62 |
63 | ${item?.price}
64 |
65 |
66 |
67 |
68 | {resInfo.isLoading ?
69 |
70 | :
71 | buyNowProduct (item)} className="flex items-center border px-4 py-3 justify-center gap-2" type="button">
72 | shopping_cart Buy Now
73 | }
74 |
75 |
76 |
77 | );
78 | };
79 |
80 | export default Card;
81 |
--------------------------------------------------------------------------------
/src/components/User/ViewAllUser.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { useGetAllUserQuery } from '@/redux/features/auth/userAuth';
3 | import React from 'react';
4 | import { useState } from 'react';
5 | import RoleChangeModal from './RoleChangeModal';
6 | import DeleteUser from './DeleteUser';
7 | import LoadingSpinner from '../Shared/Loading/LoadingSpinner';
8 |
9 | const ViewAllUserf = () => {
10 | const { data, isLoading, isError, refetch } = useGetAllUserQuery();
11 | const [userRoleChange, setUserRoleChange] = useState(false)
12 | const [deleteUser, setDeleteUser] = useState(false)
13 | const [user, setUser] = useState()
14 | return (
15 |
16 | {isLoading ?
:
17 |
18 |
19 |
20 |
21 |
22 | User Name
23 | User Email
24 | Role
25 | Action
26 |
27 |
28 |
29 | {data?.data?.map((item) => <>
30 |
31 |
32 | {item?.firstName} {item?.lastName}
33 |
34 |
35 | {item?.email}
36 |
37 |
38 | {item?.role}
39 |
40 |
41 | {
42 | setUserRoleChange(true)
43 | setUser(item)
44 | }} className='bg-gray-200 p-3 rounded font-bold'>
45 | Change User Role
46 |
47 | {
48 | setDeleteUser(true)
49 | setUser(item)
50 | }} className='bg-gray-200 p-3 rounded font-bold'>
51 | Delete User
52 |
53 |
54 |
55 | >)}
56 |
57 |
58 |
59 | {
60 | userRoleChange &&
61 | }
62 | {
63 | deleteUser &&
64 | }
65 |
66 |
}
67 |
68 | );
69 | };
70 |
71 | export default ViewAllUserf;
--------------------------------------------------------------------------------
/src/components/Shared/Navbar/MainNavbar.js:
--------------------------------------------------------------------------------
1 |
2 | import { useRouter } from "next/router";
3 | import { useState } from "react";
4 | import { useEffect } from "react";
5 | import { toast } from "react-hot-toast";
6 | import { BsPerson } from 'react-icons/bs';
7 | import { CiShoppingCart } from 'react-icons/ci';
8 | function MainNavbar() {
9 | const router = useRouter();
10 | const [user, setUser] = useState(false);
11 | useEffect(() => {
12 | if (typeof window !== "undefined") {
13 | const user = localStorage.getItem("user");
14 | const jsonObject = JSON.parse(user);
15 | setUser(jsonObject)
16 | }
17 | }, [router]);
18 | const logOut = () => {
19 | toast.success("Your Account Logout Successfully..!");
20 | localStorage.removeItem("accessToken");
21 | localStorage.removeItem("refreshToken");
22 | localStorage.removeItem("user");
23 | router.push('/')
24 | }
25 | const addToCartFun = () => {
26 | router.push('/add-to-cart')
27 | }
28 | console.log(user);
29 | return (
30 |
31 |
32 |
33 |
34 |
router.push('/')}>
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {user?.role === 'ADMIN' &&
router.push('/add-product')} className="flex items-center gap-2 bg-gray-200 p-3 rounded hover:bg-blue-400 hover:text-white">
44 | Add Product
45 | }
46 | {user?.role === 'ADMIN' &&
router.push('/products/view-all-products')} className="flex items-center gap-2 bg-gray-200 p-3 rounded hover:bg-blue-400 hover:text-white">
47 | Manage Product
48 | }
49 | {user?.role === 'ADMIN' &&
router.push('/auth/view-all-user')} className="flex items-center gap-2 bg-gray-200 p-3 rounded hover:bg-blue-400 hover:text-white">
50 | View All User
51 | }
52 |
53 | Cart
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
Account
62 |
63 | {!user ?
64 | router.push('/sign-up')}>Register
65 |
or
66 | router.push('/sign-in')}>Login
67 | :
68 | router.push('/auth/profile')}>Profile
69 |
or
70 | Logout
71 | }
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | }
80 |
81 | export default MainNavbar;
82 |
--------------------------------------------------------------------------------
/src/pages/auth/profile.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useGetUserProfileQuery, useUpdateProfileMutation } from '@/redux/features/auth/userAuth';
3 | import { useRouter } from 'next/router';
4 |
5 | const UserProfile = () => {
6 | const { data, isLoading, isError } = useGetUserProfileQuery();
7 | const [updateProfile, resInfo] = useUpdateProfileMutation()
8 | const router = useRouter();
9 | const [isEditing, setIsEditing] = useState(false);
10 | const [userData, setUserData] = useState({});
11 |
12 | useEffect(() => {
13 | if (data) {
14 | setUserData(data?.user);
15 | }
16 | }, [data]);
17 |
18 | const handleEditClick = () => {
19 | setIsEditing(true);
20 | };
21 |
22 | const handleSaveClick = async () => {
23 | const res = await updateProfile(userData)
24 | if (res) {
25 | setIsEditing(false);
26 | }
27 | };
28 |
29 | const handleInputChange = (e) => {
30 | const { name, value } = e.target;
31 | setUserData({
32 | ...userData,
33 | [name]: value,
34 | });
35 | };
36 |
37 | return (
38 |
39 |
40 |
User Profile
41 |
42 |
Name:
43 | {isEditing ? (
44 |
51 | ) : (
52 |
{userData?.name || ''}
53 | )}
54 |
55 |
56 |
Email:
57 | {isEditing ? (
58 |
65 | ) : (
66 |
{userData.email || ''}
67 | )}
68 |
69 |
70 |
Phone:
71 | {isEditing ? (
72 |
79 | ) : (
80 |
{userData.phone || ''}
81 | )}
82 |
83 |
84 |
Address:
85 | {isEditing ? (
86 |
92 | ) : (
93 |
{userData.address || ''}
94 | )}
95 |
96 |
97 | {isEditing ? (
98 |
102 | Save
103 |
104 | ) : (
105 |
109 | Edit
110 |
111 | )}
112 | router.push('/products/order')}
114 | className="bg-green-500 text-white px-4 py-2 rounded-md mr-2"
115 | >
116 | View Order
117 |
118 |
119 |
120 |
121 | );
122 | };
123 |
124 | export default UserProfile;
125 |
--------------------------------------------------------------------------------
/src/pages/profile/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useGetUserProfileQuery, useUpdateProfileMutation } from '@/redux/features/auth/userAuth';
3 | import { useRouter } from 'next/router';
4 |
5 | const UserProfile = () => {
6 | const { data, isLoading, isError } = useGetUserProfileQuery();
7 | const [updateProfile, resInfo] = useUpdateProfileMutation()
8 | const router = useRouter();
9 | const [isEditing, setIsEditing] = useState(false);
10 | const [userData, setUserData] = useState({});
11 |
12 | useEffect(() => {
13 | if (data) {
14 | setUserData(data?.user);
15 | }
16 | }, [data]);
17 |
18 | const handleEditClick = () => {
19 | setIsEditing(true);
20 | };
21 |
22 | const handleSaveClick = async () => {
23 | const res = await updateProfile(userData)
24 | if (res) {
25 | setIsEditing(false);
26 | }
27 | };
28 |
29 | const handleInputChange = (e) => {
30 | const { name, value } = e.target;
31 | setUserData({
32 | ...userData,
33 | [name]: value,
34 | });
35 | };
36 |
37 | return (
38 |
39 |
40 |
User Profile
41 |
42 |
Name:
43 | {isEditing ? (
44 |
51 | ) : (
52 |
{userData?.name || ''}
53 | )}
54 |
55 |
56 |
Email:
57 | {isEditing ? (
58 |
65 | ) : (
66 |
{userData.email || ''}
67 | )}
68 |
69 |
70 |
Phone:
71 | {isEditing ? (
72 |
79 | ) : (
80 |
{userData.phone || ''}
81 | )}
82 |
83 |
84 |
Address:
85 | {isEditing ? (
86 |
92 | ) : (
93 |
{userData.address || ''}
94 | )}
95 |
96 |
97 | {isEditing ? (
98 |
102 | Save
103 |
104 | ) : (
105 |
109 | Edit
110 |
111 | )}
112 | router.push('/products/order')}
114 | className="bg-green-500 text-white px-4 py-2 rounded-md mr-2"
115 | >
116 | View Order
117 |
118 |
119 |
120 |
121 | );
122 | };
123 |
124 | export default UserProfile;
125 |
--------------------------------------------------------------------------------
/src/pages/add-featehr.js:
--------------------------------------------------------------------------------
1 | import { PostImage } from '@/components/ImageBB/imageUpload';
2 | import { useAddFeatherMutation } from '@/redux/features/productFeather/productFeather';
3 | import { useState } from 'react';
4 |
5 | const FeatherPosting = () => {
6 | const [selectedImage, setSelectedImage] = useState(null);
7 | const [image, setImage] = useState(null);
8 | const [uploadProgress, setUploadProgress] = useState(0);
9 | const [addFeather, resCreateFeather] = useAddFeatherMutation()
10 |
11 | const handleImageUpload = async (e) => {
12 | const file = e.target.files[0];
13 | const imageUrl = await PostImage(file);
14 | setSelectedImage(file);
15 | setImage(imageUrl);
16 | let progress = 0;
17 | const interval = setInterval(() => {
18 | progress += 10;
19 | setUploadProgress(progress);
20 | if (progress === 100) {
21 | clearInterval(interval);
22 | }
23 | }, 100);
24 | };
25 | const handleSubmit = async (e) => {
26 | e.preventDefault();
27 |
28 | const data = {
29 | featherName: e.target.featherName.value,
30 | featherPhoto: image,
31 | dis: e.target.description.value,
32 | };
33 | e.target.reset();
34 | setSelectedImage();
35 | await addFeather(data)
36 | };
37 | return (
38 |
39 |
40 |
Add Yo
41 |
42 |
102 |
103 |
104 | );
105 | };
106 |
107 | export default FeatherPosting;
108 |
--------------------------------------------------------------------------------
/src/components/Product/ProductDetails.js:
--------------------------------------------------------------------------------
1 | import { addComponent } from "@/redux/features/pcbuild/pcbuildSlice";
2 | import { ShoppingCartIcon } from "@heroicons/react/24/outline";
3 | import { StarIcon } from "@heroicons/react/24/solid";
4 | import Image from "next/image";
5 | import { useRouter } from "next/router";
6 | import Skeleton from "react-loading-skeleton";
7 | import { useDispatch } from "react-redux";
8 |
9 | const ProductDetails = ({
10 | id,
11 | productName,
12 | price,
13 | description,
14 | category,
15 | status,
16 | image,
17 | reviews,
18 | keyFeatures,
19 | }) => {
20 | const router = useRouter();
21 | const dispatch = useDispatch();
22 |
23 | const addItemToBuild = () => {
24 | dispatch(
25 | addComponent({
26 | id,
27 | name: productName,
28 | price,
29 | category,
30 | image,
31 | })
32 | );
33 | router.push("/pc-builder");
34 | };
35 |
36 | return (
37 |
38 |
39 |
40 | {router.isFallback ? (
41 |
42 | ) : (
43 |
44 |
51 |
52 | )}
53 |
54 | {router.isFallback ? (
55 |
56 | ) : (
57 | <>
58 |
59 | {productName}
60 |
61 |
62 |
63 |
64 | Status:
65 |
66 | {status}
67 |
68 |
69 |
70 | Category:
71 |
72 | {category}
73 |
74 |
75 |
76 |
77 | Key Features
78 |
79 |
80 | {keyFeatures.map((feature, i) => (
81 |
82 |
83 | {feature.keyName}:
84 |
85 | {feature.value}
86 |
87 | ))}
88 |
89 |
90 |
91 | {description}
92 |
93 |
94 | $ {price}
95 |
96 |
97 | {...Array(reviews)
98 | .fill()
99 | .map((_, i) => (
100 |
104 | ))}
105 |
106 |
107 |
111 |
112 | Add to Build
113 |
114 |
115 | >
116 | )}
117 |
118 |
119 |
120 |
121 | );
122 | };
123 |
124 | export default ProductDetails;
125 |
--------------------------------------------------------------------------------
/src/components/Shared/SidebarMenu/SidebarMenu.js:
--------------------------------------------------------------------------------
1 | import Dropdown from "@/components/UI/Dropdown";
2 | import { NavbarCategories, NavbarDropdown } from "@/constants/constants";
3 | import {
4 | ChevronDownIcon,
5 | ComputerDesktopIcon,
6 | HomeIcon,
7 | ShoppingBagIcon,
8 | } from "@heroicons/react/24/outline";
9 | import { XMarkIcon } from "@heroicons/react/24/solid";
10 | import { signIn, useSession } from "next-auth/react";
11 | import Image from "next/image";
12 | import Link from "next/link";
13 | import { useRouter } from "next/router";
14 | import { useState } from "react";
15 | import Skeleton from "react-loading-skeleton";
16 | import onClickOutside from "react-onclickoutside";
17 |
18 | function SideBarMenu({ closeSideBar }) {
19 | const [dropDown, setDropDown] = useState(false);
20 | const { data: session, status } = useSession();
21 | const router = useRouter();
22 | SideBarMenu.handleClickOutside = closeSideBar;
23 | const sideBarClickHandler = (href) => {
24 | closeSideBar();
25 | router.push(href);
26 | };
27 | const userImage = session?.user?.image || "/img/profile_pic.svg";
28 | return (
29 |
30 |
31 | Badget Galaxy
32 |
33 |
34 |
35 | {status === "loading" ? (
36 |
37 | ) : session?.user ? (
38 |
setDropDown((value) => !value)}
41 | >
42 |
43 |
51 |
52 |
53 | {dropDown && (
54 |
55 | setDropDown(false)}
58 | />
59 |
60 | )}
61 |
62 | ) : (
63 |
router.push('/sign-in')}
66 | >
67 | Login
68 |
69 | )}
70 |
71 |
72 |
73 | sideBarClickHandler("/")}
75 | className="link inline-flex"
76 | >
77 | Home
78 |
79 |
80 |
81 | sideBarClickHandler("/pc-builder")}
83 | className="link inline-flex"
84 | >
85 | PC Builder
86 |
87 |
88 |
89 |
sideBarClickHandler("/orders")}
91 | className="link inline-flex relative"
92 | >
93 | Categories
94 |
95 | {NavbarCategories.map((item) => (
96 | sideBarClickHandler(`${item?.path}`)}
99 | className="link text-sm mb-3 font-medium"
100 | >
101 | {item?.name}
102 |
103 | ))}
104 |
105 |
106 |
107 | {/* {session && (
108 |
109 |
{
111 | signOut();
112 | }}
113 | className="link inline-flex"
114 | >
115 | Logout
116 |
117 |
118 | )} */}
119 |
120 |
121 |
122 |
123 |
124 | );
125 | }
126 |
127 | const clickOutsideConfig = {
128 | handleClickOutside: () => SideBarMenu.handleClickOutside,
129 | };
130 |
131 | export default onClickOutside(SideBarMenu, clickOutsideConfig);
132 |
--------------------------------------------------------------------------------
/src/components/FeedbackForm/FeedFrom.js:
--------------------------------------------------------------------------------
1 | import { useAddFeedBackMutation } from '@/redux/features/userFeedBack/FeedBack';
2 | import React from 'react';
3 | import Loader from '../Shared/Loader/Loader';
4 | import toast from 'react-hot-toast';
5 |
6 | const FeedFrom = () => {
7 | const [addFeedBack, resInfo] = useAddFeedBackMutation()
8 | const handleFeedBack = async (e) => {
9 | e.preventDefault();
10 | const form = e.target;
11 | const name = form.name.value;
12 | const email = form.email.value;
13 | const comment = form.comments.value;
14 | const suggestions = form.Suggestions.value;
15 | const data = {
16 | name,
17 | email,
18 | comment,
19 | suggestions
20 | }
21 | await addFeedBack(data)
22 | toast.success("Your feed back send successfully..!");
23 | e.target.reset()
24 | };
25 | return (
26 |
27 |
28 |
54 |
55 |
56 |
57 | Your Comments
58 |
59 |
66 |
67 |
68 |
69 |
70 |
71 | Your Suggestions
72 |
73 |
80 |
81 |
82 |
83 | {resInfo.isLoading ?
86 |
87 | :
90 | Send Feedback
91 | }
92 |
93 |
94 |
95 | );
96 | };
97 |
98 | export default FeedFrom;
--------------------------------------------------------------------------------
/src/pages/build-pc.js:
--------------------------------------------------------------------------------
1 | // import BuildProduct from "@/components/BuildProduct/BuildProduct";
2 | // import { wrapper } from "@/redux/app/store";
3 | // import categoriesApi from "@/redux/features/categories/categoriesApi";
4 | // import { clearComponent } from "@/redux/features/pcbuild/pcbuildSlice";
5 | // import { ComputerDesktopIcon } from "@heroicons/react/24/outline";
6 | // import Head from "next/head";
7 | // import { useRouter } from "next/router";
8 | // import { useDispatch, useSelector } from "react-redux";
9 |
10 | // const PcBuilder = ({ categories }) => {
11 | // const router = useRouter();
12 |
13 | // const pcbuild = useSelector((state) => state.pcbuild);
14 | // const dispatch = useDispatch();
15 | // const findCurrentCategory = (category) => {
16 | // category = category?.replace(/\s+/g, "");
17 | // category = category?.toLowerCase();
18 | // return pcbuild[category];
19 | // };
20 |
21 | // return (
22 | // <>
23 | //
24 | // PC Builder
25 | //
26 | //
27 | //
28 | //
29 | //
30 | //
31 | //
32 | // PC BUILD
33 | //
34 | // dispatch(clearComponent())}
36 | // className={`button-red py-2 px-2 xs:px-10`}
37 | // >
38 | // Clear
39 | //
40 | //
41 | // {categories.map((category, i) => (
42 | //
46 | // {findCurrentCategory(category?.name)?.name ? (
47 | //
55 | // ) : (
56 | // <>
57 | //
{category.name}
58 | //
61 | // router.push(`/categories/${category?.name}`)
62 | // }
63 | // >
64 | // Choose
65 | //
66 | // >
67 | // )}
68 | //
69 | // ))}
70 | //
71 | //
72 | // {pcbuild?.qty === 0 ? (
73 | //
74 | //
75 | //
76 | // Your Build Is Empty
77 | //
78 | //
79 | //
80 | // ) : (
81 | //
82 | //
83 | //
84 | // Subtotal ({pcbuild?.qty} items) :
85 | //
86 | //
87 | // ${pcbuild?.total}
88 | //
89 | //
90 | //
94 | //
95 | // {
98 | // alert("Your Build has been successfully ordered");
99 | // dispatch(clearComponent());
100 | // }}
101 | // >
102 | // Complete Build
103 | //
104 | //
105 | //
106 | // )}
107 | //
108 | //
109 | // >
110 | // );
111 | // };
112 |
113 | // export default PcBuilder;
114 |
115 | // export const getServerSideProps = wrapper.getServerSideProps(
116 | // (store) => async () => {
117 | // const categories = await store.dispatch(
118 | // categoriesApi.endpoints.getCategories.initiate()
119 | // );
120 |
121 | // try {
122 | // return {
123 | // props: {
124 | // categories: categories.data,
125 | // },
126 | // };
127 | // } catch (error) {
128 | // console.log(error);
129 | // return {
130 | // notFound: true,
131 | // };
132 | // }
133 | // }
134 | // );
135 | import React from 'react';
136 |
137 | const Index = () => {
138 | return (
139 |
140 |
141 |
142 | );
143 | };
144 |
145 | export default Index;
--------------------------------------------------------------------------------
/src/pages/products/pc-builder/index.jsx:
--------------------------------------------------------------------------------
1 | // import BuildProduct from "@/components/BuildProduct/BuildProduct";
2 | // import { wrapper } from "@/redux/app/store";
3 | // import categoriesApi from "@/redux/features/categories/categoriesApi";
4 | // import { clearComponent } from "@/redux/features/pcbuild/pcbuildSlice";
5 | // import { ComputerDesktopIcon } from "@heroicons/react/24/outline";
6 | // import Head from "next/head";
7 | // import { useRouter } from "next/router";
8 | // import { useDispatch, useSelector } from "react-redux";
9 |
10 | // const PcBuilder = ({ categories }) => {
11 | // const router = useRouter();
12 |
13 | // const pcbuild = useSelector((state) => state.pcbuild);
14 | // const dispatch = useDispatch();
15 | // const findCurrentCategory = (category) => {
16 | // category = category?.replace(/\s+/g, "");
17 | // category = category?.toLowerCase();
18 | // return pcbuild[category];
19 | // };
20 |
21 | // return (
22 | // <>
23 | //
24 | // PC Builder
25 | //
26 | //
27 | //
28 | //
29 | //
30 | //
31 | //
32 | // PC BUILD
33 | //
34 | // dispatch(clearComponent())}
36 | // className={`button-red py-2 px-2 xs:px-10`}
37 | // >
38 | // Clear
39 | //
40 | //
41 | // {categories.map((category, i) => (
42 | //
46 | // {findCurrentCategory(category?.name)?.name ? (
47 | //
55 | // ) : (
56 | // <>
57 | //
{category.name}
58 | //
61 | // router.push(`/categories/${category?.name}`)
62 | // }
63 | // >
64 | // Choose
65 | //
66 | // >
67 | // )}
68 | //
69 | // ))}
70 | //
71 | //
72 | // {pcbuild?.qty === 0 ? (
73 | //
74 | //
75 | //
76 | // Your Build Is Empty
77 | //
78 | //
79 | //
80 | // ) : (
81 | //
82 | //
83 | //
84 | // Subtotal ({pcbuild?.qty} items) :
85 | //
86 | //
87 | // ${pcbuild?.total}
88 | //
89 | //
90 | //
94 | //
95 | // {
98 | // alert("Your Build has been successfully ordered");
99 | // dispatch(clearComponent());
100 | // }}
101 | // >
102 | // Complete Build
103 | //
104 | //
105 | //
106 | // )}
107 | //
108 | //
109 | // >
110 | // );
111 | // };
112 |
113 | // export default PcBuilder;
114 |
115 | // export const getServerSideProps = wrapper.getServerSideProps(
116 | // (store) => async () => {
117 | // const categories = await store.dispatch(
118 | // categoriesApi.endpoints.getCategories.initiate()
119 | // );
120 |
121 | // try {
122 | // return {
123 | // props: {
124 | // categories: categories.data,
125 | // },
126 | // };
127 | // } catch (error) {
128 | // console.log(error);
129 | // return {
130 | // notFound: true,
131 | // };
132 | // }
133 | // }
134 | // );
135 | import React from 'react';
136 |
137 | const index = () => {
138 | return (
139 |
140 |
141 |
142 | );
143 | };
144 |
145 | export default index;
--------------------------------------------------------------------------------
/src/components/Shared/AddToCart/Card.js:
--------------------------------------------------------------------------------
1 | import { useConfirmProductMutation, useDeleteToCartMutation } from '@/redux/features/addToCard/addToCard';
2 | import { useRouter } from 'next/router';
3 | import React from 'react';
4 | import { useState } from 'react';
5 | import { useEffect } from 'react';
6 | import { toast } from 'react-hot-toast';
7 | import Loader from '../Loader/Loader';
8 |
9 | const Card = ({ item, refetch }) => {
10 | const router = useRouter()
11 | const [totalPrice, setTotalPrice] = useState(0);
12 | const [deleteToCart, resInfo] = useDeleteToCartMutation()
13 | const [confirmProduct, resInfoOrder] = useConfirmProductMutation()
14 | useEffect(() => {
15 | const prices = item?.map(item => parseFloat(item?.price));
16 | const total = prices?.reduce((acc, price) => acc + price, 0);
17 | setTotalPrice(total);
18 | }, [item]);
19 |
20 | const casedPage = () => {
21 | window.location.reload()
22 | }
23 | const deleteToCartItem = async (id) => {
24 | await deleteToCart(id)
25 | await refetch()
26 | toast.error("Yah..! Delete successfully");
27 | }
28 | const confirmOrder = async () => {
29 | await confirmProduct()
30 | await refetch()
31 | toast.success("Yah..! Your Order confirm successfully");
32 | }
33 |
34 |
35 | const continueShopping = () => {
36 | router.push('/')
37 | }
38 |
39 | return (
40 |
41 |
42 |
Invoices
43 |
44 |
45 |
46 |
47 |
48 |
49 | Photo
50 | Product Name
51 | Quantity
52 | Unit Price
53 | Total
54 |
55 |
56 |
57 | {item?.map((data) => <>
58 |
59 |
60 |
67 |
68 |
69 | {data?.name}
70 |
71 |
72 |
80 |
81 | cached
82 |
83 | deleteToCartItem(data?._id)}>
84 | clear
85 |
86 |
87 |
88 | {data?.price}
89 |
90 |
91 | {data?.price}
92 |
93 |
94 | >)}
95 |
96 |
97 |
98 |
99 | {item?.length !== 0 &&
100 |
101 |
Sub-Total: ${totalPrice}
102 | Total: ${totalPrice}
103 |
104 |
105 | Continue Shopping
106 | {resInfoOrder.isLoading ?
107 |
108 | : Confirm Order }
109 |
110 |
}
111 |
112 | );
113 | }
114 |
115 | export default Card;
--------------------------------------------------------------------------------
/src/pages/sign-in.js:
--------------------------------------------------------------------------------
1 | import Loader from "@/components/Shared/Loader/Loader";
2 | import { useLoginUserMutation } from "@/redux/features/auth/userAuth";
3 | import { useRouter } from "next/router";
4 | import React, { useState } from "react";
5 | import { useEffect } from "react";
6 | import { toast } from "react-hot-toast";
7 | import { FiEye, FiEyeOff } from "react-icons/fi";
8 |
9 | const SignIn = () => {
10 | const [showPassword, setShowPassword] = useState(false);
11 | const router = useRouter();
12 | const [loginUser, resInfo] = useLoginUserMutation();
13 |
14 |
15 | useEffect(() => {
16 | if (typeof window !== "undefined") {
17 | if (resInfo?.status === "fulfilled") {
18 | const { accessToken, refreshToken, userDetails } = resInfo.data;
19 | localStorage.setItem("accessToken", accessToken);
20 | localStorage.setItem("refreshToken", refreshToken);
21 | localStorage.setItem("user", JSON.stringify(userDetails));
22 | router.push('/');
23 | } else if (resInfo.status === "rejected") {
24 | }
25 | }
26 | }, [resInfo, router]);
27 |
28 |
29 | const handleLogin = async (e) => {
30 | e.preventDefault();
31 | const form = e.target;
32 | const email = form.email.value;
33 | const password = form.password.value;
34 | const data = {
35 | email,
36 | password
37 | }
38 |
39 | const res = await loginUser(data);
40 | if (resInfo.isSuccess) {
41 | toast.success("Your Account Login Successfully..!");
42 | }
43 | if (res.error) {
44 | toast.error(res.error.data.message);
45 | }
46 | };
47 |
48 | return (
49 |
50 |
51 |
52 |
53 | Sign In Gadget Galaxy
54 |
55 |
56 |
57 |
58 | Email / Username
59 |
60 |
61 |
69 |
70 |
71 |
79 |
80 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | Password
92 |
93 |
94 |
102 |
103 | {showPassword ? (
104 | setShowPassword(!showPassword)}
107 | />
108 | ) : (
109 | setShowPassword(!showPassword)}
112 | />
113 | )}
114 |
115 |
116 |
117 |
118 | {resInfo?.isLoading ? (
119 |
120 |
121 |
122 | ) : (
123 |
127 | Sign In
128 |
129 | )}
130 |
131 |
132 |
133 |
Do not have an account?
134 | router.push('/sign-up')}>
135 | Sign up
136 |
137 |
138 |
139 |
140 |
141 | );
142 | };
143 |
144 | export default SignIn;
145 |
--------------------------------------------------------------------------------
/src/components/ChooseUs/ChooseUs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // Choose us
3 | const ChooseUs = () => {
4 | return (
5 |
6 |
7 |
8 |
Why Choose Us?
9 | Select us for excellent service and a tranquil setting. You will receive customized treatments from our skilled therapists, who will make sure you depart feeling renewed and invigorated. Our top priority is your well-being.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Services Done
17 |
1000+
18 |
19 |
20 |
21 |
22 |
23 |
Active User
24 |
1M
25 |
26 |
27 |
28 |
29 |
30 |
Professional Expert
31 |
200+
32 |
33 |
34 |
35 |
36 |
37 |
Online Support
38 |
24 hours
39 |
40 |
41 |
42 |
43 |
44 | );
45 | };
46 |
47 | export default ChooseUs;
--------------------------------------------------------------------------------
/src/pages/sign-up.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { useRouter } from 'next/router';
3 | import { FiEye, FiEyeOff } from "react-icons/fi";
4 | import { useCreateUserMutation } from "@/redux/features/auth/userAuth";
5 | import Loader from "@/components/Shared/Loader/Loader";
6 | import { toast } from "react-hot-toast";
7 | import { useDispatch } from "react-redux";
8 | import { setCredentials } from "@/redux/Slice/authSlice";
9 |
10 |
11 | const SignIn = () => {
12 | const [createUser, resInfo] = useCreateUserMutation();
13 | const router = useRouter();
14 | const [showPassword, setShowPassword] = useState(false);
15 | const dispatch = useDispatch();
16 |
17 | // useEffect(() => {
18 | // if (typeof window !== "undefined") {
19 | // if (resInfo?.status === "fulfilled") {
20 | // const { accessToken, refreshToken, userDetails } = resInfo.data;
21 | // localStorage.setItem("accessToken", accessToken);
22 | // localStorage.setItem("refreshToken", refreshToken);
23 | // localStorage.setItem("user", JSON.stringify(userDetails));
24 | // router.push('/');
25 | // } else if (resInfo.status === "rejected") {
26 | // }
27 | // }
28 | // }, [resInfo, router]);
29 | useEffect(() => {
30 | if (resInfo.status === "fulfilled") {
31 | const { accessToken, refreshToken, userDetails } = resInfo.data;
32 | const data = {
33 | accessToken: accessToken,
34 | refreshToken: refreshToken,
35 | user: JSON.stringify(userDetails),
36 | };
37 | dispatch(setCredentials(data));
38 | // toast.success("Login Successfully..!");
39 | router.push('/');
40 | } else if (resInfo.status === "rejected") {
41 | toast.error(resInfo.error.data.message);
42 | }
43 | }, [resInfo.status, resInfo.data, resInfo.error, dispatch, router]);
44 |
45 | const handleLogin = async (e) => {
46 | e.preventDefault();
47 | const form = e.target;
48 | const lastName = form.lastName.value;
49 | const firstName = form.firstName.value;
50 | const email = form.email.value;
51 | const password = form.password.value;
52 | const data = {
53 | firstName,
54 | lastName,
55 | email,
56 | password,
57 | // confirmPassword
58 | }
59 | const res = await createUser(data);
60 | if (resInfo.isSuccess) {
61 | toast.success("Your Account Login Successfully..!");
62 | }
63 | if (res.error) {
64 | toast.error(res.error.data.message);
65 | }
66 | };
67 | return (
68 |
69 |
70 |
71 |
72 | Register Gadget Galaxy
73 |
74 |
75 |
76 |
77 |
78 | First Name
79 |
80 |
81 |
88 |
89 |
90 |
91 |
92 | Last Name
93 |
94 |
95 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | Email
112 |
113 |
114 |
121 |
122 |
123 |
131 |
132 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | Password
145 |
146 |
147 |
154 |
155 | {showPassword ? (
156 | setShowPassword(!showPassword)}
159 | />
160 | ) : (
161 | setShowPassword(!showPassword)}
164 | />
165 | )}
166 |
167 |
168 |
169 |
170 |
171 | {resInfo?.isLoading ? (
172 |
173 |
174 |
175 | ) : (
176 |
180 | Sign Up
181 |
182 | )}
183 |
184 |
185 |
186 |
Already have an account?
187 | router.push('/sign-in')}>
188 | Sign In
189 |
190 |
191 |
192 |
193 |
194 | );
195 | };
196 |
197 | export default SignIn;
198 |
--------------------------------------------------------------------------------
/public/img/hero.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/pages/add-product.js:
--------------------------------------------------------------------------------
1 | import { PostImage } from '@/components/ImageBB/imageUpload';
2 | import { useGetFeatherQuery } from '@/redux/features/productFeather/productFeather';
3 | import { useAddProductsMutation } from '@/redux/features/productFeather/products';
4 | import { useState } from 'react';
5 |
6 | const PostProduct = () => {
7 | const [selectedImage, setSelectedImage] = useState(null);
8 | const [imageLink, setImageLink] = useState();
9 | const [addProducts, resInfo] = useAddProductsMutation()
10 | const { data: productFeather, isLoading, isError } = useGetFeatherQuery()
11 | const [uploadProgress, setUploadProgress] = useState(0);
12 | const [productData, setProductData] = useState({
13 | name: '',
14 | dis: '',
15 | image: imageLink,
16 | price: '',
17 | regPrice: '',
18 | productCode: '',
19 | brand: '',
20 | status: 'in_stock',
21 | feather: 'drone',
22 | keyFeatures: [{ key: '', feature: '' }],
23 | });
24 |
25 | const handleInputChange = (e, index, field) => {
26 |
27 | const { value, name } = e.target;
28 |
29 | if (index !== null) {
30 | const updatedKeyFeatures = [...productData.keyFeatures];
31 | updatedKeyFeatures[index][field] = value;
32 | setProductData({ ...productData, keyFeatures: updatedKeyFeatures });
33 | } else {
34 | setProductData({ ...productData, [name]: value });
35 | }
36 | };
37 |
38 | const addKeyFeaturePair = () => {
39 | setProductData({
40 | ...productData,
41 | keyFeatures: [...productData.keyFeatures, { key: '', feature: '' }],
42 | });
43 | };
44 |
45 | const removeKeyFeaturePair = (index) => {
46 | const updatedKeyFeatures = [...productData.keyFeatures];
47 | updatedKeyFeatures.splice(index, 1);
48 | setProductData({ ...productData, keyFeatures: updatedKeyFeatures });
49 | };
50 |
51 | const handleSubmit = async (e) => {
52 | e.preventDefault();
53 | const filteredData = productData.keyFeatures.filter(item => item.key !== "" || item.feature !== "");
54 |
55 | const cleanedData = { ...productData, keyFeatures: filteredData };
56 | cleanedData.image = imageLink
57 | addProducts(cleanedData)
58 | setProductData(
59 | {
60 | name: '',
61 | dis: '',
62 | image: imageLink,
63 | price: '',
64 | regPrice: '',
65 | productCode: '',
66 | brand: '',
67 | status: 'in_stock',
68 | feather: '',
69 | keyFeatures: [{ key: '', feature: '' }],
70 | }
71 | )
72 | setImageLink()
73 | setSelectedImage()
74 | };
75 |
76 |
77 |
78 | const handleImageUpload = async (e) => {
79 | const file = e.target.files[0];
80 | console.log(file);
81 | setSelectedImage(file);
82 | const imageUrl = await PostImage(file);
83 | setImageLink(imageUrl)
84 | let progress = 0;
85 | const interval = setInterval(() => {
86 | progress += 10;
87 | setUploadProgress(progress);
88 | if (progress === 100) {
89 | clearInterval(interval);
90 | }
91 | }, 100);
92 | };
93 |
94 | return (
95 |
96 |
97 |
Add a Product
98 |
99 |
100 |
101 | Product Name
102 |
103 | handleInputChange(e, null, 'name')}
110 | className="w-full border-2 border-gray-300 p-2 rounded-md focus:outline-none focus:border-blue-400"
111 | placeholder="Product Name"
112 | />
113 |
114 |
115 |
116 | Description
117 |
118 | handleInputChange(e, null, 'dis')}
124 | className="w-full border-2 border-gray-300 p-2 rounded-md focus:outline-none focus:border-blue-400"
125 | rows="4"
126 | placeholder="Product Description"
127 | />
128 |
129 |
130 |
Upload Product Image
131 |
135 | {selectedImage ? (
136 |
137 |
143 | {uploadProgress < 100 && (
144 |
149 | )}
150 |
151 | ) : (
152 | Drag & drop an image or click to select
153 | )}
154 |
155 |
163 |
164 |
226 |
227 |
228 |
229 | Status
230 |
231 | handleInputChange(e, null, 'status')}
237 | className="w-full border-2 border-gray-300 p-2 rounded-md focus:outline-none focus-border-blue-400"
238 | >
239 | In Stock
240 | Out of Stock
241 |
242 |
243 |
244 |
245 | Feather
246 |
247 | handleInputChange(e, null, 'feather')}
252 | className="w-full border-2 uppercase border-gray-300 p-2 rounded-md focus:outline-none focus-border-blue-400"
253 | >
254 |
255 | {
256 | productFeather?.data?.map((item) => {item.dis} )
257 |
258 | }
259 |
260 |
261 |
262 |
301 |
305 | Post Product
306 |
307 |
308 |
309 |
310 | );
311 | };
312 |
313 | export default PostProduct;
314 |
--------------------------------------------------------------------------------