diff --git a/src/hooks/useApprovalWorkflows.tsx b/src/hooks/useApprovalWorkflow.tsx similarity index 58% rename from src/hooks/useApprovalWorkflows.tsx rename to src/hooks/useApprovalWorkflow.tsx index 884f1cb4..9908cfbe 100644 --- a/src/hooks/useApprovalWorkflows.tsx +++ b/src/hooks/useApprovalWorkflow.tsx @@ -2,23 +2,23 @@ import { ApprovalWorkflow } from "@/interfaces/approval.workflow"; import axios from "axios"; import { useCallback, useEffect, useState } from "react"; -export default function useApprovalWorkflows() { - const [workflows, setWorkflows] = useState([]); +export default function useApprovalWorkflow(id: string) { + const [workflow, setWorkflow] = useState(); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const getData = useCallback(() => { setIsLoading(true); axios - .get(`/api/approval-workflows`) - .then((response) => setWorkflows(response.data)) + .get(`/api/approval-workflows/${id}`) + .then((response) => setWorkflow(response.data)) .catch((error) => { - setIsError(true); + setIsError(true); }) .finally(() => setIsLoading(false)); }, []); useEffect(getData, [getData]); - return { workflows, isLoading, isError, reload: getData }; + return { workflow, isLoading, isError, reload: getData }; } diff --git a/src/pages/api/approval-workflows/[id]/index.ts b/src/pages/api/approval-workflows/[id]/index.ts index 6664ac9d..7bf8ecf3 100644 --- a/src/pages/api/approval-workflows/[id]/index.ts +++ b/src/pages/api/approval-workflows/[id]/index.ts @@ -2,7 +2,7 @@ import { ApprovalWorkflow } from "@/interfaces/approval.workflow"; import { sessionOptions } from "@/lib/session"; import { requestUser } from "@/utils/api"; -import { deleteApprovalWorkflow, updateApprovalWorkflow } from "@/utils/approval.workflows.be"; +import { deleteApprovalWorkflow, getApprovalWorkflow, updateApprovalWorkflow } from "@/utils/approval.workflows.be"; import { withIronSessionApiRoute } from "iron-session/next"; import { ObjectId } from "mongodb"; import type { NextApiRequest, NextApiResponse } from "next"; @@ -12,6 +12,7 @@ export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === "DELETE") return await del(req, res); if (req.method === "PUT") return await put(req, res); + if (req.method === "GET") return await get(req, res); } async function del(req: NextApiRequest, res: NextApiResponse) { @@ -44,3 +45,18 @@ async function put(req: NextApiRequest, res: NextApiResponse) { return res.status(204).end(); } } + +async function get(req: NextApiRequest, res: NextApiResponse) { + const user = await requestUser(req, res); + if (!user) return res.status(401).json({ ok: false }); + + if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) { + return res.status(403).json({ ok: false }); + } + + const { id } = req.query as { id?: string }; + + if (id) { + return res.status(200).json(await getApprovalWorkflow("configured-workflows", id)); + } +} diff --git a/src/pages/approval-workflows/[id]/index.tsx b/src/pages/approval-workflows/[id]/index.tsx index 44416384..c3ae3cae 100644 --- a/src/pages/approval-workflows/[id]/index.tsx +++ b/src/pages/approval-workflows/[id]/index.tsx @@ -6,6 +6,7 @@ import UserWithProfilePic from "@/components/ApprovalWorkflows/UserWithProfilePi import WorkflowStepComponent from "@/components/ApprovalWorkflows/WorkflowStepComponent"; import Layout from "@/components/High/Layout"; import Button from "@/components/Low/Button"; +import useApprovalWorkflow from "@/hooks/useApprovalWorkflow"; import { ApprovalWorkflow, getUserTypeLabelShort, WorkflowStep } from "@/interfaces/approval.workflow"; import { User } from "@/interfaces/user"; import { sessionOptions } from "@/lib/session"; @@ -19,7 +20,6 @@ import { AnimatePresence, 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 { useState } from "react"; import { BsChevronLeft } from "react-icons/bs"; import { FaSpinner, FaWpforms } from "react-icons/fa6"; @@ -57,7 +57,8 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params } return { props: serialize({ user, - workflow, + initialWorkflow: workflow, + id, workflowAssignees: await getSpecificUsers(allAssigneeIds), workflowRequester: await getUser(workflow.requester), }), @@ -66,25 +67,26 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params } interface Props { user: User, - workflow: ApprovalWorkflow, + initialWorkflow: ApprovalWorkflow, + id: string, workflowAssignees: User[], workflowRequester: User, } -export default function Home({ user, workflow, workflowAssignees, workflowRequester }: Props) { - const steps = workflow.steps; +export default function Home({ user, initialWorkflow, id, workflowAssignees, workflowRequester }: Props) { - let currentStep = steps.findIndex(step => !step.completed || step.rejected); + const { workflow, reload, isLoading } = useApprovalWorkflow(id); + + const currentWorkflow = workflow || initialWorkflow; + + let currentStep = currentWorkflow.steps.findIndex(step => !step.completed || step.rejected); if (currentStep === -1) - currentStep = steps.length - 1; + currentStep = currentWorkflow.steps.length - 1; const [selectedStepIndex, setSelectedStepIndex] = useState(currentStep); - const [selectedStep, setSelectedStep] = useState(steps[selectedStepIndex]); + const [selectedStep, setSelectedStep] = useState(currentWorkflow.steps[selectedStepIndex]); const [isPanelOpen, setIsPanelOpen] = useState(true); const [comments, setComments] = useState(selectedStep.comments || ""); - const [isLoading, setIsLoading] = useState(false); - const [isRedirecting, setIsRedirecting] = useState(false); - const router = useRouter(); const handleStepClick = (index: number, stepInfo: WorkflowStep) => { setSelectedStep(stepInfo); @@ -94,11 +96,9 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques }; const handleSaveComments = () => { - setIsLoading(true); - const updatedWorkflow: ApprovalWorkflow = { - ...workflow, - steps: workflow.steps.map((step, index) => + ...currentWorkflow, + steps: currentWorkflow.steps.map((step, index) => index === selectedStepIndex ? { ...step, @@ -109,11 +109,10 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques }; axios - .put(`/api/approval-workflows/${workflow._id}`, updatedWorkflow) + .put(`/api/approval-workflows/${id}`, updatedWorkflow) .then(() => { toast.success("Comments saved successfully."); - setIsRedirecting(true); - router.reload(); + reload(); }) .catch((reason) => { if (reason.response.status === 401) { @@ -123,19 +122,16 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques } else { toast.error("Something went wrong, please try again later."); } - setIsLoading(false); console.log("Submitted Values:", updatedWorkflow); return; }) }; const handleApproveStep = () => { - setIsLoading(true); - const updatedWorkflow: ApprovalWorkflow = { - ...workflow, - status: selectedStepIndex === workflow.steps.length - 1 ? "approved" : "pending", - steps: workflow.steps.map((step, index) => + ...currentWorkflow, + status: selectedStepIndex === currentWorkflow.steps.length - 1 ? "approved" : "pending", + steps: currentWorkflow.steps.map((step, index) => index === selectedStepIndex ? { ...step, @@ -148,11 +144,10 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques }; axios - .put(`/api/approval-workflows/${workflow._id}`, updatedWorkflow) + .put(`/api/approval-workflows/${id}`, updatedWorkflow) .then(() => { toast.success("Step approved successfully."); - setIsRedirecting(true); - router.reload(); + reload(); }) .catch((reason) => { if (reason.response.status === 401) { @@ -162,20 +157,20 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques } else { toast.error("Something went wrong, please try again later."); } - setIsLoading(false); console.log("Submitted Values:", updatedWorkflow); return; }) + + handleStepClick(selectedStepIndex + 1, currentWorkflow.steps[selectedStepIndex + 1]); }; const handleRejectStep = () => { if (!confirm(`Are you sure you want to reject this step?`)) return; - setIsLoading(true); const updatedWorkflow: ApprovalWorkflow = { - ...workflow, + ...currentWorkflow, status: "rejected", - steps: workflow.steps.map((step, index) => + steps: currentWorkflow.steps.map((step, index) => index === selectedStepIndex ? { ...step, @@ -189,11 +184,10 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques }; axios - .put(`/api/approval-workflows/${workflow._id}`, updatedWorkflow) + .put(`/api/approval-workflows/${id}`, updatedWorkflow) .then(() => { toast.success("Step rejected successfully."); - setIsRedirecting(true); - router.reload(); + reload(); }) .catch((reason) => { if (reason.response.status === 401) { @@ -203,14 +197,13 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques } else { toast.error("Something went wrong, please try again later."); } - setIsLoading(false); console.log("Submitted Values:", updatedWorkflow); return; }) }; const handleViewExam = () => { - + } const handleEditExam = () => { @@ -220,7 +213,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques return ( <> - {workflow.name} | EnCoach + Workflow | EnCoach -

{workflow.name}

+

{currentWorkflow.name}

@@ -249,10 +242,10 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques profileImage={workflowRequester.profilePicture} />
@@ -261,7 +254,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques variant="solid" onClick={handleViewExam} padding="px-6 py-2" - className="w-[240px] text-lg flex items-center justify-center gap-2 text-left" + className="w-[240px] text-lg flex items-center justify-center gap-2 text-left" > View Exam @@ -271,20 +264,20 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques variant="solid" onClick={handleEditExam} padding="px-6 py-2" - className="w-[240px] text-lg flex items-center justify-center gap-2 text-left" + className="w-[240px] text-lg flex items-center justify-center gap-2 text-left" > Edit Exam - +
- {steps.find((step) => !step.completed) === undefined && + {currentWorkflow.steps.find((step) => !step.completed) === undefined && }
- {steps.map((step, index) => ( + {currentWorkflow.steps.map((step, index) => ( handleStepClick(index, step)} @@ -407,12 +400,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques padding="px-6 py-2" className="mb-3 w-full text-lg flex items-center justify-center gap-2 text-left" > - {isRedirecting ? ( - <> - - Reloading... - - ) : isLoading ? ( + {isLoading ? ( <> Loading... @@ -433,12 +421,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques padding="px-6 py-2" className="mb-3 w-1/2 text-lg flex items-center justify-center gap-2 text-left" > - {isRedirecting ? ( - <> - - Reloading... - - ) : isLoading ? ( + {isLoading ? ( <> Loading... @@ -471,12 +454,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques padding="px-6 py-2" className="mt-6 mb-3 w-full text-lg flex items-center justify-center gap-2 text-left" > - {isRedirecting ? ( - <> - - Reloading... - - ) : isLoading ? ( + {isLoading ? ( <> Loading...