diff --git a/README.md b/README.md index b15a9ef64..0f5b73fb4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +#Website : https://l4phs.github.io/starter_helpi/ + # Getting Started with Helpi Packages Included: diff --git a/package-lock.json b/package-lock.json index aa66f461c..be43a888e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,10 @@ "@types/node": "^16.18.76", "@types/react": "^18.2.48", "@types/react-dom": "^18.2.18", + "axios": "^1.6.8", "bootstrap": "^5.3.2", "gh-pages": "^6.1.1", - "openai": "^4.26.0", + "openai": "^4.38.3", "react": "^18.2.0", "react-bootstrap": "^2.10.1", "react-dom": "^18.2.0", @@ -5004,6 +5005,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -5225,11 +5236,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5561,14 +5567,6 @@ "node": ">=10" } }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -5989,14 +5987,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -6695,15 +6685,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/digest-fetch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", - "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", - "dependencies": { - "base-64": "^0.1.0", - "md5": "^2.3.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -8178,9 +8159,9 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -9323,11 +9304,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -11139,16 +11115,6 @@ "tmpl": "1.0.5" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -11767,15 +11733,14 @@ } }, "node_modules/openai": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.28.4.tgz", - "integrity": "sha512-RNIwx4MT/F0zyizGcwS+bXKLzJ8QE9IOyigDG/ttnwB220d58bYjYFp0qjvGwEFBO6+pvFVIDABZPGDl46RFsg==", + "version": "4.38.3", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.38.3.tgz", + "integrity": "sha512-mIL9WtrFNOanpx98mJ+X/wkoepcxdqqu0noWFoNQHl/yODQ47YM7NEYda7qp8JfjqpLFVxY9mQhshoS/Fqac0A==", "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", - "digest-fetch": "^1.3.0", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7", @@ -13464,6 +13429,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index d2189d12e..6d716a2aa 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,10 @@ "@types/node": "^16.18.76", "@types/react": "^18.2.48", "@types/react-dom": "^18.2.18", + "axios": "^1.6.8", "bootstrap": "^5.3.2", "gh-pages": "^6.1.1", - "openai": "^4.26.0", + "openai": "^4.38.3", "react": "^18.2.0", "react-bootstrap": "^2.10.1", "react-dom": "^18.2.0", diff --git a/public/index.html b/public/index.html index b5db6414a..590cb813d 100644 --- a/public/index.html +++ b/public/index.html @@ -26,6 +26,7 @@ --> React App + @import + url('https://fonts.googleapis.com/css2?family=Varela+Round&display=swap') +; + +//Lauren Pham +//Mantra Yang +//David Bui +//Neil Irungu //local storage and API Key: key should be entered in by the user and will be stored in local storage (NOT session storage) let keyData = ""; @@ -11,43 +24,68 @@ if (prevKey !== null) { keyData = JSON.parse(prevKey); } -function App() { +function App(): JSX.Element { const [key, setKey] = useState(keyData); //for api key input - + //setting states for each page + + //sets the local storage item to the api key the user inputed function handleSubmit() { localStorage.setItem(saveKeyData, JSON.stringify(key)); window.location.reload(); //when making a mistake and changing the key again, I found that I have to reload the whole site before openai refreshes what it has stores for the local storage variable } + //adding a comment //whenever there's a change it'll store the api key in a local state called key but it won't be set in the local storage until the user clicks the submit button function changeKey(event: React.ChangeEvent) { setKey(event.target.value); } + + const [page, setPage] = useState("HomePage"); + return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
- API Key: - -

- -
-
+ +
+
    +
    + {/*
  • Sign In
  • */} +
  • setPage("DQPage")}> + {" "} + Detailed Questions{" "} +
  • +
  • setPage("BQPage")}> + {" "} + Basic Questions{" "} +
  • +
  • setPage("HomePage")}> + {" "} + Home{" "} +
  • +
  • +
    + + +
  • +
  • + Submit +
  • +
    +
  • setPage("HomePage")}> Jobspresso +
  • +
+
+ + {page === "HomePage" && } + + {page === "BQPage" && } + + {page === "DQPage" && } +
); } -export default App; +export default App; \ No newline at end of file diff --git a/src/CoffeeShopBKG.jpg b/src/CoffeeShopBKG.jpg new file mode 100644 index 000000000..70a614bcb Binary files /dev/null and b/src/CoffeeShopBKG.jpg differ diff --git a/src/DCoffeeShop.jpeg b/src/DCoffeeShop.jpeg new file mode 100644 index 000000000..35b592ec8 Binary files /dev/null and b/src/DCoffeeShop.jpeg differ diff --git a/src/Pages/BASE.png b/src/Pages/BASE.png new file mode 100644 index 000000000..fc7bb0fb1 Binary files /dev/null and b/src/Pages/BASE.png differ diff --git a/src/Pages/BQPage.css b/src/Pages/BQPage.css new file mode 100644 index 000000000..a93a9e0da --- /dev/null +++ b/src/Pages/BQPage.css @@ -0,0 +1,307 @@ +.Bbody { + height: 100vh; + width: 100vw; + /* background-image: url("BKGBase.png"); */ +} + +.Bbody .text { + display: block; + position: absolute; + font-family: "Lilita One"; + text-align: center; + top: 10vh; + font-size: 30px; + left: 11vw; +} + +.Background{ + height: 125vh; + background-image: url(MatchaBQ.jpg); + filter: blur(5px); + background-size: cover; + z-index: 10; +} + + +.BasicQuestions-List { + top: 15vw; + width: 100%; + position: absolute; + text-align: center; + list-style-type: none; + background-color: #3c2918; + left: -1vw; +} +.Ul-BQ{ + text-align: left; + display: table; + margin-right: auto; + margin-left: auto; +} + +.BQH { + position: absolute; + top: 10vh; + font-size: 30px; + font-weight: 600; + letter-spacing: 0.5px; + width: 100vw; + margin: auto; + text-align: center; + color: #3c2918; + text-shadow:1px 1px 10px #fee4c3, 1px 1px 10px #fee4c3; + /* background-color: red; */ +} + +.BQB { + position: absolute; + top: 18vh; + left: 28vw; + text-align: center; +} + +.BQQuestionNum { + position: relative; + padding: 0px; + margin-bottom: 0px; + font-family: "Lilita One"; + font-size: 25px; + color: #3c2918; + top: 30vh; + width: 100vw; + text-align: center; +} + +.BQQuestion { + position: absolute; + margin-top: 0vh; + font-size: 25px; + color: #3c2918; + top: 37vh; + left: auto; + width: 100vw; + margin: auto; + text-align: center; +} + +.NextButton { + margin-top: 20vh; + height: 8vh; + bottom: 20px; + width: 100px; + padding: 10px; + background-color: #3c2918; + color: #fee4c3; + border: none; + border-radius: 5px; + cursor: pointer; + z-index: 3; +} +.NextButton:hover { + background-color: #fee4c3; + color: #3c2918; +} +.NextButton:visted { + background-color: #fee4c3; + color: #3c2918; +} +.NextButton:focus { + background-color: #fee4c3; + color: #3c2918; +} + +.PrevButton { + margin-top: 20vh; + height: 8vh; + bottom: 0px; /* Adjust the vertical position from the bottom */ + width: 100px; /* Adjust the width as needed */ + padding: 2px; + background-color: #3c2918; + color: #fee4c3; + border: none; + border-radius: 5px; + cursor: pointer; + z-index: 3; +} +.PrevButton:hover { + background-color: #fee4c3; + color: #3c2918; +} +.PrevButton:visited { + background-color: #fee4c3; + color: #3c2918; +} +.PrevButton:focus { + background-color: #fee4c3; + color: #3c2918; +} + +.PrevButton:disabled { + background-color: #fee4c3; + color: #3c2918; +} + +.BasicSubmitButton { + position: absolute; + height: 55px; + left: 55vw; + top: 83.5vh; + margin-top: 12vh; + bottom: 20px; /* Adjust the vertical position from the bottom */ + width: 30vh; /* Adjust the width as needed */ + padding: 10px; + background-color: #3c2918; + color: #fee4c3; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.BasicSubmitButton:hover { + background-color: #fee4c3; + color: #3c2918; +} +.BasicSubmitButton:visted { + background-color: #fee4c3; + color: #3c2918; +} +.BasicSubmitButton:focus { + background-color: #fee4c3; + color: #3c2918; +} + +.textboxclassBQ { + position: absolute; + margin-top: 10px; + top: 41vh; + height: 40px; + width: 60vh; + font-size: 20px; + font-family: "Josefin Sans"; + left: 35vw; + background-color: #3c2918; + color: #fee4c3; + text-align: center; +} + +.ProgressBarBQ { + position: absolute; + border-radius: 1.5em; + + left: 5vw; + top: 42vh; + height: 6vh; + width: 90vw; + background-color: #3c2918; +} + +.ActiveProgressBQ { + position: absolute; + align-self: center; + border-radius: 1.5em; + transition: width 0.3s ease-in-out; /* Adjust the duration and timing function as needed */ + left: 0.6vw; + top: 1vh; + height: 4vh; + max-width: 98.5%; + min-width: 0.5%; + background-color: #fee4c3; +} + +/* +//putting in styalizing things and will edit later +*/ + +.Bbody { + height: 100vh; + width: 100vw; + background-color: #fee4c3; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.QuestionHeader { + position: absolute; + border-radius: 1.5em; + /* height: ; */ + top: 50vh; + font-size: 25px; + font-weight: 600; + letter-spacing: 0.5px; + width: 100vw; + margin: auto; + text-align: center; + font-family: "Josefin Sans"; + color: #3c2918; + text-shadow:1px 1px 10px #fee4c3, 1px 1px 10px #fee4c3; + +} + +.QuestionContainer { + text-align: center; + font-size: 20px; + font-family: "Lilita One"; + position: absolute; + top: 55vh; /* Adjust this value to move the container downward */ + left: 50%; /* Center horizontally */ + transform: translateX(-50%); + width: 85%; + background-color: #fff3e4; + height: 50vh; + border-radius: 1.5em; + padding-top: 10px; + color: #3c2918; + /* margin: auto; */ +} + +.QuestionText { + font-size: 20px; +} + +.AnswerInput { + width: 300px; + height: 30px; + margin-top: 10px; + padding: 5px; +} + +.QCount { + position: absolute; + top: -5vh; + left: 42vw; +} + +.BQH { + position: absolute; + border-radius: 1.5em; + + top: 15vh; + font-size: 35px; + font-weight: 600; + letter-spacing: 0.5px; + width: 100vw; + margin: auto; + text-align: center; + font-family: "Lilita One"; + color: #3c2918; +} + +.Description { + position: absolute; + border-radius: 1.5em; + + top: 23vh; + font-size: 20px; + font-weight: 600; + letter-spacing: 0.5px; + width: 100vw; + margin: auto; + text-align: center; + font-family: "Lilita One"; + color: #000000; + text-shadow:1px 1px 10px #fee4c3, 1px 1px 10px #fee4c3; + +} + diff --git a/src/Pages/BQPage.tsx b/src/Pages/BQPage.tsx new file mode 100644 index 000000000..976eee6ee --- /dev/null +++ b/src/Pages/BQPage.tsx @@ -0,0 +1,324 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-template-curly-in-string */ +//import { Button } from "react-bootstrap"; +import { useState } from "react"; +import "./BQPage.css"; +import OpenAI from "openai"; +import { Button } from "react-bootstrap"; + + +//Code written with the assistance of Gemini AI. + +interface Props { + setPage: (page: string) => void; // Define the type of setPage prop + apiKey: string; // Add apiKey as a prop +} + +interface Question { + question: string; + answers: string[] | null; + type: "short answer" | "multiple choice"; +} + +function BQPage(props: Props): JSX.Element { + console.log("API Key:", props.apiKey); + + const questions: Question[] = [ + //25 questions total + { + question: + "Would you prefer working from home, in an office / on site, or hybrid? (pick one)", + answers: ["Working from home", "In an office / on site", "Hybrid"], + type: "multiple choice", + }, + { + question: + "What salary would you not feel comfortable earning less than? (pick one)", + answers: ["50K", "70K","100K", "160K", "200K"], + type: "multiple choice", + }, + { + question: + "Do you prefer to do work individually, in a small group (2-4 people), or a team (more than 4 people)?", + answers: [ + "Individually", + "Small group (2-4 people)", + "Team (more than 4 people)", + ], + type: "multiple choice", + }, + { + question: + "How would you describe your ideal work environment in one word?", + answers: null, + type: "short answer", + }, + { + question: "How can you describe yourself in one word?", + answers: null, + type: "short answer", + }, + { + question: "What subject are you the best at?", + answers: ["english", "math", "science", "physical activity"], + type: "multiple choice", + }, + { + question: + "Please select your response to the following statement: I work well in fast paced environments", + answers: ["Yes", "No"], + type: "multiple choice", + }, + { + question: "Would you enjoy traveling for work?", + answers: ["Yes", "No"], + type: "multiple choice", + }, + { + question: "What is the maximum amount of hours you would prefer to work?", + answers: [ + "30 Hrs", + "40 Hrs", + "60 Hrs", + "As many hours as needed", + ], + type: "multiple choice", + }, + { + question: "What is your ideal shift time?", + answers: [ + "Early Bird (7am - 3pm)", + "Regular Hours (9am - 5pm)", + "Overnight (7pm - 7am)", + "I want to work when I want.", + ], + type: "multiple choice", + }, + { + question: + "Would you prefer to be relatively sedentary or active at work?", + answers: ["Sedentary", "Active"], + type: "multiple choice", + }, + { + question: "Would you prefer to dress formally at work or casually?", + answers: ["Formal", "Casual"], + type: "multiple choice", + }, + { + question: "Favorite activity? (one word answer)", + answers: null, + type: "short answer", + }, + { + question: "What is your dreamjob? (one to two word answer)", + answers: null, + type: "short answer", + }, + { + question: "Do you enjoy helping others?", + answers: ["Yes", "No"], + type: "multiple choice", + }, + { + question: + "Please select your level of agreement with the following statement: I prefer jobs that require a lot of attention to detail", + answers: ["True", "False"], + type: "multiple choice", + }, + { + question: "Do you enjoy working in high-pressure situations?", + answers: ["Yes", "No"], + type: "multiple choice", + }, + { + question: + "Please select your level of agreement with the following statement: I prefer consistent work hours over a flexible schedule.", + answers: ["True", "False"], + type: "multiple choice", + }, + ]; + + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [answers, setAnswers] = useState<(string | null)[]>( + Array(questions.length).fill(null) + ); + const [progress, setProgress] = useState(0); + const [submitted, setSubmitted] = useState(false); // State to track if answers have been submitted + const [check, setCheck] = useState(false); + const [gptReport, setGptReport] = useState(""); + + const openai = new OpenAI({ + apiKey: props.apiKey, + dangerouslyAllowBrowser: true, + }); + + const handleNext = () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1); + } + }; + + const handlePrevious = () => { + if (currentQuestionIndex > 0) { + setCurrentQuestionIndex(currentQuestionIndex - 1); + } + }; + + const handleAnswerChange = (selectedAnswer: string) => { + const updatedAnswers = [...answers]; + updatedAnswers[currentQuestionIndex] = selectedAnswer; + setAnswers(updatedAnswers); + + const answeredCount = updatedAnswers.filter((answer) => answer !== null) + .length; + const totalQuestions = questions.length; + const percentage = (answeredCount / totalQuestions) * 100; + setProgress(percentage); + }; + + const handleReturn = () => { + setSubmitted(false); + } + + const handleResponseCheck = () => { + setSubmitted(true); + } + + const handleSubmitBasicAnswers = async () => { + const userContent = answers + .map((answer, index) => `${questions[index].question}: ${answer}`) + .join("\n"); + + try { + const response = await openai.chat.completions.create({ + model: "gpt-4-turbo", + messages: [ + { + role: "system", + content: + "You are a career genie helping lead to the greatest career choices while implementing your love for coffee. Give a detailed paragraph analysis of the answers given and then the top 3 job choices formatted as follows: job name,pay rate, description, and why matched. Then generate a short list of jobs that did not match the answers provided. Have a sweet closer about coffee", }, + { + role: "user", + content: userContent, + }, + ], + temperature: 1, + max_tokens: 1000, + top_p: 1, + frequency_penalty: 0, + presence_penalty: 0, + }); + + const careerReport = response.choices[0].message.content || ""; + console.log("Career Report:", careerReport); + setGptReport(careerReport); + + // Update state to indicate answers have been submitted + setCheck(true); + } catch (error) { + console.error("Error generating career insights:", error); + // Handle error or display error message + } + }; + + return ( +
+
+ {submitted ? ( + // Display submitted answers if submitted is true +
+

Submitted Answers

+ + {questions.map((question, index) => ( +

+ {question.question} +

+ {answers[index]} +

+

+ ))} +
+
+

+

{gptReport}

+ + +
+
+ ) : ( +
+

Basic Questions

+

+

Welcome to the Basic Questions! There are 18 total questions but you can answer as many or as few as you would like!

+

The more questions you answer, the more accurate your results will be!

+

You will be able to review your answers and go back and change any of them before you submit your results.

+
+
+
+
+ Question {currentQuestionIndex + 1} of {questions.length} +
+
+

{questions[currentQuestionIndex].question}

+ {questions[currentQuestionIndex].type === "multiple choice" ? ( +
    + {questions[currentQuestionIndex].answers?.map((answer) => ( +
  • + handleAnswerChange(answer)} + /> + +
  • + ))} +
+ ) : ( +