Exam generation rework, batch user tables, fastapi endpoint switch
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user