Finish Approval Workflow builder for the most part. TODO: implement permissions
This commit is contained in:
14
src/components/ApprovalWorkflows/Tip.tsx
Normal file
14
src/components/ApprovalWorkflows/Tip.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { MdTipsAndUpdates } from "react-icons/md";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Tip({ text }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row gap-3 text-gray-500 font-medium">
|
||||||
|
<MdTipsAndUpdates size={25} />
|
||||||
|
<p>{text}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,19 +1,13 @@
|
|||||||
|
import { ApprovalWorkflow, WorkflowStep } from "@/interfaces/approval.workflow";
|
||||||
import Option from "@/interfaces/option";
|
import Option from "@/interfaces/option";
|
||||||
import { AnimatePresence, Reorder } from "framer-motion";
|
import { CorporateUser, TeacherUser } from "@/interfaces/user";
|
||||||
import { useEffect, useState } from "react";
|
import { AnimatePresence, AnimateSharedLayout, Reorder, motion } from "framer-motion";
|
||||||
|
import { useState } from "react";
|
||||||
import { FaRegCheckCircle } from "react-icons/fa";
|
import { FaRegCheckCircle } 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 WorkflowEditableStepComponent from "./WorkflowEditableStepComponent";
|
import WorkflowEditableStepComponent from "./WorkflowEditableStepComponent";
|
||||||
import { ApprovalWorkflow, WorkflowStep } from "@/interfaces/approval.workflow";
|
import Tip from "./Tip";
|
||||||
import { CorporateUser, TeacherUser } from "@/interfaces/user";
|
|
||||||
|
|
||||||
// Variants for animating steps when they are added/removed
|
|
||||||
const itemVariants = {
|
|
||||||
initial: { opacity: 0, y: -20 },
|
|
||||||
animate: { opacity: 1, y: 0 },
|
|
||||||
exit: { opacity: 0, x: 20 },
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
workflow: ApprovalWorkflow;
|
workflow: ApprovalWorkflow;
|
||||||
@@ -99,17 +93,26 @@ export default function WorkflowForm({ workflow, onWorkflowChange, entityTeacher
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{workflow.entityId && workflow.name &&
|
{workflow.entityId && workflow.name &&
|
||||||
<div className="flex flex-col gap-6">
|
<div>
|
||||||
<Button
|
<motion.div
|
||||||
color="purple"
|
className="flex flex-col gap-6"
|
||||||
variant="solid"
|
initial={{ opacity: 0, y: 20 }}
|
||||||
onClick={addStep}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
type="button"
|
exit={{ opacity: 0, x: 20 }}
|
||||||
className="max-w-fit text-lg font-medium flex items-center gap-2 text-left mb-9"
|
transition={{ duration: 0.3 }}
|
||||||
>
|
>
|
||||||
<IoIosAddCircleOutline className="size-6" />
|
<Tip text="Introduce here all the steps associated with this instance." />
|
||||||
Add Step
|
<Button
|
||||||
</Button>
|
color="purple"
|
||||||
|
variant="solid"
|
||||||
|
onClick={addStep}
|
||||||
|
type="button"
|
||||||
|
className="max-w-fit text-lg font-medium flex items-center gap-2 text-left mb-7"
|
||||||
|
>
|
||||||
|
<IoIosAddCircleOutline className="size-6" />
|
||||||
|
Add Step
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
<Reorder.Group
|
<Reorder.Group
|
||||||
axis="y"
|
axis="y"
|
||||||
@@ -122,10 +125,9 @@ export default function WorkflowForm({ workflow, onWorkflowChange, entityTeacher
|
|||||||
<Reorder.Item
|
<Reorder.Item
|
||||||
key={step.key}
|
key={step.key}
|
||||||
value={step}
|
value={step}
|
||||||
variants={itemVariants}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
initial="initial"
|
animate={{ opacity: 1, y: 0 }}
|
||||||
animate="animate"
|
exit={{ opacity: 0, x: 20 }}
|
||||||
exit="exit"
|
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
layout
|
layout
|
||||||
drag={!(step.firstStep || step.finalStep)}
|
drag={!(step.firstStep || step.finalStep)}
|
||||||
@@ -146,7 +148,7 @@ export default function WorkflowForm({ workflow, onWorkflowChange, entityTeacher
|
|||||||
type="submit"
|
type="submit"
|
||||||
color="purple"
|
color="purple"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
className="max-w-fit text-lg font-medium flex items-center gap-2 text-left mt-7"
|
className="max-w-fit text-lg font-medium flex items-center gap-2 text-left -mt-4"
|
||||||
>
|
>
|
||||||
<FaRegCheckCircle className="size-5" />
|
<FaRegCheckCircle className="size-5" />
|
||||||
Confirm Exam Workflow Pipeline
|
Confirm Exam Workflow Pipeline
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { BsChevronLeft, BsTrash } from "react-icons/bs";
|
|||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import Tip from "@/components/ApprovalWorkflows/Tip";
|
||||||
import WorkflowForm from "@/components/ApprovalWorkflows/WorkflowForm";
|
import WorkflowForm from "@/components/ApprovalWorkflows/WorkflowForm";
|
||||||
import Button from "@/components/Low/Button";
|
import Button from "@/components/Low/Button";
|
||||||
import Input from "@/components/Low/Input";
|
import Input from "@/components/Low/Input";
|
||||||
@@ -19,6 +20,7 @@ import { Entity } from "@/interfaces/entity";
|
|||||||
import { CorporateUser, TeacherUser, User } from "@/interfaces/user";
|
import { CorporateUser, TeacherUser, User } from "@/interfaces/user";
|
||||||
import { getEntities } from "@/utils/entities.be";
|
import { getEntities } from "@/utils/entities.be";
|
||||||
import { getEntitiesUsers } from "@/utils/users.be";
|
import { getEntitiesUsers } from "@/utils/users.be";
|
||||||
|
import { LayoutGroup, motion } from "framer-motion";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { MdFormatListBulletedAdd } from "react-icons/md";
|
import { MdFormatListBulletedAdd } from "react-icons/md";
|
||||||
|
|
||||||
@@ -116,9 +118,12 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
const handleDeleteWorkflow = (id: string) => {
|
const handleDeleteWorkflow = (id: string) => {
|
||||||
if (!confirm(`Are you sure you want to delete this Approval Workflow?`)) return;
|
if (!confirm(`Are you sure you want to delete this Approval Workflow?`)) return;
|
||||||
|
|
||||||
setWorkflows(prev => prev.filter(wf => wf.id !== id));
|
const updatedWorkflows = workflows.filter(wf => wf.id !== id);
|
||||||
|
|
||||||
|
setWorkflows(updatedWorkflows);
|
||||||
|
|
||||||
if (selectedWorkflowId === id) {
|
if (selectedWorkflowId === id) {
|
||||||
workflows.length > 0 ? setSelectedWorkflowId(workflows[0].id) : setSelectedWorkflowId(undefined);
|
setSelectedWorkflowId(updatedWorkflows.find(wf => wf.id)?.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -212,15 +217,15 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
: ENTITY_OPTIONS.find(option => option.value === currentWorkflow.entityId)
|
: ENTITY_OPTIONS.find(option => option.value === currentWorkflow.entityId)
|
||||||
}
|
}
|
||||||
onChange={(selectedEntity) => {
|
onChange={(selectedEntity) => {
|
||||||
if (selectedEntity?.value) {
|
if (!currentWorkflow.entityId && selectedEntity?.value) {
|
||||||
setEntityId(selectedEntity.value);
|
setEntityId(selectedEntity.value);
|
||||||
const updatedWorkflow = {
|
const updatedWorkflow = {
|
||||||
...currentWorkflow,
|
...currentWorkflow,
|
||||||
entityId: selectedEntity.value,
|
entityId: selectedEntity.value,
|
||||||
};
|
};
|
||||||
onWorkflowChange(updatedWorkflow);
|
onWorkflowChange(updatedWorkflow);
|
||||||
} else if (selectedEntity === null) {
|
} else {
|
||||||
if (!confirm("Clearing entity will reset this workflow. Are you sure you want to proceed?")) return;
|
if (!confirm("Clearing or changing entity will reset this workflow. Are you sure you want to proceed?")) return;
|
||||||
handleResetWorkflow(currentWorkflow.id);
|
handleResetWorkflow(currentWorkflow.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -239,12 +244,34 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowForm
|
<LayoutGroup>
|
||||||
workflow={currentWorkflow}
|
{(!currentWorkflow.name || !currentWorkflow.entityId) && (
|
||||||
onWorkflowChange={onWorkflowChange}
|
<motion.div
|
||||||
entityTeachers={entityTeachers}
|
key={0}
|
||||||
entityCorporates={entityCorporates}
|
initial={{ opacity: 0, y: -20 }}
|
||||||
/>
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -20 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<Tip text="Please fill workflow name and associated entity to start configuring workflow." />
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
key={1}
|
||||||
|
initial={{ opacity: 0, y: -20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -20 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<WorkflowForm
|
||||||
|
workflow={currentWorkflow}
|
||||||
|
onWorkflowChange={onWorkflowChange}
|
||||||
|
entityTeachers={entityTeachers}
|
||||||
|
entityCorporates={entityCorporates}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</LayoutGroup>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user