├── .github ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── App.jsx ├── components │ ├── bongodev │ │ ├── Counter.jsx │ │ ├── NumberComparator.jsx │ │ ├── index.jsx │ │ └── render-products │ │ │ ├── RenderProducts.jsx │ │ │ └── useProducts.jsx │ ├── index.jsx │ ├── project │ │ ├── Project.jsx │ │ ├── ProjectRenderer.jsx │ │ └── Projects.jsx │ ├── promi │ │ ├── counter-app │ │ │ └── Counter.jsx │ │ ├── grade-calculator │ │ │ └── GradeCalculator.jsx │ │ ├── index.js │ │ └── number-comparator │ │ │ └── NumberComparator.jsx │ ├── propTypesDefs │ │ ├── index.jsx │ │ └── project.jsx │ ├── sidebar │ │ └── Sidebar.jsx │ ├── sumiya │ │ ├── Hooks │ │ │ ├── index.js │ │ │ └── useTheme.jsx │ │ ├── counter │ │ │ ├── Count.jsx │ │ │ ├── Counter.jsx │ │ │ ├── CounterProvider.jsx │ │ │ ├── Display.jsx │ │ │ ├── index.js │ │ │ └── useCounter.jsx │ │ ├── grade-calculator │ │ │ ├── GradeCalculator.jsx │ │ │ ├── index.js │ │ │ └── useGrade.jsx │ │ ├── index.js │ │ ├── multiplication-table │ │ │ ├── MultiplicationTable.jsx │ │ │ ├── index.js │ │ │ └── useMultiplicationTable.jsx │ │ ├── number-comparator │ │ │ ├── comparator.jsx │ │ │ ├── index.js │ │ │ └── useComparator.jsx │ │ ├── render-products │ │ │ ├── App.jsx │ │ │ ├── index.js │ │ │ └── useData.jsx │ │ └── themeContext │ │ │ ├── ThemeContext.jsx │ │ │ ├── ThemeProvider.jsx │ │ │ └── index.js │ ├── talha │ │ ├── counter │ │ │ └── Counter.jsx │ │ ├── grade-calculator │ │ │ └── GradeCalculator.jsx │ │ ├── index.jsx │ │ ├── multiplication-table │ │ │ └── MultiplicationTable.jsx │ │ ├── number-comparator │ │ │ └── NumberComparator.jsx │ │ └── render-products │ │ │ └── RenderProducts.jsx │ └── thanwin │ │ ├── counter │ │ └── Counter.jsx │ │ ├── ecommerce │ │ ├── Cart.jsx │ │ ├── CartIcon.jsx │ │ ├── CartItem.jsx │ │ ├── Ecommerce.jsx │ │ ├── FilterButton.jsx │ │ ├── FilterContainer.jsx │ │ ├── Product.jsx │ │ ├── ProductList.jsx │ │ ├── Spinner.jsx │ │ ├── ecommerContext.jsx │ │ └── hooks │ │ │ └── useProducts.js │ │ ├── grade-calculator │ │ ├── GradeCalculator.jsx │ │ └── Input.jsx │ │ ├── index.js │ │ └── number-comparator │ │ ├── Input.jsx │ │ └── NumberComparator.jsx ├── config │ └── index.js ├── data │ └── index.js ├── hooks │ ├── index.jsx │ └── useProjects.jsx ├── index.css ├── main.jsx └── pages │ ├── RootLayout.jsx │ ├── about │ └── Page.jsx │ ├── bongodev │ ├── Page.jsx │ └── index.jsx │ ├── index.jsx │ ├── projects │ ├── ProjectLayout.jsx │ └── useContributor.jsx │ ├── promi │ ├── PromiPage.jsx │ └── index.js │ ├── sumiya │ ├── SumiyaProjectPage.jsx │ └── index.js │ ├── talha │ ├── TalhaProjectPage.jsx │ └── index.jsx │ └── thanwin │ ├── ThanWinProjectPage.jsx │ └── index.js ├── tailwind.config.js └── vite.config.js /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Project Name: 2 | 3 | ### Description 4 | 5 | 6 | 7 | ### QA Steps 8 | 9 | - [ ] -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Deploy react vite app to Github pages 2 | 3 | on: 4 | # Runs on pushes targeting the default branch 5 | push: 6 | branches: ['main'] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 12 | permissions: 13 | contents: write 14 | pages: write 15 | id-token: write 16 | 17 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 18 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 19 | concurrency: 20 | group: 'pages' 21 | cancel-in-progress: false 22 | 23 | jobs: 24 | build-and-deploy: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | # Step 1: Checkout the repository, install node js, and install node packages 29 | - uses: actions/checkout@v4 30 | - name: Use Node.js 20 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: 20 34 | cache: 'npm' 35 | - run: npm ci 36 | - run: npm run build 37 | 38 | # Step 2: Deploy to GitHub Pages 39 | - name: Deploy 40 | uses: peaceiris/actions-gh-pages@v4 41 | with: 42 | github_token: ${{ secrets.GITHUB_TOKEN }} 43 | publish_dir: ./dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-fall-2024 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React JS Fall 2024 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-js-fall-2024", 3 | "version": "1.0.0", 4 | "description": "Learning React JS", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "dependencies": { 13 | "lucide-react": "^0.474.0", 14 | "path": "^0.12.7", 15 | "react": "^19.0.0", 16 | "react-dom": "^19.0.0", 17 | "react-router": "^7.1.3", 18 | "react-select": "^5.10.0" 19 | }, 20 | "devDependencies": { 21 | "@vitejs/plugin-react": "^4.3.4", 22 | "autoprefixer": "^10.4.20", 23 | "postcss": "^8.5.1", 24 | "prop-types": "^15.8.1", 25 | "tailwindcss": "^3.4.17", 26 | "vite": "^6.0.7" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+ssh://git@github.com/bongodev/react-fall-2024.git" 31 | }, 32 | "author": "", 33 | "license": "ISC", 34 | "bugs": { 35 | "url": "https://github.com/bongodev/react-fall-2024/issues" 36 | }, 37 | "homepage": "https://github.com/bongodev/react-fall-2024#readme" 38 | } 39 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Projects } from './components'; 2 | 3 | import { projects } from './data'; 4 | 5 | export default function App() { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/components/bongodev/Counter.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | export const Counter = () => { 4 | const [count, setCount] = useState(0); 5 | 6 | return ( 7 |
8 |

{count}

9 |
10 | 11 | 12 |
13 |
14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/components/bongodev/NumberComparator.jsx: -------------------------------------------------------------------------------- 1 | import { useReducer } from 'react'; 2 | 3 | export const NumberComparator = () => { 4 | const initialState = { 5 | firstNumber: 0, 6 | secondNumber: 0, 7 | }; 8 | 9 | const reducer = (state, action) => { 10 | if (action.type === 'SET_FIRST_NUMBER') { 11 | return { 12 | ...state, 13 | firstNumber: action.payload, 14 | }; 15 | } else if (action.type === 'SET_SECOND_NUMBER') { 16 | return { 17 | ...state, 18 | secondNumber: action.payload, 19 | }; 20 | } 21 | return state; 22 | }; 23 | 24 | const [state, dispatch] = useReducer(reducer, initialState); 25 | 26 | return ( 27 |
28 | 32 | dispatch({ 33 | type: 'SET_FIRST_NUMBER', 34 | payload: parseFloat(e.target.value), 35 | }) 36 | } 37 | /> 38 | 42 | dispatch({ 43 | type: 'SET_SECOND_NUMBER', 44 | payload: parseFloat(e.target.value), 45 | }) 46 | } 47 | /> 48 |

49 | {state.firstNumber > state.secondNumber 50 | ? 'First number is greater' 51 | : state.firstNumber < state.secondNumber 52 | ? 'Second number is greater' 53 | : 'Both numbers are equal'} 54 |

55 |
56 | ); 57 | }; 58 | -------------------------------------------------------------------------------- /src/components/bongodev/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './Counter'; 2 | export * from './render-products/RenderProducts'; 3 | export * from './NumberComparator'; 4 | -------------------------------------------------------------------------------- /src/components/bongodev/render-products/RenderProducts.jsx: -------------------------------------------------------------------------------- 1 | import { useProducts } from './useProducts'; 2 | 3 | export const RenderProducts = () => { 4 | const { isLoading, products } = useProducts(); 5 | 6 | if (isLoading) { 7 | return
Loading...
; 8 | } 9 | 10 | return ( 11 |
12 | {products.map((product) => ( 13 |

{product.name}

14 | ))} 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /src/components/bongodev/render-products/useProducts.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const useProducts = () => { 4 | const [isLoading, setIsLoading] = useState(true); 5 | const [products, setProducts] = useState([]); 6 | 7 | useEffect(() => { 8 | setIsLoading(true); 9 | fetch( 10 | 'https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json' 11 | ) 12 | .then((response) => response.json()) 13 | .then((data) => setProducts(data)) 14 | .catch((error) => alert('Error fetching data')) 15 | .finally(() => setIsLoading(false)); 16 | }, []); 17 | 18 | return { isLoading, products }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './project/Projects'; 2 | export * from './sidebar/Sidebar'; 3 | export * from './project/ProjectRenderer'; 4 | -------------------------------------------------------------------------------- /src/components/project/Project.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | import { ProjectPropType } from '../propTypesDefs'; 4 | 5 | export const Project = ({ project }) => { 6 | const [selectedContributor, setSelectedContributor] = useState(); 7 | 8 | return ( 9 |
10 |

{project.name}

11 |
12 | {project.contributors.map((contributor) => ( 13 | 20 | ))} 21 | {project.contributors.length !== 0 && ( 22 | 28 | )} 29 |
30 | {!selectedContributor ? ( 31 |

No contributor is selected!

32 | ) : ( 33 |

{selectedContributor}

34 | )} 35 |
36 | ); 37 | }; 38 | 39 | Project.propTypes = { 40 | project: ProjectPropType, 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/project/ProjectRenderer.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Counter as BongoDevCounter } from '../bongodev'; 3 | 4 | const projects = { 5 | 'counter-app': { bongodev: }, 6 | }; 7 | 8 | export const ProjectRenderer = ({ projectId, contributorId }) => { 9 | return ( 10 |
11 | {projects[projectId]?.[contributorId] ||
Project not found
} 12 |
13 | ); 14 | }; 15 | 16 | ProjectRenderer.propTypes = { 17 | projectId: PropTypes.string.isRequired, 18 | contributorId: PropTypes.string.isRequired, 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/project/Projects.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | import { ProjectPropType } from '../propTypesDefs'; 4 | 5 | import { Project } from './Project'; 6 | 7 | export const Projects = ({ projects }) => { 8 | return ( 9 |
10 | {projects.map((project) => ( 11 | 12 | ))} 13 |
14 | ); 15 | }; 16 | 17 | Projects.propTypes = { 18 | projects: PropTypes.arrayOf(ProjectPropType).isRequired, 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/promi/counter-app/Counter.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | const Counter = () => { 4 | const [count,setCount]=useState(0); 5 | const increaseButton=()=>{ 6 | setCount(count+1); 7 | 8 | } 9 | const decreaseButton=()=>{ 10 | if(count>0){ 11 | setCount(count-1) 12 | } 13 | else 14 | alert(`Counter value can not be less than 0`) 15 | } 16 | return ( 17 |
19 |

Counter App

20 |

Counter value : {count}

21 | 22 | 23 | 24 |
25 | ) 26 | } 27 | 28 | export default Counter -------------------------------------------------------------------------------- /src/components/promi/grade-calculator/GradeCalculator.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const GradeCalculator = () => { 4 | const [score, setScore] = useState(""); 5 | const [result, setResult] = useState(""); 6 | const [error, setError] = useState(""); 7 | const [showGrade, setShowGrade] = useState(false); // For showing "Your Grade is:" 8 | 9 | // Grade Calculation 10 | const gradeCalculate = (marks) => { 11 | if (marks > 100 || marks < 0) { 12 | setResult("Please enter a number between 0 and 100."); 13 | return; 14 | } 15 | 16 | if (marks >= 80 && marks <= 100) setResult("A+"); 17 | else if (marks >= 75) setResult("A"); 18 | else if (marks >= 70) setResult("A-"); 19 | else if (marks >= 65) setResult("B+"); 20 | else if (marks >= 60) setResult("B"); 21 | else if (marks >= 55) setResult("B-"); 22 | else if (marks >= 50) setResult("C+"); 23 | else if (marks >= 45) setResult("C"); 24 | else if (marks >= 40) setResult("D"); 25 | else setResult("F"); 26 | }; 27 | 28 | // Handle Calculate button 29 | const handleCalculate = () => { 30 | if (score === "") { 31 | setError("Score cannot be empty."); 32 | setResult(""); 33 | setShowGrade(false); // Hide the grade when no score is entered 34 | return; 35 | } 36 | 37 | setError(""); 38 | setShowGrade(true); // Show the grade label after calculation 39 | gradeCalculate(parseFloat(score)); 40 | }; 41 | 42 | // Handle Reset 43 | const handleReset = () => { 44 | setScore(""); 45 | setResult(""); 46 | setError(""); 47 | setShowGrade(false); // Hide the grade section when reset 48 | }; 49 | 50 | return ( 51 |
52 |
53 |

Grade Calculator

54 | 55 | {/* Input Field */} 56 | setScore(e.target.value)} 64 | /> 65 | 66 | {error &&

{error}

} 67 | 68 | {/* Calculate Button */} 69 | 75 | 76 | {/* Reset Button */} 77 | 83 | 84 | {/* Display Grade Result */} 85 | {showGrade && ( 86 |
87 |

Your Grade is:

88 |
93 | {result} 94 |
95 |
96 | )} 97 |
98 |
99 | ); 100 | }; 101 | 102 | export default GradeCalculator; 103 | -------------------------------------------------------------------------------- /src/components/promi/index.js: -------------------------------------------------------------------------------- 1 | import Counter from './counter-app/Counter'; 2 | import GradeCalculator from './grade-calculator/GradeCalculator'; 3 | import NumberComparator from './number-comparator/NumberComparator'; 4 | export { Counter, GradeCalculator, NumberComparator }; 5 | -------------------------------------------------------------------------------- /src/components/promi/number-comparator/NumberComparator.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const NumberComparator = () => { 4 | const [num1, setNum1] = useState(0); 5 | const [num2, setNum2] = useState(0); 6 | const [result, setResult] = useState(""); 7 | 8 | const handleCompare = () => { 9 | if (isNaN(num1) || isNaN(num2)) { 10 | setResult("Please enter valid numbers."); 11 | return; 12 | } 13 | 14 | if (num1 > num2) { 15 | setResult("First Number is larger than Second Number"); 16 | } else if (num1 === num2) { 17 | setResult("Two numbers are equal"); 18 | } else { 19 | setResult("Second Number is larger than First Number"); 20 | } 21 | }; 22 | 23 | const handleReset = () => { 24 | setNum1(0); 25 | setNum2(0); 26 | setResult(""); 27 | }; 28 | 29 | return ( 30 |
31 |
32 |

Number Comparator App

33 | 34 | {/* Input Fields */} 35 |
36 | setNum1(parseFloat(e.target.value))} 42 | /> 43 | setNum2(parseFloat(e.target.value))} 49 | /> 50 |
51 | 52 | {/* Compare Button */} 53 | 59 | 60 | 61 |
62 | {result} 63 |
64 | 65 | {/* Reset Button */} 66 | 72 |
73 |
74 | ); 75 | }; 76 | 77 | export default NumberComparator; 78 | -------------------------------------------------------------------------------- /src/components/propTypesDefs/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './project'; 2 | -------------------------------------------------------------------------------- /src/components/propTypesDefs/project.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | const ContributorPropType = PropTypes.arrayOf(PropTypes.string); 4 | 5 | export const ProjectPropType = PropTypes.shape({ 6 | name: PropTypes.string.isRequired, 7 | contributors: ContributorPropType, 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/sidebar/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import { NavLink } from 'react-router'; 2 | 3 | import { projects } from '@/data'; 4 | import { appConfig } from '@/config'; 5 | 6 | export const Sidebar = () => { 7 | return ( 8 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /src/components/sumiya/Hooks/index.js: -------------------------------------------------------------------------------- 1 | export * from './useTheme.jsx' -------------------------------------------------------------------------------- /src/components/sumiya/Hooks/useTheme.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { ThemeContext } from '../themeContext'; 3 | 4 | 5 | export const useTheme = () => { 6 | return useContext(ThemeContext); 7 | }; 8 | -------------------------------------------------------------------------------- /src/components/sumiya/counter/Count.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useCounter } from './useCounter'; 3 | 4 | export function Count() { 5 | const { count, setCount } = useCounter(); 6 | return ( 7 | <> 8 |
9 | 17 | 27 |
28 | 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/components/sumiya/counter/Counter.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useTheme } from '../Hooks'; 3 | import { Count } from './Count'; 4 | import { Display } from './Display'; 5 | import { CounterProvider } from './CounterProvider'; 6 | 7 | export const Counter = () => { 8 | const { theme } = useTheme(); 9 | return ( 10 | 11 |
16 | 17 | 18 |
19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/sumiya/counter/CounterProvider.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { createContext } from 'react'; 3 | 4 | export const CounterContext = createContext(); 5 | 6 | export const CounterProvider = ({ children }) => { 7 | const [count, setCount] = useState(0); 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/components/sumiya/counter/Display.jsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "../Hooks"; 2 | import { useCounter } from "./useCounter"; 3 | 4 | export function Display() { 5 | const { theme, toggleTheme } = useTheme(); 6 | const { count } = useCounter(); 7 | return ( 8 |
9 |

Counter App

10 | 18 | 19 |
20 |

21 | {count} 22 |

23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/components/sumiya/counter/index.js: -------------------------------------------------------------------------------- 1 | export * from './Count'; 2 | export * from './Display' 3 | export * from './Counter' 4 | export * from './CounterProvider'; 5 | export * from './useCounter' 6 | -------------------------------------------------------------------------------- /src/components/sumiya/counter/useCounter.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { CounterContext } from './CounterProvider'; 3 | 4 | export function useCounter() { 5 | return useContext(CounterContext); 6 | } 7 | -------------------------------------------------------------------------------- /src/components/sumiya/grade-calculator/GradeCalculator.jsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from '../Hooks'; 2 | import { useGrade } from './useGrade'; 3 | 4 | export function GradeCalculator() { 5 | const { resetGrade, calculateGrade, errorText, grade, marks, setMarks } = 6 | useGrade(); 7 | 8 | const inputMarks = (event) => { 9 | setMarks(event.target.value); 10 | }; 11 | 12 | const { theme, toggleTheme } = useTheme(false); 13 | 14 | return ( 15 |
16 |
20 |

21 | Grade Calculator 22 |

23 | 31 | 39 | {errorText && ( 40 |

{errorText}

41 | )} 42 | 48 | 49 | {grade && ( 50 |

56 | {grade} 57 |

58 | )} 59 | 67 |
68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /src/components/sumiya/grade-calculator/index.js: -------------------------------------------------------------------------------- 1 | export * from './GradeCalculator'; 2 | export * from './useGrade'; 3 | -------------------------------------------------------------------------------- /src/components/sumiya/grade-calculator/useGrade.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | export const useGrade = () => { 4 | const [grade, setGrade] = useState(); 5 | const [errorText, setErrorText] = useState(); 6 | const [marks, setMarks] = useState(''); 7 | 8 | const calculateGrade = () => { 9 | const score = parseFloat(marks); 10 | 11 | if (isNaN(score) || score > 100 || score < 0) { 12 | setGrade(''); 13 | setErrorText('Please enter a valid input (0-100).'); 14 | return; 15 | } else { 16 | setErrorText(''); 17 | } 18 | if (score >= 80) { 19 | setGrade('A+'); 20 | } else if (score >= 70) { 21 | setGrade('A'); 22 | } else if (score >= 60) { 23 | setGrade('A-'); 24 | } else if (score >= 55) { 25 | setGrade('B'); 26 | } else if (score >= 50) { 27 | setGrade('B-'); 28 | } else if (score >= 40) { 29 | setGrade('C'); 30 | } else { 31 | setGrade('F'); 32 | } 33 | }; 34 | const resetGrade = () => { 35 | setErrorText(''); 36 | setGrade(''); 37 | setMarks(''); 38 | }; 39 | 40 | return { 41 | resetGrade, 42 | calculateGrade, 43 | errorText, 44 | grade, 45 | marks, 46 | setMarks, 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /src/components/sumiya/index.js: -------------------------------------------------------------------------------- 1 | export * from './Hooks' 2 | export * from './themeContext' 3 | export * from './counter' 4 | export * from './number-comparator' 5 | export * from './grade-calculator' 6 | export * from './multiplication-table' 7 | export *from './render-products' -------------------------------------------------------------------------------- /src/components/sumiya/multiplication-table/MultiplicationTable.jsx: -------------------------------------------------------------------------------- 1 | import useMultiplicationTable from './useMultiplicationTable'; 2 | 3 | function MultiplicationTable() { 4 | const { input, setInput, generateTable, resetTable, multiplicationTable, error} = useMultiplicationTable() 5 | return ( 6 |
7 |
8 |

Multiplication Table Generator

9 | { 15 | setInput(e.target.value)} 16 | } 17 | /> 18 |
19 | 25 | 31 |
32 | {error &&

{error}

} 33 |
34 | {multiplicationTable.length>0 && ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {multiplicationTable.map((row, index)=>( 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ))} 56 | 57 |
Table of {multiplicationTable[0].multiplicand}
MultiplicandMultiplierProduct
{row.multiplicand}X{row.multiplier}={row.product}
58 | )} 59 |
60 | ); 61 | } 62 | 63 | 64 | 65 | export default MultiplicationTable 66 | -------------------------------------------------------------------------------- /src/components/sumiya/multiplication-table/index.js: -------------------------------------------------------------------------------- 1 | export * from './MultiplicationTable' 2 | export * from './useMultiplicationTable' -------------------------------------------------------------------------------- /src/components/sumiya/multiplication-table/useMultiplicationTable.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | function useMultiplicationTable() { 4 | 5 | const [input, setInput] =useState(); 6 | const [multiplicationTable, setMultiplicationTable] = useState([]); 7 | const [error, setError] = useState(null) 8 | 9 | const generateTable = ()=>{ 10 | const num= Number(input); 11 | 12 | if(!num || isNaN(num)){ 13 | setError("Enter a valid input") 14 | setMultiplicationTable("") 15 | return 16 | }else{ 17 | setError("") 18 | } 19 | const Table = Array.from({length:10}, (_, i) => ({ 20 | multiplicand: num, 21 | multiplier: i+1, 22 | product: num*(i+1) 23 | })) 24 | 25 | setMultiplicationTable(Table) 26 | } 27 | 28 | const resetTable =() =>{ 29 | setError(""); 30 | setInput(""); 31 | setMultiplicationTable([]); 32 | } 33 | 34 | return { 35 | input, 36 | setInput, 37 | generateTable, 38 | resetTable, 39 | multiplicationTable, 40 | error, 41 | } 42 | } 43 | 44 | export default useMultiplicationTable -------------------------------------------------------------------------------- /src/components/sumiya/number-comparator/comparator.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useComparator } from './useComparator'; 3 | 4 | 5 | export function NumberComparator() { 6 | const { 7 | number1, 8 | setNumber1, 9 | number2, 10 | setNumber2, 11 | result, 12 | error, 13 | input1Error, 14 | input2Error, 15 | compareNumbers, 16 | resetNumbers, 17 | } = useComparator(); 18 | 19 | return ( 20 |
21 |

Number Comparator

22 |
23 | setNumber1(e.target.value)} 29 | /> 30 | setNumber2(e.target.value)} 36 | /> 37 |

{error}

38 |
39 |
40 | 44 | 48 |
49 |

{result}

50 |
51 | ) 52 | } -------------------------------------------------------------------------------- /src/components/sumiya/number-comparator/index.js: -------------------------------------------------------------------------------- 1 | export * from './comparator' 2 | export * from './useComparator' -------------------------------------------------------------------------------- /src/components/sumiya/number-comparator/useComparator.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export function useComparator() { 4 | 5 | const [number1, setNumber1] = useState(0); 6 | const [number2, setNumber2] = useState(0); 7 | const [result, setResult] = useState(''); 8 | const [error, setError] = useState(''); 9 | const [input1Error, setInput1Error] = useState(false); 10 | const [input2Error, setInput2Error] = useState(false) 11 | const num1 = parseFloat(number1) 12 | const num2 = parseFloat(number2) 13 | 14 | const compareNumbers = ()=>{ 15 | const isInput1Empty = isNaN(num1); 16 | const isInput2Empty = isNaN(num2); 17 | 18 | if ( isInput1Empty || isInput2Empty ) { 19 | setError('Please enter valid numbers in both fields.'); 20 | setInput1Error(isInput1Empty); 21 | setInput2Error(isInput2Empty); 22 | setResult(''); 23 | return; 24 | } 25 | 26 | setError(" "); 27 | setInput1Error(false); 28 | setInput2Error(false); 29 | const comparedResult = num1 > num2 ? `${number1} is larger` : num1{ 33 | setResult(" "); 34 | setNumber1(0); 35 | setNumber2(0); 36 | setError(" "); 37 | setInput1Error(false); 38 | setInput2Error(false); 39 | } 40 | return { 41 | number1, 42 | setNumber1, 43 | number2, 44 | setNumber2, 45 | result, 46 | error, 47 | input1Error, 48 | input2Error, 49 | compareNumbers, 50 | resetNumbers, 51 | }; 52 | 53 | } -------------------------------------------------------------------------------- /src/components/sumiya/render-products/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import useData from './useData'; 3 | 4 | 5 | export function App() { 6 | const {loading, error, products} = useData() 7 | 8 | const columns = products.length > 0 ? Object.keys(products[0]).filter((key) => key !== 'image') : []; 9 | 10 | 11 | return ( 12 | <> 13 |
14 | {products && 15 | 16 | 17 | {columns.map((column)=>)} 18 | 19 | 20 | {products.map((product)=>( 21 | 22 | {columns.map((column)=>())} 23 | 24 | )) 25 | } 26 | 27 |
{column}
{product[column]}
28 | } 29 |
30 |
31 | { loading == true &&
Loading....
} 32 | {error &&

Error Fetching Data

} 33 |
34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/components/sumiya/render-products/index.js: -------------------------------------------------------------------------------- 1 | export * from './App' 2 | export * from './useData' -------------------------------------------------------------------------------- /src/components/sumiya/render-products/useData.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useState } from 'react'; 3 | 4 | function useData() { 5 | const url = 'https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json'; 6 | const [products, setProducts] = useState([]); 7 | const [loading, setLoading] = useState(true); 8 | const [error, setError] = useState(null); 9 | useEffect(()=>{ 10 | const fetchData = async ()=>{ 11 | setLoading(true); 12 | try{ 13 | const response = await fetch(url) 14 | const data = await response.json() 15 | setProducts(data) 16 | }catch(error){ 17 | setError(error) 18 | }finally{ 19 | setLoading(false) 20 | } 21 | } 22 | fetchData() 23 | }, []) 24 | 25 | return {loading, products, error} 26 | 27 | } 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | export default useData -------------------------------------------------------------------------------- /src/components/sumiya/themeContext/ThemeContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const ThemeContext = createContext(); 4 | -------------------------------------------------------------------------------- /src/components/sumiya/themeContext/ThemeProvider.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { ThemeContext } from './ThemeContext'; 3 | 4 | export const ThemeProvider = ({ children }) => { 5 | const [theme, setTheme] = useState(true); 6 | const toggleTheme = () => { 7 | setTheme(!theme); 8 | }; 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/components/sumiya/themeContext/index.js: -------------------------------------------------------------------------------- 1 | export * from './ThemeContext'; 2 | export * from './ThemeProvider'; 3 | -------------------------------------------------------------------------------- /src/components/talha/counter/Counter.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | export const Counter = () => { 4 | const [count, setCount] = useState(0); 5 | 6 | const increaseCount = () => { 7 | if (count === 15) { 8 | alert('Maximum limit reached'); 9 | return; 10 | } 11 | setCount(count + 1); 12 | }; 13 | 14 | const decreaseCount = () => { 15 | if (count === 0) { 16 | alert('Minimum limit reached'); 17 | return; 18 | } 19 | setCount(count - 1); 20 | }; 21 | 22 | const resetCount = () => { 23 | setCount(0); 24 | }; 25 | 26 | return ( 27 |
28 |

Counter App

29 |

{count}

30 |
31 | 37 | 43 |
44 | 50 |
51 | ); 52 | }; 53 | -------------------------------------------------------------------------------- /src/components/talha/grade-calculator/GradeCalculator.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | export const GradeCalculator = () => { 4 | const [grade, setGrade] = useState(''); 5 | const [error, setError] = useState(''); 6 | const [result, setResult] = useState(''); 7 | 8 | const getGradeDetails = (gradeLetter) => { 9 | switch (gradeLetter) { 10 | case 'A+': 11 | return { color: 'text-green-600', message: 'Outstanding - Grade A+' }; 12 | case 'A': 13 | return { color: 'text-green-800', message: 'Excellent - Grade A' }; 14 | case 'B': 15 | return { color: 'text-blue-600', message: 'Great job - Grade B' }; 16 | case 'C': 17 | return { color: 'text-yellow-600', message: 'Good effort - Grade C' }; 18 | case 'D': 19 | return { color: 'text-orange-600', message: 'Needs improvement - Grade D' }; 20 | case 'F': 21 | return { color: 'text-red-600', message: 'Failed - Grade F' }; 22 | default: 23 | return { color: 'text-gray-800', message: '' }; 24 | } 25 | }; 26 | 27 | const calculateGrade = () => { 28 | setError(''); 29 | setResult(''); 30 | 31 | if (grade.trim() === '') { 32 | setError('Please enter a number.'); 33 | return; 34 | } 35 | 36 | if (grade < 0 || grade > 100) { 37 | setError('Please enter a number between 0 and 100.'); 38 | return; 39 | } 40 | 41 | const num = parseFloat(grade); 42 | 43 | if (num >= 80) { 44 | setResult('A+'); 45 | } else if (num >= 70) { 46 | setResult('A'); 47 | } else if (num >= 60) { 48 | setResult('B'); 49 | } else if (num >= 50) { 50 | setResult('C'); 51 | } else if (num >= 40) { 52 | setResult('D'); 53 | } else { 54 | setResult('F'); 55 | } 56 | }; 57 | 58 | const resetGrade = () => { 59 | setGrade(''); 60 | setResult(''); 61 | setError(''); 62 | }; 63 | 64 | return ( 65 |
66 |

Grade Calculator App

67 |
68 | { 75 | setGrade(e.target.value); 76 | if (e.target.value.trim() !== '') { 77 | setError(''); 78 | } 79 | }} 80 | /> 81 | {error &&

{error}

} 82 |
83 |
84 | 90 | 96 |
97 |
100 | {result && getGradeDetails(result).message} 101 |
102 |
103 | ); 104 | }; 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/components/talha/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './counter/Counter'; 2 | export * from './number-comparator/NumberComparator'; 3 | export * from './grade-calculator/GradeCalculator'; 4 | export * from './multiplication-table/MultiplicationTable'; 5 | export * from './render-products/RenderProducts'; -------------------------------------------------------------------------------- /src/components/talha/multiplication-table/MultiplicationTable.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | export const MultiplicationTable = () => { 4 | const [number, setNumber] = useState(''); 5 | const [error, setError] = useState(''); 6 | const [table, setTable] = useState([]); 7 | 8 | const generateTable = () => { 9 | setError(''); 10 | if (number.trim() === '') { 11 | setError('Please enter a number.'); 12 | setTable([]); 13 | return; 14 | } 15 | 16 | const num = parseInt(number); 17 | 18 | if (num < 1) { 19 | setError('Please enter a positive number.'); 20 | setTable([]); 21 | return; 22 | } 23 | 24 | let generatedRow = []; 25 | 26 | for (let i = 1; i <= 10; i++) { 27 | generatedRow.push({ 28 | number: num, 29 | multiply: 'x', 30 | multiplier: i, 31 | equals: '=', 32 | product: num * i, 33 | }); 34 | } 35 | setTable(generatedRow); 36 | }; 37 | 38 | const resetTable = () => { 39 | setNumber(''); 40 | setError(''); 41 | setTable([]); 42 | }; 43 | 44 | return ( 45 |
46 |

Multiplication Table Generator

47 |
48 | { 57 | setNumber(e.target.value); 58 | if (e.target.value.trim() !== '') { 59 | setError(''); 60 | } 61 | }} 62 | /> 63 | {error &&

{error}

} 64 |
65 |
66 | 72 | 78 |
79 | {/* Render table only if data exists */} 80 | {table.length > 0 && ( 81 | 82 | 83 | {table.map((row, rowIndex) => ( 84 | 85 | {Object.values(row).map((value, colIndex) => ( 86 | 89 | ))} 90 | 91 | ))} 92 | 93 |
87 | {value} 88 |
94 | )} 95 |
96 | ); 97 | }; 98 | -------------------------------------------------------------------------------- /src/components/talha/number-comparator/NumberComparator.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | export const NumberComparator = () => { 4 | const [firstNumber, setFirstNumber] = useState(''); 5 | const [secondNumber, setSecondNumber] = useState(''); 6 | const [result, setResult] = useState(''); 7 | const [errorFirst, setErrorFirst] = useState(''); 8 | const [errorSecond, setErrorSecond] = useState(''); 9 | 10 | const handleCompare = () => { 11 | setErrorFirst(''); 12 | setErrorSecond(''); 13 | setResult(''); 14 | 15 | 16 | if (firstNumber.trim() === '') { 17 | setErrorFirst('Please enter a number.'); 18 | } 19 | if (secondNumber.trim() === '') { 20 | setErrorSecond('Please enter a number.'); 21 | } 22 | if (firstNumber.trim() === '' || secondNumber.trim() === '') { 23 | return; 24 | } 25 | 26 | const num1 = parseFloat(firstNumber); 27 | const num2 = parseFloat(secondNumber); 28 | 29 | if (num1 === num2) { 30 | setResult('Both numbers are equal'); 31 | } else if (num1 > num2) { 32 | setResult('First number is greater'); 33 | } else { 34 | setResult('Second number is greater'); 35 | } 36 | }; 37 | 38 | const handleReset = () => { 39 | setFirstNumber(''); 40 | setSecondNumber(''); 41 | setResult(''); 42 | setErrorFirst(''); 43 | setErrorSecond(''); 44 | }; 45 | 46 | return ( 47 |
48 |

Number Comparator App

49 |
50 |
51 | { 58 | setFirstNumber(e.target.value); 59 | if (e.target.value.trim() !== '') { 60 | setErrorFirst(''); 61 | } 62 | }} 63 | /> 64 | {errorFirst && ( 65 |

{errorFirst}

66 | )} 67 |
68 |
69 | { 76 | setSecondNumber(e.target.value); 77 | if (e.target.value.trim() !== '') { 78 | setErrorSecond(''); 79 | } 80 | }} 81 | /> 82 | {errorSecond && ( 83 |

{errorSecond}

84 | )} 85 |
86 |
87 |
88 | 94 | 100 |
101 |
102 | {result} 103 |
104 |
105 | ); 106 | }; 107 | -------------------------------------------------------------------------------- /src/components/talha/render-products/RenderProducts.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const RenderProducts = () => { 4 | const [isLoading, setIsLoading] = useState(true); 5 | const [products, setProducts] = useState([]); 6 | 7 | const fetchData = async () => { 8 | try { 9 | setIsLoading(true); 10 | const response = await fetch( 11 | 'https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json' 12 | ); 13 | const data = await response.json(); 14 | setProducts(data); 15 | } catch (error) { 16 | alert('Error fetching data'); 17 | } finally { 18 | setIsLoading(false); 19 | } 20 | }; 21 | 22 | useEffect(() => { 23 | fetchData(); 24 | }, []); 25 | return ( 26 |
27 | {isLoading &&
Loading...
} 28 | {products.map((product) => ( 29 |

{product.name}

30 | ))} 31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/thanwin/counter/Counter.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | const Counter = () => { 4 | const [count, setCount] = useState(0); 5 | const countIsNegative = count === 0; 6 | function handleIncrement() { 7 | setCount((count) => count + 1); 8 | } 9 | function handleDecrement() { 10 | if (count > 0) { 11 | setCount((count) => count - 1); 12 | } 13 | } 14 | return ( 15 |
16 |
17 |

18 | Counter App 19 |

20 |

21 | {count} 22 |

23 |
24 | 30 | 38 |
39 |
40 | 41 | Counter App By Than Win Hline 42 | 43 |
44 | ); 45 | }; 46 | 47 | export default Counter; 48 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/Cart.jsx: -------------------------------------------------------------------------------- 1 | import CartItem from './CartItem'; 2 | 3 | import { X } from 'lucide-react'; 4 | import { useEcommerceContext } from './ecommerContext'; 5 | 6 | const Cart = ({ className }) => { 7 | const { 8 | showCart, 9 | cartItems, 10 | increaseQuantity, 11 | decreaseQuantity, 12 | removeItem, 13 | toggleShowCart, 14 | } = useEcommerceContext(); 15 | const isCartEmpty = cartItems.length === 0; 16 | const totalPrice = cartItems.reduce( 17 | (prev, curr) => prev + curr.quantity * curr.price, 18 | 0 19 | ); 20 | 21 | return ( 22 | <> 23 | {showCart && ( 24 |
25 |
28 |
29 |

30 | My Shopping Cart 31 |

32 | 36 |
37 | {isCartEmpty && ( 38 |

Your cart is empty

39 | )} 40 | {!isCartEmpty && ( 41 | <> 42 |
    43 | {cartItems.map((item) => ( 44 | 54 | ))} 55 |
56 |
57 |
58 | Total : 59 | ${totalPrice.toFixed(2)} 60 |
61 | 62 | )} 63 | 66 |
67 |
68 | )} 69 | 70 | ); 71 | }; 72 | 73 | export default Cart; 74 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/CartIcon.jsx: -------------------------------------------------------------------------------- 1 | import { ShoppingCart } from 'lucide-react'; 2 | import { useEcommerceContext } from './ecommerContext'; 3 | 4 | const CartIcon = () => { 5 | const { toggleShowCart, cartItems } = useEcommerceContext(); 6 | return ( 7 |
8 | 17 |
18 | ); 19 | }; 20 | 21 | export default CartIcon; 22 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/CartItem.jsx: -------------------------------------------------------------------------------- 1 | import { Plus, Minus, Trash2 } from 'lucide-react'; 2 | const CartItem = ({ 3 | id = 0, 4 | itemName = 'itemName', 5 | quantity = 0, 6 | price = 20, 7 | increaseQuantity, 8 | decreaseQuantity, 9 | removeItem, 10 | }) => { 11 | return ( 12 |
  • 13 | {itemName} 14 |
    15 | 18 | {quantity} 19 | 32 |
    33 |
    34 | ${(price * quantity).toFixed(2)} 35 | 43 |
    44 |
  • 45 | ); 46 | }; 47 | 48 | export default CartItem; 49 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/Ecommerce.jsx: -------------------------------------------------------------------------------- 1 | import ProductList from './ProductList'; 2 | import FilterContainer from './FilterContainer'; 3 | import CartIcon from './CartIcon'; 4 | import Spinner from './Spinner'; 5 | import { EcommerceProvider, useEcommerceContext } from './ecommerContext'; 6 | import Cart from './Cart'; 7 | 8 | const Ecommerce = () => { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | const Content = () => { 17 | const { isLoading } = useEcommerceContext(); 18 | if (isLoading) { 19 | return ( 20 |
    21 | 22 |
    23 | ); 24 | } 25 | return ( 26 |
    27 |
    28 |

    29 | Win Store 30 |

    31 | 32 |
    33 |
    34 | 35 | 36 |
    37 | 38 |
    39 | ); 40 | }; 41 | 42 | export default Ecommerce; 43 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/FilterButton.jsx: -------------------------------------------------------------------------------- 1 | import { useEcommerceContext } from './ecommerContext'; 2 | 3 | const FilterButton = ({ categoryName }) => { 4 | const { handleFilter, selectedCategories } = useEcommerceContext(); 5 | return ( 6 | 16 | ); 17 | }; 18 | 19 | export default FilterButton; 20 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/FilterContainer.jsx: -------------------------------------------------------------------------------- 1 | import { useEcommerceContext } from './ecommerContext'; 2 | import FilterButton from './FilterButton'; 3 | 4 | const FilterContainer = () => { 5 | const { handleClear, categories } = useEcommerceContext(); 6 | return ( 7 |
    8 | 14 | {categories.map((category) => ( 15 | 16 | ))} 17 |
    18 | ); 19 | }; 20 | 21 | export default FilterContainer; 22 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/Product.jsx: -------------------------------------------------------------------------------- 1 | import { CircleCheckBig } from 'lucide-react'; 2 | const Product = ({ 3 | imgSrc, 4 | title, 5 | price, 6 | addToCart, 7 | id, 8 | isAdded, 9 | description, 10 | }) => { 11 | return ( 12 |
    13 |
    14 | 15 |
    16 |
    17 |

    {title}

    18 |

    {description}

    19 |

    ${price}

    20 |
    21 |
    22 | {isAdded ? ( 23 |

    24 | 25 | Added 26 |

    27 | ) : ( 28 | 34 | )} 35 |
    36 |
    37 | ); 38 | }; 39 | 40 | export default Product; 41 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/ProductList.jsx: -------------------------------------------------------------------------------- 1 | import { useEcommerceContext } from './ecommerContext'; 2 | import Product from './Product'; 3 | 4 | const ProductList = () => { 5 | const { handleAddToCart, cartItems, productList } = useEcommerceContext(); 6 | 7 | return ( 8 |
    9 | {productList?.map((product) => { 10 | const selectedProduct = cartItems.find( 11 | (item) => item.id === product.id 12 | ); 13 | 14 | return ( 15 | 25 | ); 26 | })} 27 |
    28 | ); 29 | }; 30 | 31 | export default ProductList; 32 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/Spinner.jsx: -------------------------------------------------------------------------------- 1 | const Spinner = ({ size = 'w-12 h-12', color = 'text-white' }) => { 2 | return ( 3 |
    4 |
    7 |
    8 | ); 9 | }; 10 | 11 | export default Spinner; 12 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/ecommerContext.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, createContext, useContext } from 'react'; 2 | import { useProducts } from './hooks/useProducts'; 3 | 4 | const ecommerceContext = createContext(undefined); 5 | const getCategories = (products) => { 6 | return Array.from(new Set(products?.map((product) => product.category))); 7 | }; 8 | 9 | export const EcommerceProvider = ({ children }) => { 10 | const { data: products, isLoading } = useProducts(); 11 | const [productList, setProductList] = useState([]); 12 | const [showCart, setShowCart] = useState(false); 13 | const categories = getCategories(products); 14 | 15 | const [selectedCategories, setSelectedCategories] = useState([]); 16 | 17 | const [cartItems, setCartItems] = useState(() => { 18 | const carts = localStorage.getItem('carts'); 19 | if (carts) { 20 | return JSON.parse(carts); 21 | } else { 22 | return []; 23 | } 24 | }); 25 | 26 | function toggleShowCart() { 27 | setShowCart(!showCart); 28 | console.log(showCart); 29 | } 30 | 31 | function handleFilter(category) { 32 | const newSelectedCategories = selectedCategories.includes(category) 33 | ? selectedCategories.filter((item) => item !== category) 34 | : [...selectedCategories, category]; 35 | console.log(newSelectedCategories); 36 | setSelectedCategories(newSelectedCategories); 37 | const productList = products.filter((product) => 38 | newSelectedCategories.includes(product.category) 39 | ); 40 | if (newSelectedCategories.length > 0) { 41 | setProductList(productList); 42 | } else { 43 | setProductList(products); 44 | } 45 | } 46 | function handleClear() { 47 | setProductList(products); 48 | setSelectedCategories([]); 49 | } 50 | function handleAddToCart(productId) { 51 | const product = products.find((item) => item.id === productId); 52 | const newCartItems = [...cartItems, { ...product, quantity: 1 }]; 53 | if (!cartItems.includes(productId)) { 54 | setCartItems(newCartItems); 55 | } 56 | localStorage.setItem('carts', JSON.stringify(newCartItems)); 57 | } 58 | function increaseQuantity(id) { 59 | const newCartItems = cartItems.map((item) => 60 | item.id === id ? { ...item, quantity: item.quantity + 1 } : item 61 | ); 62 | setCartItems(newCartItems); 63 | localStorage.setItem('carts', JSON.stringify(newCartItems)); 64 | } 65 | function decreaseQuantity(id) { 66 | const newCartItems = cartItems.map((item) => 67 | item.id === id ? { ...item, quantity: item.quantity - 1 } : item 68 | ); 69 | setCartItems(newCartItems); 70 | localStorage.setItem('carts', JSON.stringify(newCartItems)); 71 | } 72 | function removeItem(id) { 73 | const newCartItems = cartItems.filter((item) => item.id !== id); 74 | setCartItems(newCartItems); 75 | localStorage.setItem('carts', JSON.stringify(newCartItems)); 76 | } 77 | 78 | useEffect(() => { 79 | setProductList(products); 80 | }, [products]); 81 | 82 | return ( 83 | 100 | {children} 101 | 102 | ); 103 | }; 104 | 105 | export const useEcommerceContext = () => { 106 | const context = useContext(ecommerceContext); 107 | if (!context) { 108 | throw Error( 109 | 'useEcommerce context should be used within Ecommerce component' 110 | ); 111 | } 112 | return context; 113 | }; 114 | -------------------------------------------------------------------------------- /src/components/thanwin/ecommerce/hooks/useProducts.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | const url = 4 | 'https://api.freeapi.app/api/v1/public/randomproducts?page=1&limit=20'; 5 | export const useProducts = () => { 6 | const [state, setState] = useState({ 7 | data: null, 8 | isLoading: false, 9 | error: false, 10 | isError: false, 11 | }); 12 | 13 | useEffect(() => { 14 | async function getData() { 15 | try { 16 | setState((prev) => ({ ...prev, isLoading: true })); 17 | const response = await fetch(url); 18 | const data = await response.json(); 19 | 20 | setState((prev) => ({ 21 | ...prev, 22 | isLoading: false, 23 | data: data.data.data, 24 | })); 25 | } catch (error) { 26 | setState((prev) => ({ 27 | ...prev, 28 | isLoading: false, 29 | isError: true, 30 | error: error, 31 | })); 32 | } 33 | } 34 | getData(); 35 | }, []); 36 | 37 | return { 38 | ...state, 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/thanwin/grade-calculator/GradeCalculator.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import Input from './Input'; 3 | function calculateGrade(marks) { 4 | if (marks >= 90 && marks <= 100) { 5 | return 'A'; 6 | } else if (marks >= 80 && marks <= 89) { 7 | return 'B'; 8 | } else if (marks >= 70 && marks <= 79) { 9 | return 'C'; 10 | } else if (marks >= 60 && marks <= 69) { 11 | return 'D'; 12 | } else if (marks >= 0 && marks <= 59) { 13 | return 'F'; 14 | } else { 15 | return 'Not Valid Score'; 16 | } 17 | } 18 | const GradeCalculator = () => { 19 | const [marks, setMarks] = useState(''); 20 | const [grade, setGrade] = useState(null); 21 | const invalidScore = grade?.includes('Not'); 22 | function handleSubmit(e) { 23 | e.preventDefault(); 24 | const grade = calculateGrade(Number(marks)); 25 | setGrade(grade); 26 | } 27 | function reset() { 28 | setGrade(null); 29 | setMarks(''); 30 | } 31 | return ( 32 |
    33 |

    34 | Grade Calculator 35 |

    36 |
    37 | setMarks(e.target.value)} 41 | /> 42 |
    43 | 46 | 55 |
    56 |
    57 | {grade && !invalidScore && ( 58 |

    59 | Your grade is {grade} 60 |

    61 | )} 62 | {grade && invalidScore && ( 63 |

    64 | Please enter a valid score 65 |

    66 | )} 67 |
    68 | ); 69 | }; 70 | 71 | export default GradeCalculator; 72 | -------------------------------------------------------------------------------- /src/components/thanwin/grade-calculator/Input.jsx: -------------------------------------------------------------------------------- 1 | const Input = ({ type = 'text', ...props }) => { 2 | return ( 3 | 8 | ); 9 | }; 10 | 11 | export default Input; 12 | -------------------------------------------------------------------------------- /src/components/thanwin/index.js: -------------------------------------------------------------------------------- 1 | import Counter from './counter/Counter'; 2 | import GradeCalculator from './grade-calculator/GradeCalculator'; 3 | import Ecommerce from './ecommerce/Ecommerce'; 4 | 5 | import NumberComparator from './number-comparator/NumberComparator'; 6 | export { Counter, GradeCalculator, NumberComparator, Ecommerce }; 7 | -------------------------------------------------------------------------------- /src/components/thanwin/number-comparator/Input.jsx: -------------------------------------------------------------------------------- 1 | export default function Input({ 2 | label, 3 | name, 4 | value, 5 | placeholder, 6 | type = 'text', 7 | ...props 8 | }) { 9 | return ( 10 |
    11 | {label && } 12 | 20 |
    21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/thanwin/number-comparator/NumberComparator.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import Input from './Input'; 3 | 4 | function compareNumbers(firstNumber, secondNumber) { 5 | if (firstNumber > secondNumber) { 6 | return 'First Number is greater than second Number'; 7 | } else if (firstNumber === secondNumber) { 8 | return 'Both numbers are equal'; 9 | } else { 10 | return 'Second number is greater than first number'; 11 | } 12 | } 13 | const NumberComparator = () => { 14 | const [numbers, setNumbers] = useState({ 15 | firstNumber: '', 16 | secondNumber: '', 17 | }); 18 | const [result, setResult] = useState(''); 19 | const [isReset, setIsReset] = useState(false); 20 | const isEmpty = numbers.firstNumber === '' || numbers.secondNumber === ''; 21 | 22 | function handleChange(e) { 23 | setNumbers((prev) => ({ 24 | ...prev, 25 | [e.target.name]: e.target.value, 26 | })); 27 | setIsReset(false); 28 | setResult(''); 29 | } 30 | function handleSubmit(e) { 31 | e.preventDefault(); 32 | setResult( 33 | compareNumbers(Number(numbers.firstNumber), Number(numbers.secondNumber)) 34 | ); 35 | 36 | setIsReset(true); 37 | } 38 | function handleReset() { 39 | setNumbers({ firstNumber: '', secondNumber: '' }); 40 | setIsReset(false); 41 | setResult(''); 42 | } 43 | 44 | return ( 45 |
    46 |
    47 |

    48 | Number Comparator 49 |

    50 |
    51 | 58 | 65 |
    66 | {!isReset ? ( 67 | 77 | ) : ( 78 | 85 | )} 86 |
    87 | {result !== '' && ( 88 |

    {result}

    89 | )} 90 |
    91 | ); 92 | }; 93 | 94 | export default NumberComparator; 95 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | export const appConfig = { 2 | BASE_ROUTE: process.env.NODE_ENV === 'production' ? '/react-fall-2024' : '', 3 | }; 4 | -------------------------------------------------------------------------------- /src/data/index.js: -------------------------------------------------------------------------------- 1 | export const projects = [ 2 | { 3 | id: 'counter-app', 4 | name: 'Counter App', 5 | description: 'A simple counter app.', 6 | contributors: [ 7 | 'bongoDev', 8 | 'talha', 9 | 'nazma', 10 | 'abu taher', 11 | 'thanwin', 12 | 'promi', 13 | 'sumiya' 14 | ], 15 | }, 16 | { 17 | id: 'number-comparator', 18 | name: 'Number Comparator', 19 | description: 'A simple number comparator app.', 20 | contributors: ['bongoDev', 'shobuz', 'sumiya', 'thanwin', 'promi','talha'], 21 | }, 22 | { 23 | id: 'grade-calculator', 24 | name: 'Grade Calculator', 25 | description: 'A simple grade calculator app.', 26 | contributors: ['thanwin', 'promi','talha','sumiya'], 27 | }, 28 | { 29 | id: 'multiplication-table', 30 | name: 'Multiplication Table', 31 | description: 'A simple multiplication table app.', 32 | contributors: ['talha','sumiya'], 33 | }, 34 | { 35 | id: 'render-products', 36 | name: 'Render Products', 37 | description: 'A simple product rendering app using useEffect hook.', 38 | contributors: ['bongoDev', 'thanwin', 'promi','talha','sumiya'], 39 | }, 40 | { 41 | id: 'simple-ecommerce', 42 | name: 'A simple Ecommerce project', 43 | description: 'A simple Ecommerce project', 44 | contributors: ['bongoDev', 'thanwin', 'promi','talha'], 45 | }, 46 | ]; 47 | -------------------------------------------------------------------------------- /src/hooks/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './useProjects'; 2 | -------------------------------------------------------------------------------- /src/hooks/useProjects.jsx: -------------------------------------------------------------------------------- 1 | import { projects } from '@/data'; 2 | 3 | export const useProjects = () => { 4 | const getProject = (projectId) => 5 | projects.find((project) => project.id === projectId); 6 | 7 | return { 8 | projects, 9 | getProject, 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import { BrowserRouter, Route, Routes } from 'react-router'; 4 | 5 | import App from './App'; 6 | import { AboutPage, ProjectLayout, RootLayout } from '@/pages'; 7 | import { BongoDevProjectPage } from '@/pages/bongodev'; 8 | import { ThanWinProjectPage } from '@/pages/thanwin'; 9 | import { TalhaProjectPage } from '@/pages/talha'; 10 | // import { SumiyaProjectPage } from '@/pages/sumiya'; 11 | 12 | import { appConfig } from './config'; 13 | 14 | import './index.css'; 15 | import { SumiyaProjectPage } from './pages/sumiya/SumiyaProjectPage'; 16 | 17 | const root = createRoot(document.getElementById('react-app-root')); 18 | root.render( 19 | 20 | 21 | 22 | }> 23 | } /> 24 | }> 25 | } /> 26 | } /> 27 | } /> 28 | } /> 29 | 30 | } /> 31 | 32 | 33 | 34 | 35 | ); 36 | -------------------------------------------------------------------------------- /src/pages/RootLayout.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router'; 2 | import { Sidebar } from '../components'; 3 | 4 | export const RootLayout = () => { 5 | return ( 6 |
    7 |
    8 | 9 |
    10 |
    11 | 12 |
    13 |
    14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/pages/about/Page.jsx: -------------------------------------------------------------------------------- 1 | export const AboutPage = () => { 2 | return
    AboutPage
    ; 3 | }; 4 | -------------------------------------------------------------------------------- /src/pages/bongodev/Page.jsx: -------------------------------------------------------------------------------- 1 | import { useParams } from 'react-router'; 2 | import { 3 | Counter, 4 | NumberComparator, 5 | RenderProducts, 6 | } from '@/components/bongodev'; 7 | 8 | export const BongoDevProjectPage = () => { 9 | const { projectId } = useParams(); 10 | 11 | const componentMap = { 12 | 'counter-app': , 13 | 'render-products': , 14 | 'number-comparator': , 15 | }; 16 | 17 | const selectedProject = componentMap[projectId]; 18 | 19 | if (!selectedProject) { 20 | return
    Project not found
    ; 21 | } 22 | 23 | return
    {selectedProject}
    ; 24 | }; 25 | -------------------------------------------------------------------------------- /src/pages/bongodev/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './Page'; 2 | -------------------------------------------------------------------------------- /src/pages/index.jsx: -------------------------------------------------------------------------------- 1 | export * from './about/Page'; 2 | export * from './RootLayout'; 3 | export * from './projects/ProjectLayout'; 4 | -------------------------------------------------------------------------------- /src/pages/projects/ProjectLayout.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet, useParams } from 'react-router'; 2 | import Select from 'react-select'; 3 | 4 | import { useProjects } from '@/hooks'; 5 | import { useContributor } from './useContributor'; 6 | 7 | const options = [ 8 | { value: 'bongodev', label: 'bongoDev' }, 9 | { value: 'talha', label: 'Talha' }, 10 | { value: 'sumiya', label: 'Sumiya' }, 11 | { value: 'thanwin', label: 'Than Win' }, 12 | ]; 13 | 14 | export const ProjectLayout = () => { 15 | const { projectId } = useParams(); 16 | 17 | const { getProject } = useProjects(); 18 | const { selectedContributor, handleContributorChange } = useContributor({ 19 | projectId, 20 | }); 21 | 22 | const project = getProject(projectId); 23 | 24 | if (!project) { 25 | return
    404 | Project not found
    ; 26 | } 27 | 28 | return ( 29 |
    30 |
    31 |
    32 |

    {project.name}

    33 |