├── .gitignore ├── .env ├── README.md ├── public └── index.html ├── src ├── utils │ ├── debounce.ts │ ├── EnvProvider.ts │ ├── constants.ts │ └── CatSubcatBrands.ts ├── Redux │ ├── CartSlice │ │ ├── module │ │ │ ├── clearCart.ts │ │ │ ├── setLoading.ts │ │ │ ├── setError.ts │ │ │ ├── initialState.ts │ │ │ └── setCartData.ts │ │ └── slice.ts │ ├── OrderSlice │ │ ├── module │ │ │ ├── clearOrder.ts │ │ │ ├── setLoading.ts │ │ │ ├── setError.ts │ │ │ ├── initialState.ts │ │ │ └── setOrderData.ts │ │ └── slice.ts │ ├── ProductsSlice │ │ ├── modules │ │ │ ├── setLoading.ts │ │ │ ├── setError.ts │ │ │ ├── addToProductCache.ts │ │ │ ├── setProductData.ts │ │ │ └── initialState.ts │ │ └── slice.ts │ ├── UserSlice │ │ ├── module │ │ │ ├── setLoading.ts │ │ │ ├── logout.ts │ │ │ ├── setError.ts │ │ │ ├── initialState.ts │ │ │ └── setUserData.ts │ │ └── slice.ts │ ├── CheckOutSlice │ │ ├── module │ │ │ ├── setLoading.ts │ │ │ ├── onChangeCheckout.ts │ │ │ ├── setError.ts │ │ │ └── intialState.ts │ │ └── slice.ts │ ├── FilterSlice │ │ ├── modules │ │ │ ├── initialState.ts │ │ │ └── applyFilters.ts │ │ └── slice.ts │ └── ReduxStore.ts ├── components │ ├── BrandsFilter │ │ └── index.tsx │ ├── Logo │ │ └── index.tsx │ ├── DesktopFilters │ │ └── index.tsx │ ├── CardOrderDetail │ │ └── index.tsx │ ├── SelectFilter │ │ └── index.tsx │ ├── TypeAndSelect │ │ └── index.tsx │ ├── SearchQuerryFilter │ │ └── index.tsx │ ├── CartData │ │ └── index.tsx │ ├── OrderSection │ │ └── index.tsx │ ├── ProductLists │ │ └── index.tsx │ ├── BrandsFilterOptions │ │ └── index.tsx │ ├── Categoryfilter │ │ └── index.tsx │ ├── ProductCartCalculation │ │ └── index.tsx │ ├── SubCategoryFilter │ │ └── index.tsx │ ├── MinMaxPriceFilter │ │ └── index.tsx │ ├── UserMenu │ │ └── index.tsx │ ├── RattingFilter │ │ └── index.tsx │ ├── Navbar │ │ └── index.tsx │ ├── Hamburgur │ │ └── index.tsx │ ├── SignInPage │ │ └── index.tsx │ ├── CartAndOrderProduct │ │ └── index.tsx │ ├── PaymentForm │ │ └── index.tsx │ ├── Review │ │ └── index.tsx │ ├── SignUpPage │ │ └── index.tsx │ ├── ProductCard │ │ └── index.tsx │ ├── CheckoutData │ │ └── index.tsx │ └── AddressForm │ │ └── index.tsx ├── index.tsx ├── View │ ├── Home │ │ └── index.tsx │ ├── Auth │ │ └── index.tsx │ └── CartAndCheckOut │ │ └── index.tsx ├── HOC │ └── index.tsx ├── app.css ├── App.tsx └── api │ └── apiService.ts ├── package.json ├── webpack.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | yarn.lock -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | LOCAL_SERVER_URL = http://localhost:3000 2 | PRODUCTION_SERVER_URL = http://localhost:3000 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ecomm-frontend 2 | 3 | # For authentication 4 | ``` 5 | Email Example : deepak@gmail.com 6 | passsword Example : Deepak@123 7 | ``` 8 | # password should have 9 | - One cap. 10 | - One small. 11 | - One numaric 12 | - One special charac. 13 | 14 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /src/utils/debounce.ts: -------------------------------------------------------------------------------- 1 | export default function debounce(callback: Function, delay: number = 500) { 2 | let timeoutId: NodeJS.Timeout; 3 | 4 | return function (...args: any[]) { 5 | clearTimeout(timeoutId); 6 | 7 | timeoutId = setTimeout(() => { 8 | callback(...args); 9 | }, delay); 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/Redux/CartSlice/module/clearCart.ts: -------------------------------------------------------------------------------- 1 | import { cartSliceInitialStateInterface } from "./initialState"; 2 | 3 | const clearCart = ( 4 | state: cartSliceInitialStateInterface 5 | ): cartSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | cartData: [], 9 | }; 10 | }; 11 | 12 | export default clearCart; 13 | -------------------------------------------------------------------------------- /src/Redux/OrderSlice/module/clearOrder.ts: -------------------------------------------------------------------------------- 1 | import { orderSliceInitialStateInterface } from "./initialState"; 2 | 3 | const clearOrder = ( 4 | state: orderSliceInitialStateInterface 5 | ): orderSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | orderedProducts: [], 9 | }; 10 | }; 11 | 12 | export default clearOrder; 13 | -------------------------------------------------------------------------------- /src/Redux/ProductsSlice/modules/setLoading.ts: -------------------------------------------------------------------------------- 1 | import { productSliceInitialStateI } from "./initialState"; 2 | 3 | const setLoading = ( 4 | state: productSliceInitialStateI 5 | ): productSliceInitialStateI => { 6 | return { 7 | ...state, 8 | loading: true, 9 | message: "", 10 | status: false, 11 | }; 12 | }; 13 | 14 | export default setLoading; 15 | -------------------------------------------------------------------------------- /src/Redux/CartSlice/module/setLoading.ts: -------------------------------------------------------------------------------- 1 | import { cartSliceInitialStateInterface } from "./initialState"; 2 | 3 | const setLoading = ( 4 | state: cartSliceInitialStateInterface 5 | ): cartSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | loading: true, 9 | message: "", 10 | status: false, 11 | }; 12 | }; 13 | 14 | export default setLoading; 15 | -------------------------------------------------------------------------------- /src/Redux/UserSlice/module/setLoading.ts: -------------------------------------------------------------------------------- 1 | import { userSliceInitialStateInterface } from "./initialState"; 2 | 3 | const setLoading = ( 4 | state: userSliceInitialStateInterface 5 | ): userSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | loading: true, 9 | message: "", 10 | status: false, 11 | }; 12 | }; 13 | 14 | export default setLoading; 15 | -------------------------------------------------------------------------------- /src/Redux/OrderSlice/module/setLoading.ts: -------------------------------------------------------------------------------- 1 | import { orderSliceInitialStateInterface } from "./initialState"; 2 | 3 | const setLoading = ( 4 | state: orderSliceInitialStateInterface 5 | ): orderSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | loading: true, 9 | message: "", 10 | status: false, 11 | }; 12 | }; 13 | 14 | export default setLoading; 15 | -------------------------------------------------------------------------------- /src/Redux/CheckOutSlice/module/setLoading.ts: -------------------------------------------------------------------------------- 1 | import { checkOuSliceInitialStateInterface } from "./intialState"; 2 | 3 | const setLoading = ( 4 | state: checkOuSliceInitialStateInterface 5 | ): checkOuSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | loading: true, 9 | message: "", 10 | status: false, 11 | }; 12 | }; 13 | 14 | export default setLoading; 15 | -------------------------------------------------------------------------------- /src/Redux/FilterSlice/modules/initialState.ts: -------------------------------------------------------------------------------- 1 | export interface filterSliceInitialStateI { 2 | category?: string; 3 | subcategory?: string; 4 | brand?: string; 5 | averageRating?: number; 6 | avgtype?: string; 7 | name?: string; 8 | querry?: string; 9 | minPrice?: string; 10 | maxPrice?: string; 11 | _id?:string 12 | } 13 | 14 | export const filterSliceInitialState: filterSliceInitialStateI = {}; 15 | -------------------------------------------------------------------------------- /src/Redux/UserSlice/module/logout.ts: -------------------------------------------------------------------------------- 1 | import { userSliceInitialStateInterface } from "./initialState"; 2 | 3 | const logout = ( 4 | state: userSliceInitialStateInterface 5 | ): userSliceInitialStateInterface => { 6 | return { 7 | ...state, 8 | userData: { 9 | email: "", 10 | name: "", 11 | token: "", 12 | userId: "", 13 | }, 14 | }; 15 | }; 16 | 17 | export default logout; 18 | -------------------------------------------------------------------------------- /src/utils/EnvProvider.ts: -------------------------------------------------------------------------------- 1 | interface EnvProviderI { 2 | SERVER_BASE_URL: string; 3 | } 4 | 5 | export const EnvProvider = (): EnvProviderI => { 6 | if (process.env.NODE_ENV === "production") { 7 | return { 8 | SERVER_BASE_URL: process.env.PRODUCTION_SERVER_URL as string, 9 | }; 10 | } else { 11 | return { 12 | SERVER_BASE_URL: process.env.LOCAL_SERVER_URL as string, 13 | }; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/Redux/CheckOutSlice/module/onChangeCheckout.ts: -------------------------------------------------------------------------------- 1 | import { checkOuSliceInitialStateInterface } from "./intialState"; 2 | 3 | const onChangeCheckout = ( 4 | state: checkOuSliceInitialStateInterface, 5 | action: { payload: Partial } 6 | ): checkOuSliceInitialStateInterface => { 7 | return { 8 | ...state, 9 | ...action.payload, 10 | }; 11 | }; 12 | 13 | export default onChangeCheckout; 14 | -------------------------------------------------------------------------------- /src/Redux/FilterSlice/modules/applyFilters.ts: -------------------------------------------------------------------------------- 1 | import { filterSliceInitialStateI } from "./initialState"; 2 | import { PayloadAction } from "@reduxjs/toolkit"; 3 | 4 | const applyFilters = ( 5 | state: filterSliceInitialStateI, 6 | action: PayloadAction 7 | ): filterSliceInitialStateI => { 8 | return { 9 | ...state, 10 | ...action.payload, 11 | }; 12 | }; 13 | 14 | export default applyFilters; 15 | -------------------------------------------------------------------------------- /src/Redux/ProductsSlice/modules/setError.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { productSliceInitialStateI } from "./initialState"; 3 | 4 | const setError = ( 5 | state: productSliceInitialStateI, 6 | action: { payload: apiResponse } 7 | ): productSliceInitialStateI => { 8 | return { 9 | ...state, 10 | loading: false, 11 | message: action.payload.message, 12 | status: action.payload.status, 13 | }; 14 | }; 15 | 16 | export default setError; 17 | -------------------------------------------------------------------------------- /src/Redux/CartSlice/module/setError.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { cartSliceInitialStateInterface } from "./initialState"; 3 | 4 | const setError = ( 5 | state: cartSliceInitialStateInterface, 6 | action: { payload: apiResponse } 7 | ): cartSliceInitialStateInterface => { 8 | return { 9 | ...state, 10 | loading: false, 11 | message: action.payload.message, 12 | status: action.payload.status, 13 | }; 14 | }; 15 | 16 | export default setError; 17 | -------------------------------------------------------------------------------- /src/Redux/UserSlice/module/setError.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { userSliceInitialStateInterface } from "./initialState"; 3 | 4 | const setError = ( 5 | state: userSliceInitialStateInterface, 6 | action: { payload: apiResponse } 7 | ): userSliceInitialStateInterface => { 8 | return { 9 | ...state, 10 | loading: false, 11 | message: action.payload.message, 12 | status: action.payload.status, 13 | }; 14 | }; 15 | 16 | export default setError; 17 | -------------------------------------------------------------------------------- /src/Redux/FilterSlice/slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { filterSliceInitialState } from "./modules/initialState"; 3 | import applyFilters from "./modules/applyFilters"; 4 | 5 | const filterSlice = createSlice({ 6 | name: "filterSlice", 7 | initialState: filterSliceInitialState, 8 | reducers: { 9 | applyFiltersReducer: applyFilters, 10 | }, 11 | 12 | }); 13 | 14 | export const { applyFiltersReducer } = filterSlice.actions; 15 | export default filterSlice.reducer; 16 | -------------------------------------------------------------------------------- /src/Redux/OrderSlice/module/setError.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { orderSliceInitialStateInterface } from "./initialState"; 3 | 4 | const setError = ( 5 | state: orderSliceInitialStateInterface, 6 | action: { payload: apiResponse } 7 | ): orderSliceInitialStateInterface => { 8 | return { 9 | ...state, 10 | loading: false, 11 | message: action.payload.message, 12 | status: action.payload.status, 13 | }; 14 | }; 15 | 16 | export default setError; 17 | -------------------------------------------------------------------------------- /src/components/BrandsFilter/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from "react"; 2 | import Stack from "@mui/material/Stack"; 3 | import FormLabel from "@mui/material/FormLabel"; 4 | import BrandsFilterOptions from "../BrandsFilterOptions"; 5 | 6 | const BrandsFilter: React.FC = () => { 7 | return ( 8 | 9 | Brand's 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default memo(BrandsFilter); 16 | -------------------------------------------------------------------------------- /src/Redux/CheckOutSlice/module/setError.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { checkOuSliceInitialStateInterface } from "./intialState"; 3 | 4 | const setError = ( 5 | state: checkOuSliceInitialStateInterface, 6 | action: { payload: apiResponse } 7 | ): checkOuSliceInitialStateInterface => { 8 | return { 9 | ...state, 10 | loading: false, 11 | message: action.payload.message, 12 | status: action.payload.status, 13 | }; 14 | }; 15 | 16 | export default setError; 17 | -------------------------------------------------------------------------------- /src/Redux/CartSlice/module/initialState.ts: -------------------------------------------------------------------------------- 1 | export interface cartDataInterface { 2 | productId: string; 3 | productCount: number; 4 | productTotal: number; 5 | userId: string; 6 | } 7 | export interface cartSliceInitialStateInterface { 8 | loading: boolean; 9 | status: boolean; 10 | message: string; 11 | cartData: cartDataInterface[]; 12 | } 13 | 14 | export const cartSliceInitialState: cartSliceInitialStateInterface = { 15 | cartData: [], 16 | loading: false, 17 | message: "", 18 | status: true, 19 | }; 20 | -------------------------------------------------------------------------------- /src/Redux/ProductsSlice/modules/addToProductCache.ts: -------------------------------------------------------------------------------- 1 | import { productCardI, productSliceInitialStateI } from "./initialState"; 2 | import { PayloadAction } from "@reduxjs/toolkit"; 3 | 4 | const addToProductCache = ( 5 | state: productSliceInitialStateI, 6 | action: PayloadAction 7 | ): productSliceInitialStateI => { 8 | return { 9 | ...state, 10 | productCache: { 11 | ...state.productCache, 12 | [action.payload._id as string]: action.payload, 13 | }, 14 | }; 15 | }; 16 | 17 | export default addToProductCache; 18 | -------------------------------------------------------------------------------- /src/components/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from "@mui/material"; 2 | import React from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | 5 | const Logo: React.FC = () => { 6 | const router = useNavigate(); 7 | const handleClick = () => router("/"); 8 | return ( 9 | 17 | App Logo 18 | 19 | ); 20 | }; 21 | 22 | export default Logo; 23 | -------------------------------------------------------------------------------- /src/Redux/UserSlice/module/initialState.ts: -------------------------------------------------------------------------------- 1 | export interface userDataInterface { 2 | email: string; 3 | name: string; 4 | token: string; 5 | userId: string; 6 | } 7 | 8 | export interface userSliceInitialStateInterface { 9 | loading: boolean; 10 | status: boolean; 11 | message: string; 12 | userData: userDataInterface; 13 | } 14 | 15 | export const userSliceInitialState: userSliceInitialStateInterface = { 16 | loading: false, 17 | message: "", 18 | status: false, 19 | userData: { 20 | email: "", 21 | name: "", 22 | token: "", 23 | userId: "", 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /src/Redux/CartSlice/module/setCartData.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { cartSliceInitialStateInterface } from "./initialState"; 3 | import { PayloadAction } from "@reduxjs/toolkit"; 4 | 5 | const getCartData = ( 6 | state: cartSliceInitialStateInterface, 7 | action: PayloadAction 8 | ): cartSliceInitialStateInterface => { 9 | return { 10 | ...state, 11 | loading: false, 12 | cartData: action.payload.data, 13 | message: action.payload.message, 14 | status: action.payload.status, 15 | }; 16 | }; 17 | 18 | export default getCartData; 19 | -------------------------------------------------------------------------------- /src/Redux/ProductsSlice/modules/setProductData.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { productSliceInitialStateI } from "./initialState"; 3 | import { PayloadAction } from "@reduxjs/toolkit"; 4 | 5 | const setProductData = ( 6 | state: productSliceInitialStateI, 7 | action: PayloadAction 8 | ): productSliceInitialStateI => { 9 | return { 10 | ...state, 11 | loading: false, 12 | products: action.payload.data, 13 | message: action.payload.message, 14 | status: action.payload.status, 15 | }; 16 | }; 17 | 18 | export default setProductData; 19 | -------------------------------------------------------------------------------- /src/Redux/UserSlice/module/setUserData.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { userSliceInitialStateInterface } from "./initialState"; 3 | import { PayloadAction } from "@reduxjs/toolkit"; 4 | 5 | const getCartData = ( 6 | state: userSliceInitialStateInterface, 7 | action: PayloadAction 8 | ): userSliceInitialStateInterface => { 9 | return { 10 | ...state, 11 | loading: false, 12 | message: action.payload.message, 13 | status: action.payload.status, 14 | userData: action.payload.data, 15 | }; 16 | }; 17 | 18 | export default getCartData; 19 | -------------------------------------------------------------------------------- /src/Redux/OrderSlice/module/initialState.ts: -------------------------------------------------------------------------------- 1 | import { cartDataInterface } from "../../CartSlice/module/initialState"; 2 | 3 | export interface OrderedProductInterface extends cartDataInterface { 4 | OrderId: string; 5 | ExpectedDelivery: string; 6 | } 7 | export interface orderSliceInitialStateInterface { 8 | loading: boolean; 9 | status: boolean; 10 | message: string; 11 | orderedProducts: OrderedProductInterface[]; 12 | } 13 | 14 | export const orderSliceInitialState: orderSliceInitialStateInterface = { 15 | loading: false, 16 | message: "", 17 | orderedProducts: [], 18 | status: false, 19 | }; 20 | -------------------------------------------------------------------------------- /src/Redux/OrderSlice/module/setOrderData.ts: -------------------------------------------------------------------------------- 1 | import { apiResponse } from "../../../api/apiService"; 2 | import { orderSliceInitialStateInterface } from "./initialState"; 3 | import { PayloadAction } from "@reduxjs/toolkit"; 4 | 5 | const getCartData = ( 6 | state: orderSliceInitialStateInterface, 7 | action: PayloadAction 8 | ): orderSliceInitialStateInterface => { 9 | return { 10 | ...state, 11 | loading: false, 12 | orderedProducts: action.payload.data, 13 | message: action.payload.message, 14 | status: action.payload.status, 15 | }; 16 | }; 17 | 18 | export default getCartData; 19 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { RouterProvider } from "react-router-dom"; 4 | import AppRoute from "./App"; 5 | import { Provider } from "react-redux"; 6 | import reduxStore from "./Redux/ReduxStore"; 7 | 8 | const container: HTMLElement | null = document.getElementById("root"); 9 | if (container) { 10 | const root = createRoot(container); 11 | root.render( 12 | 13 | 14 | 15 | ); 16 | } else { 17 | throw new Error("Target container is not dom element"); 18 | } 19 | -------------------------------------------------------------------------------- /src/View/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import Stack from "@mui/material/Stack"; 2 | import React, { memo } from "react"; 3 | const DesktopFilters = React.lazy( 4 | () => import("../../components/DesktopFilters") 5 | ); 6 | const ProductLists = React.lazy(() => import("../../components/ProductLists")); 7 | 8 | const HomePage: React.FC = () => { 9 | return ( 10 | 20 | 21 | 22 | 23 | ); 24 | }; 25 | 26 | export default memo(HomePage); 27 | -------------------------------------------------------------------------------- /src/View/Auth/index.tsx: -------------------------------------------------------------------------------- 1 | import Stack from "@mui/material/Stack"; 2 | import React, { memo, useState } from "react"; 3 | 4 | const SignInPage = React.lazy(() => import("../../components/SignInPage")); 5 | const SignUpPage = React.lazy(() => import("../../components/SignUpPage")); 6 | 7 | const Authentication: React.FC = () => { 8 | const [IsalreadyUser, setIsalreadyUser] = useState(true); 9 | const toggle = () => setIsalreadyUser((prev) => !prev); 10 | return ( 11 | 12 | {IsalreadyUser ? ( 13 | 14 | ) : ( 15 | 16 | )} 17 | 18 | ); 19 | }; 20 | 21 | export default memo(Authentication); 22 | -------------------------------------------------------------------------------- /src/Redux/ReduxStore.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import productSlice from "./ProductsSlice/slice"; 3 | import filterSlice from "./FilterSlice/slice"; 4 | import cartSlice from "./CartSlice/slice"; 5 | import CheckoutSlice from "./CheckOutSlice/slice"; 6 | import orderSlice from "./OrderSlice/slice"; 7 | import UserSlice from "./UserSlice/slice"; 8 | 9 | const reduxStore = configureStore({ 10 | reducer: { 11 | productSlice, 12 | filterSlice, 13 | cartSlice, 14 | CheckoutSlice, 15 | orderSlice, 16 | UserSlice, 17 | }, 18 | }); 19 | 20 | export type RootState = ReturnType; 21 | export type AppDispatch = typeof reduxStore.dispatch; 22 | 23 | export default reduxStore; 24 | -------------------------------------------------------------------------------- /src/HOC/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { RootState } from "../Redux/ReduxStore"; 4 | import { userSliceInitialStateInterface } from "../Redux/UserSlice/module/initialState"; 5 | import { Navigate, useLocation } from "react-router-dom"; 6 | 7 | const PrivateRoute: React.FC<{ children: ReactNode }> = ({ children }) => { 8 | const { userData } = useSelector( 9 | (store) => store.UserSlice 10 | ) as userSliceInitialStateInterface; 11 | 12 | const loc = useLocation(); 13 | 14 | return userData.token ? ( 15 | children 16 | ) : ( 17 | 18 | ); 19 | }; 20 | 21 | export default PrivateRoute; 22 | -------------------------------------------------------------------------------- /src/Redux/CheckOutSlice/slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { CheckoutInitialState } from "./module/intialState"; 3 | import setLoading from "./module/setLoading"; 4 | import setError from "./module/setError"; 5 | import onChangeCheckout from "./module/onChangeCheckout"; 6 | 7 | const CheckoutSlice = createSlice({ 8 | name: "cartSlice", 9 | initialState: CheckoutInitialState, 10 | reducers: { 11 | setCartLoadingReducer: setLoading, 12 | setCartErrorReducer: setError, 13 | onChangeCheckoutReducer: onChangeCheckout, 14 | }, 15 | }); 16 | 17 | export const { 18 | onChangeCheckoutReducer, 19 | setCartLoadingReducer, 20 | setCartErrorReducer, 21 | } = CheckoutSlice.actions; 22 | export default CheckoutSlice.reducer; 23 | -------------------------------------------------------------------------------- /src/components/DesktopFilters/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from "react"; 2 | import Stack from "@mui/material/Stack"; 3 | import SearchQuerryFilter from "../SearchQuerryFilter"; 4 | import MinMaxPriceFilter from "../MinMaxPriceFilter"; 5 | import RattingFilter from "../RattingFilter"; 6 | import BrandsFilter from "../BrandsFilter"; 7 | 8 | const DesktopFilters: React.FC = () => { 9 | return ( 10 | 19 | {" "} 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default memo(DesktopFilters); 29 | -------------------------------------------------------------------------------- /src/Redux/UserSlice/slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { userSliceInitialState } from "./module/initialState"; 3 | import setLoading from "./module/setLoading"; 4 | import getCartData from "./module/setUserData"; 5 | import setError from "./module/setError"; 6 | import logout from "./module/logout"; 7 | 8 | const cartSlice = createSlice({ 9 | name: "cartSlice", 10 | initialState: userSliceInitialState, 11 | reducers: { 12 | setUserLoadingReducer: setLoading, 13 | setUserErrorReducer: setError, 14 | setUserDataReducer: getCartData, 15 | logoutReducer: logout, 16 | }, 17 | }); 18 | 19 | export const { 20 | setUserDataReducer, 21 | setUserLoadingReducer, 22 | setUserErrorReducer, 23 | logoutReducer, 24 | } = cartSlice.actions; 25 | export default cartSlice.reducer; 26 | -------------------------------------------------------------------------------- /src/Redux/CartSlice/slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { cartSliceInitialState } from "./module/initialState"; 3 | import setLoading from "./module/setLoading"; 4 | import getCartData from "./module/setCartData"; 5 | import setError from "./module/setError"; 6 | import clearCart from "./module/clearCart"; 7 | 8 | const cartSlice = createSlice({ 9 | name: "cartSlice", 10 | initialState: cartSliceInitialState, 11 | reducers: { 12 | setCartLoadingReducer: setLoading, 13 | setCartErrorReducer: setError, 14 | setCartDataReducer: getCartData, 15 | clearCartReducer: clearCart, 16 | }, 17 | }); 18 | 19 | export const { 20 | setCartDataReducer, 21 | setCartLoadingReducer, 22 | setCartErrorReducer, 23 | clearCartReducer, 24 | } = cartSlice.actions; 25 | export default cartSlice.reducer; 26 | -------------------------------------------------------------------------------- /src/Redux/OrderSlice/slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { orderSliceInitialState } from "./module/initialState"; 3 | import setLoading from "./module/setLoading"; 4 | import setOrderData from "./module/setOrderData"; 5 | import setError from "./module/setError"; 6 | import clearOrder from "./module/clearOrder"; 7 | 8 | const orderSlice = createSlice({ 9 | name: "orderSlice", 10 | initialState: orderSliceInitialState, 11 | reducers: { 12 | setOrderLoadingReducer: setLoading, 13 | setOrderErrorReducer: setError, 14 | setOrdertDataReducer: setOrderData, 15 | clearOrderReducer: clearOrder, 16 | }, 17 | }); 18 | 19 | export const { 20 | setOrderErrorReducer, 21 | setOrderLoadingReducer, 22 | setOrdertDataReducer, 23 | clearOrderReducer, 24 | } = orderSlice.actions; 25 | export default orderSlice.reducer; 26 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export interface priceRangeI { 2 | label: string; 3 | value: string; 4 | } 5 | 6 | export const minPriceRange: priceRangeI[] = [ 7 | { label: "100", value: "100" }, 8 | { label: "1000", value: "1000" }, 9 | { label: "10000", value: "10000" }, 10 | { label: "100000", value: "100000" }, 11 | ]; 12 | export const maxPriceRange: priceRangeI[] = [ 13 | { label: "1000", value: "1000" }, 14 | { label: "10000", value: "10000" }, 15 | { label: "100000", value: "100000" }, 16 | ]; 17 | 18 | export interface RattingRangeI extends priceRangeI {} 19 | export const RattingRange: RattingRangeI[] = [ 20 | { label: "5", value: "5" }, 21 | { label: "4", value: "4" }, 22 | { label: "3", value: "3" }, 23 | { label: "2", value: "2" }, 24 | { label: "1", value: "1" }, 25 | ]; 26 | 27 | export const steps = ["Shipping address", "Payment details", "Review your order"]; 28 | -------------------------------------------------------------------------------- /src/Redux/ProductsSlice/slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { productsInitialState } from "./modules/initialState"; 3 | import setLoading from "../ProductsSlice/modules/setLoading"; 4 | import setError from "./modules/setError"; 5 | import setProductData from "./modules/setProductData"; 6 | import addToProductCache from "./modules/addToProductCache"; 7 | 8 | const productSlice = createSlice({ 9 | name: "productSlice", 10 | initialState: productsInitialState, 11 | reducers: { 12 | setProductLoadingReducer: setLoading, 13 | setProductErrorReducer: setError, 14 | setProductDataReducer: setProductData, 15 | addToProductCacheReducer: addToProductCache, 16 | }, 17 | }); 18 | 19 | export const { 20 | setProductLoadingReducer, 21 | setProductErrorReducer, 22 | setProductDataReducer, 23 | addToProductCacheReducer, 24 | } = productSlice.actions; 25 | export default productSlice.reducer; 26 | -------------------------------------------------------------------------------- /src/Redux/ProductsSlice/modules/initialState.ts: -------------------------------------------------------------------------------- 1 | export interface productCardI { 2 | _id: string; 3 | name: string; 4 | description: string; 5 | category: string; 6 | subcategory: string; 7 | brand: string; 8 | price: number; 9 | quantityAvailable: number; 10 | image: string; 11 | averageRating: number; 12 | totalRatings: number; 13 | comments: [ 14 | { 15 | userId: string; 16 | username: string; 17 | comment: string; 18 | rating: number; 19 | timestamp: string; 20 | } 21 | ]; 22 | } 23 | export interface productSliceInitialStateI { 24 | loading: boolean; 25 | message: string; 26 | status: boolean; 27 | products: productCardI[]; 28 | productCache: { 29 | [key: string]: productCardI; // optimised 30 | }; 31 | } 32 | 33 | export const productsInitialState: productSliceInitialStateI = { 34 | loading: false, 35 | message: "", 36 | products: [], 37 | productCache: {}, 38 | status: true, 39 | }; 40 | -------------------------------------------------------------------------------- /src/View/CartAndCheckOut/index.tsx: -------------------------------------------------------------------------------- 1 | import Stack from "@mui/material/Stack"; 2 | import React, { memo } from "react"; 3 | import CartData from "../../components/CartData"; 4 | import CheckoutData from "../../components/CheckoutData"; 5 | import OrderSection from "../../components/OrderSection"; 6 | 7 | const CartAndCheckout: React.FC = () => { 8 | return ( 9 | 10 | 19 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default memo(CartAndCheckout); 37 | -------------------------------------------------------------------------------- /src/components/CardOrderDetail/index.tsx: -------------------------------------------------------------------------------- 1 | import { Stack, Typography } from "@mui/material"; 2 | import React from "react"; 3 | 4 | interface componentInterface { 5 | ExpectedDelivery: string; 6 | } 7 | const CardOrderDetail: React.FC = ({ 8 | ExpectedDelivery, 9 | }) => { 10 | return ( 11 | 17 | {" "} 18 | CardOrderDetail:{" "} 19 | {Date.now() >= parseInt(ExpectedDelivery) ? ( 20 | 25 | Order Delivered 26 | 27 | ) : ( 28 | 33 | Dispatched 34 | 35 | )} 36 | 37 | ); 38 | }; 39 | 40 | export default CardOrderDetail; 41 | -------------------------------------------------------------------------------- /src/components/SelectFilter/index.tsx: -------------------------------------------------------------------------------- 1 | import { Autocomplete, TextField } from "@mui/material"; 2 | import React from "react"; 3 | import { priceRangeI } from "../../utils/constants"; 4 | 5 | interface ComponentProps { 6 | options: { label: string; value: string }[]; 7 | defaultValue: string | undefined; 8 | onChange: ( 9 | event: React.SyntheticEvent, 10 | value: string | priceRangeI | null 11 | ) => void; 12 | disabled: boolean; 13 | label: string; 14 | } 15 | 16 | const SelectFilter: React.FC = ({ 17 | options, 18 | defaultValue, 19 | onChange, 20 | disabled, 21 | label, 22 | }) => { 23 | const optionLabels = options.map((option) => option.label); 24 | 25 | return ( 26 | option} 32 | defaultValue={defaultValue} 33 | renderInput={(params) => ( 34 | 35 | )} 36 | /> 37 | ); 38 | }; 39 | 40 | export default SelectFilter; 41 | -------------------------------------------------------------------------------- /src/components/TypeAndSelect/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from "react"; 2 | import { priceRangeI } from "../../utils/constants"; 3 | import TextField from "@mui/material/TextField"; 4 | import Autocomplete from "@mui/material/Autocomplete"; 5 | interface ComponentProps { 6 | label: string; 7 | defaultValue: string | undefined; 8 | onChange: ( 9 | event: React.SyntheticEvent, 10 | value: string | priceRangeI | null 11 | ) => void; 12 | options: priceRangeI[]; 13 | } 14 | 15 | const TypeAndSelect: React.FC = ({ 16 | label, 17 | onChange, 18 | options, 19 | defaultValue, 20 | }) => { 21 | const defaultOption = options.find((option) => option.value === defaultValue); 22 | const defaultLabel = defaultOption ? defaultOption.label : defaultValue || ""; 23 | 24 | return ( 25 | option.label)} 29 | renderInput={(params) => ( 30 | 31 | )} 32 | onChange={onChange} 33 | fullWidth 34 | /> 35 | ); 36 | }; 37 | 38 | export default memo(TypeAndSelect); 39 | -------------------------------------------------------------------------------- /src/components/SearchQuerryFilter/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ChangeEvent, memo } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import TextField from "@mui/material/TextField"; 4 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 5 | import { filterSliceInitialStateI } from "../../Redux/FilterSlice/modules/initialState"; 6 | import { applyFiltersReducer } from "../../Redux/FilterSlice/slice"; 7 | import debounce from "../../utils/debounce"; 8 | 9 | const SearchQuerryFilter: React.FC = () => { 10 | const { querry } = useSelector( 11 | (store) => store.filterSlice 12 | ) as filterSliceInitialStateI; 13 | 14 | const dispatch = useDispatch(); 15 | 16 | const handleQuerryChange = debounce( 17 | (event: ChangeEvent) => { 18 | dispatch( 19 | applyFiltersReducer({ 20 | querry: event.target.value, 21 | }) 22 | ); 23 | }, 24 | 300 25 | ); 26 | 27 | return ( 28 | 38 | ); 39 | }; 40 | 41 | export default memo(SearchQuerryFilter); 42 | -------------------------------------------------------------------------------- /src/components/CartData/index.tsx: -------------------------------------------------------------------------------- 1 | import Stack from "@mui/material/Stack"; 2 | import Typography from "@mui/material/Typography"; 3 | import React, { memo } from "react"; 4 | import { RootState } from "../../Redux/ReduxStore"; 5 | import { useSelector } from "react-redux"; 6 | import { cartSliceInitialStateInterface } from "../../Redux/CartSlice/module/initialState"; 7 | import CartAndOrderProduct from "../CartAndOrderProduct"; 8 | 9 | const CartData: React.FC = () => { 10 | const { cartData } = useSelector( 11 | (store) => store.cartSlice 12 | ) as cartSliceInitialStateInterface; 13 | 14 | return ( 15 | 21 | 22 | Cart 23 | 24 | 25 | {cartData.map((cartitem) => { 26 | const { productCount, productId, productTotal } = cartitem; 27 | return ( 28 | 35 | ); 36 | })} 37 | 38 | 39 | ); 40 | }; 41 | 42 | export default memo(CartData); 43 | -------------------------------------------------------------------------------- /src/Redux/CheckOutSlice/module/intialState.ts: -------------------------------------------------------------------------------- 1 | export type AddressInterface = { 2 | firstName: string; 3 | lastName: string; 4 | addressline: string; 5 | city: string; 6 | state: string; 7 | zipcode: string; 8 | country: string; 9 | }; 10 | export interface paymentsInterface { 11 | cardNumber: string; 12 | cardName: string; 13 | ExpiryDate: string; 14 | cvv: string; 15 | } 16 | 17 | export interface checkOuSliceInitialStateInterface { 18 | loading: boolean; 19 | status: boolean; 20 | message: string; 21 | address: AddressInterface; 22 | payment: paymentsInterface; 23 | } 24 | 25 | export const CheckoutInitialState: checkOuSliceInitialStateInterface = { 26 | address: localStorage.getItem("address") 27 | ? JSON.parse(localStorage.getItem("address") as string) 28 | : { 29 | addressline: "Indranager, Bangalore", 30 | city: "Bangalore", 31 | country: "INDIA", 32 | firstName: "Deepak", 33 | lastName: "Mandal", 34 | state: "Karnataka", 35 | zipcode: "560075", 36 | }, 37 | loading: false, 38 | message: "", 39 | payment: localStorage.getItem("payment") 40 | ? JSON.parse(localStorage.getItem("payment") as string) 41 | : { 42 | cardName: "Deepak Mandal", 43 | cardNumber: "1111 2222 3333", 44 | cvv: 123, 45 | ExpiryDate: "24/04", 46 | }, 47 | status: false, 48 | }; 49 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | /* App.css */ 2 | 3 | body, 4 | html { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .container { 10 | width: 100vw; 11 | height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: space-between; 15 | } 16 | 17 | .header, 18 | .footer { 19 | --tw-bg-opacity: 1; 20 | background-color: rgb(31 41 55 / var(--tw-bg-opacity)); 21 | --tw-text-opacity: 1; 22 | color: rgb(255 255 255 / var(--tw-text-opacity)); 23 | text-align: center; 24 | padding-top: 1rem /* 16px */; 25 | padding-bottom: 1rem /* 16px */; 26 | } 27 | 28 | .title { 29 | font-size: 1.875rem /* 30px */; 30 | line-height: 2.25rem /* 36px */; 31 | } 32 | .main { 33 | max-width: 42rem /* 672px */; 34 | margin-left: auto; 35 | margin-right: auto; 36 | padding: 1rem /* 16px */; 37 | } 38 | .section-title { 39 | font-size: 1.5rem /* 24px */; 40 | line-height: 2rem /* 32px */; 41 | } 42 | 43 | .code-block { 44 | --tw-bg-opacity: 1; 45 | background-color: rgb(229 231 235 / var(--tw-bg-opacity)); 46 | padding: 0.5rem /* 8px */; 47 | } 48 | 49 | .links-container { 50 | text-align: center; 51 | margin-top: 20px; 52 | } 53 | 54 | .link { 55 | display: inline-block; 56 | margin: 8px; 57 | padding: 8px 16px; 58 | text-decoration: none; 59 | color: #fff; 60 | background-color: #333; 61 | border-radius: 4px; 62 | } 63 | 64 | .link:hover { 65 | background-color: #555; 66 | } 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webelight-frontend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.tsx", 6 | "author": "Deepak Mandal", 7 | "license": "ISC", 8 | "scripts": { 9 | "build": "webpack --mode production", 10 | "build:dev": "webpack --mode development", 11 | "build:start": "cd dist && PORT=7001 npx serve", 12 | "start": "webpack serve --open --mode development", 13 | "start:live": "webpack serve --open --mode development --live-reload --hot" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.23.7", 17 | "@babel/preset-env": "^7.15.8", 18 | "@babel/preset-react": "^7.14.5", 19 | "@babel/preset-typescript": "^7.23.3", 20 | "@types/react": "^18.2.48", 21 | "@types/react-dom": "^18.2.18", 22 | "babel-loader": "^8.2.2", 23 | "css-loader": "^6.3.0", 24 | "file-loader": "^6.2.0", 25 | "html-webpack-plugin": "^5.3.2", 26 | "sass-loader": "^14.0.0", 27 | "style-loader": "^3.3.0", 28 | "webpack": "^5.90.2", 29 | "webpack-cli": "^5.1.4", 30 | "webpack-dev-server": "^4.3.1" 31 | }, 32 | "dependencies": { 33 | "@emotion/react": "^11.11.3", 34 | "@emotion/styled": "^11.11.0", 35 | "@mui/icons-material": "^5.15.10", 36 | "@mui/material": "^5.15.10", 37 | "@reduxjs/toolkit": "^2.2.1", 38 | "axios": "^1.8.2", 39 | "dotenv": "^16.3.1", 40 | "framer-motion": "^10.18.0", 41 | "process": "^0.11.10", 42 | "react": "^18.2.0", 43 | "react-dom": "^18.2.0", 44 | "react-redux": "^9.1.0", 45 | "react-router-dom": "^6.22.0", 46 | "sass": "^1.70.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/OrderSection/index.tsx: -------------------------------------------------------------------------------- 1 | import Typography from "@mui/material/Typography"; 2 | import Stack from "@mui/material/Stack"; 3 | import React, { memo } from "react"; 4 | import { useSelector } from "react-redux"; 5 | import { RootState } from "../../Redux/ReduxStore"; 6 | import { orderSliceInitialStateInterface } from "../../Redux/OrderSlice/module/initialState"; 7 | import CartAndOrderProduct from "../CartAndOrderProduct"; 8 | 9 | const OrderSection: React.FC = () => { 10 | const { orderedProducts } = useSelector( 11 | (store) => store.orderSlice 12 | ) as orderSliceInitialStateInterface; 13 | return ( 14 | 20 | 21 | Orders 22 | 23 | 24 | {orderedProducts?.map((orderedItem) => { 25 | const { 26 | ExpectedDelivery, 27 | OrderId, 28 | productCount, 29 | productId, 30 | productTotal, 31 | } = orderedItem; 32 | 33 | return ( 34 | 43 | ); 44 | })} 45 | 46 | 47 | ); 48 | }; 49 | 50 | export default memo(OrderSection) 51 | -------------------------------------------------------------------------------- /src/components/ProductLists/index.tsx: -------------------------------------------------------------------------------- 1 | import Stack from "@mui/material/Stack"; 2 | import React, { memo } from "react"; 3 | import { useSelector } from "react-redux"; 4 | import { RootState } from "../../Redux/ReduxStore"; 5 | import CategoryFilter from "../Categoryfilter"; 6 | import SubCategoryFilter from "../SubCategoryFilter"; 7 | import { productSliceInitialStateI } from "../../Redux/ProductsSlice/modules/initialState"; 8 | import ProductCard from "../ProductCard"; 9 | import { cartSliceInitialStateInterface } from "../../Redux/CartSlice/module/initialState"; 10 | 11 | const ProductLists: React.FC = () => { 12 | const { products } = useSelector( 13 | (store) => store.productSlice 14 | ) as productSliceInitialStateI; 15 | 16 | const { cartData } = useSelector( 17 | (store) => store.cartSlice 18 | ) as cartSliceInitialStateInterface; 19 | return ( 20 | 26 | 32 | 33 | 34 | 35 | 36 | {products.map((product) => { 37 | return ( 38 | cart.productId == product._id) 43 | ?.productCount || 0 44 | } 45 | /> 46 | ); 47 | })} 48 | 49 | 50 | ); 51 | }; 52 | 53 | export default memo(ProductLists); 54 | -------------------------------------------------------------------------------- /src/components/BrandsFilterOptions/index.tsx: -------------------------------------------------------------------------------- 1 | import FormControlLabel from "@mui/material/FormControlLabel"; 2 | import Radio from "@mui/material/Radio"; 3 | import RadioGroup from "@mui/material/RadioGroup"; 4 | import React, { ChangeEvent, memo } from "react"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 7 | import { filterSliceInitialStateI } from "../../Redux/FilterSlice/modules/initialState"; 8 | import { applyFiltersReducer } from "../../Redux/FilterSlice/slice"; 9 | import { CategoryLists } from "../../utils/CatSubcatBrands"; 10 | 11 | const BrandsFilterOptions: React.FC = () => { 12 | const { subcategory, brand, category } = useSelector( 13 | (store) => store.filterSlice 14 | ) as filterSliceInitialStateI; 15 | 16 | const dispatch = useDispatch(); 17 | 18 | const handleApplyBrands = (event: ChangeEvent) => { 19 | dispatch( 20 | applyFiltersReducer({ 21 | brand: event.target.value, 22 | }) 23 | ); 24 | }; 25 | 26 | const Categories = CategoryLists.find((item) => item.name == category) || { 27 | name: undefined, 28 | subCategories: [], 29 | }; 30 | 31 | const subCategories = Categories.subCategories.find( 32 | (item) => item.name == subcategory 33 | ); 34 | 35 | const brands = subCategories?.brands || []; 36 | return ( 37 | 43 | {brands.map((brand) => { 44 | return ( 45 | } 49 | label={brand} 50 | /> 51 | ); 52 | })} 53 | 54 | ); 55 | }; 56 | 57 | export default memo(BrandsFilterOptions); 58 | -------------------------------------------------------------------------------- /src/components/Categoryfilter/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from "react"; 2 | import SelectFilter from "../SelectFilter"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 5 | import { filterSliceInitialStateI } from "../../Redux/FilterSlice/modules/initialState"; 6 | import { priceRangeI } from "../../utils/constants"; 7 | import { CategoryLists } from "../../utils/CatSubcatBrands"; 8 | import { applyFiltersReducer } from "../../Redux/FilterSlice/slice"; 9 | 10 | const CategoryFilter = () => { 11 | const { category } = useSelector( 12 | (store) => store.filterSlice 13 | ) as filterSliceInitialStateI; 14 | 15 | const dispatch = useDispatch(); 16 | 17 | const handleApplyCatSubCate = ( 18 | event: React.SyntheticEvent, 19 | value: string | priceRangeI | null 20 | ) => { 21 | if (value === null) { 22 | dispatch( 23 | applyFiltersReducer({ 24 | category: undefined, 25 | subcategory: undefined, 26 | brand: undefined, 27 | }) 28 | ); 29 | } else if (typeof value == "string") { 30 | dispatch( 31 | applyFiltersReducer({ 32 | category: value, 33 | subcategory: undefined, 34 | brand: undefined, 35 | }) 36 | ); 37 | } else { 38 | dispatch( 39 | applyFiltersReducer({ 40 | category: value.value, 41 | subcategory: undefined, 42 | brand: undefined, 43 | }) 44 | ); 45 | } 46 | }; 47 | return ( 48 | { 51 | return { 52 | label: item.name, 53 | value: item.name, 54 | }; 55 | })} 56 | label={"Select category"} 57 | disabled={false} 58 | defaultValue={category} 59 | /> 60 | ); 61 | }; 62 | 63 | export default memo(CategoryFilter); 64 | -------------------------------------------------------------------------------- /src/components/ProductCartCalculation/index.tsx: -------------------------------------------------------------------------------- 1 | import { Stack, Typography } from "@mui/material"; 2 | import React from "react"; 3 | 4 | interface componentInterface { 5 | price: number; 6 | productCount: number; 7 | } 8 | const ProductCartCalculation: React.FC = ({ 9 | price, 10 | productCount, 11 | }) => { 12 | return ( 13 | 19 | 20 | 27 | Per Item Price:{" "} 28 | 29 | 36 | ${price?.toFixed(2)} 37 | 38 | / 39 | 46 | No of Item: 47 | 48 | 55 | {productCount} 56 | 57 | 58 | 59 | 60 | 67 | Total: 68 | 69 | 76 | {productCount * +price?.toFixed(2)} 77 | 78 | 79 | 80 | ); 81 | }; 82 | 83 | export default ProductCartCalculation; 84 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const htmlWebpackPlugin = require("html-webpack-plugin"); 3 | const webpack = require("webpack"); 4 | 5 | // dotenv configuration settings 6 | const dotenv = require("dotenv").config({ path: __dirname + "/.env" }); 7 | const isProduction = process.env.NODE_ENV === "production"; 8 | 9 | module.exports = { 10 | resolve: { 11 | extensions: [".js", ".jsx", ".ts", ".tsx"], 12 | }, 13 | entry: "./src/index.tsx", 14 | output: { 15 | path: path.resolve(__dirname, "dist"), 16 | }, 17 | plugins: [ 18 | new htmlWebpackPlugin({ 19 | template: "./public/index.html", 20 | }), 21 | new webpack.DefinePlugin({ 22 | "process.env": JSON.stringify({ 23 | ...dotenv.parsed, 24 | NODE_ENV: JSON.stringify(isProduction ? "production" : "development"), 25 | }), 26 | }), 27 | ], 28 | module: { 29 | rules: [ 30 | { 31 | test: /.(js|jsx|ts|tsx)$/, 32 | exclude: /node_modules/, 33 | use: { 34 | loader: "babel-loader", 35 | options: { 36 | presets: [ 37 | "@babel/preset-env", 38 | "@babel/preset-react", 39 | "@babel/preset-typescript", 40 | ], 41 | }, 42 | }, 43 | }, 44 | { 45 | test: /\.(css|scss)$/, 46 | use: ["style-loader", "css-loader", "sass-loader"], 47 | }, 48 | { 49 | test: /\.(png|jpe?g|gif|svg)$/i, 50 | use: [ 51 | { 52 | loader: "file-loader", 53 | options: { 54 | name: "[name].[ext]", 55 | outputPath: "images", 56 | }, 57 | }, 58 | ], 59 | }, 60 | { 61 | test: /\.(woff|woff2|eot|ttf|otf)$/i, 62 | use: [ 63 | { 64 | loader: "file-loader", 65 | options: { 66 | name: "[name].[ext]", 67 | outputPath: "fonts", 68 | }, 69 | }, 70 | ], 71 | }, 72 | ], 73 | }, 74 | devServer: { 75 | port: 7000, 76 | open: true, 77 | hot: true, 78 | }, 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/SubCategoryFilter/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 4 | import { filterSliceInitialStateI } from "../../Redux/FilterSlice/modules/initialState"; 5 | import { priceRangeI } from "../../utils/constants"; 6 | import { applyFiltersReducer } from "../../Redux/FilterSlice/slice"; 7 | import SelectFilter from "../SelectFilter"; 8 | import { CategoryLists } from "../../utils/CatSubcatBrands"; 9 | 10 | const SubCategoryFilter: React.FC = () => { 11 | const { subcategory, category } = useSelector( 12 | (store) => store.filterSlice 13 | ) as filterSliceInitialStateI; 14 | 15 | const dispatch = useDispatch(); 16 | 17 | const handleApplyCatSubCate = ( 18 | event: React.SyntheticEvent, 19 | value: string | priceRangeI | null 20 | ) => { 21 | if (value == null) { 22 | dispatch( 23 | applyFiltersReducer({ 24 | subcategory: undefined, 25 | brand : undefined 26 | }) 27 | ); 28 | } else if (typeof value == "string") { 29 | dispatch( 30 | applyFiltersReducer({ 31 | subcategory: value, 32 | brand : undefined 33 | }) 34 | ); 35 | } else { 36 | dispatch( 37 | applyFiltersReducer({ 38 | subcategory: value.value, 39 | brand : undefined 40 | }) 41 | ); 42 | } 43 | }; 44 | 45 | const SelectedCategory = CategoryLists.find( 46 | (item) => item.name == category 47 | ) || { 48 | name: "none", 49 | subCategories: [], 50 | }; 51 | 52 | const options: { label: string; value: string }[] = 53 | SelectedCategory.subCategories.map((item) => { 54 | return { 55 | label: item.name, 56 | value: item.name, 57 | }; 58 | }); 59 | 60 | return ( 61 | 68 | ); 69 | }; 70 | 71 | export default memo(SubCategoryFilter); 72 | -------------------------------------------------------------------------------- /src/components/MinMaxPriceFilter/index.tsx: -------------------------------------------------------------------------------- 1 | import Stack from "@mui/material/Stack"; 2 | import Typography from "@mui/material/Typography"; 3 | import React, { memo } from "react"; 4 | import TypeAndSelect from "../TypeAndSelect"; 5 | import { 6 | maxPriceRange, 7 | minPriceRange, 8 | priceRangeI, 9 | } from "../../utils/constants"; 10 | import { useDispatch, useSelector } from "react-redux"; 11 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 12 | import { filterSliceInitialStateI } from "../../Redux/FilterSlice/modules/initialState"; 13 | import { applyFiltersReducer } from "../../Redux/FilterSlice/slice"; 14 | 15 | const MinMaxPriceFilter: React.FC = () => { 16 | const { minPrice, maxPrice } = useSelector( 17 | (store) => store.filterSlice 18 | ) as filterSliceInitialStateI; 19 | 20 | const dispatch = useDispatch(); 21 | 22 | const handleOnChange = ( 23 | event: React.SyntheticEvent, 24 | value: string | priceRangeI | null, 25 | key: "minPrice" | "maxPrice" 26 | ) => { 27 | if (value == null) { 28 | dispatch( 29 | applyFiltersReducer({ 30 | [key]: undefined, 31 | }) 32 | ); 33 | } else if (typeof value == "string") { 34 | dispatch( 35 | applyFiltersReducer({ 36 | [key]: value, 37 | }) 38 | ); 39 | } else { 40 | dispatch( 41 | applyFiltersReducer({ 42 | [key]: value.value, 43 | }) 44 | ); 45 | } 46 | }; 47 | 48 | return ( 49 | 50 | handleOnChange(event, value, "minPrice")} 54 | options={minPriceRange} 55 | /> 56 | 57 | to 58 | 59 | handleOnChange(event, value, "maxPrice")} 63 | options={maxPriceRange} 64 | /> 65 | 66 | ); 67 | }; 68 | 69 | export default memo(MinMaxPriceFilter); 70 | -------------------------------------------------------------------------------- /src/components/UserMenu/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button, Menu, MenuItem, Stack, Typography } from "@mui/material"; 2 | import React, { useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import AccountCircleIcon from "@mui/icons-material/AccountCircle"; 5 | import { useDispatch } from "react-redux"; 6 | import { AppDispatch } from "../../Redux/ReduxStore"; 7 | import { logoutReducer } from "../../Redux/UserSlice/slice"; 8 | import { clearCartReducer } from "../../Redux/CartSlice/slice"; 9 | import { clearOrderReducer } from "../../Redux/OrderSlice/slice"; 10 | 11 | const UserMenu = () => { 12 | const [anchorEl, setAnchorEl] = useState(null); 13 | const open = Boolean(anchorEl); 14 | const handleClick = (event: React.MouseEvent) => { 15 | setAnchorEl(event.currentTarget); 16 | }; 17 | 18 | const route = useNavigate(); 19 | const dispatch = useDispatch(); 20 | const handleClose = () => { 21 | setAnchorEl(null); 22 | }; 23 | const handleLogin = () => { 24 | route("/auth", { 25 | // replace: true, 26 | }); 27 | handleClose(); 28 | }; 29 | const handleLogout = () => { 30 | dispatch(logoutReducer()); 31 | dispatch(clearCartReducer()); 32 | dispatch(clearOrderReducer()); 33 | handleClose(); 34 | }; 35 | return ( 36 | 37 | 43 | 52 | User 53 | 54 | 60 | 61 | 70 | Login / Signup 71 | 72 | Logout 73 | 74 | 75 | ); 76 | }; 77 | 78 | export default UserMenu; 79 | -------------------------------------------------------------------------------- /src/components/RattingFilter/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TypeAndSelect from "../TypeAndSelect"; 3 | import { RattingRange, priceRangeI } from "../../utils/constants"; 4 | import { useDispatch, useSelector } from "react-redux"; 5 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 6 | import { filterSliceInitialStateI } from "../../Redux/FilterSlice/modules/initialState"; 7 | import Stack from "@mui/material/Stack"; 8 | import { applyFiltersReducer } from "../../Redux/FilterSlice/slice"; 9 | import SelectFilter from "../SelectFilter"; 10 | 11 | const RattingFilter = () => { 12 | const { averageRating } = useSelector( 13 | (store) => store.filterSlice 14 | ) as filterSliceInitialStateI; 15 | 16 | const dispatch = useDispatch(); 17 | 18 | const handleOnChange = ( 19 | event: React.SyntheticEvent, 20 | value: string | priceRangeI | null 21 | ) => { 22 | if (value == null) { 23 | dispatch( 24 | applyFiltersReducer({ 25 | averageRating: undefined, 26 | }) 27 | ); 28 | } else if (typeof value == "string") { 29 | dispatch( 30 | applyFiltersReducer({ 31 | averageRating: parseFloat(value), 32 | }) 33 | ); 34 | } else { 35 | dispatch( 36 | applyFiltersReducer({ 37 | averageRating: parseFloat(value.value), 38 | }) 39 | ); 40 | } 41 | }; 42 | 43 | const handleApplyCatSubCate = ( 44 | event: React.SyntheticEvent, 45 | value: string | priceRangeI | null 46 | ) => { 47 | if (value === null) { 48 | dispatch( 49 | applyFiltersReducer({ 50 | avgtype: undefined, 51 | }) 52 | ); 53 | } else if (typeof value == "string") { 54 | dispatch( 55 | applyFiltersReducer({ 56 | avgtype: value, 57 | }) 58 | ); 59 | } else { 60 | dispatch( 61 | applyFiltersReducer({ 62 | avgtype: value.value, 63 | }) 64 | ); 65 | } 66 | }; 67 | return ( 68 | 69 | 85 | 91 | 92 | ); 93 | }; 94 | 95 | export default RattingFilter; 96 | -------------------------------------------------------------------------------- /src/components/Navbar/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Stack, TextField, Typography } from "@mui/material"; 2 | import React from "react"; 3 | import Logo from "../Logo"; 4 | import UserMenu from "../UserMenu"; 5 | import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; 6 | import Hamburgur from "../Hamburgur"; 7 | import { useNavigate } from "react-router-dom"; 8 | 9 | const Navbar: React.FC = () => { 10 | const router = useNavigate(); 11 | 12 | const handleCartRoute = () => { 13 | router("/cart"); 14 | }; 15 | return ( 16 | 23 | 36 | 42 | 43 | 53 | 66 | 67 | 68 | 69 | 81 | 90 | Cart 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | ); 101 | }; 102 | 103 | export default Navbar; 104 | -------------------------------------------------------------------------------- /src/components/Hamburgur/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Button, 4 | Divider, 5 | List, 6 | ListItem, 7 | ListItemButton, 8 | ListItemIcon, 9 | ListItemText, 10 | Stack, 11 | SwipeableDrawer, 12 | TextField, 13 | } from "@mui/material"; 14 | import React, { useState } from "react"; 15 | import MenuIcon from "@mui/icons-material/Menu"; 16 | import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; 17 | import AccountCircleIcon from "@mui/icons-material/AccountCircle"; 18 | import { useNavigate } from "react-router-dom"; 19 | const hamburgerPaths = [ 20 | { 21 | name: "User", 22 | Icon: , 23 | path: "/auth", 24 | }, 25 | { 26 | name: "cart", 27 | Icon: , 28 | path: "/cart", 29 | }, 30 | ]; 31 | const Hamburgur: React.FC = () => { 32 | const [isOpen, setIsOpen] = useState(false); 33 | const toggleDrawer = (bool: boolean) => { 34 | setIsOpen((prev) => (prev = bool)); 35 | }; 36 | const Router = useNavigate(); 37 | const handleRoute = (route: string) => { 38 | Router(route); 39 | toggleDrawer(false); 40 | }; 41 | return ( 42 | 48 | 55 | toggleDrawer(false)} 59 | onOpen={() => toggleDrawer(true)} 60 | sx={{ 61 | width: "100%", 62 | height: "100%", 63 | }} 64 | > 65 | todo need to work here 66 | 74 | 80 | {hamburgerPaths.map((text, index) => ( 81 | 82 | 83 | {text.Icon} 84 | handleRoute(text.path)} 86 | primary={text.name} 87 | /> 88 | 89 | 90 | ))} 91 | 92 | 93 | 107 | 108 | 109 | 110 | ); 111 | }; 112 | 113 | export default Hamburgur; 114 | 115 | { 116 | /* */ 121 | } 122 | -------------------------------------------------------------------------------- /src/components/SignInPage/index.tsx: -------------------------------------------------------------------------------- 1 | import Typography from "@mui/material/Typography"; 2 | import TextField from "@mui/material/TextField"; 3 | import Link from "@mui/material/Link"; 4 | import Grid from "@mui/material/Grid"; 5 | import FormControlLabel from "@mui/material/FormControlLabel"; 6 | import Container from "@mui/material/Container"; 7 | import Checkbox from "@mui/material/Checkbox"; 8 | import Button from "@mui/material/Button"; 9 | import Box from "@mui/material/Box"; 10 | import Avatar from "@mui/material/Avatar"; 11 | import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; 12 | import React, { memo } from "react"; 13 | import { useDispatch } from "react-redux"; 14 | import { AppDispatch } from "../../Redux/ReduxStore"; 15 | import { 16 | setUserDataReducer, 17 | setUserErrorReducer, 18 | setUserLoadingReducer, 19 | } from "../../Redux/UserSlice/slice"; 20 | import { SignInApiService, apiResponse } from "../../api/apiService"; 21 | import { useNavigate } from "react-router-dom"; 22 | 23 | interface pageProps { 24 | toggle: () => void; 25 | } 26 | 27 | const SignInPage: React.FC = ({ toggle }) => { 28 | const dispatch = useDispatch(); 29 | 30 | const route = useNavigate(); 31 | 32 | const handleSubmit = async (event: React.FormEvent) => { 33 | event.preventDefault(); 34 | const data = new FormData(event.currentTarget); 35 | 36 | if (!data.get("email")) { 37 | alert("Please Enter Email"); 38 | return; 39 | } 40 | if (!data.get("password")) { 41 | alert("Please Enter password"); 42 | return; 43 | } 44 | 45 | dispatch(setUserLoadingReducer()); 46 | const response: apiResponse = await SignInApiService({ 47 | email: data.get("email") as string, 48 | password: data.get("password") as string, 49 | }); 50 | if (response.status) { 51 | dispatch(setUserDataReducer(response)); 52 | route("/"); 53 | } else { 54 | dispatch(setUserErrorReducer(response)); 55 | } 56 | }; 57 | 58 | return ( 59 | 60 | 68 | 69 | 70 | 71 | 72 | Sign in 73 | 74 | 75 | 85 | 95 | } 97 | label="Remember me" 98 | /> 99 | 107 | 108 | 109 | Forgot password? 110 | 111 | 112 | 113 | {"Don't have an account? Sign Up"} 114 | 115 | 116 | 117 | 118 | 119 | 120 | ); 121 | }; 122 | 123 | export default memo(SignInPage); 124 | -------------------------------------------------------------------------------- /src/components/CartAndOrderProduct/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useEffect } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 4 | import { productSliceInitialStateI } from "../../Redux/ProductsSlice/modules/initialState"; 5 | import { GetFilteredDataApiService, apiResponse } from "../../api/apiService"; 6 | import { 7 | Card, 8 | CardContent, 9 | CardMedia, 10 | Typography, 11 | Grid, 12 | Stack, 13 | } from "@mui/material"; 14 | import ProductCartCalculation from "../ProductCartCalculation"; 15 | import { addToProductCacheReducer } from "../../Redux/ProductsSlice/slice"; 16 | import CardOrderDetail from "../CardOrderDetail"; 17 | 18 | interface pageProps { 19 | productCount: number; 20 | productId: string; 21 | productTotal: number; 22 | OrderId?: string; 23 | ExpectedDelivery?: string; 24 | type: "Cart" | "Order"; 25 | } 26 | let isFirst: boolean = true; 27 | const CartAndOrderProduct: React.FC = ({ 28 | type, 29 | productCount, 30 | productId, 31 | ExpectedDelivery, 32 | }) => { 33 | const { productCache } = useSelector( 34 | (store) => store.productSlice 35 | ) as productSliceInitialStateI; 36 | const dispatch = useDispatch(); 37 | 38 | useEffect(() => { 39 | isFirst && 40 | (async function () { 41 | const response: apiResponse = await GetFilteredDataApiService({ 42 | _id: productId, 43 | }); 44 | isFirst = false 45 | dispatch(addToProductCacheReducer(response.data[0])); 46 | })(); 47 | }, []); 48 | 49 | return ( 50 | 51 | 52 | 57 | 66 | 79 | 80 | 81 | 82 | 83 | {productCache[productId]?.name} 84 | 85 | 86 | {productCache[productId]?.description} 87 | 88 | 89 | Category: {productCache[productId]?.category} | Subcategory:{" "} 90 | {productCache[productId]?.subcategory} 91 | 92 | 93 | Brand: {productCache[productId]?.brand} 94 | 95 | 96 | Average Rating: {productCache[productId]?.averageRating} ( 97 | {productCache[productId]?.totalRatings} ratings) 98 | 99 | 100 | {type == "Order" ? ( 101 | 102 | ) : ( 103 | 107 | )} 108 | 109 | 110 | 111 | 112 | ); 113 | }; 114 | 115 | export default memo(CartAndOrderProduct); 116 | -------------------------------------------------------------------------------- /src/components/PaymentForm/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Typography from "@mui/material/Typography"; 3 | import Grid from "@mui/material/Grid"; 4 | import TextField from "@mui/material/TextField"; 5 | import FormControlLabel from "@mui/material/FormControlLabel"; 6 | import Checkbox from "@mui/material/Checkbox"; 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 9 | import { 10 | checkOuSliceInitialStateInterface, 11 | paymentsInterface, 12 | } from "../../Redux/CheckOutSlice/module/intialState"; 13 | import { onChangeCheckoutReducer } from "../../Redux/CheckOutSlice/slice"; 14 | 15 | export default function PaymentForm() { 16 | const { payment } = useSelector( 17 | (store) => store.CheckoutSlice 18 | ) as checkOuSliceInitialStateInterface; 19 | const { ExpiryDate, cardNumber, cvv, cardName } = payment; 20 | 21 | const dispatch = useDispatch(); 22 | 23 | const onChange = ({ 24 | key, 25 | value, 26 | }: { 27 | key: keyof paymentsInterface; 28 | value: string; 29 | }) => { 30 | dispatch( 31 | onChangeCheckoutReducer({ 32 | payment: { 33 | ...payment, 34 | [key]: value, 35 | }, 36 | }) 37 | ); 38 | }; 39 | return ( 40 | 41 | 42 | Payment method 43 | 44 | 45 | 46 | ) => 54 | onChange({ 55 | key: "cardName", 56 | value: event.target.value, 57 | }) 58 | } 59 | defaultValue={cardName} 60 | /> 61 | 62 | 63 | ) => 71 | onChange({ 72 | key: "cardNumber", 73 | value: event.target.value, 74 | }) 75 | } 76 | defaultValue={cardNumber} 77 | /> 78 | 79 | 80 | ) => 88 | onChange({ 89 | key: "ExpiryDate", 90 | value: event.target.value, 91 | }) 92 | } 93 | defaultValue={ExpiryDate} 94 | /> 95 | 96 | 97 | ) => 106 | onChange({ 107 | key: "cvv", 108 | value: event.target.value, 109 | }) 110 | } 111 | defaultValue={cvv} 112 | /> 113 | 114 | 115 | } 117 | label="Remember credit card details for next time" 118 | /> 119 | 120 | 121 | 122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /src/components/Review/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Typography from "@mui/material/Typography"; 3 | import List from "@mui/material/List"; 4 | import ListItem from "@mui/material/ListItem"; 5 | import ListItemText from "@mui/material/ListItemText"; 6 | import Grid from "@mui/material/Grid"; 7 | import { useSelector } from "react-redux"; 8 | import { RootState } from "../../Redux/ReduxStore"; 9 | import { 10 | cartDataInterface, 11 | cartSliceInitialStateInterface, 12 | } from "../../Redux/CartSlice/module/initialState"; 13 | import { 14 | productCardI, 15 | productSliceInitialStateI, 16 | } from "../../Redux/ProductsSlice/modules/initialState"; 17 | import { checkOuSliceInitialStateInterface } from "../../Redux/CheckOutSlice/module/intialState"; 18 | 19 | export default function Review() { 20 | const { address, payment } = useSelector( 21 | (store) => store.CheckoutSlice 22 | ) as checkOuSliceInitialStateInterface; 23 | const { addressline, city, country, firstName, lastName, state, zipcode } = 24 | address; 25 | const { ExpiryDate, cardNumber, cardName } = payment; 26 | 27 | const { cartData } = useSelector( 28 | (store) => store.cartSlice 29 | ) as cartSliceInitialStateInterface; 30 | 31 | const { productCache } = useSelector( 32 | (store) => store.productSlice 33 | ) as productSliceInitialStateI; 34 | 35 | return ( 36 | 37 | 38 | Order summary 39 | 40 | 41 | {cartData.map(({ productId, productCount }: cartDataInterface) => ( 42 | 43 | 50 | 51 | {" "} 52 | {productCount} * {productCache[productId]?.price} 53 | 54 | 55 | ))} 56 | 57 | 58 | 62 | {/* count total ammount */} 63 | {cartData.reduce((_, { productCount, productTotal }) => { 64 | return _ + productCount * productTotal; 65 | }, 0)} 66 | 67 | 68 | 69 | 70 | 71 | 72 | Shipping 73 | 74 | {`${firstName} ${lastName}`} 75 | 76 | {[addressline, city, state, zipcode, country].join(", ")} 77 | 78 | 79 | 80 | 81 | Payment details 82 | 83 | 84 | 85 | Card type 86 | 87 | 88 | Visa 89 | 90 | 91 | 92 | 93 | Card holder 94 | 95 | 96 | {cardName} 97 | 98 | 99 | 100 | 101 | Card number 102 | 103 | 104 | {cardNumber} 105 | 106 | 107 | 108 | 109 | Expiry date 110 | 111 | 112 | {ExpiryDate} 113 | 114 | 115 | 116 | 117 | 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /src/components/SignUpPage/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Avatar, 3 | Box, 4 | Button, 5 | Checkbox, 6 | Container, 7 | FormControlLabel, 8 | Grid, 9 | TextField, 10 | Typography, 11 | Link, 12 | } from "@mui/material"; 13 | import React from "react"; 14 | import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; 15 | import { useDispatch } from "react-redux"; 16 | import { AppDispatch } from "../../Redux/ReduxStore"; 17 | import { 18 | setUserDataReducer, 19 | setUserErrorReducer, 20 | setUserLoadingReducer, 21 | } from "../../Redux/UserSlice/slice"; 22 | import { SignUpApiService, apiResponse } from "../../api/apiService"; 23 | import { useNavigate } from "react-router-dom"; 24 | 25 | interface pageProps { 26 | toggle: () => void; 27 | } 28 | 29 | const SignUpPage: React.FC = ({ toggle }) => { 30 | const dispatch = useDispatch(); 31 | const route = useNavigate(); 32 | 33 | const handleSubmit = async (event: React.FormEvent) => { 34 | event.preventDefault(); 35 | const data = new FormData(event.currentTarget); 36 | 37 | if (!data.get("email")) { 38 | alert("Please Enter Email"); 39 | return; 40 | } 41 | if (!data.get("password")) { 42 | alert("Please Enter password"); 43 | return; 44 | } 45 | if (!data.get("firstName")) { 46 | alert("Please Enter firstName"); 47 | return; 48 | } 49 | if (!data.get("lastName")) { 50 | alert("Please Enter lastName"); 51 | return; 52 | } 53 | 54 | dispatch(setUserLoadingReducer()); 55 | 56 | const response: apiResponse = await SignUpApiService({ 57 | email: data.get("email") as string, 58 | password: data.get("password") as string, 59 | name: `${data.get("firstName") as string} ${ 60 | data.get("lastName") as string 61 | }`, 62 | }); 63 | 64 | if (response.status) { 65 | dispatch(setUserDataReducer(response)); 66 | route("/"); 67 | } else { 68 | dispatch(setUserErrorReducer(response)); 69 | } 70 | }; 71 | 72 | return ( 73 | 74 | 82 | 83 | 84 | 85 | 86 | Sign up 87 | 88 | 89 | 90 | 91 | 100 | 101 | 102 | 110 | 111 | 112 | 120 | 121 | 122 | 131 | 132 | 133 | } 135 | label="I want to receive inspiration, marketing promotions and updates via email." 136 | /> 137 | 138 | 139 | 147 | 148 | 149 | 150 | Already have an account? Sign in 151 | 152 | 153 | 154 | 155 | 156 | 157 | ); 158 | }; 159 | 160 | export default SignUpPage; 161 | -------------------------------------------------------------------------------- /src/components/ProductCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from "react"; 2 | import IconButton from "@mui/material/IconButton"; 3 | import Button from "@mui/material/Button"; 4 | import Typography from "@mui/material/Typography"; 5 | import CardMedia from "@mui/material/CardMedia"; 6 | import CardContent from "@mui/material/CardContent"; 7 | import CardActions from "@mui/material/CardActions"; 8 | import CardActionArea from "@mui/material/CardActionArea"; 9 | import Card from "@mui/material/Card"; 10 | import AddShoppingCartIcon from "@mui/icons-material/AddShoppingCart"; 11 | import RemoveIcon from "@mui/icons-material/Remove"; 12 | import AddIcon from "@mui/icons-material/Add"; 13 | import { productCardI } from "../../Redux/ProductsSlice/modules/initialState"; 14 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 15 | import { useDispatch, useSelector } from "react-redux"; 16 | import { 17 | setCartDataReducer, 18 | setCartErrorReducer, 19 | setCartLoadingReducer, 20 | } from "../../Redux/CartSlice/slice"; 21 | import { AddtoCartApiService, apiResponse } from "../../api/apiService"; 22 | import { userSliceInitialStateInterface } from "../../Redux/UserSlice/module/initialState"; 23 | import { useNavigate } from "react-router-dom"; 24 | 25 | interface componentInterface { 26 | product: productCardI; 27 | productCount: number; 28 | } 29 | 30 | const ProductCard: React.FC = ({ 31 | product, 32 | productCount, 33 | }) => { 34 | const route = useNavigate(); 35 | const { name, price, image, averageRating, _id } = product; 36 | 37 | const { userData } = useSelector( 38 | (store) => store.UserSlice 39 | ) as userSliceInitialStateInterface; 40 | 41 | const dispatch = useDispatch(); 42 | 43 | const handleDecrement = async () => { 44 | if (!userData.token) { 45 | route("/auth"); 46 | return; 47 | } 48 | dispatch(setCartLoadingReducer()); 49 | const response: apiResponse = await AddtoCartApiService({ 50 | payload: { 51 | productCount: productCount - 1, 52 | productId: _id, 53 | productTotal: (productCount + 1) * price, 54 | userId: userData.userId, 55 | }, 56 | headers: { 57 | Authorization: userData.token, 58 | }, 59 | }); 60 | if (response.status) { 61 | dispatch(setCartDataReducer(response)); 62 | } else { 63 | dispatch(setCartErrorReducer(response)); 64 | } 65 | }; 66 | const handleIncrement = async () => { 67 | if (!userData.token) { 68 | route("/auth"); 69 | return; 70 | } 71 | 72 | dispatch(setCartLoadingReducer()); 73 | const response: apiResponse = await AddtoCartApiService({ 74 | payload: { 75 | productCount: productCount + 1, 76 | productId: _id, 77 | productTotal: (productCount + 1) * price, 78 | userId: userData.userId, 79 | }, 80 | headers: { 81 | Authorization: userData.token, 82 | }, 83 | }); 84 | if (response.status) { 85 | dispatch(setCartDataReducer(response)); 86 | } else { 87 | dispatch(setCartErrorReducer(response)); 88 | } 89 | }; 90 | 91 | return ( 92 | 100 | 101 | 114 | 115 | 116 | {name} 117 | 118 | Price: ${price.toFixed(2)} 119 | 120 | Average Rating: {averageRating} 121 | 122 | 123 | 124 | 125 | {productCount > 0 ? ( 126 | <> 127 | 128 | 129 | 130 | {productCount} 131 | 132 | 133 | 134 | 135 | ) : ( 136 | 144 | )} 145 | 146 | 147 | ); 148 | }; 149 | 150 | export default memo(ProductCard); 151 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { lazy, Suspense, useEffect } from "react"; 2 | import "./app.css"; 3 | import Stack from "@mui/material/Stack"; 4 | import { Outlet, createHashRouter } from "react-router-dom"; 5 | 6 | const HomePage = lazy(() => import("./View/Home")); 7 | const Authentication = lazy(() => import("./View/Auth")); 8 | const CartAndCheckout = lazy(() => import("./View/CartAndCheckOut")); 9 | const PrivateRoute = lazy(() => import("./HOC")); 10 | const Navbar = lazy(() => import("./components/Navbar")); 11 | 12 | import { useDispatch, useSelector } from "react-redux"; 13 | import { AppDispatch, RootState } from "./Redux/ReduxStore"; 14 | import { filterSliceInitialStateI } from "./Redux/FilterSlice/modules/initialState"; 15 | import { 16 | setProductDataReducer, 17 | setProductErrorReducer, 18 | setProductLoadingReducer, 19 | } from "./Redux/ProductsSlice/slice"; 20 | import { 21 | GetCartDataApiService, 22 | GetFilteredDataApiService, 23 | apiResponse, 24 | getOrdersDataApiService, 25 | } from "./api/apiService"; 26 | import { 27 | setCartDataReducer, 28 | setCartErrorReducer, 29 | setCartLoadingReducer, 30 | } from "./Redux/CartSlice/slice"; 31 | import { 32 | setOrderErrorReducer, 33 | setOrderLoadingReducer, 34 | setOrdertDataReducer, 35 | } from "./Redux/OrderSlice/slice"; 36 | import { userSliceInitialStateInterface } from "./Redux/UserSlice/module/initialState"; 37 | 38 | let isfirst: boolean = true; 39 | 40 | const App: React.FC = () => { 41 | const dispatch = useDispatch(); 42 | const { userData } = useSelector( 43 | (store) => store.UserSlice 44 | ) as userSliceInitialStateInterface; 45 | 46 | const { 47 | averageRating, 48 | avgtype, 49 | brand, 50 | category, 51 | maxPrice, 52 | minPrice, 53 | name, 54 | querry, 55 | subcategory, 56 | } = useSelector( 57 | (store) => store.filterSlice 58 | ) as filterSliceInitialStateI; 59 | 60 | useEffect(() => { 61 | (async function () { 62 | dispatch(setProductLoadingReducer()); 63 | 64 | const payload = isfirst 65 | ? undefined 66 | : { 67 | averageRating, 68 | avgtype, 69 | brand, 70 | category, 71 | maxPrice, 72 | minPrice, 73 | name, 74 | querry, 75 | subcategory, 76 | }; 77 | 78 | isfirst = false; 79 | const response: apiResponse = await GetFilteredDataApiService(payload); 80 | if (response.status) { 81 | dispatch(setProductDataReducer(response)); 82 | } else { 83 | dispatch(setProductErrorReducer(response)); 84 | } 85 | })(); 86 | }, [ 87 | averageRating, 88 | avgtype, 89 | brand, 90 | category, 91 | maxPrice, 92 | minPrice, 93 | name, 94 | querry, 95 | subcategory, 96 | ]); 97 | 98 | useEffect(() => { 99 | userData.token && 100 | (async function () { 101 | dispatch(setCartLoadingReducer()); 102 | const response: apiResponse = await GetCartDataApiService({ 103 | userId: userData.userId, 104 | headers: { 105 | Authorization: userData.token, 106 | }, 107 | }); 108 | if (response.status) { 109 | dispatch(setCartDataReducer(response)); 110 | } else { 111 | dispatch(setCartErrorReducer(response)); 112 | } 113 | })(); 114 | }, []); 115 | 116 | useEffect(() => { 117 | // getOrder 118 | userData.token && 119 | (async function () { 120 | dispatch(setOrderLoadingReducer()); 121 | 122 | const response: apiResponse = await getOrdersDataApiService({ 123 | userId: userData.userId, 124 | headers: { 125 | Authorization: userData.token, 126 | }, 127 | }); 128 | 129 | if (response.status) { 130 | dispatch(setOrdertDataReducer(response)); 131 | } else { 132 | dispatch(setOrderErrorReducer(response)); 133 | } 134 | })(); 135 | }, []); 136 | 137 | return ( 138 | 144 | 145 | 146 | 147 | ); 148 | }; 149 | 150 | const AppRoute = createHashRouter([ 151 | { 152 | path: "/", 153 | element: , 154 | children: [ 155 | { 156 | path: "/", 157 | element: ( 158 | 159 | {" "} 160 | 161 | 162 | ), 163 | }, 164 | 165 | { 166 | path: "/cart", 167 | element: ( 168 | 169 | 170 | {" "} 171 | 172 | 173 | 174 | ), 175 | }, 176 | ], 177 | }, 178 | { 179 | path: "/auth", 180 | element: ( 181 | 182 | {" "} 183 | 184 | 185 | ), 186 | }, 187 | ]); 188 | 189 | export default AppRoute; 190 | -------------------------------------------------------------------------------- /src/components/CheckoutData/index.tsx: -------------------------------------------------------------------------------- 1 | import Typography from "@mui/material/Typography" 2 | import Stepper from "@mui/material/Stepper" 3 | import StepLabel from "@mui/material/StepLabel" 4 | import Step from "@mui/material/Step" 5 | import Stack from "@mui/material/Stack" 6 | import Button from "@mui/material/Button" 7 | import Box from "@mui/material/Box" 8 | 9 | import React, { useState } from "react"; 10 | import AddressForm from "../AddressForm"; 11 | import PaymentForm from "../PaymentForm"; 12 | import Review from "../Review"; 13 | import { cartSliceInitialStateInterface } from "../../Redux/CartSlice/module/initialState"; 14 | import { useDispatch, useSelector } from "react-redux"; 15 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 16 | import { OrderedProductInterface } from "../../Redux/OrderSlice/module/initialState"; 17 | import { 18 | setOrderErrorReducer, 19 | setOrderLoadingReducer, 20 | setOrdertDataReducer, 21 | } from "../../Redux/OrderSlice/slice"; 22 | import { 23 | ClearCartDataApiService, 24 | OrderProductApiService, 25 | apiResponse, 26 | } from "../../api/apiService"; 27 | import { 28 | setCartDataReducer, 29 | setCartErrorReducer, 30 | setCartLoadingReducer, 31 | } from "../../Redux/CartSlice/slice"; 32 | import { steps } from "../../utils/constants"; 33 | import { userSliceInitialStateInterface } from "../../Redux/UserSlice/module/initialState"; 34 | 35 | function getStepContent(step: number) { 36 | switch (step) { 37 | case 0: 38 | return ; 39 | case 1: 40 | return ; 41 | case 2: 42 | return ; 43 | default: 44 | throw new Error("Unknown step"); 45 | } 46 | } 47 | 48 | const CheckoutData: React.FC = () => { 49 | const [activeStep, setActiveStep] = useState(0); 50 | const [orderID, setorderID] = useState(""); 51 | 52 | const { cartData } = useSelector( 53 | (store) => store.cartSlice 54 | ) as cartSliceInitialStateInterface; 55 | 56 | const { userData } = useSelector( 57 | (store) => store.UserSlice 58 | ) as userSliceInitialStateInterface; 59 | 60 | const dispatch = useDispatch(); 61 | 62 | const handleCartOrder = async () => { 63 | const OrderId = `ORD_${Date.now()}`; 64 | setorderID(orderID); 65 | const ExpectedDelivery = `${Date.now() + 48 * 60 * 60 * 1000}`; 66 | 67 | const ProductDetails: OrderedProductInterface[] = cartData.map((cart) => ({ 68 | ...cart, 69 | ExpectedDelivery, 70 | OrderId, 71 | })); 72 | 73 | dispatch(setOrderLoadingReducer()); 74 | const response: apiResponse = await OrderProductApiService({ 75 | payload: { 76 | userId: userData.userId, 77 | requestOrders: ProductDetails, 78 | }, 79 | headers: { 80 | Authorization: userData.token, 81 | }, 82 | }); 83 | if (response.status) { 84 | dispatch(setOrdertDataReducer(response)); 85 | } else { 86 | dispatch(setOrderErrorReducer(response)); 87 | } 88 | // clear cart data as item purchaged 89 | dispatch(setCartLoadingReducer()); 90 | const clearCartResponse: apiResponse = await ClearCartDataApiService({ 91 | userId: userData.userId, 92 | headers: { 93 | Authorization: userData.token, 94 | }, 95 | }); 96 | 97 | if (clearCartResponse.status) { 98 | dispatch(setCartDataReducer(clearCartResponse)); 99 | } else { 100 | dispatch(setCartErrorReducer(clearCartResponse)); 101 | } 102 | }; 103 | const handleNext = async () => { 104 | if (activeStep === steps.length - 1) { 105 | await handleCartOrder(); 106 | } 107 | setActiveStep(activeStep + 1); 108 | }; 109 | 110 | const handleBack = () => { 111 | setActiveStep(activeStep - 1); 112 | }; 113 | 114 | return ( 115 | 121 | 122 | Checkout 123 | 124 | 125 | {steps.map((label) => ( 126 | 127 | {label} 128 | 129 | ))} 130 | 131 | 132 | {/* steps */} 133 | 134 | {activeStep === steps.length ? ( 135 | 136 | 137 | Thank you for your order. 138 | 139 | 140 | Your order number is {orderID}. We have emailed your order 141 | confirmation, and will send you an update when your order has 142 | shipped. 143 | 144 | 145 | ) : ( 146 | 147 | {getStepContent(activeStep)} 148 | 149 | {activeStep !== 0 && ( 150 | 153 | )} 154 | 161 | 162 | 163 | )} 164 | 165 | ); 166 | }; 167 | 168 | export default CheckoutData; 169 | -------------------------------------------------------------------------------- /src/api/apiService.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosHeaders } from "axios"; 2 | import { EnvProvider } from "../utils/EnvProvider"; 3 | import { filterSliceInitialStateI } from "../Redux/FilterSlice/modules/initialState"; 4 | import { 5 | cartDataInterface, 6 | cartSliceInitialStateInterface, 7 | } from "../Redux/CartSlice/module/initialState"; 8 | import { OrderedProductInterface } from "../Redux/OrderSlice/module/initialState"; 9 | 10 | const AxiosInstance = axios.create({ 11 | baseURL: EnvProvider().SERVER_BASE_URL, 12 | headers: { 13 | "Content-Type": "application/json", 14 | }, 15 | }); 16 | 17 | export interface apiResponse { 18 | statusCode: number; 19 | message: string; 20 | status: boolean; 21 | data?: any; 22 | } 23 | 24 | export const GetFilteredDataApiService = async ( 25 | payload: undefined | filterSliceInitialStateI 26 | ) => { 27 | let requestBody = {}; 28 | if (payload !== undefined) requestBody = payload; 29 | try { 30 | const response: apiResponse = await AxiosInstance.post( 31 | "/products/filter", 32 | requestBody 33 | ); 34 | 35 | if (!response.status) { 36 | throw response; 37 | } 38 | return response.data; 39 | } catch (err) { 40 | return (err as AxiosError).response?.data as apiResponse; 41 | } 42 | }; 43 | // payload: cartDataInterface 44 | export const AddtoCartApiService = async (payload: { 45 | payload: cartDataInterface; 46 | headers: { 47 | Authorization: string; 48 | }; 49 | }) => { 50 | let requestBody = {}; 51 | if (payload !== undefined) requestBody = payload.payload; 52 | try { 53 | const response: apiResponse = await AxiosInstance.post( 54 | "/cart/add", 55 | requestBody, 56 | { 57 | headers: { 58 | ...payload.headers, 59 | }, 60 | } 61 | ); 62 | 63 | if (!response.status) { 64 | throw response; 65 | } 66 | return response.data; 67 | } catch (err) { 68 | return (err as AxiosError).response?.data as apiResponse; 69 | } 70 | }; 71 | export const ClearCartDataApiService = async (payload: { 72 | userId: string; 73 | headers: { 74 | Authorization: string; 75 | }; 76 | }) => { 77 | try { 78 | const response: apiResponse = await AxiosInstance.post( 79 | "/cart/clear", 80 | payload, 81 | { 82 | headers: { 83 | ...payload.headers, 84 | }, 85 | } 86 | ); 87 | 88 | if (!response.status) { 89 | throw response; 90 | } 91 | return response.data; 92 | } catch (err) { 93 | return (err as AxiosError).response?.data as apiResponse; 94 | } 95 | }; 96 | export const GetCartDataApiService = async (payload: { 97 | userId: string; 98 | headers: { 99 | Authorization: string; 100 | }; 101 | }) => { 102 | try { 103 | const response: apiResponse = await AxiosInstance.post( 104 | "/cart/getCartData", 105 | payload, 106 | { 107 | headers: { 108 | ...payload.headers, 109 | }, 110 | } 111 | ); 112 | if (!response.status) { 113 | throw response; 114 | } 115 | return response.data; 116 | } catch (err) { 117 | return (err as AxiosError).response?.data as apiResponse; 118 | } 119 | }; 120 | 121 | export const OrderProductApiService = async ( 122 | // orderData: OrderedProductInterface[] 123 | 124 | { 125 | payload, 126 | headers, 127 | }: { 128 | payload: { 129 | userId: string; 130 | requestOrders: OrderedProductInterface[]; 131 | }; 132 | headers: { 133 | Authorization: string; 134 | }; 135 | } 136 | ) => { 137 | try { 138 | const response: apiResponse = await AxiosInstance.post( 139 | "/order/placeOrder", 140 | payload, 141 | { 142 | headers: { 143 | ...headers, 144 | }, 145 | } 146 | ); 147 | if (!response.status) { 148 | throw response; 149 | } 150 | return response.data; 151 | } catch (err) { 152 | return (err as AxiosError).response?.data as apiResponse; 153 | } 154 | }; 155 | export const getOrdersDataApiService = async (payload: { 156 | userId: string; 157 | headers: { 158 | Authorization: string; 159 | }; 160 | }) => { 161 | try { 162 | const response: apiResponse = await AxiosInstance.post( 163 | "/order/getOrder", 164 | payload, 165 | { 166 | headers: { 167 | ...payload.headers, 168 | }, 169 | } 170 | ); 171 | if (!response.status) { 172 | throw response; 173 | } 174 | return response.data; 175 | } catch (err) { 176 | return (err as AxiosError).response?.data as apiResponse; 177 | } 178 | }; 179 | export const SignInApiService = async (requestBody: { 180 | email: string; 181 | password: string; 182 | }) => { 183 | try { 184 | const response: apiResponse = await AxiosInstance.post( 185 | "/user/login", 186 | requestBody 187 | ); 188 | if (!response.status) { 189 | throw response; 190 | } 191 | return response.data; 192 | } catch (err) { 193 | return (err as AxiosError).response?.data as apiResponse; 194 | } 195 | }; 196 | export const SignUpApiService = async (requestBody: { 197 | email: string; 198 | password: string; 199 | name: string; 200 | }) => { 201 | try { 202 | const response: apiResponse = await AxiosInstance.post( 203 | "/user/signup", 204 | requestBody 205 | ); 206 | if (!response.status) { 207 | throw response; 208 | } 209 | return response.data; 210 | } catch (err) { 211 | return (err as AxiosError).response?.data as apiResponse; 212 | } 213 | }; 214 | -------------------------------------------------------------------------------- /src/components/AddressForm/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Grid from "@mui/material/Grid"; 3 | import Typography from "@mui/material/Typography"; 4 | import TextField from "@mui/material/TextField"; 5 | import FormControlLabel from "@mui/material/FormControlLabel"; 6 | import Checkbox from "@mui/material/Checkbox"; 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { AppDispatch, RootState } from "../../Redux/ReduxStore"; 9 | import { 10 | AddressInterface, 11 | checkOuSliceInitialStateInterface, 12 | } from "../../Redux/CheckOutSlice/module/intialState"; 13 | import { onChangeCheckoutReducer } from "../../Redux/CheckOutSlice/slice"; 14 | 15 | export default function AddressForm() { 16 | const { address } = useSelector( 17 | (store) => store.CheckoutSlice 18 | ) as checkOuSliceInitialStateInterface; 19 | 20 | const dispatch = useDispatch(); 21 | const { firstName, addressline, city, country, lastName, state, zipcode } = 22 | address; 23 | 24 | const onChange = ({ 25 | key, 26 | value, 27 | }: { 28 | key: keyof AddressInterface; 29 | value: string; 30 | }) => { 31 | dispatch( 32 | onChangeCheckoutReducer({ 33 | address: { 34 | ...address, 35 | [key]: value, 36 | }, 37 | }) 38 | ); 39 | }; 40 | return ( 41 | 42 | 43 | Shipping address 44 | 45 | 46 | 47 | ) => 57 | onChange({ 58 | key: "firstName", 59 | value: event.target.value, 60 | }) 61 | } 62 | /> 63 | 64 | 65 | ) => 75 | onChange({ 76 | key: "lastName", 77 | value: event.target.value, 78 | }) 79 | } 80 | /> 81 | 82 | 83 | ) => 93 | onChange({ 94 | key: "addressline", 95 | value: event.target.value, 96 | }) 97 | } 98 | /> 99 | 100 | 101 | ) => 111 | onChange({ 112 | key: "city", 113 | value: event.target.value, 114 | }) 115 | } 116 | /> 117 | 118 | 119 | ) => 127 | onChange({ 128 | key: "state", 129 | value: event.target.value, 130 | }) 131 | } 132 | /> 133 | 134 | 135 | ) => 145 | onChange({ 146 | key: "zipcode", 147 | value: event.target.value, 148 | }) 149 | } 150 | /> 151 | 152 | 153 | ) => 163 | onChange({ 164 | key: "country", 165 | value: event.target.value, 166 | }) 167 | } 168 | /> 169 | 170 | 171 | 174 | } 175 | label="Use this address for payment details" 176 | /> 177 | 178 | 179 | 180 | ); 181 | } 182 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | "jsx": "react-jsx", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "commonjs", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | }, 101 | "include": ["src"], 102 | "exclude": ["node_modules"] 103 | } -------------------------------------------------------------------------------- /src/utils/CatSubcatBrands.ts: -------------------------------------------------------------------------------- 1 | // CATEGORY'S 2 | export const ELECTRONICS = "Electronics"; 3 | export const GROCERY = "Grocery"; 4 | export const CLOTHING_AND_APPAREL = "Clothing and Apparel"; 5 | export const HOME_AND_KITCHEN = "Home and Kitchen"; 6 | export const HEALTH_AND_BEAUTY = "Health and Beauty"; 7 | export const SPORTS_AND_OUTDOORS = "Sports and Outdoors"; 8 | export const BOOKS_AND_MEDIA = "Books and Media"; 9 | export const TOYS_AND_GAMES = "Toys and Games"; 10 | 11 | // SUBCATEGORY'S 12 | export const MOBILES = "Mobiles"; 13 | export const LAPTOPS = "Laptops"; 14 | export const TABLETS = "Tablets"; 15 | export const ACCESSORIES = "Accessories"; 16 | export const FRUITS_AND_VEGETABLES = "Fruits & Vegetables"; 17 | export const SNACKS = "Snacks"; 18 | export const BEVERAGES = "Beverages"; 19 | export const CANNED_GOODS = "Canned Goods"; 20 | export const MENS_CLOTHING = "Men's Clothing"; 21 | export const WOMENS_CLOTHING = "Women's Clothing"; 22 | export const KIDS_CLOTHING = "Kid's Clothing"; 23 | export const FURNITURE = "Furniture"; 24 | export const COOKWARE = "Cookware"; 25 | export const HOME_DECOR = "Home Decor"; 26 | export const APPLIANCES = "Appliances"; 27 | export const SKINCARE = "Skincare"; 28 | export const HAIRCARE = "Haircare"; 29 | export const MAKEUP = "Makeup"; 30 | export const PERSONAL_CARE = "Personal Care"; 31 | export const FITNESS_EQUIPMENT = "Fitness Equipment"; 32 | export const OUTDOOR_GEAR = "Outdoor Gear"; 33 | export const ATHLETIC_CLOTHING = "Athletic Clothing"; 34 | export const CAMPING_GEAR = "Camping Gear"; 35 | export const ACTION_FIGURES = "Action Figures"; 36 | export const BOARD_GAMES = "Board Games"; 37 | export const OUTDOOR_TOYS = "Outdoor Toys"; 38 | export const FICTION = "Fiction"; 39 | export const NON_FICTION = "Non-Fiction"; 40 | export const CHILDRENS_BOOKS = "Children's Books"; 41 | 42 | // BRAND'S 43 | // Mobiles 44 | export const APPLE = "Apple"; 45 | export const SAMSUNG = "Samsung"; 46 | export const XIAOMI = "Xiaomi"; 47 | export const HUAWEI = "Huawei"; 48 | export const ONEPLUS = "OnePlus"; 49 | 50 | // Laptops 51 | export const HP = "HP"; 52 | export const DELL = "Dell"; 53 | export const LENOVO = "Lenovo"; 54 | // export const APPLE = "Apple"; 55 | export const ASUS = "Asus"; 56 | 57 | // Tablets 58 | 59 | // export const APPLE = "Apple"; 60 | // export const SAMSUNG = "Samsung"; 61 | export const MICROSOFT = "Microsoft"; 62 | // export const HUAWEI = "Huawei"; 63 | 64 | // Accessories 65 | export const LOGITECH = "Logitech"; 66 | export const JBL = "JBL"; 67 | export const BELKIN = "Belkin"; 68 | export const ANKER = "Anker"; 69 | export const SONY = "Sony"; 70 | 71 | export const Fruits_Vegetables = "Fruits & Vegetables"; 72 | // Snacks 73 | export const LAYS = "Lay's"; 74 | export const DORITOS = "Doritos"; 75 | export const PRINGLES = "Pringles"; 76 | export const HERSHEY = "Hershey's"; 77 | export const PLANTERS = "Planters"; 78 | 79 | // Beverages 80 | export const COCA_COLA = "Coca-Cola"; 81 | export const PEPSI = "Pepsi"; 82 | export const STARBUCKS = "Starbucks"; 83 | export const NESTLE = "Nestlé"; 84 | export const RED_BULL = "Red Bull"; 85 | 86 | // Canned Goods 87 | export const CAMPBELL = "Campbell's"; 88 | export const DEL_MONTE = "Del Monte"; 89 | export const HEINZ = "Heinz"; 90 | export const LIBBYS = "Libby's"; 91 | export const PROGRESSO = "Progresso"; 92 | 93 | // Men's Clothing 94 | export const NIKE = "Nike"; 95 | export const ADIDAS = "Adidas"; 96 | export const LEVI = "Levi's"; 97 | export const RALPH_LAUREN = "Ralph Lauren"; 98 | export const TOMMY_HILFIGER = "Tommy Hilfiger"; 99 | 100 | // Women's Clothing 101 | export const ZARA = "Zara"; 102 | export const FOREVER_21 = "Forever 21"; 103 | export const GAP = "Gap"; 104 | 105 | // Kid's Clothing 106 | export const CARTERS = "Carter's"; 107 | export const GAP_KIDS = "Gap Kids"; 108 | export const OLD_NAVY_KIDS = "Old Navy Kids"; 109 | export const HM_KIDS = "H&M Kids"; 110 | export const GYMBOREE = "Gymboree"; 111 | 112 | // Furniture 113 | export const IKEA = "IKEA"; 114 | export const ASHLEY_FURNITURE = "Ashley Furniture"; 115 | export const WAYFAIR = "Wayfair"; 116 | export const CRATE_AND_BARREL = "Crate and Barrel"; 117 | export const POTTERY_BARN = "Pottery Barn"; 118 | 119 | // Cookware 120 | export const CALPHALON = "Calphalon"; 121 | export const CUISINART = "Cuisinart"; 122 | export const ALL_CLAD = "All-Clad"; 123 | export const LE_CREUSET = "Le Creuset"; 124 | export const LODGE = "Lodge"; 125 | 126 | // Home Decor 127 | export const WEST_ELM = "West Elm"; 128 | // export const CRATE_AND_BARREL = "Crate and Barrel"; 129 | // export const POTTERY_BARN = "Pottery Barn"; 130 | export const ANTHROPOLOGIE = "Anthropologie"; 131 | export const HOME_GOODS = "HomeGoods"; 132 | 133 | // Appliances 134 | export const LG = "LG Appliances"; 135 | export const WHIRLPOOL = "Whirlpool"; 136 | export const KITCHEN_AID = "KitchenAid"; 137 | export const GE = "GE Appliances"; 138 | 139 | // Skincare 140 | export const CETAPHIL = "Cetaphil"; 141 | export const NEUTROGENA = "Neutrogena"; 142 | export const OLAY = "Olay"; 143 | export const LA_ROCHE_POSAY = "La Roche-Posay"; 144 | export const AVEENO = "Aveeno"; 145 | 146 | // Haircare 147 | export const PANTENE = "Pantene"; 148 | export const HEAD_AND_SHOULDERS = "Head & Shoulders"; 149 | export const LOREAL = "L'Oréal"; 150 | export const GARNIER = "Garnier"; 151 | export const TRESEMME = "Tresemme"; 152 | 153 | // Makeup 154 | export const MAYBELLINE = "Maybelline New York"; 155 | export const MAC = "MAC Cosmetics"; 156 | export const COVER_GIRL = "CoverGirl"; 157 | export const REVLON = "Revlon"; 158 | 159 | // Personal Care 160 | export const DOVE = "Dove"; 161 | export const COLGATE = "Colgate"; 162 | export const NIVEA = "Nivea"; 163 | export const GILLETTE = "Gillette"; 164 | export const JOHNSON_AND_JOHNSON = "Johnson & Johnson"; 165 | 166 | // Fitness Equipment 167 | export const NORDIC_TRACK = "NordicTrack"; 168 | export const BOWFLEX = "Bowflex"; 169 | export const PELOTON = "Peloton"; 170 | export const LIFE_FITNESS = "Life Fitness"; 171 | export const SCHWINN = "Schwinn"; 172 | 173 | // Outdoor Gear 174 | export const THE_NORTH_FACE = "The North Face"; 175 | export const PATAGONIA = "Patagonia"; 176 | export const COLUMBIA = "Columbia"; 177 | export const REI = "REI"; 178 | export const MARMOT = "Marmot"; 179 | 180 | // Athletic Clothing 181 | export const UNDER_ARMOUR = "Under Armour"; 182 | export const LULULEMON = "Lululemon"; 183 | export const PUMA = "Puma"; 184 | 185 | // Camping Gear 186 | export const COLEMAN = "Coleman"; 187 | export const KELTY = "Kelty"; 188 | 189 | // Action Figures 190 | export const HASBRO = "Hasbro"; 191 | export const MATTEL = "Mattel"; 192 | export const FUNKO = "Funko"; 193 | export const MCFARLANE_TOYS = "McFarlane Toys"; 194 | export const BANDAI = "Bandai"; 195 | 196 | // Board Games 197 | export const RAVENSBURGER = "Ravensburger"; 198 | export const ASMODEE = "Asmodee"; 199 | export const CATAN_STUDIO = "Catan Studio"; 200 | 201 | // Outdoor Toys 202 | export const LITTLE_TIKES = "Little Tikes"; 203 | export const STEP_2 = "Step2"; 204 | export const RADIO_FLYER = "Radio Flyer"; 205 | export const NERF = "Nerf"; 206 | export const BUNCH_O_BALLOONS = "Bunch O Balloons"; 207 | 208 | // Fiction 209 | export const PENGUIN_RANDOM_HOUSE = "Penguin Random House"; 210 | export const HARPER_COLLINS = "HarperCollins"; 211 | export const SIMON_AND_SCHUSTER = "Simon & Schuster"; 212 | export const HACHETTE_BOOK_GROUP = "Hachette Book Group"; 213 | export const BARNES_AND_NOBLE = "Barnes & Noble"; 214 | 215 | // Non-Fiction 216 | // export const PENGUIN_RANDOM_HOUSE = "Penguin Random House"; 217 | // export const HARPER_COLLINS = "HarperCollins"; 218 | // export const SIMON_AND_SCHUSTER = "Simon & Schuster"; 219 | // export const HACHETTE_BOOK_GROUP = "Hachette Book Group"; 220 | export const OXFORD_UNIVERSITY_PRESS = "Oxford University Press"; 221 | 222 | // Children's Books 223 | export const SCHOLASTIC = "Scholastic"; 224 | export const PENGUIN_RANDOM_HOUSE_CHILDRENS = "Penguin Random House Children's"; 225 | export const HARPER_COLLINS_CHILDRENS_BOOKS = "HarperCollins Children's Books"; 226 | export const SIMON_AND_SCHUSTER_CHILDRENS_PUBLISHING = 227 | "Simon & Schuster Children's Publishing"; 228 | export const CANDLEWICK_PRESS = "Candlewick Press"; 229 | 230 | interface CategoryListsI { 231 | name: string; 232 | subCategories: { 233 | name: string; 234 | brands: string[]; 235 | }[]; 236 | } 237 | 238 | // category and subcategory 239 | export const CategoryLists: CategoryListsI[] = [ 240 | { 241 | name: ELECTRONICS, 242 | subCategories: [ 243 | { 244 | name: MOBILES, 245 | brands: [APPLE, SAMSUNG, XIAOMI, HUAWEI, ONEPLUS], 246 | }, 247 | { 248 | name: LAPTOPS, 249 | brands: [HP, DELL, LENOVO, APPLE, ASUS], 250 | }, 251 | { 252 | name: TABLETS, 253 | brands: [APPLE, SAMSUNG, MICROSOFT, HUAWEI], 254 | }, 255 | { 256 | name: ACCESSORIES, 257 | brands: [LOGITECH, JBL, BELKIN, ANKER, SONY], 258 | }, 259 | ], 260 | }, 261 | { 262 | name: GROCERY, 263 | subCategories: [ 264 | { 265 | name: FRUITS_AND_VEGETABLES, 266 | brands: [Fruits_Vegetables], 267 | }, 268 | { 269 | name: SNACKS, 270 | brands: [LAYS, DORITOS, PRINGLES, HERSHEY, PLANTERS], 271 | }, 272 | { 273 | name: BEVERAGES, 274 | brands: [COCA_COLA, PEPSI, STARBUCKS, NESTLE, RED_BULL], 275 | }, 276 | { 277 | name: CANNED_GOODS, 278 | brands: [CAMPBELL, DEL_MONTE, HEINZ, LIBBYS, PROGRESSO], 279 | }, 280 | ], 281 | }, 282 | { 283 | name: CLOTHING_AND_APPAREL, 284 | subCategories: [ 285 | { 286 | name: MENS_CLOTHING, 287 | brands: [NIKE, ADIDAS, LEVI, RALPH_LAUREN, TOMMY_HILFIGER], 288 | }, 289 | { 290 | name: WOMENS_CLOTHING, 291 | brands: [ZARA, FOREVER_21, GAP, NIKE], 292 | }, 293 | { 294 | name: KIDS_CLOTHING, 295 | brands: [CARTERS, GAP_KIDS, OLD_NAVY_KIDS, GYMBOREE], 296 | }, 297 | ], 298 | }, 299 | { 300 | name: HOME_AND_KITCHEN, 301 | subCategories: [ 302 | { 303 | name: FURNITURE, 304 | brands: [ 305 | IKEA, 306 | ASHLEY_FURNITURE, 307 | WAYFAIR, 308 | CRATE_AND_BARREL, 309 | POTTERY_BARN, 310 | ], 311 | }, 312 | { 313 | name: COOKWARE, 314 | brands: [CALPHALON, CUISINART, ALL_CLAD, LODGE], 315 | }, 316 | { 317 | name: HOME_DECOR, 318 | brands: [ 319 | WEST_ELM, 320 | CRATE_AND_BARREL, 321 | POTTERY_BARN, 322 | ANTHROPOLOGIE, 323 | HOME_GOODS, 324 | ], 325 | }, 326 | { 327 | name: APPLIANCES, 328 | brands: [LG, WHIRLPOOL, KITCHEN_AID, GE], 329 | }, 330 | ], 331 | }, 332 | { 333 | name: HEALTH_AND_BEAUTY, 334 | subCategories: [ 335 | { 336 | name: SKINCARE, 337 | brands: [CETAPHIL, NEUTROGENA, OLAY, LA_ROCHE_POSAY, AVEENO], 338 | }, 339 | { 340 | name: HAIRCARE, 341 | brands: [PANTENE, HEAD_AND_SHOULDERS, LOREAL, GARNIER, TRESEMME], 342 | }, 343 | { 344 | name: MAKEUP, 345 | brands: [MAYBELLINE, MAC, COVER_GIRL, REVLON], 346 | }, 347 | { 348 | name: PERSONAL_CARE, 349 | brands: [DOVE, COLGATE, NIVEA, GILLETTE, JOHNSON_AND_JOHNSON], 350 | }, 351 | ], 352 | }, 353 | { 354 | name: SPORTS_AND_OUTDOORS, 355 | subCategories: [ 356 | { 357 | name: FITNESS_EQUIPMENT, 358 | brands: [NORDIC_TRACK, BOWFLEX, PELOTON, LIFE_FITNESS, SCHWINN], 359 | }, 360 | { 361 | name: OUTDOOR_GEAR, 362 | brands: [THE_NORTH_FACE, PATAGONIA, COLUMBIA, REI, MARMOT], 363 | }, 364 | { 365 | name: ATHLETIC_CLOTHING, 366 | brands: [UNDER_ARMOUR, LULULEMON, PUMA], 367 | }, 368 | { 369 | name: CAMPING_GEAR, 370 | brands: [COLEMAN, KELTY], 371 | }, 372 | ], 373 | }, 374 | { 375 | name: TOYS_AND_GAMES, 376 | subCategories: [ 377 | { 378 | name: ACTION_FIGURES, 379 | brands: [HASBRO, MATTEL, FUNKO, MCFARLANE_TOYS, BANDAI], 380 | }, 381 | { 382 | name: BOARD_GAMES, 383 | brands: [RAVENSBURGER, ASMODEE, CATAN_STUDIO], 384 | }, 385 | { 386 | name: OUTDOOR_TOYS, 387 | brands: [LITTLE_TIKES, STEP_2, RADIO_FLYER, NERF, BUNCH_O_BALLOONS], 388 | }, 389 | ], 390 | }, 391 | { 392 | name: BOOKS_AND_MEDIA, 393 | subCategories: [ 394 | { 395 | name: FICTION, 396 | brands: [ 397 | PENGUIN_RANDOM_HOUSE, 398 | HARPER_COLLINS, 399 | SIMON_AND_SCHUSTER, 400 | HACHETTE_BOOK_GROUP, 401 | BARNES_AND_NOBLE, 402 | ], 403 | }, 404 | { 405 | name: NON_FICTION, 406 | brands: [ 407 | PENGUIN_RANDOM_HOUSE, 408 | HARPER_COLLINS, 409 | SIMON_AND_SCHUSTER, 410 | HACHETTE_BOOK_GROUP, 411 | OXFORD_UNIVERSITY_PRESS, 412 | ], 413 | }, 414 | { 415 | name: CHILDRENS_BOOKS, 416 | brands: [ 417 | SCHOLASTIC, 418 | PENGUIN_RANDOM_HOUSE_CHILDRENS, 419 | HARPER_COLLINS_CHILDRENS_BOOKS, 420 | SIMON_AND_SCHUSTER_CHILDRENS_PUBLISHING, 421 | CANDLEWICK_PRESS, 422 | ], 423 | }, 424 | ], 425 | }, 426 | ]; 427 | --------------------------------------------------------------------------------