instanciate all workflows configured for an exam author based on different entities.
This commit is contained in:
@@ -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."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
} else {
|
const creationResponse = await createApprovalWorkflow("active-workflows", configuredWorkflow);
|
||||||
return res.status(404).json("No configured workflow found for examAuthor.");
|
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 {
|
||||||
|
return res.status(404).json({ ok: false, message: "No workflows were created", results });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user