├── .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 | 
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 |
--------------------------------------------------------------------------------