Exam generation rework, batch user tables, fastapi endpoint switch

This commit is contained in:
Carlos-Mesquita
2024-11-04 23:29:14 +00:00
parent a2bc997e8f
commit 15c9c4d4bd
148 changed files with 11348 additions and 3901 deletions

View File

@@ -0,0 +1,69 @@
import { useCallback, useEffect, useState } from "react";
import useExamEditorStore from "@/stores/examEditor";
import ExamEditorStore from "@/stores/examEditor/types";
import Header from "../../Shared/Header";
import { Module } from "@/interfaces";
interface Props {
sectionId: number;
title: string;
description: string;
editing: boolean;
renderContent: (editing: boolean) => React.ReactNode;
mode?: "edit" | "delete";
onSave: () => void;
onDiscard: () => void;
onEdit?: () => void;
module?: Module;
}
const SectionContext: React.FC<Props> = ({ sectionId, title, description, renderContent, editing, onSave, onDiscard, onEdit, mode = "edit", module}) => {
const { currentModule, dispatch } = useExamEditorStore();
const { generating } = useExamEditorStore(
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
);
const [loading, setLoading] = useState(generating && generating == "context");
const updateRoot = useCallback((updates: Partial<ExamEditorStore>) => {
dispatch({ type: 'UPDATE_ROOT', payload: { updates } });
}, [dispatch]);
useEffect(() => {
const loading = generating && generating == "context";
setLoading(loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [generating, updateRoot]);
return (
<div className="p-8 shadow-inner border border-gray-200 bg-gray-50 rounded-xl">
<div className='relative pb-4'>
<Header
title={title}
description={description}
editing={editing}
handleSave={onSave}
handleDiscard={onDiscard}
modeHandle={onEdit}
mode={mode}
module={module}
/>
</div>
<div className="mt-4">
{loading ? (
<div className="w-full cursor-text px-7 py-8 border-2 border-mti-gray-platinum bg-white rounded-3xl">
<div className="flex flex-col items-center justify-center animate-pulse">
<span className={`loading loading-infinity w-32 bg-ielts-${currentModule}`} />
<span className={`font-bold text-2xl text-ielts-${currentModule}`}>Generating...</span>
</div>
</div>
) : (
renderContent(editing)
)}
</div>
</div>
);
};
export default SectionContext;

View File

@@ -0,0 +1,79 @@
import { useEffect, useState } from "react";
import { ListeningPart } from "@/interfaces/exam";
import SectionContext from ".";
import useExamEditorStore from "@/stores/examEditor";
import { FaFemale, FaMale } from "react-icons/fa";
import useSectionEdit from "../../Hooks/useSectionEdit";
import ScriptRender from "../../Exercises/Shared/Script";
import { Card, CardContent } from "@/components/ui/card";
const ListeningContext: React.FC<{ sectionId: number; }> = ({ sectionId }) => {
const { currentModule, dispatch } = useExamEditorStore();
const { genResult, state, generating } = useExamEditorStore(
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
);
const listeningPart = state as ListeningPart;
const [script, setScript] = useState(listeningPart.script);
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
sectionId,
mode: "edit",
onSave: () => {
const newState = { ...listeningPart };
newState.script = script;
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } })
setEditing(false);
},
onDiscard: () => {
setScript(listeningPart.script);
},
});
useEffect(() => {
if (genResult !== undefined && generating === "context") {
setEditing(true);
setScript(genResult[0].script)
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: undefined } })
}
}, [genResult, dispatch, sectionId, setEditing, currentModule]);
const renderContent = (editing: boolean) => {
if (script === undefined && !editing) {
return (<p className="w-full text-gray-600 px-7 py-8 border-2 bg-white rounded-3xl whitespace-pre-line">
Generate or import audio to add exercises!
</p>
)
}
return (
<Card>
<CardContent className="py-10">
<ScriptRender
script={script}
setScript={setScript}
editing={editing}
/>
</CardContent>
</Card>
);
};
return (
<SectionContext
sectionId={sectionId}
title={(sectionId === 1 || sectionId === 3) ? "Conversation" : "Monologue"}
description={`Enter the section's ${(sectionId === 1 || sectionId === 3) ? "conversation" : "monologue"} or import your own`}
renderContent={renderContent}
editing={editing}
onSave={handleSave}
onEdit={modeHandle}
onDiscard={handleDiscard}
module={currentModule}
/>
);
};
export default ListeningContext;

View File

@@ -0,0 +1,106 @@
import { useEffect, useState } from "react";
import { ReadingPart } from "@/interfaces/exam";
import Input from "@/components/Low/Input";
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import Passage from "../../Shared/Passage";
import SectionContext from ".";
import useExamEditorStore from "@/stores/examEditor";
import useSectionEdit from "../../Hooks/useSectionEdit";
const ReadingContext: React.FC<{sectionId: number;}> = ({sectionId}) => {
const {currentModule, dispatch } = useExamEditorStore();
const { genResult, state, generating } = useExamEditorStore(
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
);
const readingPart = state as ReadingPart;
const [title, setTitle] = useState(readingPart.text.title);
const [content, setContent] = useState(readingPart.text.content);
const [passageOpen, setPassageOpen] = useState(false);
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
sectionId,
mode: "edit",
onSave: () => {
const newState = {...state} as ReadingPart;
newState.text.title = title;
newState.text.content = content;
dispatch({type: 'UPDATE_SECTION_STATE', payload: {sectionId, update: newState}})
setEditing(false);
},
onDiscard: () => {
setTitle(readingPart.text.title);
setContent(readingPart.text.content);
},
onMode: () => {
setPassageOpen(false);
}
});
useEffect(()=> {
if (genResult !== undefined && generating === "context") {
setEditing(true);
setTitle(genResult[0].title);
setContent(genResult[0].text)
dispatch({type: "UPDATE_SECTION_SINGLE_FIELD", payload: {sectionId, module: currentModule, field: "genResult", value: undefined}})
}
}, [genResult, dispatch, sectionId, setEditing, currentModule]);
const renderContent = (editing: boolean) => {
if (editing) {
return (
<div className="flex flex-col text-mti-gray-dim p-4 gap-4">
<Input
type="text"
placeholder="Insert a title here"
name="title"
label="Title"
onChange={setTitle}
roundness="xl"
defaultValue={title}
required
/>
<div className="flex flex-col gap-3">
<label className="font-normal text-base text-mti-gray-dim">Content *</label>
<AutoExpandingTextArea
value={content}
placeholder="Insert a passage here"
onChange={(text) => setContent(text)}
/>
</div>
</div>
);
}
return content === "" || title === "" ? (
<p className="w-full text-gray-600 px-7 py-8 border-2 bg-white rounded-3xl whitespace-pre-line">
Generate or edit the passage to add exercises!
</p>
) : (
<Passage
title={title}
content={content}
open={passageOpen}
setIsOpen={setPassageOpen}
/>
);
};
return (
<SectionContext
sectionId={sectionId}
title="Reading Passage"
description="The reading passage that the exercises will refer to."
renderContent={renderContent}
editing={editing}
onSave={handleSave}
onEdit={modeHandle}
module={currentModule}
onDiscard={handleDiscard}
/>
);
};
export default ReadingContext;