├── .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 |
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 |
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 | Table of {multiplicationTable[0].multiplicand}
37 |
38 |
39 | Multiplicand |
40 | |
41 | Multiplier |
42 | |
43 | Product |
44 |
45 |
46 |
47 | {multiplicationTable.map((row, index)=>(
48 |
49 | {row.multiplicand} |
50 | X |
51 | {row.multiplier} |
52 | = |
53 | {row.product} |
54 |
55 | ))}
56 |
57 |
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 |
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)=>{column} | )}
18 |
19 |
20 | {products.map((product)=>(
21 |
22 | {columns.map((column)=>({product[column]} | ))}
23 |
24 | ))
25 | }
26 |
27 |
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 |
87 | {value}
88 | |
89 | ))}
90 |
91 | ))}
92 |
93 |
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 |
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 |
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 |
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 |
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 |
39 |
40 |
{project.description}
41 |
42 |
43 |
44 |
45 |
46 | );
47 | };
48 |
--------------------------------------------------------------------------------
/src/pages/projects/useContributor.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useNavigate } from 'react-router';
3 | import { appConfig } from '../../config';
4 |
5 | export const useContributor = ({ projectId }) => {
6 | const navigate = useNavigate();
7 | const [selectedContributor, setSelectedContributor] = useState(null);
8 |
9 | useEffect(() => {
10 | setSelectedContributor(null);
11 | }, [projectId]);
12 |
13 | const handleContributorChange = (selectedOption) => {
14 | setSelectedContributor(selectedOption);
15 | navigate(
16 | `${appConfig.BASE_ROUTE}/projects/${projectId}/${selectedOption.value}`
17 | );
18 | };
19 |
20 | return {
21 | selectedContributor,
22 | handleContributorChange,
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/src/pages/promi/PromiPage.jsx:
--------------------------------------------------------------------------------
1 | import { useParams } from 'react-router';
2 | import { Counter, GradeCalculator , NumberComparator} from '@/components/promi';
3 |
4 |
5 | export const PromiPage = () => {
6 | const { projectId } = useParams();
7 |
8 | const componentMap = {
9 | 'counter-app': ,
10 | 'grade-calculator': ,
11 | 'number-comparator':
12 | };
13 |
14 | const selectedProject = componentMap[projectId];
15 |
16 | if (!selectedProject) {
17 | return Project not found
;
18 | }
19 |
20 | return {selectedProject}
;
21 | };
22 |
--------------------------------------------------------------------------------
/src/pages/promi/index.js:
--------------------------------------------------------------------------------
1 | export * from './PromiPage';
2 |
--------------------------------------------------------------------------------
/src/pages/sumiya/SumiyaProjectPage.jsx:
--------------------------------------------------------------------------------
1 | import { useParams } from 'react-router';
2 | import { Counter, NumberComparator, ThemeProvider } from '../../components/sumiya';
3 | import { GradeCalculator } from '../../components/sumiya/grade-calculator';
4 | import MultiplicationTable from '../../components/sumiya/multiplication-table/MultiplicationTable';
5 | import { App } from '../../components/sumiya/render-products';
6 |
7 |
8 |
9 |
10 | export const SumiyaProjectPage = () => {
11 | const { projectId } = useParams();
12 |
13 | const componentMap = {
14 | 'counter-app':
15 |
16 | ,
17 | ,
18 | 'number-comparator': ,
19 | 'grade-calculator' :
20 |
21 |
22 | ,
23 | 'multiplication-table': ,
24 | 'render-products':
25 | };
26 |
27 | const selectedProject = componentMap[projectId];
28 |
29 | if (!selectedProject) {
30 | return Project not found
;
31 | }
32 |
33 | return {selectedProject}
;
34 | };
35 |
--------------------------------------------------------------------------------
/src/pages/sumiya/index.js:
--------------------------------------------------------------------------------
1 | export * from './SumiyaProjectPage'
--------------------------------------------------------------------------------
/src/pages/talha/TalhaProjectPage.jsx:
--------------------------------------------------------------------------------
1 | import { useParams } from 'react-router';
2 | import { Counter, NumberComparator,GradeCalculator,MultiplicationTable,RenderProducts } from '@/components/talha';
3 |
4 |
5 | export const TalhaProjectPage = () => {
6 | const { projectId } = useParams();
7 |
8 | const componentMap = {
9 | 'counter-app': ,
10 | 'number-comparator': ,
11 | 'grade-calculator': ,
12 | 'multiplication-table': ,
13 | 'render-products': ,
14 | };
15 |
16 | const selectedProject = componentMap[projectId];
17 |
18 | if (!selectedProject) {
19 | return Project not found
;
20 | }
21 |
22 | return {selectedProject}
;
23 | };
24 |
--------------------------------------------------------------------------------
/src/pages/talha/index.jsx:
--------------------------------------------------------------------------------
1 | export * from './TalhaProjectPage';
--------------------------------------------------------------------------------
/src/pages/thanwin/ThanWinProjectPage.jsx:
--------------------------------------------------------------------------------
1 | import { useParams } from 'react-router';
2 | import {
3 | Counter,
4 | NumberComparator,
5 | GradeCalculator,
6 | Ecommerce,
7 | } from '@/components/thanwin';
8 |
9 | export const ThanWinProjectPage = () => {
10 | const { projectId } = useParams();
11 |
12 | const componentMap = {
13 | 'counter-app': ,
14 | 'grade-calculator': ,
15 | 'number-comparator': ,
16 | 'simple-ecommerce': ,
17 | };
18 |
19 | const selectedProject = componentMap[projectId];
20 |
21 | if (!selectedProject) {
22 | return Project not found
;
23 | }
24 |
25 | return {selectedProject}
;
26 | };
27 |
--------------------------------------------------------------------------------
/src/pages/thanwin/index.js:
--------------------------------------------------------------------------------
1 | import { ThanWinProjectPage } from './ThanWinProjectPage';
2 |
3 | export { ThanWinProjectPage };
4 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | };
9 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { defineConfig } from 'vite';
3 | import react from '@vitejs/plugin-react';
4 |
5 | export default defineConfig({
6 | // ...
7 | plugins: [react()],
8 | resolve: {
9 | alias: {
10 | '@': path.resolve(__dirname, './src/'),
11 | },
12 | },
13 | server: {
14 | port: 3000,
15 | },
16 | base: process.env.NODE_ENV === 'production' ? '/react-fall-2024/' : '/',
17 | });
18 |
--------------------------------------------------------------------------------