major change on how workflow builder works. It now fetches in edit mode all the currently configured workflows

This commit is contained in:
Joao Correia
2025-02-01 22:36:42 +00:00
parent a0229cd971
commit ac539332e6
12 changed files with 159 additions and 56 deletions

View File

@@ -22,9 +22,9 @@ import { useEffect, useState } from "react";
import { BsChevronLeft } from "react-icons/bs";
import { GrClearOption } from "react-icons/gr";
import { toast, ToastContainer } from "react-toastify";
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
import { useRouter } from "next/router";
import axios from "axios";
import { getConfiguredWorkflow } from "@/utils/approval.workflows.be";
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
const user = await requestUser(req, res);
@@ -35,7 +35,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
const { id } = params as { id: string };
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow(id);
const workflow: ApprovalWorkflow | null = await getConfiguredWorkflow(id);
if (!workflow)
return redirect("/approval-workflows")
@@ -91,6 +91,7 @@ export default function Home({ user, workflow, userEntitiesWithLabel, userEntiti
key: step.stepNumber + 999, // just making sure they are unique because new steps that users add will have key=3 key=4 etc
stepType: step.stepType,
stepNumber: step.stepNumber,
completed: false,
assignees: step.assignees.map(id => id),
firstStep: step.firstStep || false,
finalStep: step.finalStep || false,
@@ -179,8 +180,8 @@ export default function Home({ user, workflow, userEntitiesWithLabel, userEntiti
startDate: Date.now(),
status: "pending",
steps: [
{ key: 9998, stepType: "form-intake", stepNumber: 1, firstStep: true, finalStep: false, assignees: [null] },
{ key: 9999, stepType: "approval-by", stepNumber: 2, firstStep: false, finalStep: true, assignees: [null] },
{ key: 9998, stepType: "form-intake", stepNumber: 1, completed: false, firstStep: true, finalStep: false, assignees: [null] },
{ key: 9999, stepType: "approval-by", stepNumber: 2, completed: false, firstStep: false, finalStep: true, assignees: [null] },
],
};
setCloneWorkflow(newWorkflow);

View File

@@ -8,18 +8,18 @@ import { CorporateUser, DeveloperUser, MasterCorporateUser, TeacherUser, User }
import { sessionOptions } from "@/lib/session";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getConfiguredWorkflow } 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 { 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";
import axios from "axios";
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
import { useRouter } from "next/router";
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
const user = await requestUser(req, res);
@@ -30,7 +30,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
const { id } = params as { id: string };
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow(id);
const workflow: ApprovalWorkflow | null = await getConfiguredWorkflow(id);
if (!workflow)
return redirect("/approval-workflows")
@@ -62,6 +62,9 @@ export default function Home({ user, workflow, workflowEntityApprovers }: Props)
key: step.stepNumber + 999, // just making sure they are unique because new steps that users add will have key=3 key=4 etc
stepType: step.stepType,
stepNumber: step.stepNumber,
completed: step.completed,
completedBy: step.completedBy || undefined,
completedDate: step.completedDate || undefined,
assignees: step.assignees.map(id => id),
firstStep: step.firstStep || false,
finalStep: step.finalStep || false,

View File

@@ -11,7 +11,7 @@ import { User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
import { getConfiguredWorkflow } from "@/utils/approval.workflows.be";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { getSpecificUsers, getUser } from "@/utils/users.be";
import axios from "axios";
@@ -23,12 +23,14 @@ import { useRouter } from "next/router";
import { useState } from "react";
import { BsChevronLeft } from "react-icons/bs";
import { FaSpinner, FaWpforms } from "react-icons/fa6";
import { FiSave } from "react-icons/fi";
import { IoMdCheckmarkCircleOutline } from "react-icons/io";
import { IoDocumentTextOutline } from "react-icons/io5";
import { MdOutlineDoubleArrow } from "react-icons/md";
import { RiThumbUpLine } from "react-icons/ri";
import { toast, ToastContainer } from "react-toastify";
import { IoMdCheckmarkCircleOutline } from "react-icons/io";
import { FiSave } from "react-icons/fi";
import { RxCrossCircled } from "react-icons/rx";
import { TiEdit } from "react-icons/ti";
import { toast, ToastContainer } from "react-toastify";
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
const user = await requestUser(req, res);
@@ -39,7 +41,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
const { id } = params as { id: string };
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow(id);
const workflow: ApprovalWorkflow | null = await getConfiguredWorkflow(id);
if (!workflow)
return redirect("/approval-workflows")
@@ -132,6 +134,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
const updatedWorkflow: ApprovalWorkflow = {
...workflow,
status: selectedStepIndex === workflow.steps.length - 1 ? "approved" : "pending",
steps: workflow.steps.map((step, index) =>
index === selectedStepIndex ?
{
@@ -206,6 +209,14 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
})
};
const handleViewExam = () => {
}
const handleEditExam = () => {
}
return (
<>
<Head>
@@ -244,6 +255,29 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
status={workflow.status}
/>
</div>
<div className="flex flex-row gap-3">
<Button
color="purple"
variant="solid"
onClick={handleViewExam}
padding="px-6 py-2"
className="w-[240px] text-lg flex items-center justify-center gap-2 text-left"
>
<IoDocumentTextOutline />
View Exam
</Button>
<Button
color="purple"
variant="solid"
onClick={handleEditExam}
padding="px-6 py-2"
className="w-[240px] text-lg flex items-center justify-center gap-2 text-left"
>
<TiEdit size={20} />
Edit Exam
</Button>
</div>
{steps.find((step) => !step.completed) === undefined &&
<Tip text="All steps in this instance have been completed." />
}
@@ -271,7 +305,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
{/* Side panel */}
<AnimatePresence mode="wait">
<LayoutGroup key="sidePanel">
<section className={`absolute inset-y-0 right-0 h-full bg-mti-purple-ultralight bg-opacity-50 shadow-xl shadow-mti-purple transition-all duration-300 overflow-hidden ${isPanelOpen ? 'w-2/5' : 'w-0'}`}>
<section className={`absolute inset-y-0 right-0 h-full bg-mti-purple-ultralight bg-opacity-50 shadow-xl shadow-mti-purple transition-all duration-300 overflow-hidden ${isPanelOpen ? 'w-[500px]' : 'w-0'}`}>
{isPanelOpen && selectedStep && (
<motion.div
className="p-6"
@@ -412,7 +446,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
) : (
<>
<RxCrossCircled size={20} />
Reject Step
Reject
</>
)}
</Button>

View File

@@ -10,6 +10,7 @@ import { CorporateUser, DeveloperUser, MasterCorporateUser, TeacherUser, User }
import { sessionOptions } from "@/lib/session";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getConfiguredWorkflowsByEntities } from "@/utils/approval.workflows.be";
import { getEntities } from "@/utils/entities.be";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { getEntitiesUsers } from "@/utils/users.be";
@@ -33,10 +34,13 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
return redirect("/")
const userEntitiesWithLabel = await getEntities(user.entities.map(entity => entity.id));
const allConfiguredWorkflows = await getConfiguredWorkflowsByEntities(userEntitiesWithLabel.map(entity => entity.id));
return {
props: serialize({
user,
allConfiguredWorkflows,
userEntitiesWithLabel,
userEntitiesApprovers: await getEntitiesUsers(userEntitiesWithLabel.map(entity => entity.id), { type: { $in: ["teacher", "corporate", "mastercorporate", "developer"] } }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
}),
@@ -45,12 +49,13 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
interface Props {
user: User,
allConfiguredWorkflows: EditableApprovalWorkflow[],
userEntitiesWithLabel: Entity[],
userEntitiesApprovers: (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
}
export default function Home({ user, userEntitiesWithLabel, userEntitiesApprovers }: Props) {
const [workflows, setWorkflows] = useState<EditableApprovalWorkflow[]>([]);
export default function Home({ user, allConfiguredWorkflows, userEntitiesWithLabel, userEntitiesApprovers }: Props) {
const [workflows, setWorkflows] = useState<EditableApprovalWorkflow[]>(allConfiguredWorkflows);
const [selectedWorkflowId, setSelectedWorkflowId] = useState<string | undefined>(undefined);
const [entityId, setEntityId] = useState<string | null | undefined>(null);
const [entityApprovers, setEntityApprovers] = useState<(TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[]>([]);
@@ -93,13 +98,14 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesApprover
...workflow,
steps: workflow.steps.map(step => ({
...step,
completed: false,
assignees: step.assignees.filter((assignee): assignee is string => assignee !== null && assignee !== undefined)
}))
}));
const requestData = {filteredWorkflows, userEntitiesWithLabel};
axios
.post(`/api/approval-workflows/create`, filteredWorkflows)
.post(`/api/approval-workflows/create`, requestData)
.then(() => {
toast.success("Approval Workflows created successfully.");
setIsRedirecting(true);
@@ -135,8 +141,8 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesApprover
startDate: Date.now(),
status: "pending",
steps: [
{ key: 9998, stepType: "form-intake", stepNumber: 1, firstStep: true, finalStep: false, assignees: [null] },
{ key: 9999, stepType: "approval-by", stepNumber: 2, firstStep: false, finalStep: true, assignees: [null] },
{ key: 9998, stepType: "form-intake", stepNumber: 1, completed: false, firstStep: true, finalStep: false, assignees: [null] },
{ key: 9999, stepType: "approval-by", stepNumber: 2, completed: false, firstStep: false, finalStep: true, assignees: [null] },
],
};
setWorkflows((prev) => [...prev, newWorkflow]);

View File

@@ -1,5 +1,6 @@
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 { Module, ModuleTypeLabels } from "@/interfaces";
import { ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel, StepTypeLabel } from "@/interfaces/approval.workflow";
@@ -8,6 +9,7 @@ import { User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getConfiguredWorkflows } from "@/utils/approval.workflows.be";
import { getEntities } from "@/utils/entities.be";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { getSpecificUsers } from "@/utils/users.be";
@@ -17,18 +19,14 @@ import clsx from "clsx";
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 { BsTrash } from "react-icons/bs";
import { FaRegEdit } from "react-icons/fa";
import { FaRegClone } from "react-icons/fa6";
import { IoIosAddCircleOutline } from "react-icons/io";
import { toast, ToastContainer } from "react-toastify";
import Input from "@/components/Low/Input";
import { FaRegClone } from "react-icons/fa6";
import useApprovalWorkflows from "@/hooks/useApprovalWorkflows";
import { getApprovalWorkflows } from "@/utils/approval.workflows.be";
import { useRouter } from "next/router";
const columnHelper = createColumnHelper<ApprovalWorkflow>();
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
@@ -38,7 +36,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
if (shouldRedirectHome(user) || !["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type))
return redirect("/")
const workflows = await getApprovalWorkflows();
const workflows = await getConfiguredWorkflows();
const allAssigneeIds: string[] = [
...new Set(
@@ -331,7 +329,7 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees,
className="min-w-fit text-lg font-medium flex items-center gap-2 text-left"
>
<IoIosAddCircleOutline className="size-6" />
Configure New Workflows
Configure Workflows
</Button>
</Link>
</div>