Skip to content

Commit

Permalink
Merge pull request #105 from eduardconstantin/feat/add-images
Browse files Browse the repository at this point in the history
Add images to the scrapped questions
  • Loading branch information
eduardconstantin authored Feb 23, 2024
2 parents cc3fbfe + 6ebd245 commit 54987b2
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 21 deletions.
15 changes: 12 additions & 3 deletions app/exam/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ const questionsQuery = gql`
isAnswer
text
}
images {
url
alt
}
}
}
`;

const Exam: NextPage = ({ searchParams }) => {
const Exam: NextPage<{ searchParams: { url: string; name: string } }> = ({
searchParams,
}) => {
const { url } = searchParams;
const { minutes, seconds } = {
minutes: 15,
seconds: 0,
Expand All @@ -34,12 +41,12 @@ const Exam: NextPage = ({ searchParams }) => {
const [currentQuestionIndex, setCurrentQuestionIndex] = useState<number>(0);
const [countAnswered, setCountAnswered] = useState<number>(0);
const { data, loading, error } = useQuery(questionsQuery, {
variables: { range: 30, link: searchParams.url },
variables: { range: 30, link: url },
});
const [resultPoints, setResultPoints] = useState<number>(0);
const [passed, setPassed] = useState<boolean>(false);
const [windowWidth, setWindowWidth] = useState<number>(0);

const editedUrl = url.substring(0, url.lastIndexOf("/") + 1);
const elapsedSeconds =
totalTimeInSeconds -
(parseInt(remainingTime.split(":")[0]) * 60 +
Expand Down Expand Up @@ -122,13 +129,15 @@ const Exam: NextPage = ({ searchParams }) => {
currentQuestionIndex={currentQuestionIndex}
question={currentQuestion?.question ?? ""}
options={currentQuestion?.options}
images={currentQuestion?.images}
stopTimer={stopTimer}
revealExam={revealExam}
getResultPoints={getResultPoints}
questions={data.randomQuestions}
hideExam={() => {
setRevealExam(false);
}}
link={editedUrl}
/>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion app/modes/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { NextPage } from "next";
import ExamLink from "@azure-fundamentals/components/ExamLink";

const Modes: NextPage = ({ searchParams }) => {
const Modes: NextPage<{ searchParams: { url: string; name: string } }> = ({
searchParams,
}) => {
const { url, name } = searchParams;

return (
Expand Down
16 changes: 12 additions & 4 deletions app/practice/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const questionQuery = gql`
isAnswer
text
}
images {
alt
url
}
}
}
`;
Expand All @@ -25,24 +29,27 @@ const questionsQuery = gql`
}
`;

const Practice: NextPage = ({ searchParams }) => {
const Practice: NextPage<{ searchParams: { url: string; name: string } }> = ({
searchParams,
}) => {
const [currentQuestionIndex, setCurrentQuestionIndex] = useState<number>(1);
const [windowWidth, setWindowWidth] = useState<number>(0);

const { url } = searchParams;
const editedUrl = url.substring(0, url.lastIndexOf("/") + 1);
useEffect(() => {
setWindowWidth(window.innerWidth);
}, []);

const { loading, error, data } = useQuery(questionQuery, {
variables: { id: currentQuestionIndex - 1, link: searchParams.url },
variables: { id: currentQuestionIndex - 1, link: url },
});

const {
data: questionsData,
loading: questionsLoading,
error: questionsError,
} = useQuery(questionsQuery, {
variables: { link: searchParams.url },
variables: { link: url },
});

const handleNextQuestion = (questionNo: number) => {
Expand All @@ -63,6 +70,7 @@ const Practice: NextPage = ({ searchParams }) => {
handleNextQuestion={handleNextQuestion}
totalQuestions={questionsData?.questions?.count}
currentQuestionIndex={currentQuestionIndex}
link={editedUrl}
/>
</div>
);
Expand Down
57 changes: 50 additions & 7 deletions components/QuizExamForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
import Image from "next/image";
import { Question } from "./types";
import { FieldArray, FormikProvider, Field, useFormik } from "formik";
import { Button } from "./Button";
Expand All @@ -19,6 +20,8 @@ type Props = {
revealExam?: boolean;
hideExam?: () => void;
remainingTime?: string;
link: string;
images?: { url: string; alt: string }[];
};

const QuizExamForm: React.FC<Props> = ({
Expand All @@ -36,11 +39,16 @@ const QuizExamForm: React.FC<Props> = ({
revealExam,
hideExam,
remainingTime,
link,
images,
}) => {
const [showCorrectAnswer, setShowCorrectAnswer] = useState<boolean>(false);
const [savedAnswers, setSavedAnswers] = useState<any>([]);
const { points, reCount } = useResults(savedAnswers);

const [selectedImage, setSelectedImage] = useState<{
url: string;
alt: string;
} | null>(null);
const noOfAnswers = options
? options?.filter((el: any) => el.isAnswer === true).length
: 0;
Expand Down Expand Up @@ -194,6 +202,41 @@ const QuizExamForm: React.FC<Props> = ({
{currentQuestionIndex + 1}. {question}
</p>
</div>
{images && (
<ul className="flex flex-row justify-center gap-2 mt-5 mb-8 select-none md:px-12 px-0">
{images.map((image) => (
<li
key={image.alt}
className="w-[40px] h-[40px] rounded-md border border-white overflow-hidden flex flex-row justify-center"
onClick={() => setSelectedImage(image)}
>
<Image
src={link + image.url}
alt={image.alt}
className="max-h-max max-w-max hover:opacity-60"
unoptimized
width={200}
height={200}
/>
</li>
))}
</ul>
)}
{selectedImage && (
<div className="fixed top-0 left-0 z-50 w-full h-full flex justify-center items-center bg-black bg-opacity-50">
<img
src={link + selectedImage.url}
alt={selectedImage.alt}
className="max-w-[90%] max-h-[90%]"
/>
<button
onClick={() => setSelectedImage(null)}
className="absolute top-3 right-5 px-3 py-1 bg-white text-black rounded-md"
>
Close
</button>
</div>
)}
</div>
<ul className="flex flex-col gap-2 mt-5 mb-16 select-none md:px-12 px-0 h-max min-h-[250px]">
<FieldArray
Expand Down Expand Up @@ -253,12 +296,12 @@ const QuizExamForm: React.FC<Props> = ({
: ""
}`
: formik.values.options[index]?.checked
? "border-gray-400 bg-gray-500/25 hover:border-gray-300 hover:bg-gray-600"
: `border-slate-500 bg-gray-600/25 hover:border-gray-400/75 hover:bg-gray-600/75 ${
formik.values.options[index]?.checked
? "border-gray-400 hover:border-slate-300 bg-gray-600"
: ""
}`
? "border-gray-400 bg-gray-500/25 hover:border-gray-300 hover:bg-gray-600"
: `border-slate-500 bg-gray-600/25 hover:border-gray-400/75 hover:bg-gray-600/75 ${
formik.values.options[index]?.checked
? "border-gray-400 hover:border-slate-300 bg-gray-600"
: ""
}`
}`}
>
<svg
Expand Down
47 changes: 45 additions & 2 deletions components/QuizForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Props = {
currentQuestionIndex: number;
totalQuestions: number;
windowWidth: number;
link: string;
};

const QuizForm: FC<Props> = ({
Expand All @@ -21,6 +22,7 @@ const QuizForm: FC<Props> = ({
currentQuestionIndex,
totalQuestions,
windowWidth,
link,
}) => {
const { register, handleSubmit, reset } = useForm();
const [showCorrectAnswer, setShowCorrectAnswer] = useState<boolean>(false);
Expand All @@ -29,6 +31,10 @@ const QuizForm: FC<Props> = ({
const [savedAnswers, setSavedAnswers] = useState<{
[key: number]: string | string[];
}>({});
const [selectedImage, setSelectedImage] = useState<{
url: string;
alt: string;
} | null>(null);

const onSubmit = (data: FieldValues) => {
setSavedAnswers((prev) => ({
Expand All @@ -50,7 +56,7 @@ const QuizForm: FC<Props> = ({
};

if (isLoading) return <p>Loading...</p>;
const { question, options } = questionSet!;
const { question, options, images } = questionSet!;
const imgUrl: string = `/api/og?question=${question}&width=${windowWidth}`;

const noOfAnswers = options.filter((el) => el.isAnswer === true).length;
Expand Down Expand Up @@ -88,7 +94,9 @@ const QuizForm: FC<Props> = ({
</svg>
</button>
<div className="flex justify-center relative w-[15%] z-[1]">
<span className="absolute text-white opacity-10 font-bold text-6xl bottom-0 -z-[1] select-none">Q&A</span>
<span className="absolute text-white opacity-10 font-bold text-6xl bottom-0 -z-[1] select-none">
Q&A
</span>
<input
className="w-[40px] text-white rounded-l-md border outline-0 border-slate-700 bg-slate-900 text-center text-md [-moz-appearance:_textfield] [&::-webkit-inner-spin-button]:m-0 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:m-0 [&::-webkit-outer-spin-button]:appearance-none"
type="number"
Expand Down Expand Up @@ -148,6 +156,41 @@ const QuizForm: FC<Props> = ({
unoptimized
loading="eager"
/>
{images && (
<ul className="flex flex-row justify-center gap-2 mt-5 mb-8 select-none md:px-12 px-0">
{images.map((image) => (
<li
key={image.alt}
className="w-[40px] h-[40px] rounded-md border border-white overflow-hidden flex flex-row justify-center"
onClick={() => setSelectedImage(image)}
>
<Image
src={link + image.url}
alt={image.alt}
className="max-h-max max-w-max hover:opacity-60"
unoptimized
width={200}
height={200}
/>
</li>
))}
</ul>
)}
{selectedImage && (
<div className="fixed top-0 left-0 z-50 w-full h-full flex justify-center items-center bg-black bg-opacity-50">
<img
src={link + selectedImage.url}
alt={selectedImage.alt}
className="max-w-[90%] max-h-[90%]"
/>
<button
onClick={() => setSelectedImage(null)}
className="absolute top-3 right-5 px-3 py-1 bg-white text-black rounded-md"
>
Close
</button>
</div>
)}
</div>
<ul className="flex flex-col gap-2 mt-5 mb-16 select-none md:px-12 px-0 h-max min-h-[250px]">
{options.map((option, index) => (
Expand Down
6 changes: 6 additions & 0 deletions components/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export type Question = {
question: string;
options: Option[];
images: Image[];
};

type Image = {
alt: string;
url: string;
};

type Option = {
Expand Down
19 changes: 15 additions & 4 deletions lib/graphql/repoQuestions.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
const url =
"https://raw.githubusercontent.com/Ditectrev/Microsoft-Azure-AZ-900-Microsoft-Azure-Fundamentals-Exam-Questions-Answers/main/README.md";
//const url = "https://raw.githubusercontent.com/Ditectrev/Microsoft-Azure-AZ-900-Microsoft-Azure-Fundamentals-Exam-Questions-Answers/main/README.md";

const scrapeQuestions = (markdownText: string) => {
const regex =
/### (.*?)\s*\n\n\!\[.*?\]\(.*?\)\s*\n\n((?:- \[(?:x| )\] .*?\n)+)/gs;
/### (.*?)\s*\n\n((?:\!\[.*?\]\(.*?\)\s*\n\n)*?)((?:- \[(?:x| )\] .*?\n)+)/gs;
const optionsRegex = /- \[(x| )\] (.*?)(?=\n- \[|$)/g;
const imageRegex = /\!\[(.*?)\]\((.*?)\)/g;
const questions = [];
let match;
let id = 0;

while ((match = regex.exec(markdownText)) !== null) {
const question = match[1].trim();
const optionsText = match[2].trim();
const imagesText = match[2].trim();
const optionsText = match[3].trim();

const images = [];
let imageMatch;

while ((imageMatch = imageRegex.exec(imagesText)) !== null) {
const altText = imageMatch[1].trim();
const imageUrl = imageMatch[2].trim();
images.push({ alt: altText, url: imageUrl });
}

let optionMatch;
const options = [];
Expand All @@ -25,6 +35,7 @@ const scrapeQuestions = (markdownText: string) => {
questions.push({
id: id.toString(),
question,
images,
options,
});

Expand Down
5 changes: 5 additions & 0 deletions lib/graphql/schemas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ const typeDefs = gql`
text: String
isAnswer: Boolean
}
type Images {
alt: String
url: String
}
type Question {
id: String
question: String
options: [Option]
images: [Images]
}
type Questions {
count: Int
Expand Down

0 comments on commit 54987b2

Please sign in to comment.