- edit workflow back-end implementation
- clone workflow back-end implementation - added loading and redirecting to form submissions - fixed form intake in progress bug - fixed rendering bug
This commit is contained in:
@@ -26,6 +26,7 @@ export default function WorkflowEditableStepComponent({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
|
|
||||||
const [selects, setSelects] = useState<(Option | null | undefined)[]>([null]);
|
const [selects, setSelects] = useState<(Option | null | undefined)[]>([null]);
|
||||||
|
const [isAdding, setIsAdding] = useState(false);
|
||||||
|
|
||||||
const teacherOptions: Option[] = useMemo(() =>
|
const teacherOptions: Option[] = useMemo(() =>
|
||||||
entityTeachers
|
entityTeachers
|
||||||
@@ -85,13 +86,17 @@ export default function WorkflowEditableStepComponent({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleAddSelectComponent = () => {
|
const handleAddSelectComponent = () => {
|
||||||
setSelects((prev) => {
|
setIsAdding(true); // I hate to use flags... but it was the only way i was able to prevent onSelectChange to cause parent component from re-rendering in the midle of EditableWorkflowStep rerender.
|
||||||
const updated = [...prev, null];
|
setSelects(prev => [...prev, null]);
|
||||||
onSelectChange(updated.length, updated.length - 1, null);
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAdding) {
|
||||||
|
onSelectChange(selects.length, selects.length - 1, null);
|
||||||
|
setIsAdding(false);
|
||||||
|
}
|
||||||
|
}, [selects.length, isAdding, onSelectChange]);
|
||||||
|
|
||||||
const handleSelectChangeAt = (numberOfSelects: number, index: number, option: Option | null) => {
|
const handleSelectChangeAt = (numberOfSelects: number, index: number, option: Option | null) => {
|
||||||
const updated = [...selects];
|
const updated = [...selects];
|
||||||
updated[index] = option;
|
updated[index] = option;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Option from "@/interfaces/option";
|
|||||||
import { CorporateUser, TeacherUser } from "@/interfaces/user";
|
import { CorporateUser, TeacherUser } from "@/interfaces/user";
|
||||||
import { AnimatePresence, Reorder, motion } from "framer-motion";
|
import { AnimatePresence, Reorder, motion } from "framer-motion";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FaRegCheckCircle } from "react-icons/fa";
|
import { FaRegCheckCircle, FaSpinner } from "react-icons/fa";
|
||||||
import { IoIosAddCircleOutline } from "react-icons/io";
|
import { IoIosAddCircleOutline } from "react-icons/io";
|
||||||
import Button from "../Low/Button";
|
import Button from "../Low/Button";
|
||||||
import Tip from "./Tip";
|
import Tip from "./Tip";
|
||||||
@@ -14,9 +14,11 @@ interface Props {
|
|||||||
onWorkflowChange: (workflow: EditableApprovalWorkflow) => void;
|
onWorkflowChange: (workflow: EditableApprovalWorkflow) => void;
|
||||||
entityTeachers: TeacherUser[];
|
entityTeachers: TeacherUser[];
|
||||||
entityCorporates: CorporateUser[];
|
entityCorporates: CorporateUser[];
|
||||||
|
isLoading: boolean;
|
||||||
|
isRedirecting: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function WorkflowForm({ workflow, onWorkflowChange, entityTeachers, entityCorporates }: Props) {
|
export default function WorkflowForm({ workflow, onWorkflowChange, entityTeachers, entityCorporates, isLoading, isRedirecting }: Props) {
|
||||||
const [stepCounter, setStepCounter] = useState<number>(3); // to guarantee unique keys used for animations
|
const [stepCounter, setStepCounter] = useState<number>(3); // to guarantee unique keys used for animations
|
||||||
const lastStep = workflow.steps[workflow.steps.length - 1];
|
const lastStep = workflow.steps[workflow.steps.length - 1];
|
||||||
|
|
||||||
@@ -145,10 +147,25 @@ export default function WorkflowForm({ workflow, onWorkflowChange, entityTeacher
|
|||||||
type="submit"
|
type="submit"
|
||||||
color="purple"
|
color="purple"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
disabled={isLoading}
|
||||||
className="max-w-fit text-lg font-medium flex items-center gap-2 text-left -mt-4"
|
className="max-w-fit text-lg font-medium flex items-center gap-2 text-left -mt-4"
|
||||||
>
|
>
|
||||||
<FaRegCheckCircle className="size-5" />
|
{isRedirecting ? (
|
||||||
Confirm Exam Workflow Pipeline
|
<>
|
||||||
|
<FaSpinner className="animate-spin size-5" />
|
||||||
|
Redirecting...
|
||||||
|
</>
|
||||||
|
) : isLoading ? (
|
||||||
|
<>
|
||||||
|
<FaSpinner className="animate-spin size-5" />
|
||||||
|
Loading...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FaRegCheckCircle className="size-5" />
|
||||||
|
Confirm Exam Workflow Pipeline
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
</Reorder.Item>
|
</Reorder.Item>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default function WorkflowStepComponent({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!completed && completedBy && (
|
{!completed && currentStep && (
|
||||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||||
In Progress... Assignees:
|
In Progress... Assignees:
|
||||||
<div className="flex flex-row flex-wrap gap-3 items-center">
|
<div className="flex flex-row flex-wrap gap-3 items-center">
|
||||||
|
|||||||
27
src/pages/api/approval-workflows/[id]/clone.ts
Normal file
27
src/pages/api/approval-workflows/[id]/clone.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
|
import { ApprovalWorkflow } from "@/interfaces/approval.workflow";
|
||||||
|
import { sessionOptions } from "@/lib/session";
|
||||||
|
import { requestUser } from "@/utils/api";
|
||||||
|
import { createApprovalWorkflow } 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 === "POST") return await post(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function post(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 approvalWorkflow: ApprovalWorkflow = req.body;
|
||||||
|
|
||||||
|
if (approvalWorkflow)
|
||||||
|
return res.status(201).json(await createApprovalWorkflow(approvalWorkflow));
|
||||||
|
}
|
||||||
28
src/pages/api/approval-workflows/[id]/edit.ts
Normal file
28
src/pages/api/approval-workflows/[id]/edit.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
|
import { ApprovalWorkflow } from "@/interfaces/approval.workflow";
|
||||||
|
import { sessionOptions } from "@/lib/session";
|
||||||
|
import { requestUser } from "@/utils/api";
|
||||||
|
import { createApprovalWorkflows, updateApprovalWorkflow } 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 === "PUT") return await put(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function put(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 };
|
||||||
|
const approvalWorkflow: ApprovalWorkflow = req.body;
|
||||||
|
|
||||||
|
if (id && approvalWorkflow)
|
||||||
|
return res.status(204).json(await updateApprovalWorkflow(id, approvalWorkflow));
|
||||||
|
}
|
||||||
@@ -21,8 +21,11 @@ import Link from "next/link";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { BsChevronLeft } from "react-icons/bs";
|
import { BsChevronLeft } from "react-icons/bs";
|
||||||
import { GrClearOption } from "react-icons/gr";
|
import { GrClearOption } from "react-icons/gr";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { toast, ToastContainer } from "react-toastify";
|
||||||
import approvalWorkflowsData from '../../../demo/approval_workflows.json'; // to test locally
|
import approvalWorkflowsData from '../../../demo/approval_workflows.json'; // to test locally
|
||||||
|
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
||||||
const user = await requestUser(req, res);
|
const user = await requestUser(req, res);
|
||||||
@@ -33,9 +36,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
|||||||
|
|
||||||
const { id } = params as { id: string };
|
const { id } = params as { id: string };
|
||||||
|
|
||||||
// replace later with await getApprovalWorkflow(id).
|
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow(id);
|
||||||
const approvalWorkflowsDataAsWorkflows = approvalWorkflowsData as ApprovalWorkflow[];
|
|
||||||
const workflow: ApprovalWorkflow | undefined = approvalWorkflowsDataAsWorkflows.find(workflow => workflow.id === id);
|
|
||||||
|
|
||||||
if (!workflow)
|
if (!workflow)
|
||||||
return redirect("/approval-workflows")
|
return redirect("/approval-workflows")
|
||||||
@@ -66,6 +67,10 @@ export default function Home({ user, workflow, userEntitiesWithLabel, userEntiti
|
|||||||
const [entityId, setEntityId] = useState<string | null | undefined>(workflow.entityId);
|
const [entityId, setEntityId] = useState<string | null | undefined>(workflow.entityId);
|
||||||
const [entityTeachers, setEntityTeachers] = useState<TeacherUser[]>([]);
|
const [entityTeachers, setEntityTeachers] = useState<TeacherUser[]>([]);
|
||||||
const [entityCorporates, setEntityCorporates] = useState<CorporateUser[]>([]);
|
const [entityCorporates, setEntityCorporates] = useState<CorporateUser[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [isRedirecting, setIsRedirecting] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (entityId) {
|
if (entityId) {
|
||||||
@@ -118,18 +123,47 @@ export default function Home({ user, workflow, userEntitiesWithLabel, userEntiti
|
|||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
if (!cloneWorkflow) return;
|
if (!cloneWorkflow) {
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const filteredWorkflow = {
|
const filteredWorkflow: ApprovalWorkflow = {
|
||||||
...cloneWorkflow,
|
...cloneWorkflow,
|
||||||
steps: cloneWorkflow.steps.map(step => ({
|
steps: cloneWorkflow.steps.map(step => ({
|
||||||
...step,
|
...step,
|
||||||
assignees: step.assignees.filter(assignee => assignee !== null && assignee !== undefined)
|
currentStep: step.stepNumber === 1 ? true : false,
|
||||||
|
completed: false,
|
||||||
|
assignees: step.assignees.filter((assignee): assignee is string => assignee !== null && assignee !== undefined)
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Form submitted! Filtered Workflow:", filteredWorkflow);
|
axios
|
||||||
|
.post(`/api/approval-workflows/${workflow.id}/clone`, filteredWorkflow)
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Approval Workflow cloned successfully.");
|
||||||
|
setIsRedirecting(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push("/approval-workflows");
|
||||||
|
}, 2000);
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
if (reason.response.status === 401) {
|
||||||
|
toast.error("Not logged in!");
|
||||||
|
return router.push("/login");
|
||||||
|
}
|
||||||
|
if (reason.response.status === 403) {
|
||||||
|
toast.error("You do not have permission to clone Approval Workflows!");
|
||||||
|
return router.push("/approval-workflows");
|
||||||
|
}
|
||||||
|
toast.error("Something went wrong, please try again later.");
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log("Form submitted! Filtered Values:", filteredWorkflow);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWorkflowChange = (wf: EditableApprovalWorkflow) => {
|
const onWorkflowChange = (wf: EditableApprovalWorkflow) => {
|
||||||
@@ -257,6 +291,8 @@ export default function Home({ user, workflow, userEntitiesWithLabel, userEntiti
|
|||||||
onWorkflowChange={onWorkflowChange}
|
onWorkflowChange={onWorkflowChange}
|
||||||
entityTeachers={entityTeachers}
|
entityTeachers={entityTeachers}
|
||||||
entityCorporates={entityCorporates}
|
entityCorporates={entityCorporates}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isRedirecting={isRedirecting}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</LayoutGroup>
|
</LayoutGroup>
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import Head from "next/head";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { BsChevronLeft } from "react-icons/bs";
|
import { BsChevronLeft } from "react-icons/bs";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { toast, ToastContainer } from "react-toastify";
|
||||||
import approvalWorkflowsData from '../../../demo/approval_workflows.json'; // to test locally
|
import axios from "axios";
|
||||||
|
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
||||||
const user = await requestUser(req, res);
|
const user = await requestUser(req, res);
|
||||||
@@ -28,9 +30,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
|||||||
|
|
||||||
const { id } = params as { id: string };
|
const { id } = params as { id: string };
|
||||||
|
|
||||||
// replace later with await getApprovalWorkflow(id).
|
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow(id);
|
||||||
const approvalWorkflowsDataAsWorkflows = approvalWorkflowsData as ApprovalWorkflow[];
|
|
||||||
const workflow: ApprovalWorkflow | undefined = approvalWorkflowsDataAsWorkflows.find(workflow => workflow.id === id);
|
|
||||||
|
|
||||||
if (!workflow)
|
if (!workflow)
|
||||||
return redirect("/approval-workflows")
|
return redirect("/approval-workflows")
|
||||||
@@ -54,6 +54,10 @@ interface Props {
|
|||||||
|
|
||||||
export default function Home({ user, workflow, workflowEntityTeachers, workflowEntityCorporates }: Props) {
|
export default function Home({ user, workflow, workflowEntityTeachers, workflowEntityCorporates }: Props) {
|
||||||
const [updatedWorkflow, setUpdatedWorkflow] = useState<EditableApprovalWorkflow | null>(null);
|
const [updatedWorkflow, setUpdatedWorkflow] = useState<EditableApprovalWorkflow | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [isRedirecting, setIsRedirecting] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const editableSteps: EditableWorkflowStep[] = workflow.steps.map(step => ({
|
const editableSteps: EditableWorkflowStep[] = workflow.steps.map(step => ({
|
||||||
@@ -82,23 +86,52 @@ export default function Home({ user, workflow, workflowEntityTeachers, workflowE
|
|||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
if (!updatedWorkflow) return;
|
if (!updatedWorkflow){
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const filteredWorkflow = {
|
const filteredWorkflow: ApprovalWorkflow = {
|
||||||
...updatedWorkflow,
|
...updatedWorkflow,
|
||||||
steps: updatedWorkflow.steps.map(step => ({
|
steps: updatedWorkflow.steps.map(step => ({
|
||||||
...step,
|
...step,
|
||||||
assignees: step.assignees.filter(assignee => assignee !== null && assignee !== undefined)
|
currentStep: step.stepNumber === 1 ? true : false,
|
||||||
|
completed: false,
|
||||||
|
assignees: step.assignees.filter((assignee): assignee is string => assignee !== null && assignee !== undefined)
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Form submitted! Filtered Workflow:", filteredWorkflow);
|
axios
|
||||||
|
.put(`/api/approval-workflows/${workflow.id}/edit`, filteredWorkflow)
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Approval Workflow edited successfully.");
|
||||||
|
setIsRedirecting(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push("/approval-workflows");
|
||||||
|
}, 2000);
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
if (reason.response.status === 401) {
|
||||||
|
toast.error("Not logged in!");
|
||||||
|
return router.push("/login");
|
||||||
|
}
|
||||||
|
if (reason.response.status === 403) {
|
||||||
|
toast.error("You do not have permission to edit Approval Workflows!");
|
||||||
|
return router.push("/approval-workflows");
|
||||||
|
}
|
||||||
|
toast.error("Something went wrong, please try again later.");
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log("Form submitted! Filtered Values:", filteredWorkflow);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWorkflowChange = (wf: EditableApprovalWorkflow) => {
|
const onWorkflowChange = (updatedWorkflow: EditableApprovalWorkflow) => {
|
||||||
setUpdatedWorkflow(wf);
|
setUpdatedWorkflow(updatedWorkflow);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -153,6 +186,8 @@ export default function Home({ user, workflow, workflowEntityTeachers, workflowE
|
|||||||
onWorkflowChange={onWorkflowChange}
|
onWorkflowChange={onWorkflowChange}
|
||||||
entityTeachers={workflowEntityTeachers}
|
entityTeachers={workflowEntityTeachers}
|
||||||
entityCorporates={workflowEntityCorporates}
|
entityCorporates={workflowEntityCorporates}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isRedirecting={isRedirecting}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import RequestedBy from "@/components/ApprovalWorkflows/RequestedBy";
|
import RequestedBy from "@/components/ApprovalWorkflows/RequestedBy";
|
||||||
import StartedOn from "@/components/ApprovalWorkflows/StartedOn";
|
import StartedOn from "@/components/ApprovalWorkflows/StartedOn";
|
||||||
import Status from "@/components/ApprovalWorkflows/Status";
|
import Status from "@/components/ApprovalWorkflows/Status";
|
||||||
|
import Tip from "@/components/ApprovalWorkflows/Tip";
|
||||||
import UserWithProfilePic from "@/components/ApprovalWorkflows/UserWithProfilePic";
|
import UserWithProfilePic from "@/components/ApprovalWorkflows/UserWithProfilePic";
|
||||||
import WorkflowStepComponent from "@/components/ApprovalWorkflows/WorkflowStepComponent";
|
import WorkflowStepComponent from "@/components/ApprovalWorkflows/WorkflowStepComponent";
|
||||||
import Layout from "@/components/High/Layout";
|
import Layout from "@/components/High/Layout";
|
||||||
@@ -9,8 +10,10 @@ import { User } from "@/interfaces/user";
|
|||||||
import { sessionOptions } from "@/lib/session";
|
import { sessionOptions } from "@/lib/session";
|
||||||
import { redirect, serialize } from "@/utils";
|
import { redirect, serialize } from "@/utils";
|
||||||
import { requestUser } from "@/utils/api";
|
import { requestUser } from "@/utils/api";
|
||||||
|
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
|
||||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
||||||
import { getSpecificUsers, getUser } from "@/utils/users.be";
|
import { getSpecificUsers, getUser } from "@/utils/users.be";
|
||||||
|
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
|
||||||
import { withIronSessionSsr } from "iron-session/next";
|
import { withIronSessionSsr } from "iron-session/next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -21,10 +24,6 @@ import { MdOutlineDoubleArrow } from "react-icons/md";
|
|||||||
import { RiThumbUpLine } from "react-icons/ri";
|
import { RiThumbUpLine } from "react-icons/ri";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
|
|
||||||
import approvalWorkflowsData from '../../../demo/approval_workflows.json'; // to test locally
|
|
||||||
import Tip from "@/components/ApprovalWorkflows/Tip";
|
|
||||||
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
|
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
||||||
const user = await requestUser(req, res);
|
const user = await requestUser(req, res);
|
||||||
if (!user) return redirect("/login")
|
if (!user) return redirect("/login")
|
||||||
@@ -34,9 +33,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
|||||||
|
|
||||||
const { id } = params as { id: string };
|
const { id } = params as { id: string };
|
||||||
|
|
||||||
// replace later with await getApprovalWorkflow(id).
|
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow(id);
|
||||||
const approvalWorkflowsDataAsWorkflows = approvalWorkflowsData as ApprovalWorkflow[];
|
|
||||||
const workflow: ApprovalWorkflow | undefined = approvalWorkflowsDataAsWorkflows.find(workflow => workflow.id === id);
|
|
||||||
|
|
||||||
if (!workflow)
|
if (!workflow)
|
||||||
return redirect("/approval-workflows")
|
return redirect("/approval-workflows")
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
|
|||||||
import { withIronSessionSsr } from "iron-session/next";
|
import { withIronSessionSsr } from "iron-session/next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { BsChevronLeft, BsTrash } from "react-icons/bs";
|
import { BsChevronLeft, BsTrash } from "react-icons/bs";
|
||||||
import { MdFormatListBulletedAdd } from "react-icons/md";
|
import { MdFormatListBulletedAdd } from "react-icons/md";
|
||||||
@@ -56,7 +57,11 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
const [entityId, setEntityId] = useState<string | null | undefined>(null);
|
const [entityId, setEntityId] = useState<string | null | undefined>(null);
|
||||||
const [entityTeachers, setEntityTeachers] = useState<TeacherUser[]>([]);
|
const [entityTeachers, setEntityTeachers] = useState<TeacherUser[]>([]);
|
||||||
const [entityCorporates, setEntityCorporates] = useState<CorporateUser[]>([]);
|
const [entityCorporates, setEntityCorporates] = useState<CorporateUser[]>([]);
|
||||||
const [isAdding, setIsAdding] = useState(false); // used to temporary timeout new workflow button. With animations, clicking too fast might cause state inconsistencies between renders.
|
const [isAdding, setIsAdding] = useState<boolean>(false); // used to temporary timeout new workflow button. With animations, clicking too fast might cause state inconsistencies between renders.
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [isRedirecting, setIsRedirecting] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (entityId) {
|
if (entityId) {
|
||||||
@@ -86,6 +91,11 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
if (workflows.length === 0) {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
const filteredWorkflows: ApprovalWorkflow[] = workflows.map(workflow => ({
|
const filteredWorkflows: ApprovalWorkflow[] = workflows.map(workflow => ({
|
||||||
...workflow,
|
...workflow,
|
||||||
@@ -99,24 +109,28 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
|
|
||||||
axios
|
axios
|
||||||
.post(`/api/approval-workflows/create`, filteredWorkflows)
|
.post(`/api/approval-workflows/create`, filteredWorkflows)
|
||||||
.then(() => toast.success(`Approval Workflows created successfully.`))
|
.then(() => {
|
||||||
|
toast.success("Approval Workflows created successfully.");
|
||||||
|
setIsRedirecting(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push("/approval-workflows");
|
||||||
|
}, 2000);
|
||||||
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 401) {
|
if (reason.response.status === 401) {
|
||||||
toast.error("Not logged in!");
|
toast.error("Not logged in!");
|
||||||
return redirect("/login");
|
return router.push("/login");
|
||||||
}
|
}
|
||||||
if (reason.response.status === 403) {
|
if (reason.response.status === 403) {
|
||||||
toast.error("You do not have permission to create Approval Workflows!");
|
toast.error("You do not have permission to create Approval Workflows!");
|
||||||
return;
|
return router.push("/approval-workflows");
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.error("Something went wrong, please try again later.");
|
toast.error("Something went wrong, please try again later.");
|
||||||
|
setIsLoading(false);
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log("Form submitted! Filtered Values:", filteredWorkflows);
|
console.log("Form submitted! Filtered Values:", filteredWorkflows);
|
||||||
|
|
||||||
return redirect("/approval-workflows");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddNewWorkflow = () => {
|
const handleAddNewWorkflow = () => {
|
||||||
@@ -309,6 +323,8 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
onWorkflowChange={onWorkflowChange}
|
onWorkflowChange={onWorkflowChange}
|
||||||
entityTeachers={entityTeachers}
|
entityTeachers={entityTeachers}
|
||||||
entityCorporates={entityCorporates}
|
entityCorporates={entityCorporates}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isRedirecting={isRedirecting}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</LayoutGroup>
|
</LayoutGroup>
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ export const getApprovalWorkflow = async (id: string) => {
|
|||||||
|
|
||||||
export const createApprovalWorkflow = async (workflow: ApprovalWorkflow) => {
|
export const createApprovalWorkflow = async (workflow: ApprovalWorkflow) => {
|
||||||
await db.collection("approval-workflows").insertOne(workflow);
|
await db.collection("approval-workflows").insertOne(workflow);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createApprovalWorkflows = async (workflows: ApprovalWorkflow[]) => {
|
export const createApprovalWorkflows = async (workflows: ApprovalWorkflow[]) => {
|
||||||
if (workflows.length === 0) return;
|
if (workflows.length === 0) return;
|
||||||
await db.collection<ApprovalWorkflow>("approval-workflows").insertMany(workflows);
|
await db.collection<ApprovalWorkflow>("approval-workflows").insertMany(workflows);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateApprovalWorkflow = async (id: string, workflow: ApprovalWorkflow) => {
|
||||||
|
await db.collection("approval-workflows").replaceOne({ id }, workflow);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user