├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── requirements.txt └── src ├── App.css ├── App.js ├── App.test.js ├── ai_agent ├── __init__.py ├── config │ ├── agents.yaml │ └── tasks.yaml ├── crew.py └── tools │ └── __init__.py ├── components ├── PDFUploader.js └── QuizDisplay.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js /.env.example: -------------------------------------------------------------------------------- 1 | GEMINI_API_KEY= 2 | CREWAI_STORAGE_DIR= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | 26 | .env 27 | __pycache__/ 28 | .venv 29 | venv 30 | *.log 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PDF Quiz Generator 2 | 3 | ![Project Banner](https://ocdn.eu/pulscms-transforms/1/dfuk9kpTURBXy9kMjk5NGQzMzE4MjJmOTY3ODU3ZTJhMDUzZDZmNzg3MC5qcGeTlQMAzQFFzQoozQW2lQLNBLAAw8OTCaZiMDJlN2YG3gABoTAB/quiz.jpeg) 4 | 5 | A modern web application that generates multiple-choice quizzes with explanations from uploaded PDF documents. Built with **React** for the frontend and **FastAPI** with **CrewAI** for the backend, this tool leverages AI to create educational quizzes seamlessly. 6 | 7 | --- 8 | 9 | ## Features 10 | 11 | - **PDF Upload**: Upload any text-based PDF to generate a quiz. 12 | - **AI-Powered Quiz Generation**: Uses CrewAI with the Gemini LLM to create multiple-choice questions with four options, a correct answer, and detailed explanations. 13 | - **Interactive UI**: Sleek React interface with clickable options, animations, and a responsive design. 14 | - **Professional Design**: Clean white background with vibrant orange accents and smooth transitions. 15 | - **Error Handling**: Robust validation for PDF content and API responses. 16 | 17 | --- 18 | 19 | ## Tech Stack 20 | 21 | - **Frontend**: React, Axios, Framer Motion 22 | - **Backend**: FastAPI, CrewAI, PyPDF2, Pydantic 23 | - **AI Model**: Google Gemini (`gemini-2.0-flash`) 24 | - **Deployment**: Local development with potential for cloud hosting 25 | 26 | --- 27 | 28 | ## Prerequisites 29 | 30 | Before setting up the project, ensure you have the following installed: 31 | 32 | - **Node.js** (v16+): [Download](https://nodejs.org/) 33 | - **Python** (v3.9+): [Download](https://www.python.org/) 34 | - **Git**: [Download](https://git-scm.com/) 35 | 36 | --- 37 | 38 | 39 | # Project Setup 40 | 41 | ## Clone the Repository: 42 | ```bash 43 | git clone https://github.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React.git 44 | cd PDF-Quiz-Generator-with-AI-and-React 45 | ``` 46 | 47 | ## Create a Virtual Environment: 48 | ```bash 49 | python -m venv venv 50 | source venv/bin/activate # On Windows: venv\Scripts\activate 51 | ``` 52 | 53 | ## Install Dependencies: 54 | ```bash 55 | pip install fastapi uvicorn PyPDF2 crewai pydantic 56 | ``` 57 | 58 | ## Set Environment Variables: 59 | Create a `.env` file in the backend directory: 60 | 61 | ```plaintext 62 | GEMINI_API_KEY=your-gemini-api-key 63 | ``` 64 | Replace `your-gemini-api-key` with your actual Google Gemini API key. 65 | 66 | ## Run the Backend: 67 | ```bash 68 | python crew.py 69 | ``` 70 | The server will start at [http://localhost:8000](http://localhost:8000). 71 | 72 | --- 73 | 74 | # Frontend Setup 75 | 76 | ## Install Dependencies: 77 | ```bash 78 | npm install 79 | ``` 80 | 81 | ## Run the Frontend: 82 | ```bash 83 | npm start 84 | ``` 85 | The React app will start at [http://localhost:3000](http://localhost:3000). 86 | 87 | --- 88 | 89 | # Usage 90 | 91 | ### Start the Backend: 92 | Ensure the FastAPI server is running at [http://localhost:8000](http://localhost:8000). 93 | 94 | ### Launch the Frontend: 95 | Open your browser to [http://localhost:3000](http://localhost:3000). 96 | 97 | ### Upload a PDF: 98 | 1. Click **"Choose PDF"** and select a text-based PDF file. 99 | 2. Wait for the quiz to generate (a loading message will appear). 100 | 101 | ### Take the Quiz: 102 | 1. Click on any option to select an answer. 103 | 2. Press **"Submit Quiz"** to see results with explanations. 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quiz-generator", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/dom": "^10.4.0", 7 | "@testing-library/jest-dom": "^6.6.3", 8 | "@testing-library/react": "^16.2.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "axios": "^1.8.3", 11 | "framer-motion": "^12.5.0", 12 | "react": "^19.0.0", 13 | "react-dom": "^19.0.0", 14 | "react-pdf": "^9.2.1", 15 | "react-scripts": "5.0.1", 16 | "web-vitals": "^2.1.4" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | PyPDF2 4 | crewai 5 | pydantic 6 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #ffffff; 3 | min-height: 100vh; 4 | font-family: 'Arial', sans-serif; 5 | color: #333; 6 | } 7 | 8 | .App { 9 | text-align: center; 10 | padding: 40px; 11 | max-width: 900px; 12 | margin: 0 auto; 13 | } 14 | 15 | h1 { 16 | font-size: 2.8rem; 17 | margin-bottom: 30px; 18 | color: #ff5733; 19 | font-weight: bold; 20 | text-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); 21 | } 22 | 23 | .pdf-uploader { 24 | margin: 30px 0; 25 | } 26 | 27 | .pdf-uploader h3 { 28 | font-size: 1.6rem; 29 | margin-bottom: 20px; 30 | color: #444; 31 | } 32 | 33 | .upload-button { 34 | display: inline-block; 35 | padding: 12px 30px; 36 | background: linear-gradient(45deg, #ff5733, #ff8c33); 37 | color: #fff; 38 | font-weight: bold; 39 | border-radius: 30px; 40 | cursor: pointer; 41 | box-shadow: 0 4px 15px rgba(255, 87, 33, 0.3); 42 | transition: all 0.3s ease; 43 | } 44 | 45 | .upload-button:hover { 46 | background: linear-gradient(45deg, #ff8c33, #ff5733); 47 | box-shadow: 0 6px 20px rgba(255, 87, 33, 0.5); 48 | } 49 | 50 | .upload-button input { 51 | display: none; 52 | } 53 | 54 | .quiz-display h2 { 55 | font-size: 2.2rem; 56 | margin-bottom: 30px; 57 | color: #ff5733; 58 | } 59 | 60 | .question-card { 61 | background: #f9f9f9; 62 | border-radius: 15px; 63 | padding: 25px; 64 | margin-bottom: 25px; 65 | box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); 66 | transition: all 0.3s ease; 67 | } 68 | 69 | .question-card:hover { 70 | box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1); 71 | } 72 | 73 | .question-card h3 { 74 | font-size: 1.4rem; 75 | margin-bottom: 20px; 76 | color: #333; 77 | } 78 | 79 | .option { 80 | display: flex; 81 | align-items: center; 82 | margin: 12px 0; 83 | padding: 12px 15px; 84 | border-radius: 10px; 85 | background: #fff; 86 | cursor: pointer; 87 | border: 2px solid #ddd; 88 | transition: all 0.2s ease; 89 | } 90 | 91 | .option:hover { 92 | border-color: #ff5733; 93 | } 94 | 95 | .option.selected { 96 | background: #ff5733; 97 | color: #fff; 98 | border-color: #ff5733; 99 | box-shadow: 0 4px 10px rgba(255, 87, 33, 0.3); 100 | } 101 | 102 | .option input { 103 | margin-right: 12px; 104 | cursor: pointer; 105 | } 106 | 107 | .option span { 108 | flex-grow: 1; 109 | } 110 | 111 | .result { 112 | margin-top: 20px; 113 | padding: 15px; 114 | background: #f0f0f0; 115 | border-radius: 10px; 116 | } 117 | 118 | .correct { 119 | color: #28a745; 120 | font-weight: bold; 121 | } 122 | 123 | .incorrect { 124 | color: #dc3545; 125 | font-weight: bold; 126 | } 127 | 128 | .submit-button { 129 | margin-top: 25px; 130 | padding: 12px 35px; 131 | background: linear-gradient(45deg, #ff5733, #ff8c33); 132 | border: none; 133 | border-radius: 30px; 134 | color: #fff; 135 | font-size: 1.2rem; 136 | font-weight: bold; 137 | cursor: pointer; 138 | box-shadow: 0 4px 15px rgba(255, 87, 33, 0.3); 139 | transition: all 0.3s ease; 140 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import PDFUploader from './components/PDFUploader'; 3 | import QuizDisplay from './components/QuizDisplay'; 4 | import axios from 'axios'; 5 | import './App.css'; 6 | 7 | function App() { 8 | const [quizData, setQuizData] = useState(null); 9 | const [pdfFile, setPdfFile] = useState(null); 10 | const [loading, setLoading] = useState(false); 11 | 12 | const generateQuiz = async (file) => { 13 | setLoading(true); 14 | const formData = new FormData(); 15 | formData.append('file', file); 16 | 17 | try { 18 | const response = await axios.post('http://localhost:8000/generate-quiz', formData, { 19 | headers: { 'Content-Type': 'multipart/form-data' } 20 | }); 21 | console.log('API Response:', response.data); // Log the response for debugging 22 | if (response.data.error) { 23 | alert(response.data.error); 24 | } else { 25 | setQuizData(response.data); 26 | } 27 | } catch (error) { 28 | console.error('Error details:', error.response ? error.response.data : error.message); 29 | alert('Failed to generate quiz. Please ensure the server is running and try again.'); 30 | } finally { 31 | setLoading(false); 32 | } 33 | }; 34 | 35 | const handleFileUpload = (file) => { 36 | setPdfFile(file); 37 | generateQuiz(file); 38 | }; 39 | 40 | return ( 41 |
42 |

PDF Quiz Generator

43 | 44 | {loading &&

Generating quiz, please wait...

} 45 | {quizData && !loading && } 46 |
47 | ); 48 | } 49 | 50 | export default App; -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/ai_agent/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/src/ai_agent/__init__.py -------------------------------------------------------------------------------- /src/ai_agent/config/agents.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/src/ai_agent/config/agents.yaml -------------------------------------------------------------------------------- /src/ai_agent/config/tasks.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/src/ai_agent/config/tasks.yaml -------------------------------------------------------------------------------- /src/ai_agent/crew.py: -------------------------------------------------------------------------------- 1 | import os 2 | from fastapi import FastAPI, UploadFile, File, HTTPException 3 | from fastapi.middleware.cors import CORSMiddleware 4 | from crewai import LLM, Agent, Crew, Process, Task 5 | import PyPDF2 # type: ignore 6 | import json 7 | import logging 8 | from pydantic import BaseModel, Field 9 | from typing import List 10 | 11 | # Set up logging 12 | logging.basicConfig(level=logging.INFO) 13 | logger = logging.getLogger(__name__) 14 | 15 | app = FastAPI() 16 | 17 | app.add_middleware( 18 | CORSMiddleware, 19 | allow_origins=["http://localhost:3000"], 20 | allow_credentials=True, 21 | allow_methods=["*"], 22 | allow_headers=["*"], 23 | ) 24 | 25 | # Define Pydantic models for JSON output structure 26 | class QuizQuestion(BaseModel): 27 | question: str = Field(..., description="The quiz question") 28 | options: List[str] = Field(..., min_items=4, max_items=4, description="Four answer options") 29 | correctAnswer: str = Field(..., description="The correct answer") 30 | explanation: str = Field(..., description="Explanation of the correct answer") 31 | 32 | class QuizOutput(BaseModel): 33 | questions: List[QuizQuestion] = Field(..., description="List of quiz questions") 34 | 35 | # Load API key for Gemini 36 | google_api_key = os.getenv("GEMINI_API_KEY") 37 | if not google_api_key: 38 | raise ValueError("GEMINI_API_KEY environment variable not set.") 39 | llm = LLM(model="gemini/gemini-2.0-flash", temperature=0, api_key=google_api_key) 40 | 41 | # Define Quiz Generator Agent 42 | quiz_agent = Agent( 43 | role="Quiz Generator", 44 | goal="Analyze PDF content and generate accurate multiple-choice quizzes with explanations.", 45 | backstory=( 46 | "You are an expert in educational content creation, skilled at extracting key information from documents " 47 | "and transforming it into engaging multiple-choice quizzes. You ensure each question has four options, " 48 | "a correct answer, and a detailed explanation based on the provided content." 49 | ), 50 | llm=llm, 51 | verbose=True 52 | ) 53 | 54 | # Define Quiz Generation Task with output_json 55 | quiz_task = Task( 56 | description=( 57 | "Analyze the following PDF content and generate a multiple-choice quiz: {pdf_content}. " 58 | "Create as questions as possible, each with:\n" 59 | "1. A clear, concise question.\n" 60 | "2. Four distinct answer options (one correct, three plausible distractors).\n" 61 | "3. A correct answer.\n" 62 | "4. An explanation based on the PDF content.\n" 63 | "Return the result as a JSON object with a 'questions' key. " 64 | "If the content is insufficient, return {{'error': 'Insufficient content to generate a quiz.'}}." 65 | ), 66 | expected_output=( 67 | "A JSON object with a 'questions' array containing quiz questions, or an error message." 68 | ), 69 | agent=quiz_agent, 70 | output_json=QuizOutput # Enforce JSON output with Pydantic model 71 | ) 72 | 73 | # Define Crew 74 | crew = Crew( 75 | agents=[quiz_agent], 76 | tasks=[quiz_task], 77 | verbose=True, 78 | process=Process.sequential 79 | ) 80 | 81 | def run(input): 82 | 83 | try: 84 | logger.info(f"Running CrewAI with input: {input['pdf_content'][:100]}...") 85 | result = crew.kickoff(inputs=input) 86 | logger.info(f"Raw CrewAI result: {result}") 87 | 88 | # Handle CrewOutput object 89 | if hasattr(result, 'json'): # CrewOutput with json attribute 90 | return json.loads(result.json) 91 | elif hasattr(result, 'raw'): # Check for raw attribute as fallback 92 | return json.loads(result.raw) 93 | elif isinstance(result, dict): # Direct dict output 94 | return result 95 | elif isinstance(result, str): # Stringified JSON 96 | return json.loads(result) 97 | else: 98 | logger.error(f"Unexpected result type: {type(result)}") 99 | return {"error": "Invalid quiz format returned from CrewAI."} 100 | except json.JSONDecodeError as e: 101 | logger.error(f"JSON decode error: {str(e)}") 102 | return {"error": "Failed to parse quiz output as JSON."} 103 | except Exception as e: 104 | logger.error(f"Error in run: {str(e)}") 105 | return {"error": f"Failed to generate quiz: {str(e)}"} 106 | 107 | @app.post("/generate-quiz") 108 | async def generate_quiz(file: UploadFile = File(...)): 109 | if not file.filename.endswith('.pdf'): 110 | raise HTTPException(status_code=400, detail="Only PDF files are supported.") 111 | 112 | try: 113 | logger.info("Processing uploaded PDF") 114 | pdf_reader = PyPDF2.PdfReader(file.file) 115 | pdf_content = "" 116 | for page in pdf_reader.pages: 117 | text = page.extract_text() 118 | if text: 119 | pdf_content += text + "\n" 120 | 121 | if not pdf_content.strip(): 122 | logger.warning("No readable content found in PDF") 123 | return {"error": "No readable content found in the PDF. Please upload a valid document."} 124 | 125 | logger.info(f"Extracted PDF content: {pdf_content[:100]}...") 126 | input_data = {"pdf_content": pdf_content} 127 | quiz_result = run(input_data) 128 | logger.info(f"Quiz result: {quiz_result}") 129 | return quiz_result 130 | 131 | except Exception as e: 132 | logger.error(f"Error processing PDF: {str(e)}") 133 | raise HTTPException(status_code=500, detail=f"Error processing PDF: {str(e)}") 134 | 135 | if __name__ == "__main__": 136 | import uvicorn 137 | uvicorn.run("crew:app", host="0.0.0.0", port=8000, reload=True) -------------------------------------------------------------------------------- /src/ai_agent/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullah-khaled0/PDF-Quiz-Generator-with-AI-and-React/4583ee5bb3954c305575c5df9b59af433d661e83/src/ai_agent/tools/__init__.py -------------------------------------------------------------------------------- /src/components/PDFUploader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { motion } from 'framer-motion'; 3 | 4 | function PDFUploader({ onFileUpload }) { 5 | const handleFileChange = (event) => { 6 | const file = event.target.files[0]; 7 | if (file && file.type === 'application/pdf') { 8 | onFileUpload(file); 9 | } else { 10 | alert('Please upload a valid PDF file.'); 11 | } 12 | }; 13 | 14 | return ( 15 | 21 |

Upload Your PDF

22 | 26 |
27 | ); 28 | } 29 | 30 | export default PDFUploader; -------------------------------------------------------------------------------- /src/components/QuizDisplay.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { motion } from 'framer-motion'; 3 | 4 | function QuizDisplay({ quizData }) { 5 | const [selectedAnswers, setSelectedAnswers] = useState({}); 6 | const [showResults, setShowResults] = useState(false); 7 | 8 | const handleOptionChange = (questionIndex, option) => { 9 | if (!showResults) { 10 | setSelectedAnswers({ 11 | ...selectedAnswers, 12 | [questionIndex]: option 13 | }); 14 | } 15 | }; 16 | 17 | const handleSubmit = () => { 18 | setShowResults(true); 19 | }; 20 | 21 | return ( 22 | 28 |

Generated Quiz

29 | {quizData.questions.map((q, index) => ( 30 | 37 |

{q.question}

38 | {q.options.map((option, optIndex) => ( 39 | handleOptionChange(index, option)} 43 | whileHover={{ scale: 1.03, boxShadow: '0 5px 15px rgba(0, 0, 0, 0.1)' }} 44 | whileTap={{ scale: 0.97 }} 45 | transition={{ duration: 0.2 }} 46 | > 47 | {}} // Prevent default radio behavior since we handle clicks manually 53 | disabled={showResults} 54 | /> 55 | {option} 56 | 57 | ))} 58 | {showResults && ( 59 | 65 |

66 | Your Answer: {selectedAnswers[index] || 'None'} 67 |

68 |

69 | Correct Answer: {q.correctAnswer} 70 |

71 |

72 | Explanation: {q.explanation} 73 |

74 |
75 | )} 76 |
77 | ))} 78 | {!showResults && ( 79 | 85 | Submit Quiz 86 | 87 | )} 88 |
89 | ); 90 | } 91 | 92 | export default QuizDisplay; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------