implement rejection of steps
This commit is contained in:
@@ -17,6 +17,7 @@ export default function WorkflowStepComponent({
|
|||||||
stepType,
|
stepType,
|
||||||
stepNumber,
|
stepNumber,
|
||||||
completed,
|
completed,
|
||||||
|
rejected = false,
|
||||||
completedBy,
|
completedBy,
|
||||||
assignees,
|
assignees,
|
||||||
finalStep,
|
finalStep,
|
||||||
@@ -31,11 +32,12 @@ export default function WorkflowStepComponent({
|
|||||||
<div
|
<div
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={clsx("flex flex-row gap-5 w-[600px] p-6 mb-5 rounded-2xl transition ease-in-out duration-300 cursor-pointer", {
|
className={clsx("flex flex-row gap-5 w-[600px] p-6 mb-5 rounded-2xl transition ease-in-out duration-300 cursor-pointer", {
|
||||||
|
"bg-mti-red-ultralight": rejected && selected,
|
||||||
"bg-mti-purple-ultralight": selected,
|
"bg-mti-purple-ultralight": selected,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="relative flex flex-col items-center">
|
<div className="relative flex flex-col items-center">
|
||||||
<WorkflowStepNumber stepNumber={stepNumber} selected={selected} completed={completed} finalStep={finalStep} />
|
<WorkflowStepNumber stepNumber={stepNumber} selected={selected} completed={completed} finalStep={finalStep} rejected={rejected} />
|
||||||
|
|
||||||
{/* Vertical Bar connecting steps */}
|
{/* Vertical Bar connecting steps */}
|
||||||
{!finalStep && (
|
{!finalStep && (
|
||||||
@@ -53,71 +55,45 @@ export default function WorkflowStepComponent({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-1 flex flex-col gap-0">
|
<div className="mt-1 flex flex-col gap-0">
|
||||||
{stepType === "form-intake" ? (
|
{completed && completedBy && rejected ? (
|
||||||
<>
|
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||||
<p className="text-sm font-medium text-gray-800">Form: Intake</p>
|
<p className="text-sm font-medium text-gray-800">{stepType === "approval-by" ? `Approval: ${getUserTypeLabel(completedByUser!.type)} Approval` : `Form Intake: ${getUserTypeLabel(completedByUser!.type)} Intake`} </p>
|
||||||
{completed && completedBy && (
|
<UserWithProfilePic
|
||||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
prefix={`Rejected by: ${getUserTypeLabelShort(completedByUser!.type)}`}
|
||||||
<UserWithProfilePic
|
name={completedByUser!.name}
|
||||||
prefix={`Completed by: ${getUserTypeLabelShort(completedByUser!.type)}`}
|
profileImage={completedByUser!.profilePicture}
|
||||||
name={completedByUser!.name}
|
/>
|
||||||
profileImage={completedByUser!.profilePicture}
|
</div>
|
||||||
/>
|
) : completed && completedBy && !rejected ? (
|
||||||
</div>
|
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||||
)}
|
<p className="text-sm font-medium text-gray-800">{stepType === "approval-by" ? `Approval: ${getUserTypeLabel(completedByUser!.type)} Approval` : `Form Intake: ${getUserTypeLabel(completedByUser!.type)} Intake`} </p>
|
||||||
{!completed && currentStep && (
|
<UserWithProfilePic
|
||||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
prefix={`Completed by: ${getUserTypeLabelShort(completedByUser!.type)}`}
|
||||||
In Progress... Assignees:
|
name={completedByUser!.name}
|
||||||
<div className="flex flex-row flex-wrap gap-3 items-center">
|
profileImage={completedByUser!.profilePicture}
|
||||||
{assigneesUsers.map(user => (
|
/>
|
||||||
<span key={user.id}>
|
</div>
|
||||||
<UserWithProfilePic
|
) : !completed && currentStep ? (
|
||||||
prefix={getUserTypeLabelShort(user.type)}
|
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||||
name={user.name}
|
<p className="text-sm font-medium text-gray-800">{stepType === "approval-by" ? `Approval:` : `Form Intake:`} </p>
|
||||||
profileImage={user.profilePicture}
|
In Progress... Assignees:
|
||||||
/>
|
<div className="flex flex-row flex-wrap gap-3 items-center">
|
||||||
</span>
|
{assigneesUsers.map(user => (
|
||||||
))}
|
<span key={user.id}>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
stepType === "approval-by" && (
|
|
||||||
<>
|
|
||||||
{completed && completedBy ? (
|
|
||||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
|
||||||
<p className="text-sm font-medium text-gray-800">Approval: {getUserTypeLabel(completedByUser!.type)} Approval</p>
|
|
||||||
<UserWithProfilePic
|
<UserWithProfilePic
|
||||||
prefix={`Approved by: ${getUserTypeLabelShort(completedByUser!.type)}`}
|
prefix={getUserTypeLabelShort(user.type)}
|
||||||
name={completedByUser!.name}
|
name={user.name}
|
||||||
profileImage={completedByUser!.profilePicture}
|
profileImage={user.profilePicture}
|
||||||
/>
|
/>
|
||||||
</div>
|
</span>
|
||||||
) : !completed && currentStep ? (
|
))}
|
||||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
</div>
|
||||||
<p className="text-sm font-medium text-gray-800">Approval: </p>
|
</div>
|
||||||
In Progress... Assignees:
|
) : (
|
||||||
<div className="flex flex-row flex-wrap gap-3 items-center">
|
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||||
{assigneesUsers.map(user => (
|
<p className="text-sm font-medium text-gray-800">{stepType === "approval-by" ? `Approval:` : `Form Intake:`} </p>
|
||||||
<span key={user.id}>
|
Waiting for previous steps...
|
||||||
<UserWithProfilePic
|
</div>
|
||||||
prefix={getUserTypeLabelShort(user.type)}
|
|
||||||
name={user.name}
|
|
||||||
profileImage={user.profilePicture}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
|
||||||
<p className="text-sm font-medium text-gray-800">Approval: </p>
|
|
||||||
Waiting for previous steps...
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
import { WorkflowStep } from "@/interfaces/approval.workflow";
|
import { WorkflowStep } from "@/interfaces/approval.workflow";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { IoCheckmarkDoneSharp, IoCheckmarkSharp } from "react-icons/io5";
|
import { IoCheckmarkDoneSharp, IoCheckmarkSharp } from "react-icons/io5";
|
||||||
|
import { RxCross2 } from "react-icons/rx";
|
||||||
|
|
||||||
type Props = Pick<WorkflowStep, 'stepNumber' | 'completed' | 'finalStep' | 'selected'>
|
type Props = Pick<WorkflowStep, 'stepNumber' | 'completed' | 'finalStep' | 'selected' | 'rejected'>
|
||||||
|
|
||||||
export default function WorkflowStepNumber({ stepNumber, selected = false, completed, finalStep }: Props) {
|
export default function WorkflowStepNumber({ stepNumber, selected = false, completed, rejected, finalStep }: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex items-center justify-center min-w-11 min-h-11 rounded-full',
|
'flex items-center justify-center min-w-11 min-h-11 rounded-full',
|
||||||
{
|
{
|
||||||
|
'bg-mti-red-dark text-mti-red-ultralight': rejected,
|
||||||
'bg-mti-purple-dark text-mti-purple-ultralight': selected,
|
'bg-mti-purple-dark text-mti-purple-ultralight': selected,
|
||||||
'bg-mti-purple-ultralight text-gray-500': !selected,
|
'bg-mti-purple-ultralight text-gray-500': !selected,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{completed && finalStep ? (
|
{rejected ? (
|
||||||
|
<RxCross2 className="text-xl font-bold" size={25}/>
|
||||||
|
) : completed && finalStep ? (
|
||||||
<IoCheckmarkDoneSharp className="text-xl font-bold" size={25} />
|
<IoCheckmarkDoneSharp className="text-xl font-bold" size={25} />
|
||||||
) : completed && !finalStep ? (
|
) : completed && !finalStep ? (
|
||||||
<IoCheckmarkSharp className="text-xl font-bold" size={25} />
|
<IoCheckmarkSharp className="text-xl font-bold" size={25} />
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface WorkflowStep {
|
|||||||
stepType: StepType,
|
stepType: StepType,
|
||||||
stepNumber: number,
|
stepNumber: number,
|
||||||
completed: boolean,
|
completed: boolean,
|
||||||
|
rejected?: boolean,
|
||||||
completedBy?: User["id"],
|
completedBy?: User["id"],
|
||||||
completedDate?: number,
|
completedDate?: number,
|
||||||
assignees: (User["id"])[];
|
assignees: (User["id"])[];
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
|||||||
user,
|
user,
|
||||||
workflow,
|
workflow,
|
||||||
userEntitiesWithLabel,
|
userEntitiesWithLabel,
|
||||||
userEntitiesApprovers: await getEntitiesUsers(userEntitiesWithLabel.map(entity => entity.id), { type: {$in: ["teacher", "corporate", "mastercorporate", "developer"]} }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
|
userEntitiesApprovers: await getEntitiesUsers(userEntitiesWithLabel.map(entity => entity.id), { type: { $in: ["teacher", "corporate", "mastercorporate", "developer"] } }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
@@ -134,9 +134,7 @@ export default function Home({ user, workflow, userEntitiesWithLabel, userEntiti
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Approval Workflow cloned successfully.");
|
toast.success("Approval Workflow cloned successfully.");
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
setTimeout(() => {
|
router.push("/approval-workflows");
|
||||||
router.push("/approval-workflows");
|
|
||||||
}, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 401) {
|
if (reason.response.status === 401) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
|||||||
props: serialize({
|
props: serialize({
|
||||||
user,
|
user,
|
||||||
workflow,
|
workflow,
|
||||||
workflowEntityApprovers: await getEntityUsers(workflow.entityId, undefined, { type: {$in: ["teacher", "corporate", "mastercorporate", "developer"]} }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
|
workflowEntityApprovers: await getEntityUsers(workflow.entityId, undefined, { type: { $in: ["teacher", "corporate", "mastercorporate", "developer"] } }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
@@ -54,7 +54,7 @@ export default function Home({ user, workflow, workflowEntityApprovers }: Props)
|
|||||||
const [updatedWorkflow, setUpdatedWorkflow] = useState<EditableApprovalWorkflow | null>(null);
|
const [updatedWorkflow, setUpdatedWorkflow] = useState<EditableApprovalWorkflow | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [isRedirecting, setIsRedirecting] = useState<boolean>(false);
|
const [isRedirecting, setIsRedirecting] = useState<boolean>(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -86,10 +86,10 @@ export default function Home({ user, workflow, workflowEntityApprovers }: Props)
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
if (!updatedWorkflow){
|
if (!updatedWorkflow) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredWorkflow: ApprovalWorkflow = {
|
const filteredWorkflow: ApprovalWorkflow = {
|
||||||
...updatedWorkflow,
|
...updatedWorkflow,
|
||||||
@@ -99,15 +99,13 @@ export default function Home({ user, workflow, workflowEntityApprovers }: Props)
|
|||||||
assignees: step.assignees.filter((assignee): assignee is string => assignee !== null && assignee !== undefined)
|
assignees: step.assignees.filter((assignee): assignee is string => assignee !== null && assignee !== undefined)
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.put(`/api/approval-workflows/${updatedWorkflow.id}/edit`, filteredWorkflow)
|
.put(`/api/approval-workflows/${updatedWorkflow.id}/edit`, filteredWorkflow)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Approval Workflow edited successfully.");
|
toast.success("Approval Workflow edited successfully.");
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
setTimeout(() => {
|
router.push("/approval-workflows");
|
||||||
router.push("/approval-workflows");
|
|
||||||
}, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 401) {
|
if (reason.response.status === 401) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { RiThumbUpLine } from "react-icons/ri";
|
|||||||
import { toast, ToastContainer } from "react-toastify";
|
import { toast, ToastContainer } from "react-toastify";
|
||||||
import { IoMdCheckmarkCircleOutline } from "react-icons/io";
|
import { IoMdCheckmarkCircleOutline } from "react-icons/io";
|
||||||
import { FiSave } from "react-icons/fi";
|
import { FiSave } from "react-icons/fi";
|
||||||
|
import { RxCrossCircled } from "react-icons/rx";
|
||||||
|
|
||||||
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);
|
||||||
@@ -71,7 +72,7 @@ interface Props {
|
|||||||
export default function Home({ user, workflow, workflowAssignees, workflowRequester }: Props) {
|
export default function Home({ user, workflow, workflowAssignees, workflowRequester }: Props) {
|
||||||
const steps = workflow.steps;
|
const steps = workflow.steps;
|
||||||
|
|
||||||
let currentStep = steps.findIndex(step => !step.completed);
|
let currentStep = steps.findIndex(step => !step.completed || step.rejected);
|
||||||
if (currentStep === -1)
|
if (currentStep === -1)
|
||||||
currentStep = steps.length - 1;
|
currentStep = steps.length - 1;
|
||||||
|
|
||||||
@@ -110,9 +111,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Comments saved successfully.");
|
toast.success("Comments saved successfully.");
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
setTimeout(() => {
|
router.reload();
|
||||||
router.reload();
|
|
||||||
}, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 401) {
|
if (reason.response.status === 401) {
|
||||||
@@ -129,7 +128,6 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleApproveStep = () => {
|
const handleApproveStep = () => {
|
||||||
if (!confirm(`Are you sure you want to approve this step?`)) return;
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const updatedWorkflow: ApprovalWorkflow = {
|
const updatedWorkflow: ApprovalWorkflow = {
|
||||||
@@ -151,9 +149,48 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Step approved successfully.");
|
toast.success("Step approved successfully.");
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
setTimeout(() => {
|
router.reload();
|
||||||
router.reload();
|
})
|
||||||
}, 1000);
|
.catch((reason) => {
|
||||||
|
if (reason.response.status === 401) {
|
||||||
|
toast.error("Not logged in!");
|
||||||
|
} else if (reason.response.status === 403) {
|
||||||
|
toast.error("You do not have permission to approve this step!");
|
||||||
|
} else {
|
||||||
|
toast.error("Something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
console.log("Submitted Values:", updatedWorkflow);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRejectStep = () => {
|
||||||
|
if (!confirm(`Are you sure you want to reject this step?`)) return;
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const updatedWorkflow: ApprovalWorkflow = {
|
||||||
|
...workflow,
|
||||||
|
status: "rejected",
|
||||||
|
steps: workflow.steps.map((step, index) =>
|
||||||
|
index === selectedStepIndex ?
|
||||||
|
{
|
||||||
|
...step,
|
||||||
|
completed: true,
|
||||||
|
completedBy: user.id,
|
||||||
|
completedDate: Date.now(),
|
||||||
|
rejected: true,
|
||||||
|
}
|
||||||
|
: step
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
axios
|
||||||
|
.put(`/api/approval-workflows/${workflow._id}`, updatedWorkflow)
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Step rejected successfully.");
|
||||||
|
setIsRedirecting(true);
|
||||||
|
router.reload();
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 401) {
|
if (reason.response.status === 401) {
|
||||||
@@ -219,6 +256,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
key={index}
|
key={index}
|
||||||
completed={step.completed}
|
completed={step.completed}
|
||||||
completedBy={step.completedBy}
|
completedBy={step.completedBy}
|
||||||
|
rejected={step.rejected}
|
||||||
stepNumber={step.stepNumber}
|
stepNumber={step.stepNumber}
|
||||||
stepType={step.stepType}
|
stepType={step.stepType}
|
||||||
assignees={step.assignees}
|
assignees={step.assignees}
|
||||||
@@ -276,7 +314,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
|
|
||||||
{selectedStep.completed ? (
|
{selectedStep.completed ? (
|
||||||
<div className={"text-base font-medium text-gray-500 flex flex-col gap-6"}>
|
<div className={"text-base font-medium text-gray-500 flex flex-col gap-6"}>
|
||||||
Approved on {new Date(selectedStep.completedDate!).toLocaleString("en-CA", {
|
{selectedStep.rejected ? "Rejected" : "Approved"} on {new Date(selectedStep.completedDate!).toLocaleString("en-CA", {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
@@ -286,7 +324,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
hour12: false,
|
hour12: false,
|
||||||
}).replace(", ", " at ")}
|
}).replace(", ", " at ")}
|
||||||
<div className="flex flex-row gap-1 text-sm">
|
<div className="flex flex-row gap-1 text-sm">
|
||||||
<p className="text-base">Approved by:</p>
|
<p className="text-base">{selectedStep.rejected ? "Rejected" : "Approved"} by:</p>
|
||||||
{(() => {
|
{(() => {
|
||||||
const assignee = workflowAssignees.find(
|
const assignee = workflowAssignees.find(
|
||||||
(assignee) => assignee.id === selectedStep.completedBy
|
(assignee) => assignee.id === selectedStep.completedBy
|
||||||
@@ -324,33 +362,61 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedStepIndex === currentStep && !selectedStep.completed &&
|
{selectedStepIndex === currentStep && !selectedStep.completed && !selectedStep.rejected &&
|
||||||
<Button
|
<div className="flex flex-row gap-2 ">
|
||||||
type="submit"
|
<Button
|
||||||
color="purple"
|
type="submit"
|
||||||
variant="solid"
|
color="purple"
|
||||||
disabled={!selectedStep.assignees.includes(user.id) || isLoading}
|
variant="solid"
|
||||||
onClick={handleApproveStep}
|
disabled={!selectedStep.assignees.includes(user.id) || isLoading}
|
||||||
padding="px-6 py-2"
|
onClick={handleApproveStep}
|
||||||
className="mb-3 w-full text-lg flex items-center justify-center gap-2 text-left"
|
padding="px-6 py-2"
|
||||||
>
|
className="mb-3 w-full text-lg flex items-center justify-center gap-2 text-left"
|
||||||
{isRedirecting ? (
|
>
|
||||||
<>
|
{isRedirecting ? (
|
||||||
<FaSpinner className="animate-spin size-5" />
|
<>
|
||||||
Reloading...
|
<FaSpinner className="animate-spin size-5" />
|
||||||
</>
|
Reloading...
|
||||||
) : isLoading ? (
|
</>
|
||||||
<>
|
) : isLoading ? (
|
||||||
<FaSpinner className="animate-spin size-5" />
|
<>
|
||||||
Loading...
|
<FaSpinner className="animate-spin size-5" />
|
||||||
</>
|
Loading...
|
||||||
) : (
|
</>
|
||||||
<>
|
) : (
|
||||||
<IoMdCheckmarkCircleOutline size={20} />
|
<>
|
||||||
Approve Step
|
<IoMdCheckmarkCircleOutline size={20} />
|
||||||
</>
|
Approve Step
|
||||||
)}
|
</>
|
||||||
</Button>
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="red"
|
||||||
|
variant="solid"
|
||||||
|
disabled={!selectedStep.assignees.includes(user.id) || isLoading}
|
||||||
|
onClick={handleRejectStep}
|
||||||
|
padding="px-6 py-2"
|
||||||
|
className="mb-3 w-1/2 text-lg flex items-center justify-center gap-2 text-left"
|
||||||
|
>
|
||||||
|
{isRedirecting ? (
|
||||||
|
<>
|
||||||
|
<FaSpinner className="animate-spin size-5" />
|
||||||
|
Reloading...
|
||||||
|
</>
|
||||||
|
) : isLoading ? (
|
||||||
|
<>
|
||||||
|
<FaSpinner className="animate-spin size-5" />
|
||||||
|
Loading...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<RxCrossCircled size={20} />
|
||||||
|
Reject Step
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
|
<hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
|
||||||
@@ -361,7 +427,7 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
placeholder="Input comments here"
|
placeholder="Input comments here"
|
||||||
className="w-full h-64 p-2 border-2 rounded-xl shadow-lg focus:border-mti-purple focus:outline-none mt-3 resize-none"
|
className="w-full h-64 p-2 border-2 rounded-xl shadow-lg focus:border-mti-purple focus:outline-none mt-3 resize-none"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
color="purple"
|
color="purple"
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
|
|||||||
props: serialize({
|
props: serialize({
|
||||||
user,
|
user,
|
||||||
userEntitiesWithLabel,
|
userEntitiesWithLabel,
|
||||||
userEntitiesApprovers: await getEntitiesUsers(userEntitiesWithLabel.map(entity => entity.id), { type: {$in: ["teacher", "corporate", "mastercorporate", "developer"]} }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
|
userEntitiesApprovers: await getEntitiesUsers(userEntitiesWithLabel.map(entity => entity.id), { type: { $in: ["teacher", "corporate", "mastercorporate", "developer"] } }) as (TeacherUser | CorporateUser | MasterCorporateUser | DeveloperUser)[],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
@@ -103,9 +103,7 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesApprover
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Approval Workflows created successfully.");
|
toast.success("Approval Workflows created successfully.");
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
setTimeout(() => {
|
router.push("/approval-workflows");
|
||||||
router.push("/approval-workflows");
|
|
||||||
}, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 401) {
|
if (reason.response.status === 401) {
|
||||||
|
|||||||
@@ -155,9 +155,7 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees,
|
|||||||
.delete(`/api/approval-workflows/${id}`)
|
.delete(`/api/approval-workflows/${id}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(`Successfully deleted ${name} Approval Workflow.`);
|
toast.success(`Successfully deleted ${name} Approval Workflow.`);
|
||||||
setTimeout(() => {
|
router.reload();
|
||||||
router.reload();
|
|
||||||
}, 1000);
|
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
if (reason.response.status === 404) {
|
if (reason.response.status === 404) {
|
||||||
@@ -217,6 +215,9 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees,
|
|||||||
cell: (info) => {
|
cell: (info) => {
|
||||||
const steps = info.row.original.steps;
|
const steps = info.row.original.steps;
|
||||||
const currentStep = steps.find((step) => !step.completed);
|
const currentStep = steps.find((step) => !step.completed);
|
||||||
|
const rejected = steps.find((step) => step.rejected);
|
||||||
|
|
||||||
|
if(rejected) return "";
|
||||||
|
|
||||||
const assignees = currentStep?.assignees.map((assigneeId) => {
|
const assignees = currentStep?.assignees.map((assigneeId) => {
|
||||||
const assignee = workflowsAssignees.find((user) => user.id === assigneeId);
|
const assignee = workflowsAssignees.find((user) => user.id === assigneeId);
|
||||||
@@ -243,10 +244,11 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees,
|
|||||||
cell: (info) => {
|
cell: (info) => {
|
||||||
const steps = info.row.original.steps;
|
const steps = info.row.original.steps;
|
||||||
const currentStep = steps.find((step) => !step.completed);
|
const currentStep = steps.find((step) => !step.completed);
|
||||||
|
const rejected = steps.find((step) => step.rejected);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{currentStep
|
{currentStep && !rejected
|
||||||
? `Step ${currentStep.stepNumber}: ${StepTypeLabel[currentStep.stepType]}`
|
? `Step ${currentStep.stepNumber}: ${StepTypeLabel[currentStep.stepType]}`
|
||||||
: "Completed"}
|
: "Completed"}
|
||||||
</span>
|
</span>
|
||||||
@@ -259,6 +261,7 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees,
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const steps = row.original.steps;
|
const steps = row.original.steps;
|
||||||
const currentStep = steps.find((step) => !step.completed);
|
const currentStep = steps.find((step) => !step.completed);
|
||||||
|
const rejected = steps.find((step) => step.rejected);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
@@ -282,7 +285,7 @@ export default function ApprovalWorkflows({ user, workflows, workflowsAssignees,
|
|||||||
<FaRegClone className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
<FaRegClone className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{currentStep && (
|
{currentStep && !rejected && (
|
||||||
<Link
|
<Link
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
data-tip="Edit"
|
data-tip="Edit"
|
||||||
|
|||||||
Reference in New Issue
Block a user