Exam generation rework, batch user tables, fastapi endpoint switch
This commit is contained in:
61
src/components/ExamEditor/SettingsEditor/Shared/Generate.ts
Normal file
61
src/components/ExamEditor/SettingsEditor/Shared/Generate.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import axios from "axios";
|
||||
import { playSound } from "@/utils/sound";
|
||||
import { toast } from "react-toastify";
|
||||
import { Generating } from "@/stores/examEditor/types";
|
||||
import useExamEditorStore from "@/stores/examEditor";
|
||||
import { Module } from "@/interfaces";
|
||||
|
||||
interface GeneratorConfig {
|
||||
method: 'GET' | 'POST';
|
||||
queryParams?: Record<string, string>;
|
||||
body?: Record<string, any>;
|
||||
}
|
||||
|
||||
export function generate(
|
||||
sectionId: number,
|
||||
module: Module,
|
||||
type: "context" | "exercises",
|
||||
config: GeneratorConfig,
|
||||
mapData: (data: any) => Record<string, any>[]
|
||||
) {
|
||||
const dispatch = useExamEditorStore.getState().dispatch;
|
||||
|
||||
const setGenerating = (sectionId: number, generating: Generating) => {
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: { sectionId, module, field: "generating", value: generating }
|
||||
});
|
||||
};
|
||||
|
||||
const setGeneratedExercises = (sectionId: number, exercises: Record<string, any>[] | undefined) => {
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: { sectionId, module, field: "genResult", value: exercises }
|
||||
});
|
||||
};
|
||||
|
||||
setGenerating(sectionId, type);
|
||||
|
||||
const queryString = config.queryParams
|
||||
? new URLSearchParams(config.queryParams).toString()
|
||||
: '';
|
||||
|
||||
const url = `/api/exam/generate/${module}/${sectionId}${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
const request = config.method === 'POST'
|
||||
? axios.post(url, config.body)
|
||||
: axios.get(url);
|
||||
|
||||
request
|
||||
.then((result) => {
|
||||
playSound("check");
|
||||
setGeneratedExercises(sectionId, mapData(result.data));
|
||||
})
|
||||
.catch((error) => {
|
||||
playSound("error");
|
||||
toast.error("Something went wrong! Try to generate again.");
|
||||
})
|
||||
.finally(() => {
|
||||
setGenerating(sectionId, undefined);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Module } from "@/interfaces";
|
||||
import useExamEditorStore from "@/stores/examEditor";
|
||||
import { Generating } from "@/stores/examEditor/types";
|
||||
import clsx from "clsx";
|
||||
import { BsArrowRepeat } from "react-icons/bs";
|
||||
import { GiBrain } from "react-icons/gi";
|
||||
|
||||
interface Props {
|
||||
module: Module;
|
||||
sectionId: number;
|
||||
genType: Generating;
|
||||
generateFnc: (sectionId: number) => void
|
||||
}
|
||||
|
||||
const GenerateBtn: React.FC<Props> = ({module, sectionId, genType, generateFnc}) => {
|
||||
const {generating} = useExamEditorStore((store) => store.modules[module].sections.find((s)=> s.sectionId == sectionId))!;
|
||||
const loading = generating && generating === genType;
|
||||
return (
|
||||
<button
|
||||
key={`section-${sectionId}`}
|
||||
className={clsx(
|
||||
"flex items-center w-[140px] justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300 text-lg",
|
||||
`bg-ielts-${module}/70 border border-ielts-${module} hover:bg-ielts-${module}`
|
||||
)}
|
||||
onClick={loading ? () => { } : () => generateFnc(sectionId)}
|
||||
>
|
||||
{loading ? (
|
||||
<div key={`section-${sectionId}`} className="flex items-center justify-center">
|
||||
<BsArrowRepeat className="text-white animate-spin" size={25} />
|
||||
</div>
|
||||
) : (
|
||||
<div key={`section-${sectionId}`} className="flex flex-row">
|
||||
<GiBrain className="mr-2" size={24} />
|
||||
<span>Generate</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default GenerateBtn;
|
||||
@@ -0,0 +1,33 @@
|
||||
import Dropdown from "@/components/Dropdown";
|
||||
import clsx from "clsx";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface Props {
|
||||
module: string;
|
||||
title: string;
|
||||
open: boolean;
|
||||
disabled?: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const SettingsDropdown: React.FC<Props> = ({ module, title, open, setIsOpen, children, disabled = false }) => {
|
||||
return (
|
||||
<Dropdown
|
||||
title={title}
|
||||
className={clsx(
|
||||
"w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border text-white shadow-md transition-all duration-300 disabled:cursor-not-allowed",
|
||||
`bg-ielts-${module}/70 border border-ielts-${module} hover:bg-ielts-${module} disabled:bg-ielts-${module}/30`,
|
||||
open ? "rounded-t-lg" : "rounded-lg"
|
||||
)}
|
||||
contentWrapperClassName="pt-6 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out"
|
||||
open={open}
|
||||
setIsOpen={setIsOpen}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsDropdown;
|
||||
Reference in New Issue
Block a user