From f4c7961caa7d81967d3e013353782bd693f87eb9 Mon Sep 17 00:00:00 2001 From: Joao Correia Date: Wed, 5 Feb 2025 00:43:49 +0000 Subject: [PATCH] implement edit active workflow and do not allow editing on already completed steps --- .../WorkflowEditableStepComponent.tsx | 5 +- .../ApprovalWorkflows/WorkflowForm.tsx | 159 ++++++++++-------- .../ApprovalWorkflows/WorkflowStepSelects.tsx | 3 + src/pages/api/approval-workflows/[id]/edit.ts | 2 +- src/pages/approval-workflows/[id]/edit.tsx | 46 +++-- 5 files changed, 122 insertions(+), 93 deletions(-) diff --git a/src/components/ApprovalWorkflows/WorkflowEditableStepComponent.tsx b/src/components/ApprovalWorkflows/WorkflowEditableStepComponent.tsx index 85fab5de..9936842f 100644 --- a/src/components/ApprovalWorkflows/WorkflowEditableStepComponent.tsx +++ b/src/components/ApprovalWorkflows/WorkflowEditableStepComponent.tsx @@ -12,6 +12,7 @@ import WorkflowStepSelects from "./WorkflowStepSelects"; interface Props extends Pick { entityApprovers: (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[]; onSelectChange: (numberOfSelects: number, index: number, value: Option | null) => void; + isCompleted: boolean, } export default function WorkflowEditableStepComponent({ @@ -21,6 +22,7 @@ export default function WorkflowEditableStepComponent({ onDelete, onSelectChange, entityApprovers, + isCompleted, }: Props) { const [selects, setSelects] = useState<(Option | null | undefined)[]>([null]); @@ -95,7 +97,7 @@ export default function WorkflowEditableStepComponent({ )} - {stepNumber !== 1 && !finalStep + {stepNumber !== 1 && !finalStep && !isCompleted ? :
} @@ -106,6 +108,7 @@ export default function WorkflowEditableStepComponent({ selects={selects} placeholder={stepNumber === 1 ? "Form Intake By:" : "Approval By:"} onSelectChange={handleSelectChangeAt} + isCompleted={isCompleted} /> diff --git a/src/components/ApprovalWorkflows/WorkflowForm.tsx b/src/components/ApprovalWorkflows/WorkflowForm.tsx index 4d27e150..c7065d7c 100644 --- a/src/components/ApprovalWorkflows/WorkflowForm.tsx +++ b/src/components/ApprovalWorkflows/WorkflowForm.tsx @@ -1,8 +1,7 @@ import { EditableApprovalWorkflow, EditableWorkflowStep } from "@/interfaces/approval.workflow"; import Option from "@/interfaces/option"; import { CorporateUser, DeveloperUser, MasterCorporateUser, TeacherUser } from "@/interfaces/user"; -import { AnimatePresence, Reorder } from "framer-motion"; -import { useState } from "react"; +import { AnimatePresence, Reorder, motion } from "framer-motion"; import { FaRegCheckCircle, FaSpinner } from "react-icons/fa"; import { IoIosAddCircleOutline } from "react-icons/io"; import Button from "../Low/Button"; @@ -13,9 +12,9 @@ interface Props { workflow: EditableApprovalWorkflow; onWorkflowChange: (workflow: EditableApprovalWorkflow) => void; entityApprovers: (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[]; - entityAvailableFormIntakers: (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[]; + entityAvailableFormIntakers?: (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[]; isLoading: boolean; - isRedirecting: boolean; + isRedirecting?: boolean; } export default function WorkflowForm({ workflow, onWorkflowChange, entityApprovers, entityAvailableFormIntakers, isLoading, isRedirecting }: Props) { @@ -75,19 +74,15 @@ export default function WorkflowForm({ workflow, onWorkflowChange, entityApprove }; const handleReorder = (newOrder: EditableWorkflowStep[]) => { - const firstIndex = newOrder.findIndex((s) => s.firstStep); - if (firstIndex !== -1 && firstIndex !== 0) { - const [first] = newOrder.splice(firstIndex, 1); - newOrder.unshift(first); - } - - const finalIndex = newOrder.findIndex((s) => s.finalStep); - if (finalIndex !== -1 && finalIndex !== newOrder.length - 1) { - const [final] = newOrder.splice(finalIndex, 1); - newOrder.push(final); - } - - onWorkflowChange({ ...workflow, steps: renumberSteps(newOrder) }); + let draggableIndex = 0; + const updatedSteps = workflow.steps.map((step) => { + if (!step.firstStep && !step.finalStep && !step.completed) { + return newOrder[draggableIndex++]; + } + // Keep static steps as-is + return step; + }); + onWorkflowChange({ ...workflow, steps: renumberSteps(updatedSteps) }); }; @@ -118,55 +113,87 @@ export default function WorkflowForm({ workflow, onWorkflowChange, entityApprove className="flex flex-col gap-0" > - {workflow.steps.map((step, index) => ( - - - handleDelete(step.key)} - onSelectChange={(numberOfSelects, idx, option) => handleSelectChange(step.key, numberOfSelects, idx, option)} - entityApprovers={step.stepNumber === 1 ? entityAvailableFormIntakers : entityApprovers} - /> - - {step.finalStep && - - } - - ))} + {workflow.steps.map((step, index) => + step.completed || step.firstStep || step.finalStep ? ( + + handleDelete(step.key)} + onSelectChange={(numberOfSelects, idx, option) => + handleSelectChange(step.key, numberOfSelects, idx, option) + } + entityApprovers={ + step.stepNumber === 1 && entityAvailableFormIntakers + ? entityAvailableFormIntakers + : entityApprovers + } + isCompleted={step.completed} + /> + + ) : ( + // Render non-completed steps as draggable items + + handleDelete(step.key)} + onSelectChange={(numberOfSelects, idx, option) => + handleSelectChange(step.key, numberOfSelects, idx, option) + } + entityApprovers={ + step.stepNumber === 1 && entityAvailableFormIntakers + ? entityAvailableFormIntakers + : entityApprovers + } + isCompleted={step.completed} + /> + + ) + )} + diff --git a/src/components/ApprovalWorkflows/WorkflowStepSelects.tsx b/src/components/ApprovalWorkflows/WorkflowStepSelects.tsx index a7c39f67..878b650c 100644 --- a/src/components/ApprovalWorkflows/WorkflowStepSelects.tsx +++ b/src/components/ApprovalWorkflows/WorkflowStepSelects.tsx @@ -6,6 +6,7 @@ interface Props { selects: (Option | null | undefined)[]; placeholder: string; onSelectChange: (numberOfSelects: number, index: number, value: Option | null) => void; + isCompleted: boolean; } export default function WorkflowStepSelects({ @@ -13,6 +14,7 @@ export default function WorkflowStepSelects({ selects, placeholder, onSelectChange, + isCompleted, }: Props) { return ( @@ -39,6 +41,7 @@ export default function WorkflowStepSelects({ flat isClearable className={classes} + disabled={isCompleted} /> ); diff --git a/src/pages/api/approval-workflows/[id]/edit.ts b/src/pages/api/approval-workflows/[id]/edit.ts index 556d0e52..a547f0cf 100644 --- a/src/pages/api/approval-workflows/[id]/edit.ts +++ b/src/pages/api/approval-workflows/[id]/edit.ts @@ -26,7 +26,7 @@ async function put(req: NextApiRequest, res: NextApiResponse) { if (id && approvalWorkflow) { approvalWorkflow._id = new ObjectId(id); - await updateApprovalWorkflow("configured-workflows", approvalWorkflow); + await updateApprovalWorkflow("active-workflows", approvalWorkflow); return res.status(204).end(); } } diff --git a/src/pages/approval-workflows/[id]/edit.tsx b/src/pages/approval-workflows/[id]/edit.tsx index e256a0e8..84955016 100644 --- a/src/pages/approval-workflows/[id]/edit.tsx +++ b/src/pages/approval-workflows/[id]/edit.tsx @@ -12,11 +12,10 @@ import { getApprovalWorkflow } from "@/utils/approval.workflows.be"; import { shouldRedirectHome } from "@/utils/navigation.disabled"; import { getEntityUsers } from "@/utils/users.be"; import axios from "axios"; -import { motion } from "framer-motion"; +import { LayoutGroup, motion } from "framer-motion"; import { withIronSessionSsr } from "iron-session/next"; import Head from "next/head"; import Link from "next/link"; -import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { BsChevronLeft } from "react-icons/bs"; import { toast, ToastContainer } from "react-toastify"; @@ -30,7 +29,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params } const { id } = params as { id: string }; - const workflow: ApprovalWorkflow | null = await getApprovalWorkflow("configured-workflows", id); + const workflow: ApprovalWorkflow | null = await getApprovalWorkflow("active-workflows", id); if (!workflow) return redirect("/approval-workflows") @@ -53,9 +52,6 @@ interface Props { export default function Home({ user, workflow, workflowEntityApprovers }: Props) { const [updatedWorkflow, setUpdatedWorkflow] = useState(null); const [isLoading, setIsLoading] = useState(false); - const [isRedirecting, setIsRedirecting] = useState(false); - - const router = useRouter(); useEffect(() => { const editableSteps: EditableWorkflowStep[] = workflow.steps.map(step => ({ @@ -114,8 +110,7 @@ export default function Home({ user, workflow, workflowEntityApprovers }: Props) .put(`/api/approval-workflows/${updatedWorkflow.id}/edit`, filteredWorkflow) .then(() => { toast.success("Approval Workflow edited successfully."); - setIsRedirecting(true); - router.push("/approval-workflows"); + setIsLoading(false); }) .catch((reason) => { if (reason.response.status === 401) { @@ -175,23 +170,24 @@ export default function Home({ user, workflow, workflowEntityApprovers }: Props)
- - {/* {updatedWorkflow && - - } */} - + + + {updatedWorkflow && + + } + +
)}