diff --git a/src/hooks/useApprovalWorkflows.tsx b/src/hooks/useApprovalWorkflows.tsx new file mode 100644 index 00000000..1b950c2f --- /dev/null +++ b/src/hooks/useApprovalWorkflows.tsx @@ -0,0 +1,24 @@ +import { ApprovalWorkflow } from "@/interfaces/approval.workflow"; +import axios from "axios"; +import { useCallback, useEffect, useState } from "react"; + +export default function useApprovalWorkflows() { + const [workflows, setWorkflows] = 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)) + .catch((error) => { + setIsError(true); + }) + .finally(() => setIsLoading(false)); + }, []); + + useEffect(getData, [getData]); + + return { workflows, isLoading, isError, reload: getData }; +} diff --git a/src/pages/api/approval-workflows/index.ts b/src/pages/api/approval-workflows/index.ts new file mode 100644 index 00000000..e9a5b075 --- /dev/null +++ b/src/pages/api/approval-workflows/index.ts @@ -0,0 +1,23 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import { sessionOptions } from "@/lib/session"; +import { requestUser } from "@/utils/api"; +import { getApprovalWorkflows } from "@/utils/approval.workflows.be"; +import { withIronSessionApiRoute } from "iron-session/next"; +import type { NextApiRequest, NextApiResponse } from "next"; + +export default withIronSessionApiRoute(handler, sessionOptions); + +async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === "GET") return await get(req, res); +} + +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 }); + } + + return res.status(200).json(await getApprovalWorkflows("configured-workflows")); +} diff --git a/src/pages/approval-workflows/index.tsx b/src/pages/approval-workflows/index.tsx index 18bc8d8a..ab51ee4f 100644 --- a/src/pages/approval-workflows/index.tsx +++ b/src/pages/approval-workflows/index.tsx @@ -2,6 +2,7 @@ import Layout from "@/components/High/Layout"; import Button from "@/components/Low/Button"; import Input from "@/components/Low/Input"; import Select from "@/components/Low/Select"; +import useApprovalWorkflows from "@/hooks/useApprovalWorkflows"; import { Module, ModuleTypeLabels } from "@/interfaces"; import { ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel, StepTypeLabel } from "@/interfaces/approval.workflow"; import { Entity, EntityWithRoles } from "@/interfaces/entity"; @@ -51,7 +52,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { return { props: serialize({ user, - workflows, + initialWorkflows: workflows, workflowsAssignees: await getSpecificUsers(allAssigneeIds), userEntitiesWithLabel: await getEntities(user.entities.map(entity => entity.id)), }), @@ -87,12 +88,21 @@ const STATUS_OPTIONS = [ interface Props { user: User, - workflows: ApprovalWorkflow[], + initialWorkflows: ApprovalWorkflow[], workflowsAssignees: User[], userEntitiesWithLabel: Entity[], } -export default function ApprovalWorkflows({ user, workflows, workflowsAssignees, userEntitiesWithLabel }: Props) { +export default function ApprovalWorkflows({ user, initialWorkflows, workflowsAssignees, userEntitiesWithLabel }: Props) { + + const {workflows, reload} = useApprovalWorkflows(); + const currentWorkflows = workflows || initialWorkflows; + + const [filteredWorkflows, setFilteredWorkflows] = useState([]); + + const [statusFilter, setStatusFilter] = useState(undefined); + const [entityFilter, setEntityFilter] = useState(undefined); + const [nameFilter, setNameFilter] = useState(""); const ENTITY_OPTIONS = [ ...userEntitiesWithLabel @@ -104,13 +114,6 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees, .sort((a, b) => a.label.localeCompare(b.label)), ]; - const [filteredWorkflows, setFilteredWorkflows] = useState([]); - - const [statusFilter, setStatusFilter] = useState(undefined); - const [entityFilter, setEntityFilter] = useState(undefined); - const [nameFilter, setNameFilter] = useState(""); - const router = useRouter(); - useEffect(() => { const filters: Array<(workflow: ApprovalWorkflow) => boolean> = []; @@ -135,10 +138,10 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees, } // Apply all filters - const filtered = workflows.filter(workflow => filters.every(filterFn => filterFn(workflow))); + const filtered = currentWorkflows.filter(workflow => filters.every(filterFn => filterFn(workflow))); setFilteredWorkflows(filtered); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [workflows, statusFilter, entityFilter, nameFilter]); + }, [currentWorkflows, statusFilter, entityFilter, nameFilter]); const handleNameFilterChange = (name: ApprovalWorkflow["name"]) => { @@ -153,7 +156,7 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees, .delete(`/api/approval-workflows/${id}`) .then(() => { toast.success(`Successfully deleted ${name} Approval Workflow.`); - router.reload(); + reload(); }) .catch((reason) => { if (reason.response.status === 404) { @@ -215,7 +218,7 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees, const currentStep = steps.find((step) => !step.completed); const rejected = steps.find((step) => step.rejected); - if(rejected) return ""; + if (rejected) return ""; const assignees = currentStep?.assignees.map((assigneeId) => { const assignee = workflowsAssignees.find((user) => user.id === assigneeId);