instanciate all workflows configured for an exam author based on different entities.

This commit is contained in:
Joao Correia
2025-02-05 12:37:53 +00:00
parent f4c7961caa
commit 6692c201e4
9 changed files with 129 additions and 67 deletions

View File

@@ -215,24 +215,32 @@ const LevelSettings: React.FC = () => {
}); });
const requestBody = await (async () => { const requestBody = await (async () => {
const handledExam = await getExamById("level", result.data.id); const handledExam = await getExamById("writing", result.data.id);
return { return {
examAuthor: handledExam?.createdBy ?? "Unknown Author", examAuthor: handledExam?.createdBy ?? "Unknown Author",
examEntities: handledExam?.entities ?? [],
examId: handledExam?.id ?? "Unknown ID", examId: handledExam?.id ?? "Unknown ID",
examModule: "level" examModule: "writing"
}; };
})(); })();
await axios.post(`/api/approval-workflows`, requestBody) await axios
.then(() => { .post(`/api/approval-workflows`, requestBody)
toast.success(`Approval Workflow for exam has been created`); .then((response) => {
if (response.status === 200) {
toast.success(`Approval Workflows for exam have been successfully created`);
} else if (response.status === 207) {
toast.warning(
`Approval Workflows were partially created. Exam author might not have a configured workflow for all its entities.`
);
}
}) })
.catch((reason) => { .catch((reason) => {
if (reason.response.status === 404) { if (reason.response?.status === 404) {
toast.error("No configured workflow found for examAuthor."); toast.error("No configured workflow found for examAuthor for any of its entities.");
} } else {
else { toast.error(
toast.error("Something went wrong while creating approval workflow, please try again later."); "Something went wrong while creating approval workflow, please try again later."
);
} }
}); });

View File

@@ -153,24 +153,32 @@ const ListeningSettings: React.FC = () => {
toast.success(`Submitted Exam ID: ${result.data.id}`); toast.success(`Submitted Exam ID: ${result.data.id}`);
const requestBody = await (async () => { const requestBody = await (async () => {
const handledExam = await getExamById("listening", result.data.id); const handledExam = await getExamById("writing", result.data.id);
return { return {
examAuthor: handledExam?.createdBy ?? "Unknown Author", examAuthor: handledExam?.createdBy ?? "Unknown Author",
examEntities: handledExam?.entities ?? [],
examId: handledExam?.id ?? "Unknown ID", examId: handledExam?.id ?? "Unknown ID",
examModule: "listening" examModule: "writing"
}; };
})(); })();
await axios.post(`/api/approval-workflows`, requestBody) await axios
.then(() => { .post(`/api/approval-workflows`, requestBody)
toast.success(`Approval Workflow for exam has been created`); .then((response) => {
if (response.status === 200) {
toast.success(`Approval Workflows for exam have been successfully created`);
} else if (response.status === 207) {
toast.warning(
`Approval Workflows were partially created. Exam author might not have a configured workflow for all its entities.`
);
}
}) })
.catch((reason) => { .catch((reason) => {
if (reason.response.status === 404) { if (reason.response?.status === 404) {
toast.error("No configured workflow found for examAuthor."); toast.error("No configured workflow found for examAuthor for any of its entities.");
} } else {
else { toast.error(
toast.error("Something went wrong while creating approval workflow, please try again later."); "Something went wrong while creating approval workflow, please try again later."
);
} }
}); });

View File

@@ -90,27 +90,35 @@ const ReadingSettings: React.FC = () => {
.then((result) => { .then((result) => {
playSound("sent"); playSound("sent");
toast.success(`Submitted Exam ID: ${result.data.id}`); toast.success(`Submitted Exam ID: ${result.data.id}`);
return getExamById("reading", result.data.id); return getExamById("reading", result.data.id);
}) })
.then((handledExam) => { .then((handledExam) => {
const requestBody = { const requestBody = {
examAuthor: handledExam?.createdBy ?? "Unknown Author", examAuthor: handledExam?.createdBy ?? "Unknown Author",
examEntities: handledExam?.entities ?? [],
examId: handledExam?.id ?? "Unknown ID", examId: handledExam?.id ?? "Unknown ID",
examModule: "reading" examModule: "reading"
}; };
return axios.post(`/api/approval-workflows`, requestBody); return axios.post(`/api/approval-workflows`, requestBody);
}) })
.then(() => { .then((response) => {
toast.success(`Approval Workflow for exam has been created`); if (response.status === 200) {
toast.success(`Approval Workflows for exam have been successfully created`);
} else if (response.status === 207) {
toast.warning(
`Approval Workflows were partially created. Exam author might not have a configured workflow for all its entities.`
);
}
}) })
.catch((error) => { .catch((error) => {
if (error.response && error.response.status === 404) { if (error.response && error.response.status === 404) {
toast.error("No configured workflow found for examAuthor."); toast.error("No configured workflow found for examAuthor for any of its entities.");
} else { } else {
// This error could come from either of the requests toast.error(
toast.error(error.response?.data?.error || "Something went wrong, please try again later."); error.response?.data?.error ||
"Something went wrong, please try again later."
);
} }
}); });
} }

View File

@@ -197,24 +197,32 @@ const SpeakingSettings: React.FC = () => {
}); });
const requestBody = await (async () => { const requestBody = await (async () => {
const handledExam = await getExamById("speaking", result.data.id); const handledExam = await getExamById("writing", result.data.id);
return { return {
examAuthor: handledExam?.createdBy ?? "Unknown Author", examAuthor: handledExam?.createdBy ?? "Unknown Author",
examEntities: handledExam?.entities ?? [],
examId: handledExam?.id ?? "Unknown ID", examId: handledExam?.id ?? "Unknown ID",
examModule: "speaking" examModule: "writing"
}; };
})(); })();
await axios.post(`/api/approval-workflows`, requestBody) await axios
.then(() => { .post(`/api/approval-workflows`, requestBody)
toast.success(`Approval Workflow for exam has been created`); .then((response) => {
if (response.status === 200) {
toast.success(`Approval Workflows for exam have been successfully created`);
} else if (response.status === 207) {
toast.warning(
`Approval Workflows were partially created. Exam author might not have a configured workflow for all its entities.`
);
}
}) })
.catch((reason) => { .catch((reason) => {
if (reason.response.status === 404) { if (reason.response?.status === 404) {
toast.error("No configured workflow found for examAuthor."); toast.error("No configured workflow found for examAuthor for any of its entities.");
} } else {
else { toast.error(
toast.error("Something went wrong while creating approval workflow, please try again later."); "Something went wrong while creating approval workflow, please try again later."
);
} }
}); });

View File

@@ -144,23 +144,31 @@ const WritingSettings: React.FC = () => {
const requestBody = await (async () => { const requestBody = await (async () => {
const handledExam = await getExamById("writing", result.data.id); const handledExam = await getExamById("writing", result.data.id);
return { return {
examAuthor: handledExam?.createdBy ?? "Unknown Author", examAuthor: handledExam?.createdBy ?? "Unknown Author",
examEntities: handledExam?.entities ?? [],
examId: handledExam?.id ?? "Unknown ID", examId: handledExam?.id ?? "Unknown ID",
examModule: "writing" examModule: "writing"
}; };
})(); })();
await axios.post(`/api/approval-workflows`, requestBody) await axios
.then(() => { .post(`/api/approval-workflows`, requestBody)
toast.success(`Approval Workflow for exam has been created`); .then((response) => {
if (response.status === 200) {
toast.success(`Approval Workflows for exam have been successfully created`);
} else if (response.status === 207) {
toast.warning(
`Approval Workflows were partially created. Exam author might not have a configured workflow for all its entities.`
);
}
}) })
.catch((reason) => { .catch((reason) => {
if (reason.response.status === 404) { if (reason.response?.status === 404) {
toast.error("No configured workflow found for examAuthor."); toast.error("No configured workflow found for examAuthor for any of its entities.");
} } else {
else { toast.error(
toast.error("Something went wrong while creating approval workflow, please try again later."); "Something went wrong while creating approval workflow, please try again later."
);
} }
}); });

View File

@@ -9,10 +9,11 @@ import type { NextApiRequest, NextApiResponse } from "next";
export default withIronSessionApiRoute(handler, sessionOptions); export default withIronSessionApiRoute(handler, sessionOptions);
interface PostRequestBody { interface PostRequestBody {
examAuthor: string, examAuthor: string;
examId: string, examEntities: string[];
examName: string, examId: string;
examModule: Module, examName: string;
examModule: Module;
} }
async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -39,22 +40,39 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
return res.status(403).json({ ok: false }); return res.status(403).json({ ok: false });
} }
const { examAuthor, examId, examModule } = req.body as PostRequestBody; const { examAuthor, examEntities, examId, examModule } = req.body as PostRequestBody;
const results = await Promise.all(
examEntities.map(async (entity) => {
const configuredWorkflow = await getApprovalWorkflowByFormIntaker(entity, examAuthor);
if (!configuredWorkflow) {
return { entity, created: false, error: "No configured workflow found for examAuthor." };
}
if (examAuthor) {
const configuredWorkflow = await getApprovalWorkflowByFormIntaker(examAuthor);
if(configuredWorkflow) {
configuredWorkflow.modules.push(examModule); configuredWorkflow.modules.push(examModule);
configuredWorkflow.name = `${examId}`; configuredWorkflow.name = `${examId}`;
configuredWorkflow.examId = examId; configuredWorkflow.examId = examId;
configuredWorkflow.entityId = entity;
configuredWorkflow.startDate = Date.now(); configuredWorkflow.startDate = Date.now();
return res.status(201).json(await createApprovalWorkflow("active-workflows", configuredWorkflow)); try {
const creationResponse = await createApprovalWorkflow("active-workflows", configuredWorkflow);
return { entity, created: true, creationResponse };
} catch (error) {
const err = error as Error;
return { entity, created: false, error: err.message };
}
})
);
const successCount = results.filter((r) => r.created).length;
const totalCount = examEntities.length;
if (successCount === totalCount) {
return res.status(200).json({ ok: true, results });
} else if (successCount > 0) {
return res.status(207).json({ ok: true, results });
} else { } else {
return res.status(404).json("No configured workflow found for examAuthor."); return res.status(404).json({ ok: false, message: "No workflows were created", results });
} }
}
} }

View File

@@ -256,8 +256,9 @@ export default function Home({ user, allConfiguredWorkflows, userEntitiesWithLab
</div> </div>
</section> </section>
<section className="flex flex-row gap-6"> <Tip text="Setting a teacher as a Form Intaker means the configured workflow will be instanciated when said teacher publishes an exam. Only one Form Intake per teacher per entity is allowed."/>
<section className="flex flex-row gap-6">
<Button <Button
color="purple" color="purple"
variant="solid" variant="solid"

View File

@@ -1,3 +1,4 @@
import Tip from "@/components/ApprovalWorkflows/Tip";
import Layout from "@/components/High/Layout"; import Layout from "@/components/High/Layout";
import Button from "@/components/Low/Button"; import Button from "@/components/Low/Button";
import Input from "@/components/Low/Input"; import Input from "@/components/Low/Input";
@@ -173,7 +174,7 @@ export default function ApprovalWorkflows({ user, initialWorkflows, workflowsAss
const columns = [ const columns = [
columnHelper.accessor("name", { columnHelper.accessor("name", {
header: "NAME", header: "EXAM NAME",
cell: (info) => ( cell: (info) => (
<span className="font-medium"> <span className="font-medium">
{info.getValue()} {info.getValue()}
@@ -371,6 +372,8 @@ export default function ApprovalWorkflows({ user, initialWorkflows, workflowsAss
</div> </div>
</div> </div>
<Tip text="An exam submission will instanciate the approval workflow configured for the exam author. The exam will be valid only when all the steps of the workflow have been approved."></Tip>
<div className="px-6 pb-4 bg-mti-purple-ultralight rounded-2xl border-2 border-mti-purple-light border-opacity-40"> <div className="px-6 pb-4 bg-mti-purple-ultralight rounded-2xl border-2 border-mti-purple-light border-opacity-40">
<table <table
className="w-full table-auto border-separate border-spacing-y-2" className="w-full table-auto border-separate border-spacing-y-2"

View File

@@ -22,9 +22,9 @@ export const getApprovalWorkflowsByEntities = async (collection: string, ids: st
.toArray(); .toArray();
}; };
export const getApprovalWorkflowByFormIntaker = async (/* entityId: string, */ formIntakerId: string) => { export const getApprovalWorkflowByFormIntaker = async (entityId: string, formIntakerId: string) => {
return await db.collection<ApprovalWorkflow>("configured-workflows").findOne({ return await db.collection<ApprovalWorkflow>("configured-workflows").findOne({
/* entityId, */ entityId,
steps: { steps: {
$elemMatch: { $elemMatch: {
stepNumber: 1, stepNumber: 1,