From a4ef2222e2e807411755ad3cd3fe586ef6a42e6a Mon Sep 17 00:00:00 2001 From: Joao Correia Date: Wed, 26 Feb 2025 16:51:57 +0000 Subject: [PATCH 1/7] Keep exam confidential even after approval workflow is completed --- src/lib/createWorkflowsOnExamCreation.ts | 6 +++--- src/pages/approval-workflows/[id]/index.tsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/createWorkflowsOnExamCreation.ts b/src/lib/createWorkflowsOnExamCreation.ts index 0f8e1c53..5a102c19 100644 --- a/src/lib/createWorkflowsOnExamCreation.ts +++ b/src/lib/createWorkflowsOnExamCreation.ts @@ -68,14 +68,14 @@ export async function createApprovalWorkflowOnExamCreation(examAuthor: string, e } } - // prettier-ignore - if (totalCount === 0) { // current behaviour: if no workflow was found skip approval process + // commented because they asked for every exam to stay confidential + /* if (totalCount === 0) { // current behaviour: if no workflow was found skip approval process await db.collection(examModule).updateOne( { id: examId }, { $set: { id: examId, access: "private" }}, { upsert: true } ); - } + } */ return { successCount, diff --git a/src/pages/approval-workflows/[id]/index.tsx b/src/pages/approval-workflows/[id]/index.tsx index 4048df94..a898eeb1 100644 --- a/src/pages/approval-workflows/[id]/index.tsx +++ b/src/pages/approval-workflows/[id]/index.tsx @@ -150,7 +150,7 @@ export default function Home({ user, initialWorkflow, id, workflowAssignees, wor const handleApproveStep = () => { const isLastStep = (selectedStepIndex + 1 === currentWorkflow.steps.length); if (isLastStep) { - if (!confirm(`Are you sure you want to approve the last step? Doing so will change the access type of the exam from confidential to private.`)) return; + if (!confirm(`Are you sure you want to approve the last step and complete the approval process?`)) return; } const updatedWorkflow: ApprovalWorkflow = { @@ -188,7 +188,7 @@ export default function Home({ user, initialWorkflow, id, workflowAssignees, wor if (isLastStep) { setIsPanelOpen(false); - const examModule = currentWorkflow.modules[0]; + /* const examModule = currentWorkflow.modules[0]; const examId = currentWorkflow.examId; axios @@ -207,7 +207,7 @@ export default function Home({ user, initialWorkflow, id, workflowAssignees, wor toast.error("Something went wrong, please try again later."); }) - .finally(reload); + .finally(reload); */ } else { handleStepClick(selectedStepIndex + 1, currentWorkflow.steps[selectedStepIndex + 1]); } From dd8f821e35368c5e88c9e899ac202c234d380d22 Mon Sep 17 00:00:00 2001 From: Joao Correia Date: Wed, 26 Feb 2025 17:21:37 +0000 Subject: [PATCH 2/7] only show workflows where user is assigned to at least one step. --- src/pages/api/approval-workflows/index.ts | 7 ++++++- src/utils/approval.workflows.be.ts | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pages/api/approval-workflows/index.ts b/src/pages/api/approval-workflows/index.ts index a2cd2ea4..91c1417d 100644 --- a/src/pages/api/approval-workflows/index.ts +++ b/src/pages/api/approval-workflows/index.ts @@ -23,5 +23,10 @@ async function get(req: NextApiRequest, res: NextApiResponse) { const entityIdsArray = entityIdsString.split(","); - return res.status(200).json(await getApprovalWorkflows("active-workflows", entityIdsArray)); + if (!["admin", "developer"].includes(user.type)) { + // filtering workflows that have user as assignee in at least one of the steps + return res.status(200).json(await getApprovalWorkflows("active-workflows", entityIdsArray, undefined, user.id)); + } else { + return res.status(200).json(await getApprovalWorkflows("active-workflows", entityIdsArray)); + } } diff --git a/src/utils/approval.workflows.be.ts b/src/utils/approval.workflows.be.ts index 9998a06e..833c5b06 100644 --- a/src/utils/approval.workflows.be.ts +++ b/src/utils/approval.workflows.be.ts @@ -4,7 +4,7 @@ import { ObjectId } from "mongodb"; const db = client.db(process.env.MONGODB_DB); -export const getApprovalWorkflows = async (collection: string, entityIds?: string[], ids?: string[]) => { +export const getApprovalWorkflows = async (collection: string, entityIds?: string[], ids?: string[], assignee?: string) => { const filters: any = {}; if (ids && ids.length > 0) { @@ -15,6 +15,10 @@ export const getApprovalWorkflows = async (collection: string, entityIds?: strin filters.entityId = { $in: entityIds }; } + if (assignee) { + filters["steps.assignees"] = assignee; + } + return await db.collection(collection).find(filters).toArray(); }; From ba8cc342b15956850a96297a5b9cd22d5356f51b Mon Sep 17 00:00:00 2001 From: Joao Correia Date: Wed, 26 Feb 2025 19:15:20 +0000 Subject: [PATCH 3/7] add filters to show only exams with or without approval --- src/pages/assignments/creator/index.tsx | 107 ++++++++++++++++++------ 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/src/pages/assignments/creator/index.tsx b/src/pages/assignments/creator/index.tsx index 6b3af36d..ae4a0297 100644 --- a/src/pages/assignments/creator/index.tsx +++ b/src/pages/assignments/creator/index.tsx @@ -63,26 +63,26 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const [users, groups] = await Promise.all([ isAdmin(user) ? getUsers( - {}, - 0, - {}, - { - _id: 0, - id: 1, - type: 1, - name: 1, - email: 1, - levels: 1, - } - ) - : getEntitiesUsers(mapBy(allowedEntities, "id"), {}, 0, { + {}, + 0, + {}, + { _id: 0, id: 1, type: 1, name: 1, email: 1, levels: 1, - }), + } + ) + : getEntitiesUsers(mapBy(allowedEntities, "id"), {}, 0, { + _id: 0, + id: 1, + type: 1, + name: 1, + email: 1, + levels: 1, + }), isAdmin(user) ? getGroups() : getGroupsByEntities(mapBy(allowedEntities, "id")), @@ -101,6 +101,14 @@ interface Props { const SIZE = 9; +type ExamApprovalFilterMap = { + writing: boolean; + reading: boolean; + listening: boolean; + speaking: boolean; + level: boolean; +}; + export default function AssignmentsPage({ user, users, @@ -143,6 +151,21 @@ export default function AssignmentsPage({ const [useRandomExams, setUseRandomExams] = useState(true); const [examIDs, setExamIDs] = useState<{ id: string; module: Module }[]>([]); + const [showExamsThatRequiredApproval, setShowExamsThatRequiredApproval] = useState({ + writing: true, + reading: true, + listening: true, + speaking: true, + level: true, + }); + const [showExamsThatDidntRequireApproval, setShowExamsThatDidntRequireApproval] = useState({ + writing: true, + reading: true, + listening: true, + speaking: true, + level: true, + }); + const { exams } = useExams(); const router = useRouter(); @@ -326,7 +349,7 @@ export default function AssignmentsPage({ onClick={ (!selectedModules.includes("level") && selectedModules.length === 0) || - selectedModules.includes("level") + selectedModules.includes("level") ? () => toggleModule("level") : undefined } @@ -504,10 +527,35 @@ export default function AssignmentsPage({
{selectedModules.map((module) => (
- +
+ + { + setShowExamsThatRequiredApproval(prev => ({ + ...prev, + [module]: !prev[module], + })) + }} + > + Show exams that required approval + + { + setShowExamsThatDidntRequireApproval(prev => ({ + ...prev, + [module]: !prev[module], + })) + }} + > + Show exams that did not require approval + +
e.module === module)?.id || + null, + label: + examIDs.find((e) => e.module === module)?.id || "", }} - > - Show exams that required approval - - { - setShowExamsThatDidntRequireApproval(prev => ({ - ...prev, - [module]: !prev[module], - })) - }} - > - Show exams that did not require approval - + onChange={(value) => + value + ? setExamIDs((prev) => [ + ...prev.filter((x) => x.module !== module), + { id: value.value!, module }, + ]) + : setExamIDs((prev) => + prev.filter((x) => x.module !== module) + ) + } + options={exams + .filter((x) => + !x.isDiagnostic && + x.module === module && + x.access !== "confidential" && + ( + (x.requiresApproval && showApprovedExams) || + (!x.requiresApproval && showNonApprovedExams) + ) + ) + .map((x) => ({ value: x.id, label: x.id }))} + />
-