├── public
├── F-modified.png
└── index.html
├── postcss.config.js
├── src
├── utilities
│ └── currencyFormatter.js
├── pages
│ ├── Home.js
│ ├── NotFound.js
│ ├── Products.js
│ └── Cart.js
├── components
│ ├── Footer.js
│ ├── Slide.js
│ ├── Navbar.js
│ ├── Card.js
│ └── Slider.js
├── app
│ └── store.js
├── index.js
├── App.js
├── features
│ └── products
│ │ ├── productSlice.js
│ │ └── cartSlice.js
└── index.css
├── tailwind.config.js
├── .gitignore
├── package.json
└── README.md
/public/F-modified.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muntasir-fahii/tech-alpha-fs/HEAD/public/F-modified.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/src/utilities/currencyFormatter.js:
--------------------------------------------------------------------------------
1 | export const currencyFormatter = (price) => {
2 | if (!price) return;
3 | return price.toLocaleString("en-US", { style: "currency", currency: "USD" });
4 | };
5 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./src/**/*.{js,jsx,ts,tsx}"],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | };
9 |
--------------------------------------------------------------------------------
/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import Slider from "../components/Slider";
2 |
3 | const Home = () => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default Home;
12 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | const Footer = () => {
2 | return (
3 |
4 |
© {new Date().getFullYear()} Tech Alpha, All right reserved
5 |
6 | );
7 | };
8 |
9 | export default Footer;
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/src/app/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 |
3 | import productsReducer, {
4 | productsFetching,
5 | } from "../features/products/productSlice";
6 |
7 | import cartReducer from "../features/products/cartSlice";
8 |
9 | export const store = configureStore({
10 | reducer: {
11 | products: productsReducer,
12 | cart: cartReducer,
13 | },
14 | });
15 |
16 | store.dispatch(productsFetching());
17 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Tech Alpha
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { BrowserRouter } from "react-router-dom";
4 | import App from "./App";
5 | import "./index.css";
6 |
7 | import { store } from "./app/store";
8 | import { Provider } from "react-redux";
9 |
10 | const root = ReactDOM.createRoot(document.getElementById("root"));
11 | root.render(
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 |
--------------------------------------------------------------------------------
/src/pages/NotFound.js:
--------------------------------------------------------------------------------
1 | import { MdRemoveShoppingCart } from "react-icons/md";
2 |
3 | const NotFound = () => {
4 | return (
5 |
6 |
7 | Not found
8 |
9 |
10 | 404
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default NotFound;
19 |
--------------------------------------------------------------------------------
/src/pages/Products.js:
--------------------------------------------------------------------------------
1 | import { useSelector } from "react-redux";
2 | import Card from "../components/Card";
3 |
4 | const Products = () => {
5 | const { items: data, status } = useSelector((state) => state.products);
6 | return (
7 |
8 |
9 | Browse all Products
10 |
11 |
12 | {status &&
{status}
}
13 |
14 | {data.map((product) => (
15 |
16 | ))}
17 |
18 |
19 | );
20 | };
21 |
22 | export default Products;
23 |
--------------------------------------------------------------------------------
/src/components/Slide.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | const Slide = ({ image }) => {
4 | return (
5 |
6 |
7 |
8 | {image.headline}
9 |
10 |
{image.body}
11 |
15 |
16 | {image.cta}
17 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default Slide;
25 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import Navbar from "./components/Navbar";
3 | import Footer from "./components/Footer";
4 | import Home from "./pages/Home";
5 | import Products from "./pages/Products";
6 | import Cart from "./pages/Cart";
7 | import NotFound from "./pages/NotFound";
8 | import { ToastContainer } from "react-toastify";
9 | import "react-toastify/dist/ReactToastify.css";
10 |
11 | const App = () => {
12 | return (
13 | <>
14 |
15 |
16 |
17 |
18 | } />
19 | } />
20 | } />
21 | } />
22 | } />
23 |
24 |
25 |
26 | >
27 | );
28 | };
29 |
30 | export default App;
31 |
--------------------------------------------------------------------------------
/src/features/products/productSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | const initialState = {
5 | items: [],
6 | status: null,
7 | };
8 |
9 | export const productsFetching = createAsyncThunk(
10 | "products/productsFetching",
11 | async () => {
12 | const res = await axios.get(
13 | "https://tech-alpha-server.vercel.app/api/products"
14 | );
15 | return res.data;
16 | }
17 | );
18 |
19 | export const productsSlice = createSlice({
20 | name: "products",
21 | initialState: initialState,
22 | reducers: {},
23 | extraReducers: (builder) => {
24 | builder.addCase(productsFetching.pending, (state, action) => {
25 | state.status = "Loading...";
26 | });
27 |
28 | builder.addCase(productsFetching.fulfilled, (state, action) => {
29 | state.status = "";
30 | state.items = action.payload;
31 | });
32 |
33 | builder.addCase(productsFetching.rejected, (state, action) => {
34 | state.status = "Something went wrong";
35 | });
36 | },
37 | });
38 |
39 | export default productsSlice.reducer;
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tech-alpha",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reduxjs/toolkit": "^1.9.1",
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^13.4.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "axios": "^1.2.2",
11 | "icons": "^1.0.0",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-icons": "^4.7.1",
15 | "react-redux": "^8.0.5",
16 | "react-router-dom": "^6.6.1",
17 | "react-scripts": "5.0.1",
18 | "react-toastify": "^9.1.1",
19 | "web-vitals": "^2.1.4"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | },
45 | "devDependencies": {
46 | "autoprefixer": "^10.4.13",
47 | "postcss": "^8.4.20",
48 | "tailwindcss": "^3.2.4"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import { BsCart3 } from "react-icons/bs";
4 | import { useSelector } from "react-redux";
5 | const Navbar = () => {
6 | const { cartItems } = useSelector((state) => state.cart);
7 | return (
8 |
9 |
10 |
11 |
12 | techAlpha
13 |
14 |
15 |
16 |
17 | Home
18 |
19 |
20 | Products
21 |
22 |
23 |
24 |
25 |
26 | {cartItems.length}
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default Navbar;
37 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap");
6 | @import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap");
7 |
8 | html,
9 | body {
10 | overflow-x: hidden;
11 | }
12 |
13 | body {
14 | font-family: "Poppins", sans-serif;
15 | }
16 |
17 | /* Utilities */
18 | .space-font {
19 | font-family: "Space Grotesk", sans-serif;
20 | }
21 |
22 | /* navbar */
23 | .nav-link {
24 | position: relative;
25 | }
26 |
27 | .nav-link::after {
28 | content: "";
29 | width: 0%;
30 | height: 1px;
31 | background-color: #f9fafb;
32 | position: absolute;
33 | left: 50%;
34 | bottom: 0;
35 | transform: translateX(-50%);
36 | transition: 0.3s;
37 | }
38 | .nav-link:hover::after {
39 | width: 100%;
40 | }
41 |
42 | /* slider */
43 | .slider {
44 | height: calc(100vh - 5rem);
45 | width: 500vw;
46 | display: flex;
47 | transition: 1s ease;
48 | }
49 |
50 | .slide {
51 | width: 100vw;
52 | height: 100%;
53 | background-position: center;
54 | background-repeat: no-repeat;
55 | background-size: cover;
56 | }
57 |
58 | /* Slide */
59 | .slide-btn {
60 | position: relative;
61 | }
62 |
63 | .slide-btn::before {
64 | content: "";
65 | position: absolute;
66 | z-index: 1;
67 | top: 0;
68 | left: 0;
69 | right: 0;
70 | bottom: 0;
71 | background-color: #f97316;
72 | width: 0%;
73 | transition: 0.3s ease;
74 | }
75 |
76 | .slide-btn:hover::before {
77 | width: 100%;
78 | }
79 |
--------------------------------------------------------------------------------
/src/components/Card.js:
--------------------------------------------------------------------------------
1 | // import { useDispatch } from "react-redux";
2 | import { useDispatch } from "react-redux";
3 | import { useNavigate } from "react-router-dom";
4 | import { addToCart } from "../features/products/cartSlice";
5 | import { currencyFormatter } from "../utilities/currencyFormatter";
6 |
7 | const Card = ({ product }) => {
8 | const navigate = useNavigate();
9 | const dispatch = useDispatch();
10 |
11 | const addToCartHandler = (product) => {
12 | dispatch(addToCart(product));
13 | navigate("/cart");
14 | };
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 | {product.category}
24 |
25 |
26 | {product.name}
27 |
28 |
{product.description}
29 |
30 |
31 | {currencyFormatter(product.price)}
32 |
33 | addToCartHandler(product)}
35 | className="uppercase bg-sky-500 text-sky-50 font-medium py-3 px-8 rounded-md hover:bg-orange-500 hover:text-orange-50 duration-300 shadow-lg shadow-sky-300 hover:shadow-orange-300"
36 | >
37 | Add to cart
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
45 | export default Card;
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TechAlpha
2 |
3 | TechAlpha is a technological gadget e-commerce website built using React.js, Redux, Redux Toolkit, React Router, and Tailwind CSS. This project provides a platform for buying and selling tech gadgets online.
4 |
5 | ## Project Description
6 |
7 | TechAlpha offers the following features:
8 |
9 | - Users can browse through the various categories of tech gadgets such as smartphones, laptops, and tablets.
10 | - The website uses Redux to manage the state of the application and Redux Toolkit to simplify Redux configuration.
11 | - Users can add items to their cart and complete the checkout process.
12 | - The website uses React Router to provide a seamless user experience with its dynamic routing functionality.
13 | - The website is designed with Tailwind CSS, providing a modern and responsive user interface.
14 |
15 | ## Tools Used
16 |
17 | TechAlpha was built using the following tools:
18 |
19 | - React.js
20 | - Redux
21 | - Redux Toolkit
22 | - React Router
23 | - Tailwind CSS
24 |
25 | ## Installation
26 |
27 | To run TechAlpha on your local machine, follow these steps:
28 |
29 | 1. Clone the repository using the command:
30 |
31 | `
32 | git clone https://github.com/your-username/techalpha.git
33 | `
34 |
35 | 2. Navigate to the project directory using the command:
36 |
37 | `
38 | cd techalpha
39 | `
40 |
41 | 3. Install the required dependencies using the command:
42 |
43 | `
44 | npm install
45 | `
46 |
47 | 4. Start the development server using the command:
48 |
49 | `
50 | npm start
51 | `
52 |
53 | 5. Open your web browser and navigate to http://localhost:3000
54 |
55 | ## Conclusion
56 |
57 | In conclusion, TechAlpha is a technological gadget e-commerce website built using React.js, Redux, Redux Toolkit, React Router, and Tailwind CSS. This project only implements the frontend part of the application. The website provides a seamless user experience with its dynamic routing functionality and modern user interface.
58 |
--------------------------------------------------------------------------------
/src/components/Slider.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { BsArrowLeft, BsArrowRight } from "react-icons/bs";
3 | import Slide from "./Slide";
4 | const data = [
5 | {
6 | id: 1,
7 | src: "https://i.ibb.co/TM6Kv2R/camera.jpg",
8 | headline: "DSLR cameras for stunning photos",
9 | body: "Are you an aspiring photographer looking to take your skills to the next level? Our DSLR cameras offer advanced features and high-quality image sensors to help you capture stunning photos. From landscape shots to portraits, these cameras are perfect for capturing all types of subjects.",
10 | cta: "Shop DSLR cameras now",
11 | category: "cameras",
12 | },
13 | {
14 | id: 2,
15 | src: "https://i.ibb.co/4jz61ys/tv.jpg",
16 | headline: "Upgrade your home entertainment with our TVs",
17 | body: "Experience the latest in home entertainment with our selection of TVs. From sleek and modern designs to advanced features like 4K resolution and smart capabilities, our TVs will bring your favorite movies, TV shows, and streaming content to life.",
18 | cta: "Shop TVs and upgrade now",
19 | category: "tvs",
20 | },
21 | {
22 | id: 3,
23 | src: "https://i.ibb.co/G5rKN9j/headphone.jpg",
24 | headline: "Enhance your listening experience",
25 | body: "Take your music, movies, and more to the next level with our headphones. Our selection offers a range of styles and features, including noise-cancelling technology, wireless connectivity, and comfortable designs for all-day wear.",
26 | cta: "Experience enhanced sound",
27 | category: "headphones",
28 | },
29 | {
30 | id: 4,
31 | src: "https://i.ibb.co/JqxDhvZ/console.jpg",
32 | headline: "Take your gaming to the next level",
33 | body: "Elevate your gaming experience with our selection of gaming consoles. From the latest models to classic systems, we have a console for every type of gamer. Our consoles offer advanced graphics, fast processing speeds, and a variety of exclusive games to choose from.",
34 | cta: "Shop consoles and play now",
35 | category: "consoles",
36 | },
37 | {
38 | id: 5,
39 | src: "https://i.ibb.co/QvPxv65/smart-watch.jpg",
40 | headline: "Stay connected with smart watches",
41 | body: "Stay connected and on top of your day with our smart watches. Our selection offers a range of styles and features, including fitness tracking, phone notifications, and voice assistants. These watches are the perfect combination of functionality and style.",
42 | cta: "Connect with a smart watch",
43 | category: "smart-watches",
44 | },
45 | ];
46 |
47 | const Slider = () => {
48 | const [currentSlide, setCurrentSlide] = useState(0);
49 |
50 | const prevSlide = () => {
51 | setCurrentSlide(
52 | currentSlide === 0 ? data.length - 1 : (prevSlide) => prevSlide - 1
53 | );
54 | };
55 |
56 | const nextSlide = () => {
57 | setCurrentSlide(
58 | currentSlide === data.length - 1 ? 0 : (prevSlide) => prevSlide + 1
59 | );
60 | };
61 |
62 | return (
63 |
64 |
68 | {data.map((image) => (
69 |
70 | ))}
71 |
72 |
73 |
77 |
78 |
79 |
80 |
81 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | );
92 | };
93 |
94 | export default Slider;
95 |
--------------------------------------------------------------------------------
/src/features/products/cartSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 | import { toast } from "react-toastify";
3 |
4 | const initialState = {
5 | cartItems: localStorage.getItem("cartItems")
6 | ? JSON.parse(localStorage.getItem("cartItems"))
7 | : [],
8 | cartTotalQuantity: 0,
9 | cartTotalAmount: 0,
10 | };
11 |
12 | const cartSlice = createSlice({
13 | name: "cart",
14 | initialState,
15 | reducers: {
16 | addToCart(state, action) {
17 | const existedItemIndex = state.cartItems.findIndex(
18 | (item) => item.id === action.payload.id
19 | );
20 | if (existedItemIndex >= 0) {
21 | state.cartItems[existedItemIndex].cartQuantity += 1;
22 | toast.info("Quantity increased", {
23 | position: "bottom-left",
24 | autoClose: 5000,
25 | hideProgressBar: false,
26 | closeOnClick: true,
27 | pauseOnHover: true,
28 | draggable: true,
29 | progress: undefined,
30 | theme: "light",
31 | });
32 | } else {
33 | const assembledItem = { ...action.payload, cartQuantity: 1 };
34 | state.cartItems.push(assembledItem);
35 | toast.info("Product added into cart", {
36 | position: "bottom-left",
37 | autoClose: 5000,
38 | hideProgressBar: false,
39 | closeOnClick: true,
40 | pauseOnHover: true,
41 | draggable: true,
42 | progress: undefined,
43 | theme: "light",
44 | });
45 | }
46 | localStorage.setItem("cartItems", JSON.stringify(state.cartItems));
47 | },
48 |
49 | removeFromCart(state, action) {
50 | const updatedCartItems = state.cartItems.filter(
51 | (item) => item.id !== action.payload.id
52 | );
53 | state.cartItems = updatedCartItems;
54 |
55 | toast.warn("Product removed from cart", {
56 | position: "bottom-left",
57 | autoClose: 5000,
58 | hideProgressBar: false,
59 | closeOnClick: true,
60 | pauseOnHover: true,
61 | draggable: true,
62 | progress: undefined,
63 | theme: "light",
64 | });
65 | //update local storage
66 | localStorage.setItem("cartItems", JSON.stringify(state.cartItems));
67 | },
68 |
69 | clearCart(state, acion) {
70 | state.cartItems = [];
71 |
72 | toast.error("cart cleared", {
73 | position: "bottom-left",
74 | autoClose: 5000,
75 | hideProgressBar: false,
76 | closeOnClick: true,
77 | pauseOnHover: true,
78 | draggable: true,
79 | progress: undefined,
80 | theme: "light",
81 | });
82 |
83 | localStorage.setItem("cartItems", JSON.stringify(state.cartItems));
84 | },
85 |
86 | decreaseCart(state, action) {
87 | const itemIndex = state.cartItems.findIndex(
88 | (item) => item.id === action.payload.id
89 | );
90 |
91 | //if exist
92 | if (state.cartItems[itemIndex].cartQuantity > 1) {
93 | state.cartItems[itemIndex].cartQuantity -= 1;
94 |
95 | toast.error("Quantity decreased", {
96 | position: "bottom-left",
97 | autoClose: 5000,
98 | hideProgressBar: false,
99 | closeOnClick: true,
100 | pauseOnHover: true,
101 | draggable: true,
102 | progress: undefined,
103 | theme: "light",
104 | });
105 | } else if (state.cartItems[itemIndex].cartQuantity === 1) {
106 | const updatedCartItems = state.cartItems.filter(
107 | (item) => item.id !== action.payload.id
108 | );
109 | state.cartItems = updatedCartItems;
110 |
111 | toast.warn("Product removed from cart", {
112 | position: "bottom-left",
113 | autoClose: 5000,
114 | hideProgressBar: false,
115 | closeOnClick: true,
116 | pauseOnHover: true,
117 | draggable: true,
118 | progress: undefined,
119 | theme: "light",
120 | });
121 | }
122 | localStorage.setItem("cartItems", JSON.stringify(state.cartItems));
123 | },
124 |
125 | getSubTotal(state, action) {
126 | const subTotal = state.cartItems.reduce((acc, item) => {
127 | const { price, cartQuantity } = item;
128 | const itemTotal = price * cartQuantity;
129 |
130 | acc += itemTotal;
131 |
132 | return acc;
133 | }, 0);
134 | state.cartTotalAmount = subTotal;
135 | },
136 | },
137 | });
138 |
139 | export const {
140 | addToCart,
141 | removeFromCart,
142 | clearCart,
143 | decreaseCart,
144 | getSubTotal,
145 | } = cartSlice.actions;
146 | export default cartSlice.reducer;
147 |
--------------------------------------------------------------------------------
/src/pages/Cart.js:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from "react-redux";
2 | import {
3 | addToCart,
4 | clearCart,
5 | decreaseCart,
6 | getSubTotal,
7 | removeFromCart,
8 | } from "../features/products/cartSlice";
9 | import { currencyFormatter } from "../utilities/currencyFormatter";
10 | import { Link } from "react-router-dom";
11 | import { useEffect } from "react";
12 |
13 | const Cart = () => {
14 | const { cartItems: data, cartTotalAmount: subTotal } = useSelector(
15 | (state) => state.cart
16 | );
17 | const dispatch = useDispatch();
18 | const handleRemove = (product) => {
19 | dispatch(removeFromCart(product));
20 | };
21 | const handleDecrease = (product) => {
22 | dispatch(decreaseCart(product));
23 | };
24 | const handleIncrease = (product) => {
25 | dispatch(addToCart(product));
26 | };
27 |
28 | useEffect(() => {
29 | dispatch(getSubTotal());
30 | }, [data, dispatch]);
31 |
32 | return (
33 |
34 |
35 | {data.length > 0
36 | ? `You've added ${data.length} item${data.length > 1 ? "s" : ""}`
37 | : "Your cart is empty"}
38 |
39 | {data.length === 0 && (
40 |
41 |
45 | Start shopping now
46 |
47 |
48 | )}
49 | {data.length > 0 && (
50 | <>
51 |
52 |
53 |
Product
54 |
Unit Price
55 |
Quantity
56 |
Total Price
57 |
58 |
59 | {data?.map((product) => (
60 |
64 |
65 |
70 |
71 | {product.name}
72 | handleRemove(product)}
74 | className="uppercase text-gray-400 hover:text-rose-500 duration-300"
75 | >
76 | Remove
77 |
78 |
79 |
80 |
81 | {currencyFormatter(product.price)}
82 |
83 |
84 | handleDecrease(product)}
86 | className="h-10 w-10 bg-gray-100 border border-gray-300 active:bg-gray-700 active:text-gray-50"
87 | >
88 | -
89 |
90 |
91 | {product.cartQuantity}
92 |
93 | handleIncrease(product)}
95 | className="h-10 w-10 bg-gray-100 border border-gray-300 active:bg-gray-700 active:text-gray-50"
96 | >
97 | +
98 |
99 |
100 |
101 | {currencyFormatter(product.price * product.cartQuantity)}
102 |
103 |
104 | ))}
105 |
106 |
107 |
108 |
dispatch(clearCart())}
110 | className="clear-btn uppercase border py-3 px-8 hover:bg-rose-200 hover:text-rose-600 font-medium hover:border-rose-200 duration-300"
111 | >
112 | Clear cart
113 |
114 |
115 |
116 | Subtotal
117 |
118 | {currencyFormatter(subTotal)}
119 |
120 |
121 |
122 | Taxes and shipping costs are calculated at the checkout
123 |
124 |
128 | Checkout
129 |
130 |
134 | Continue Shopping
135 |
136 |
137 |
138 | >
139 | )}
140 |
141 | );
142 | };
143 |
144 | export default Cart;
145 |
--------------------------------------------------------------------------------