Merged in ENCOA-77_GenerationTitle (pull request #64)
Added title to the exam generate Approved-by: Tiago Ribeiro
This commit is contained in:
@@ -230,7 +230,11 @@ const TaskTab = ({section, setSection}: {section: LevelSection; setSection: (sec
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const LevelGeneration = () => {
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LevelGeneration = ({ id } : Props) => {
|
||||||
const [generatedExam, setGeneratedExam] = useState<LevelExam>();
|
const [generatedExam, setGeneratedExam] = useState<LevelExam>();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [resultingExam, setResultingExam] = useState<LevelExam>();
|
const [resultingExam, setResultingExam] = useState<LevelExam>();
|
||||||
@@ -420,10 +424,16 @@ const LevelGeneration = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!id) {
|
||||||
|
toast.error("Please insert a title before submitting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const exam = {
|
const exam = {
|
||||||
...generatedExam,
|
...generatedExam,
|
||||||
|
id,
|
||||||
parts: generatedExam.parts.map((p, i) => ({...p, exercises: parts[i].part!.exercises})),
|
parts: generatedExam.parts.map((p, i) => ({...p, exercises: parts[i].part!.exercises})),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,11 @@ interface ListeningPart {
|
|||||||
| string;
|
| string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListeningGeneration = () => {
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListeningGeneration = ({ id } : Props) => {
|
||||||
const [part1, setPart1] = useState<ListeningPart>();
|
const [part1, setPart1] = useState<ListeningPart>();
|
||||||
const [part2, setPart2] = useState<ListeningPart>();
|
const [part2, setPart2] = useState<ListeningPart>();
|
||||||
const [part3, setPart3] = useState<ListeningPart>();
|
const [part3, setPart3] = useState<ListeningPart>();
|
||||||
@@ -258,11 +262,16 @@ const ListeningGeneration = () => {
|
|||||||
console.log({parts});
|
console.log({parts});
|
||||||
if (parts.length === 0) return toast.error("Please generate at least one section!");
|
if (parts.length === 0) return toast.error("Please generate at least one section!");
|
||||||
|
|
||||||
|
if(!id) {
|
||||||
|
toast.error("Please insert a title before submitting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post(`/api/exam/listening/generate/listening`, {
|
.post(`/api/exam/listening/generate/listening`, {
|
||||||
id: generate({minLength: 4, maxLength: 8, min: 3, max: 5, join: " ", formatter: capitalize}),
|
id,
|
||||||
parts,
|
parts,
|
||||||
minTimer,
|
minTimer,
|
||||||
difficulty,
|
difficulty,
|
||||||
|
|||||||
@@ -258,7 +258,11 @@ const PartTab = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReadingGeneration = () => {
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReadingGeneration = ({ id } : Props) => {
|
||||||
const [part1, setPart1] = useState<ReadingPart>();
|
const [part1, setPart1] = useState<ReadingPart>();
|
||||||
const [part2, setPart2] = useState<ReadingPart>();
|
const [part2, setPart2] = useState<ReadingPart>();
|
||||||
const [part3, setPart3] = useState<ReadingPart>();
|
const [part3, setPart3] = useState<ReadingPart>();
|
||||||
@@ -300,13 +304,18 @@ const ReadingGeneration = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!id) {
|
||||||
|
toast.error("Please insert a title before submitting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const exam: ReadingExam = {
|
const exam: ReadingExam = {
|
||||||
parts,
|
parts,
|
||||||
isDiagnostic: false,
|
isDiagnostic: false,
|
||||||
minTimer,
|
minTimer,
|
||||||
module: "reading",
|
module: "reading",
|
||||||
id: generate({minLength: 4, maxLength: 8, min: 3, max: 5, join: " ", formatter: capitalize}),
|
id,
|
||||||
type: "academic",
|
type: "academic",
|
||||||
variant: parts.length === 3 ? "full" : "partial",
|
variant: parts.length === 3 ? "full" : "partial",
|
||||||
difficulty,
|
difficulty,
|
||||||
@@ -328,7 +337,7 @@ const ReadingGeneration = () => {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
toast.error("Something went wrong while generating, please try again later.");
|
toast.error(error.response.data.error || "Something went wrong while generating, please try again later.");
|
||||||
})
|
})
|
||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -221,7 +221,11 @@ interface SpeakingPart {
|
|||||||
avatar?: (typeof AVATARS)[number];
|
avatar?: (typeof AVATARS)[number];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpeakingGeneration = () => {
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SpeakingGeneration = ({ id } : Props) => {
|
||||||
const [part1, setPart1] = useState<SpeakingPart>();
|
const [part1, setPart1] = useState<SpeakingPart>();
|
||||||
const [part2, setPart2] = useState<SpeakingPart>();
|
const [part2, setPart2] = useState<SpeakingPart>();
|
||||||
const [part3, setPart3] = useState<SpeakingPart>();
|
const [part3, setPart3] = useState<SpeakingPart>();
|
||||||
@@ -243,6 +247,11 @@ const SpeakingGeneration = () => {
|
|||||||
const submitExam = () => {
|
const submitExam = () => {
|
||||||
if (!part1?.result && !part2?.result && !part3?.result) return toast.error("Please generate at least one task!");
|
if (!part1?.result && !part2?.result && !part3?.result) return toast.error("Please generate at least one task!");
|
||||||
|
|
||||||
|
if(!id) {
|
||||||
|
toast.error("Please insert a title before submitting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const genders = [part1?.gender, part2?.gender, part3?.gender].filter((x) => !!x);
|
const genders = [part1?.gender, part2?.gender, part3?.gender].filter((x) => !!x);
|
||||||
@@ -256,7 +265,7 @@ const SpeakingGeneration = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const exam: SpeakingExam = {
|
const exam: SpeakingExam = {
|
||||||
id: generate({minLength: 4, maxLength: 8, min: 3, max: 5, join: " ", formatter: capitalize}),
|
id,
|
||||||
isDiagnostic: false,
|
isDiagnostic: false,
|
||||||
exercises: exercises as (SpeakingExercise | InteractiveSpeakingExercise)[],
|
exercises: exercises as (SpeakingExercise | InteractiveSpeakingExercise)[],
|
||||||
minTimer,
|
minTimer,
|
||||||
|
|||||||
@@ -75,7 +75,11 @@ const TaskTab = ({task, index, difficulty, setTask}: {task?: string; difficulty:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const WritingGeneration = () => {
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WritingGeneration = ({ id } : Props) => {
|
||||||
const [task1, setTask1] = useState<string>();
|
const [task1, setTask1] = useState<string>();
|
||||||
const [task2, setTask2] = useState<string>();
|
const [task2, setTask2] = useState<string>();
|
||||||
const [minTimer, setMinTimer] = useState(60);
|
const [minTimer, setMinTimer] = useState(60);
|
||||||
@@ -116,6 +120,11 @@ const WritingGeneration = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!id) {
|
||||||
|
toast.error("Please insert a title before submitting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const exercise1 = task1
|
const exercise1 = task1
|
||||||
? ({
|
? ({
|
||||||
id: v4(),
|
id: v4(),
|
||||||
@@ -152,7 +161,7 @@ const WritingGeneration = () => {
|
|||||||
minTimer,
|
minTimer,
|
||||||
module: "writing",
|
module: "writing",
|
||||||
exercises: [...(exercise1 ? [exercise1] : []), ...(exercise2 ? [exercise2] : [])],
|
exercises: [...(exercise1 ? [exercise1] : []), ...(exercise2 ? [exercise2] : [])],
|
||||||
id: generate({minLength: 4, maxLength: 8, min: 3, max: 5, join: " ", formatter: capitalize}),
|
id,
|
||||||
variant: exercise1 && exercise2 ? "full" : "partial",
|
variant: exercise1 && exercise2 ? "full" : "partial",
|
||||||
difficulty,
|
difficulty,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
import type {NextApiRequest, NextApiResponse} from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import {app} from "@/firebase";
|
import { app } from "@/firebase";
|
||||||
import {getFirestore, setDoc, doc} from "firebase/firestore";
|
import {
|
||||||
import {withIronSessionApiRoute} from "iron-session/next";
|
getFirestore,
|
||||||
import {sessionOptions} from "@/lib/session";
|
setDoc,
|
||||||
import {Exam, InstructorGender, Variant} from "@/interfaces/exam";
|
doc,
|
||||||
import {getExams} from "@/utils/exams.be";
|
runTransaction,
|
||||||
import {Module} from "@/interfaces";
|
collection,
|
||||||
|
query,
|
||||||
|
where,
|
||||||
|
getDocs,
|
||||||
|
} from "firebase/firestore";
|
||||||
|
import { withIronSessionApiRoute } from "iron-session/next";
|
||||||
|
import { sessionOptions } from "@/lib/session";
|
||||||
|
import { Exam, InstructorGender, Variant } from "@/interfaces/exam";
|
||||||
|
import { getExams } from "@/utils/exams.be";
|
||||||
|
import { Module } from "@/interfaces";
|
||||||
const db = getFirestore(app);
|
const db = getFirestore(app);
|
||||||
|
|
||||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||||
@@ -15,40 +24,66 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
if (req.method === "GET") return await GET(req, res);
|
if (req.method === "GET") return await GET(req, res);
|
||||||
if (req.method === "POST") return await POST(req, res);
|
if (req.method === "POST") return await POST(req, res);
|
||||||
|
|
||||||
res.status(404).json({ok: false});
|
res.status(404).json({ ok: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function GET(req: NextApiRequest, res: NextApiResponse) {
|
async function GET(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ok: false});
|
res.status(401).json({ ok: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {module, avoidRepeated, variant, instructorGender} = req.query as {
|
const { module, avoidRepeated, variant, instructorGender } = req.query as {
|
||||||
module: Module;
|
module: Module;
|
||||||
avoidRepeated: string;
|
avoidRepeated: string;
|
||||||
variant?: Variant;
|
variant?: Variant;
|
||||||
instructorGender?: InstructorGender;
|
instructorGender?: InstructorGender;
|
||||||
};
|
};
|
||||||
|
|
||||||
const exams: Exam[] = await getExams(db, module, avoidRepeated, req.session.user.id, variant, instructorGender);
|
const exams: Exam[] = await getExams(
|
||||||
|
db,
|
||||||
|
module,
|
||||||
|
avoidRepeated,
|
||||||
|
req.session.user.id,
|
||||||
|
variant,
|
||||||
|
instructorGender
|
||||||
|
);
|
||||||
res.status(200).json(exams);
|
res.status(200).json(exams);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function POST(req: NextApiRequest, res: NextApiResponse) {
|
async function POST(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ok: false});
|
res.status(401).json({ ok: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.session.user.type !== "developer") {
|
if (req.session.user.type !== "developer") {
|
||||||
res.status(403).json({ok: false});
|
res.status(403).json({ ok: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {module} = req.query as {module: string};
|
const { module } = req.query as { module: string };
|
||||||
|
|
||||||
const exam = {...req.body, module: module, createdBy: req.session.user.id, createdAt: new Date().toISOString()};
|
try {
|
||||||
await setDoc(doc(db, module, req.body.id), exam);
|
const exam = {
|
||||||
|
...req.body,
|
||||||
|
module: module,
|
||||||
|
createdBy: req.session.user.id,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
await runTransaction(db, async (transaction) => {
|
||||||
|
const docRef = doc(db, module, req.body.id);
|
||||||
|
const docSnap = await transaction.get(docRef);
|
||||||
|
|
||||||
|
if (docSnap.exists()) {
|
||||||
|
throw new Error("Name already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDocRef = doc(db, module, req.body.id);
|
||||||
|
transaction.set(newDocRef, exam);
|
||||||
|
});
|
||||||
res.status(200).json(exam);
|
res.status(200).json(exam);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Transaction failed: ", error);
|
||||||
|
res.status(500).json({ ok: false, error: (error as any).message });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export default function Generation() {
|
|||||||
|
|
||||||
const { user } = useUser({ redirectTo: "/login" });
|
const { user } = useUser({ redirectTo: "/login" });
|
||||||
|
|
||||||
|
const [title, setTitle] = useState<string>("");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -73,6 +74,17 @@ export default function Generation() {
|
|||||||
<Layout user={user} className="gap-6">
|
<Layout user={user} className="gap-6">
|
||||||
<h1 className="text-2xl font-semibold">Exam Generation</h1>
|
<h1 className="text-2xl font-semibold">Exam Generation</h1>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Insert a title here"
|
||||||
|
name="title"
|
||||||
|
label="Title"
|
||||||
|
onChange={setTitle}
|
||||||
|
roundness="xl"
|
||||||
|
defaultValue={title}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
<label className="font-normal text-base text-mti-gray-dim">
|
<label className="font-normal text-base text-mti-gray-dim">
|
||||||
Module
|
Module
|
||||||
</label>
|
</label>
|
||||||
@@ -117,11 +129,11 @@ export default function Generation() {
|
|||||||
))}
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
{module === "reading" && <ReadingGeneration />}
|
{module === "reading" && <ReadingGeneration id={title} />}
|
||||||
{module === "listening" && <ListeningGeneration />}
|
{module === "listening" && <ListeningGeneration id={title} />}
|
||||||
{module === "writing" && <WritingGeneration />}
|
{module === "writing" && <WritingGeneration id={title} />}
|
||||||
{module === "speaking" && <SpeakingGeneration />}
|
{module === "speaking" && <SpeakingGeneration id={title} />}
|
||||||
{module === "level" && <LevelGeneration />}
|
{module === "level" && <LevelGeneration id={title} />}
|
||||||
</Layout>
|
</Layout>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user