Merged in feature/ExamGenRework (pull request #114)
More navigation bugs and fixed broken modal during transition Approved-by: Tiago Ribeiro
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { Fragment, useEffect, useState } from "react";
|
import { Fragment, useCallback, useEffect, useState } from "react";
|
||||||
import Button from "./Low/Button";
|
import Button from "./Low/Button";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -11,21 +11,55 @@ interface Props {
|
|||||||
|
|
||||||
export default function QuestionsModal({ isOpen, onClose, type = "module", unanswered = false }: Props) {
|
export default function QuestionsModal({ isOpen, onClose, type = "module", unanswered = false }: Props) {
|
||||||
const [isClosing, setIsClosing] = useState(false);
|
const [isClosing, setIsClosing] = useState(false);
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
const blockMultipleClicksClose = (x: boolean) => {
|
useEffect(() => {
|
||||||
if (!isClosing) {
|
if (isOpen) {
|
||||||
setIsClosing(true);
|
setMounted(true);
|
||||||
onClose(x);
|
|
||||||
}
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
setTimeout(() => {
|
useEffect(() => {
|
||||||
|
if (!isOpen && mounted) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setMounted(false);
|
||||||
setIsClosing(false);
|
setIsClosing(false);
|
||||||
}, 400);
|
}, 300);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
|
}, [isOpen, mounted]);
|
||||||
|
|
||||||
|
const blockMultipleClicksClose = useCallback((value: boolean) => {
|
||||||
|
if (isClosing) return;
|
||||||
|
|
||||||
|
setIsClosing(true);
|
||||||
|
onClose(value);
|
||||||
|
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setIsClosing(false);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [isClosing, onClose]);
|
||||||
|
|
||||||
|
if (!mounted && !isOpen) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition show={isOpen} as={Fragment}>
|
<Transition
|
||||||
<Dialog onClose={() => onClose(false)} className="relative z-50">
|
show={isOpen}
|
||||||
|
as={Fragment}
|
||||||
|
beforeEnter={() => setIsClosing(false)}
|
||||||
|
beforeLeave={() => setIsClosing(true)}
|
||||||
|
afterLeave={() => {
|
||||||
|
setIsClosing(false);
|
||||||
|
setMounted(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Dialog
|
||||||
|
onClose={() => blockMultipleClicksClose(false)}
|
||||||
|
className="relative z-50"
|
||||||
|
static
|
||||||
|
>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter="ease-out duration-300"
|
enter="ease-out duration-300"
|
||||||
@@ -33,7 +67,8 @@ export default function QuestionsModal({ isOpen, onClose, type = "module", unans
|
|||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
leave="ease-in duration-200"
|
leave="ease-in duration-200"
|
||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0">
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
<div className="fixed inset-0 bg-black/30" />
|
<div className="fixed inset-0 bg-black/30" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
@@ -44,7 +79,8 @@ export default function QuestionsModal({ isOpen, onClose, type = "module", unans
|
|||||||
enterTo="opacity-100 scale-100"
|
enterTo="opacity-100 scale-100"
|
||||||
leave="ease-in duration-200"
|
leave="ease-in duration-200"
|
||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95">
|
leaveTo="opacity-0 scale-95"
|
||||||
|
>
|
||||||
<div className="fixed inset-0 flex items-center justify-center p-4">
|
<div className="fixed inset-0 flex items-center justify-center p-4">
|
||||||
<Dialog.Panel className="w-full max-w-2xl h-fit p-8 rounded-xl bg-white flex flex-col gap-4">
|
<Dialog.Panel className="w-full max-w-2xl h-fit p-8 rounded-xl bg-white flex flex-col gap-4">
|
||||||
{type === "module" && (
|
{type === "module" && (
|
||||||
@@ -57,10 +93,21 @@ export default function QuestionsModal({ isOpen, onClose, type = "module", unans
|
|||||||
Are you sure you want to continue without completing those questions?
|
Are you sure you want to continue without completing those questions?
|
||||||
</span>
|
</span>
|
||||||
<div className="w-full flex justify-between mt-8">
|
<div className="w-full flex justify-between mt-8">
|
||||||
<Button color="purple" onClick={() => blockMultipleClicksClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
|
<Button
|
||||||
|
color="purple"
|
||||||
|
onClick={() => blockMultipleClicksClose(false)}
|
||||||
|
variant="outline"
|
||||||
|
className="max-w-[200px] self-end w-full"
|
||||||
|
disabled={isClosing}
|
||||||
|
>
|
||||||
Go Back
|
Go Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="purple" onClick={() => blockMultipleClicksClose(true)} className="max-w-[200px] self-end w-full">
|
<Button
|
||||||
|
color="purple"
|
||||||
|
onClick={() => blockMultipleClicksClose(true)}
|
||||||
|
className="max-w-[200px] self-end w-full"
|
||||||
|
disabled={isClosing}
|
||||||
|
>
|
||||||
Continue
|
Continue
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ export default function Finish({ user, practiceScores, scores, modules, informat
|
|||||||
|
|
||||||
const aiUsage = Math.round(ai_usage(solutions) * 100);
|
const aiUsage = Math.round(ai_usage(solutions) * 100);
|
||||||
|
|
||||||
//const entity = useMemo(() => assignment?.entity || user.entities[0]?.id || "", [assignment?.entity, user.entities])
|
const entity = useMemo(() => assignment?.entity || user.entities[0]?.id || "", [assignment?.entity, user.entities])
|
||||||
//const { gradingSystem } = useGradingSystem(entity);
|
const { gradingSystem } = useGradingSystem(entity);
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export default function Finish({ user, practiceScores, scores, modules, informat
|
|||||||
|
|
||||||
const showLevel = (level: number) => {
|
const showLevel = (level: number) => {
|
||||||
if (selectedModule === "level") {
|
if (selectedModule === "level") {
|
||||||
const label = getGradingLabel(level, []);
|
const label = getGradingLabel(level, gradingSystem?.steps || []);
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center gap-1">
|
<div className="flex flex-col items-center justify-center gap-1">
|
||||||
<span className="text-xl font-bold">{label}</span>
|
<span className="text-xl font-bold">{label}</span>
|
||||||
@@ -178,7 +178,7 @@ export default function Finish({ user, practiceScores, scores, modules, informat
|
|||||||
<BsPen className="h-6 w-6" />
|
<BsPen className="h-6 w-6" />
|
||||||
<span className="font-semibold">Writing</span>
|
<span className="font-semibold">Writing</span>
|
||||||
</div>
|
</div>
|
||||||
{aiUsage >= 50 && user.type !== "student" && (
|
{aiUsage >= 50 && selectedModule === "writing" && user.type !== "student" && (
|
||||||
<div
|
<div
|
||||||
className={clsx("flex items-center justify-center border px-3 h-full rounded", {
|
className={clsx("flex items-center justify-center border px-3 h-full rounded", {
|
||||||
"bg-orange-100 border-orange-400 text-orange-700": aiUsage < 80,
|
"bg-orange-100 border-orange-400 text-orange-700": aiUsage < 80,
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ const Level: React.FC<ExamProps<LevelExam>> = ({ exam, showSolutions = false, pr
|
|||||||
const [showQuestionsModal, setShowQuestionsModal] = useState(false);
|
const [showQuestionsModal, setShowQuestionsModal] = useState(false);
|
||||||
const [continueAnyways, setContinueAnyways] = useState(false);
|
const [continueAnyways, setContinueAnyways] = useState(false);
|
||||||
const [textRender, setTextRender] = useState(false);
|
const [textRender, setTextRender] = useState(false);
|
||||||
const [changedPrompt, setChangedPrompt] = useState(false);
|
|
||||||
|
|
||||||
|
|
||||||
const [questionModalKwargs, setQuestionModalKwargs] = useState<{
|
const [questionModalKwargs, setQuestionModalKwargs] = useState<{
|
||||||
@@ -98,9 +97,9 @@ const Level: React.FC<ExamProps<LevelExam>> = ({ exam, showSolutions = false, pr
|
|||||||
{
|
{
|
||||||
exam, module: "level", showBlankModal: showQuestionsModal,
|
exam, module: "level", showBlankModal: showQuestionsModal,
|
||||||
setShowBlankModal: setShowQuestionsModal, showSolutions,
|
setShowBlankModal: setShowQuestionsModal, showSolutions,
|
||||||
preview, disableBetweenParts: true, modalKwargs
|
preview, disableBetweenParts: true, modalBetweenParts: true ,modalKwargs
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const registerSolution = useCallback((updateSolution: () => UserSolution) => {
|
const registerSolution = useCallback((updateSolution: () => UserSolution) => {
|
||||||
userSolutionRef.current = updateSolution;
|
userSolutionRef.current = updateSolution;
|
||||||
@@ -299,7 +298,6 @@ const Level: React.FC<ExamProps<LevelExam>> = ({ exam, showSolutions = false, pr
|
|||||||
currentExercise!.questions[questionIndex + i].prompt = updatedPrompt;
|
currentExercise!.questions[questionIndex + i].prompt = updatedPrompt;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setChangedPrompt(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -366,14 +364,14 @@ const Level: React.FC<ExamProps<LevelExam>> = ({ exam, showSolutions = false, pr
|
|||||||
onNext={() => { setShowPartDivider(false); setIsFirstTimeRender(false); setBgColor("bg-white"); setSeenParts(prev => new Set(prev).add(partIndex)); }}
|
onNext={() => { setShowPartDivider(false); setIsFirstTimeRender(false); setBgColor("bg-white"); setSeenParts(prev => new Set(prev).add(partIndex)); }}
|
||||||
/> : (
|
/> : (
|
||||||
<>
|
<>
|
||||||
<SectionNavbar
|
{exam.parts.length > 1 && <SectionNavbar
|
||||||
module="level"
|
module="level"
|
||||||
sectionLabel="Part"
|
sectionLabel="Part"
|
||||||
seenParts={seenParts}
|
seenParts={seenParts}
|
||||||
setShowPartDivider={setShowPartDivider}
|
setShowPartDivider={setShowPartDivider}
|
||||||
setSeenParts={setSeenParts}
|
setSeenParts={setSeenParts}
|
||||||
preview={preview}
|
preview={preview}
|
||||||
/>
|
/>}
|
||||||
<ModuleTitle
|
<ModuleTitle
|
||||||
examLabel={exam.label}
|
examLabel={exam.label}
|
||||||
partLabel={partLabel()}
|
partLabel={partLabel()}
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ const useExamNavigation: UseExamNavigation = ({
|
|||||||
|
|
||||||
// when navbar is used
|
// when navbar is used
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if(startNow && partIndex !== 0) {
|
if(startNow && !showPartDivider && partIndex !== 0) {
|
||||||
setStartNow(false);
|
setStartNow(false);
|
||||||
}
|
}
|
||||||
} , [partIndex, startNow])
|
} , [partIndex, startNow, showPartDivider])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showSolutions && hasDivider(exam, isPartExam ? partIndex : exerciseIndex) && !seenParts.has(partIndex)) {
|
if (!showSolutions && hasDivider(exam, isPartExam ? partIndex : exerciseIndex) && !seenParts.has(partIndex)) {
|
||||||
@@ -161,8 +161,7 @@ const useExamNavigation: UseExamNavigation = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(modalBetweenParts);
|
if (modalBetweenParts && !seenParts.has(partIndex + 1) && !answeredEveryQuestion(exam as PartExam, userSolutions) && !keepGoing && setShowBlankModal && !showSolutions && !preview) {
|
||||||
if (modalBetweenParts && !answeredEveryQuestion(exam as PartExam, userSolutions) && !keepGoing && setShowBlankModal && !showSolutions && !preview) {
|
|
||||||
if (modalKwargs) modalKwargs();
|
if (modalKwargs) modalKwargs();
|
||||||
setShowBlankModal(true);
|
setShowBlankModal(true);
|
||||||
return;
|
return;
|
||||||
@@ -197,7 +196,7 @@ const useExamNavigation: UseExamNavigation = ({
|
|||||||
|
|
||||||
const previousPartExam = () => {
|
const previousPartExam = () => {
|
||||||
|
|
||||||
if (partIndex === 0 && exerciseIndex === 0 && !startNow) {
|
if (!showPartDivider && partIndex === 0 && exerciseIndex === 0 && questionIndex === 0 && !startNow) {
|
||||||
setStartNow(true);
|
setStartNow(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export const rootReducer = (
|
|||||||
questionIndex: 0,
|
questionIndex: 0,
|
||||||
exerciseIndex: 0,
|
exerciseIndex: 0,
|
||||||
partIndex: 0,
|
partIndex: 0,
|
||||||
exam: state.exams[moduleIndex + 1],
|
exam: state.exams[moduleIndex],
|
||||||
moduleIndex: moduleIndex
|
moduleIndex: moduleIndex
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user