├── public ├── _redirects └── index.html ├── src ├── components │ ├── CheckOutSuccess.jsx │ ├── Footer.js │ ├── Slide.js │ ├── PayButton.jsx │ ├── Navbar.js │ ├── Card.js │ └── Slider.js ├── utlities │ └── currencyFormatter.js ├── pages │ ├── Home.js │ ├── NotFound.js │ ├── Products.js │ └── Cart.js ├── app │ └── store.js ├── index.js ├── features │ └── products │ │ ├── productSlice.js │ │ └── cartSlice.js ├── App.js └── index.css ├── tailwind.config.js ├── .gitignore ├── package.json └── README.md /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /src/components/CheckOutSuccess.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const CheckOutSuccess = () => { 4 | return ( 5 |

Check Out Success

6 | ) 7 | } 8 | 9 | export default CheckOutSuccess -------------------------------------------------------------------------------- /src/utlities/currencyFormatter.js: -------------------------------------------------------------------------------- 1 | export const currencyFormatter = (price) => { 2 | if (!price) return; 3 | return price.toLocaleString("en-us", { 4 | style: "currency", 5 | currency: "USD", 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/**/*.{js,jsx,ts,tsx}", 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [require('@tailwindcss/line-clamp'),], 10 | } -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Slider from '../components/Slider' 3 | import Products from './Products' 4 | 5 | const Home = () => { 6 | return ( 7 |
8 | 9 | 10 |
11 | ) 12 | } 13 | 14 | export default Home -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | tech alpha 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Footer = () => { 4 | return ( 5 |
6 |

© {new Date().getFullYear} 7 | Tech Alpha. All rights reserved 8 |

9 |
10 | ) 11 | } 12 | 13 | export default Footer -------------------------------------------------------------------------------- /src/app/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore} from "@reduxjs/toolkit"; 2 | import productsReducer,{productsFetching} from "../features/products/productSlice" 3 | import cartReducer from "../features/products/cartSlice" 4 | 5 | export const store= configureStore({ 6 | reducer:{ 7 | products:productsReducer, 8 | cart: cartReducer 9 | } 10 | }) 11 | 12 | store.dispatch(productsFetching()) -------------------------------------------------------------------------------- /.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 | # production 12 | 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom" 2 | 3 | const NotFound = () => { 4 | return ( 5 |
6 |

7 | Page not found! 8 |

9 | 13 | Go home 14 | 15 | 16 |
17 | ) 18 | } 19 | 20 | export default NotFound -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | 7 | import {store} from './app/store'; 8 | import {Provider} from 'react-redux' 9 | 10 | 11 | const root = ReactDOM.createRoot(document.getElementById('root')); 12 | root.render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /src/pages/Products.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSelector } from 'react-redux' 3 | import Card from '../components/Card' 4 | 5 | 6 | 7 | const Products = () => { 8 | 9 | //redux state access 10 | const {items:data, status}= useSelector((state)=>state.products) 11 | console.log(data) 12 | 13 | return
14 |

Browse all Products

15 |
16 | {status &&

{status}

} 17 | {data.map((product)=>( 18 | 19 | ))} 20 |
21 |
22 | } 23 | 24 | export default Products -------------------------------------------------------------------------------- /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; -------------------------------------------------------------------------------- /src/components/PayButton.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import axios from "axios"; 3 | 4 | const PayButton = ({ data }) => { 5 | const handleCheckout = async () => { 6 | // https://tech-alpha-qtwm.onrender.com/api 7 | // http://localhost:4000/api/ 8 | axios 9 | .post(`https://tech-alpha-qtwm.onrender.com/api/stripe/create-checkout-session`, { 10 | data, 11 | userId: 3, 12 | }) 13 | .then((response) => { 14 | if (response.data.url) { 15 | console.log(response.data.url) 16 | window.location.href = response.data.url; 17 | } 18 | }) 19 | .catch((err) => console.log(err.message)); 20 | }; 21 | 22 | return ( 23 | <> 24 | 30 | 31 | ); 32 | }; 33 | 34 | export default PayButton; 35 | -------------------------------------------------------------------------------- /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 | // https://eager-sable-airedale.glitch.me/products 10 | // https://tech-alpha-qtwm.onrender.com/api/products 11 | export const productsFetching = createAsyncThunk( 12 | "Products/productsFetching", 13 | async () => { 14 | const res = await axios.get( 15 | "https://tech-alpha-qtwm.onrender.com/api/products" 16 | ); 17 | return res.data; 18 | } 19 | ); 20 | 21 | export const productSlice = createSlice({ 22 | name: "Products", 23 | initialState, 24 | reducers: {}, 25 | extraReducers: (builder) => { 26 | builder.addCase(productsFetching.pending, (state, action) => { 27 | state.status = "Loading ......."; 28 | }); 29 | builder.addCase(productsFetching.fulfilled, (state, action) => { 30 | state.status = ""; 31 | state.items = action.payload; 32 | }); 33 | builder.addCase(productsFetching.rejected, (state, action) => { 34 | state.status = "Something went wrong"; 35 | }); 36 | }, 37 | }); 38 | 39 | export default productSlice.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.6.2", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-icons": "^4.7.1", 14 | "react-redux": "^8.0.5", 15 | "react-router-dom": "^6.6.1", 16 | "react-scripts": "5.0.1", 17 | "react-toastify": "^9.1.1", 18 | "web-vitals": "^2.1.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "@tailwindcss/line-clamp": "^0.4.4", 46 | "tailwindcss": "^3.2.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 NotFound from './pages/NotFound' 7 | import Cart from './pages/Cart' 8 | import { ToastContainer } from 'react-toastify' 9 | import 'react-toastify/dist/ReactToastify.min.css'; 10 | import CheckOutSuccess from './components/CheckOutSuccess' 11 | 12 | 13 | const App = () => { 14 | 15 | const isNavActiveStyles=({isActive})=>{ 16 | return { 17 | color: isActive ? "#f97316":null, 18 | }; 19 | } 20 | 21 | return ( 22 | <> 23 |
24 | 25 | 26 | 27 | }/> 28 | }/> 29 | }/> 30 | } /> 31 | } /> 32 | }/> 33 | 34 |
35 |