Make data dynamic in workflow view. Add requester and startDate to workflows.
This commit is contained in:
@@ -3,11 +3,12 @@ import React from "react";
|
||||
import { FaRegUser } from "react-icons/fa";
|
||||
|
||||
interface Props {
|
||||
prefix: string;
|
||||
name: string;
|
||||
profileImage: string;
|
||||
}
|
||||
|
||||
export default function RequestedBy({ name, profileImage }: Props) {
|
||||
export default function RequestedBy({ prefix, name, profileImage }: Props) {
|
||||
return (
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-lg border border-gray-300">
|
||||
@@ -16,7 +17,7 @@ export default function RequestedBy({ name, profileImage }: Props) {
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-800">Requested by</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-xs font-medium text-gray-800">{name}</p>
|
||||
<p className="text-xs font-medium text-gray-800">{prefix} {name}</p>
|
||||
<Image
|
||||
src={profileImage}
|
||||
alt={name}
|
||||
|
||||
@@ -2,10 +2,24 @@ import React from "react";
|
||||
import { PiCalendarDots } from "react-icons/pi";
|
||||
|
||||
interface Props {
|
||||
date: string;
|
||||
date: number;
|
||||
}
|
||||
|
||||
export default function StartedOn({ date }: Props) {
|
||||
const formattedDate = new Date(date);
|
||||
|
||||
const yearMonthDay = formattedDate.toISOString().split("T")[0];
|
||||
|
||||
const fullDateTime = formattedDate.toLocaleString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-lg border border-gray-300">
|
||||
@@ -14,7 +28,13 @@ export default function StartedOn({ date }: Props) {
|
||||
<div>
|
||||
<p className="pb-1 text-sm font-medium text-gray-800">Started on</p>
|
||||
<div className="flex items-center">
|
||||
<p className="text-xs font-medium text-gray-800">{date}</p>
|
||||
{/* Display the formatted date and add a title attribute for hover */}
|
||||
<p
|
||||
className="text-xs font-medium text-gray-800"
|
||||
title={fullDateTime} // Shows full date and time on hover
|
||||
>
|
||||
{yearMonthDay}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
22
src/components/ApprovalWorkflows/UserWithProfilePic.tsx
Normal file
22
src/components/ApprovalWorkflows/UserWithProfilePic.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
prefix: string;
|
||||
name: string;
|
||||
profileImage: string;
|
||||
}
|
||||
|
||||
export default function UserWithProfilePic({ prefix, name, profileImage }: Props) {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-xs font-medium text-gray-800">{prefix} {name}</p>
|
||||
<Image
|
||||
src={profileImage}
|
||||
alt={name}
|
||||
width={24}
|
||||
height={24}
|
||||
className="w-6 h-6 rounded-full"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,17 @@
|
||||
import { getUserTypeLabel, WorkflowStep } from "@/interfaces/approval.workflow";
|
||||
import { getUserTypeLabel, getUserTypeLabelShort, WorkflowStep } from "@/interfaces/approval.workflow";
|
||||
import WorkflowStepNumber from "./WorkflowStepNumber";
|
||||
import clsx from "clsx";
|
||||
import { RiThumbUpLine } from "react-icons/ri";
|
||||
import { FaWpforms } from "react-icons/fa6";
|
||||
import { User } from "@/interfaces/user";
|
||||
import UserWithProfilePic from "./UserWithProfilePic";
|
||||
|
||||
interface Props extends WorkflowStep {
|
||||
workflowAssignees: User[],
|
||||
}
|
||||
|
||||
export default function WorkflowStepComponent({
|
||||
workflowAssignees,
|
||||
stepType,
|
||||
stepNumber,
|
||||
completed,
|
||||
@@ -15,12 +22,17 @@ export default function WorkflowStepComponent({
|
||||
assignees,
|
||||
assigneesType,
|
||||
onClick,
|
||||
}: WorkflowStep) {
|
||||
}: Props) {
|
||||
console.log(workflowAssignees);
|
||||
console.log(completedBy)
|
||||
|
||||
const completedByUser = workflowAssignees.find((assignee) => assignee.id === completedBy);
|
||||
const assigneesUsers = workflowAssignees.filter(user => assignees!.includes(user.id));
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={clsx("flex flex-row gap-5 w-[600px] p-6 my-4 rounded-2xl transition ease-in-out duration-300 disabled:cursor-not-allowed cursor-pointer", {
|
||||
className={clsx("flex flex-row gap-5 w-[700px] p-6 my-4 rounded-2xl transition ease-in-out duration-300 disabled:cursor-not-allowed cursor-pointer", {
|
||||
"bg-mti-purple-ultralight": selected,
|
||||
})}
|
||||
>
|
||||
@@ -29,7 +41,7 @@ export default function WorkflowStepComponent({
|
||||
|
||||
{/* Vertical Bar connecting steps */}
|
||||
{!finalStep && (
|
||||
<div className="absolute w-1 bg-mti-purple-dark -bottom-20 h-20"></div>
|
||||
<div className="absolute w-1 bg-mti-purple-dark -bottom-20 top-11"></div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -46,15 +58,19 @@ export default function WorkflowStepComponent({
|
||||
{stepType === "form-intake" ? (
|
||||
<>
|
||||
<p className="text-sm font-medium text-gray-800">Form: Intake</p>
|
||||
{completed && (
|
||||
{completed && completedBy && (
|
||||
<p className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
Completed by {completedBy}
|
||||
Completed by {getUserTypeLabelShort(completedByUser?.type)} {completedByUser?.name}
|
||||
</p>
|
||||
)}
|
||||
{!completed && (
|
||||
<p className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
In progress...
|
||||
{!completed && completedBy && (
|
||||
<>
|
||||
{assigneesUsers.map(user => (
|
||||
<p key={user.id} className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
{getUserTypeLabelShort(user.type)} {user.name}
|
||||
</p>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
@@ -63,12 +79,23 @@ export default function WorkflowStepComponent({
|
||||
<p className="text-sm font-medium text-gray-800">Approval: {getUserTypeLabel(assigneesType)}</p>
|
||||
{completed ? (
|
||||
<p className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
Approved by {completedBy}
|
||||
Approved by {workflowAssignees.find((assignee) => assignee.id === completedBy)?.name || "Unknown"}
|
||||
</p>
|
||||
) : !completed && currentStep ? (
|
||||
<p className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
In progress...
|
||||
</p>
|
||||
<div className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
In Progress... Assignees:
|
||||
<div className="flex flex-row flex-wrap gap-3 items-center">
|
||||
{assigneesUsers.map(user => (
|
||||
<span key={user.id}>
|
||||
<UserWithProfilePic
|
||||
prefix={getUserTypeLabelShort(user.type)}
|
||||
name={user.name}
|
||||
profileImage={user.profilePicture}
|
||||
/>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className={clsx("text-xs font-medium", { "text-mti-purple-ultradark": selected, "text-gray-800": !selected })}>
|
||||
Waiting for previous steps...
|
||||
|
||||
@@ -7,64 +7,61 @@
|
||||
"reading",
|
||||
"writing"
|
||||
],
|
||||
"requester": "ffdIipRyXTRmm10Sq2eg7P97rLB2",
|
||||
"startDate": 1737712243906,
|
||||
"status": "pending",
|
||||
"steps": [
|
||||
{
|
||||
"stepType": "form-intake",
|
||||
"stepNumber": 1,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. X",
|
||||
"completedBy": "231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 2,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. Y",
|
||||
"completedBy": "c5fc1514-1a94-4f8c-a046-a62099097a50",
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 3,
|
||||
"completed": false,
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 4,
|
||||
"completed": false,
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 5,
|
||||
"completed": false,
|
||||
"assignees": [
|
||||
"Dir. X",
|
||||
"Dir. Y",
|
||||
"Dir. Z"
|
||||
],
|
||||
"assigneesType": "corporate"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -79,67 +76,64 @@
|
||||
"speaking",
|
||||
"listening"
|
||||
],
|
||||
"requester": "231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"startDate": 1737712243906,
|
||||
"status": "approved",
|
||||
"steps": [
|
||||
{
|
||||
"stepType": "form-intake",
|
||||
"stepNumber": 1,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. X",
|
||||
"completedBy": "fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 2,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. Y",
|
||||
"completedBy": "231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 3,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. Y",
|
||||
"completedBy": "231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 4,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. Y",
|
||||
"completedBy": "231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"assignees": [
|
||||
"Prof. X",
|
||||
"Prof. Y",
|
||||
"Prof. Z"
|
||||
],
|
||||
"assigneesType": "teacher"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stepType": "approval-by",
|
||||
"stepNumber": 5,
|
||||
"completed": true,
|
||||
"completedBy": "Prof. Y",
|
||||
"completedBy": "c5fc1514-1a94-4f8c-a046-a62099097a50",
|
||||
"assignees": [
|
||||
"Dir. X",
|
||||
"Dir. Y",
|
||||
"Dir. Z"
|
||||
],
|
||||
"assigneesType": "corporate"
|
||||
"fd5fce42-4bcc-4150-a143-b484e750b265",
|
||||
"231c84b2-a65a-49a9-803c-c664d84b13e0",
|
||||
"c5fc1514-1a94-4f8c-a046-a62099097a50"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Module } from ".";
|
||||
import Option from "./option";
|
||||
import { CorporateUser, MasterCorporateUser, TeacherUser, userTypeLabels } from "./user";
|
||||
import { Type, User, userTypeLabels, userTypeLabelsShort } from "./user";
|
||||
|
||||
export interface ApprovalWorkflow {
|
||||
id: string,
|
||||
name: string,
|
||||
entityId: string,
|
||||
requester: User["id"],
|
||||
startDate: number,
|
||||
modules: Module[],
|
||||
status: ApprovalWorkflowStatus,
|
||||
steps: WorkflowStep[],
|
||||
@@ -17,31 +18,30 @@ export const StepTypeLabel: Record<StepType, string> = {
|
||||
"approval-by": "Approval",
|
||||
};
|
||||
|
||||
type AssigneesType = TeacherUser["type"] | CorporateUser["type"] | MasterCorporateUser["type"];
|
||||
|
||||
export interface WorkflowStep {
|
||||
key?: number,
|
||||
stepType?: StepType,
|
||||
stepNumber: number,
|
||||
completed?: boolean,
|
||||
completedBy?: string,
|
||||
assignees?: (string | null | undefined)[]; // bit of an hack, but allowing null or undefined values allows us to match one to one the select input components with the assignees array. And since select inputs allow undefined or null values, it is allowed here too, but must validate required input before form submission
|
||||
assigneesType?: AssigneesType,
|
||||
editView?: boolean,
|
||||
completedBy?: User["id"],
|
||||
assignees?: (User["id"] | null | undefined)[]; // bit of an hack, but allowing null or undefined values allows us to match one to one the select input components with the assignees array. And since select inputs allow undefined or null values, it is allowed here too, but must validate required input before form submission
|
||||
assigneesType?: Type,
|
||||
firstStep?: boolean,
|
||||
currentStep?: boolean,
|
||||
finalStep?: boolean,
|
||||
selected?: boolean,
|
||||
requestedBy?: string,
|
||||
//requestedBy: TeacherUser | CorporateUser | MasterCorporateUser,
|
||||
onDelete?: () => void;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>
|
||||
}
|
||||
|
||||
export function getUserTypeLabel(type: AssigneesType | undefined): string {
|
||||
export function getUserTypeLabel(type: Type | undefined): string {
|
||||
if (type) return userTypeLabels[type];
|
||||
return '';
|
||||
}
|
||||
export function getUserTypeLabelShort(type: Type | undefined): string {
|
||||
if (type) return userTypeLabelsShort[type];
|
||||
return '';
|
||||
}
|
||||
|
||||
export type ApprovalWorkflowStatus = "approved" | "pending" | "rejected";
|
||||
export const ApprovalWorkflowStatusLabel: Record<ApprovalWorkflowStatus, string> = {
|
||||
|
||||
@@ -180,4 +180,14 @@ export const userTypeLabels: Record<Type, string> = {
|
||||
mastercorporate: "Master Corporate",
|
||||
};
|
||||
|
||||
export const userTypeLabelsShort: Record<Type, string> = {
|
||||
student: "",
|
||||
teacher: "Prof.",
|
||||
corporate: "Dir.",
|
||||
admin: "Admin",
|
||||
developer: "Dev.",
|
||||
agent: "Agent",
|
||||
mastercorporate: "Dir.",
|
||||
};
|
||||
|
||||
export type WithUser<T> = T extends { participants: string[] } ? Omit<T, "participants"> & { participants: User[] } : T;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Layout from "@/components/High/Layout";
|
||||
import useUser from "@/hooks/useUser";
|
||||
import { ApprovalWorkflow } from "@/interfaces/approval.workflow";
|
||||
import { ApprovalWorkflow, getUserTypeLabelShort } from "@/interfaces/approval.workflow";
|
||||
import { sessionOptions } from "@/lib/session";
|
||||
import { redirect } from "@/utils";
|
||||
import { redirect, serialize } from "@/utils";
|
||||
import { requestUser } from "@/utils/api";
|
||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
||||
import { withIronSessionSsr } from "iron-session/next";
|
||||
@@ -11,7 +11,6 @@ import Link from "next/link";
|
||||
import { BsChevronLeft } from "react-icons/bs";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
|
||||
|
||||
import approvalWorkflowsData from '../../demo/approval_workflows.json'; // to test locally
|
||||
|
||||
import RequestedBy from "@/components/ApprovalWorkflows/RequestedBy";
|
||||
@@ -19,6 +18,9 @@ import StartedOn from "@/components/ApprovalWorkflows/StartedOn";
|
||||
import Status from "@/components/ApprovalWorkflows/Status";
|
||||
import WorkflowStepComponent from "@/components/ApprovalWorkflows/WorkflowStepComponent";
|
||||
import { useState } from "react";
|
||||
import useApprovalWorkflows from "@/hooks/useApprovalWorkflows";
|
||||
import { User } from "@/interfaces/user";
|
||||
import { getSpecificUsers, getUser, getUsers } from "@/utils/users.be";
|
||||
|
||||
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
||||
const user = await requestUser(req, res);
|
||||
@@ -29,19 +31,40 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
||||
|
||||
const { id } = params as { id: string };
|
||||
|
||||
const workflow = approvalWorkflowsData.find(workflow => workflow.id === id); // await getApprovalWorkflow(id);
|
||||
// replace later with await getApprovalWorkflow(id). Don't think a hook is needed here;
|
||||
|
||||
const approvalWorkflowsDataAsWorkflows = approvalWorkflowsData as ApprovalWorkflow[];
|
||||
const workflow: ApprovalWorkflow | undefined = approvalWorkflowsDataAsWorkflows.find(workflow => workflow.id === id);
|
||||
|
||||
if (!workflow)
|
||||
return redirect("/approval-workflows")
|
||||
|
||||
const allAssigneeIds: string[] = [
|
||||
...new Set(
|
||||
workflow.steps
|
||||
.map(step => step.assignees)
|
||||
.flat() as string[] // we are sure assignees coming from a db workflow are all valid strings.
|
||||
)
|
||||
];
|
||||
|
||||
return {
|
||||
props: { user, workflow },
|
||||
props: serialize({
|
||||
user,
|
||||
workflow,
|
||||
workflowAssignees: await getSpecificUsers(allAssigneeIds),
|
||||
workflowRequester: await getUser(workflow.requester),
|
||||
}),
|
||||
};
|
||||
}, sessionOptions);
|
||||
|
||||
export default function Home({ workflow }: { workflow: ApprovalWorkflow }) {
|
||||
const { user } = useUser({ redirectTo: "/login" });
|
||||
interface Props {
|
||||
user: User,
|
||||
workflow: ApprovalWorkflow,
|
||||
workflowAssignees: User[],
|
||||
workflowRequester: User,
|
||||
}
|
||||
|
||||
export default function Home({ user, workflow, workflowAssignees, workflowRequester }: Props) {
|
||||
const steps = workflow.steps;
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState(steps.length - 1);
|
||||
@@ -77,20 +100,22 @@ export default function Home({ workflow }: { workflow: ApprovalWorkflow }) {
|
||||
<section className="flex flex-col gap-6">
|
||||
<div className="flex flex-row gap-6">
|
||||
<RequestedBy
|
||||
name="Prof. Álvaro Dória"
|
||||
profileImage="/blue-stock-photo.png"
|
||||
prefix={getUserTypeLabelShort(workflowRequester.type)}
|
||||
name={workflowRequester.name}
|
||||
profileImage="/blue-stock-photo.png" //{workflowRequester.profilePicture}
|
||||
/>
|
||||
<StartedOn
|
||||
date="15/12/2024"
|
||||
date={workflow.startDate}
|
||||
/>
|
||||
<Status
|
||||
status="pending"
|
||||
status={workflow.status}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="flex flex-col gap-0">
|
||||
{steps.map((step, index) => (
|
||||
<WorkflowStepComponent
|
||||
workflowAssignees={workflowAssignees}
|
||||
key={index}
|
||||
completed={step.completed}
|
||||
completedBy={step.completedBy}
|
||||
|
||||
@@ -95,10 +95,12 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
||||
name: "",
|
||||
entityId: "",
|
||||
modules: [],
|
||||
requester: user.id,
|
||||
startDate: Date.now(),
|
||||
status: "pending",
|
||||
steps: [
|
||||
{ key: Date.now(), completed: false, editView: true, stepType: "form-intake", stepNumber: 1, firstStep: true, assignees: [null] },
|
||||
{ key: Date.now() + 1, completed: false, editView: true, stepType: "approval-by", stepNumber: 2, finalStep: true, assignees: [null] },
|
||||
{ key: Date.now(), completed: false, stepType: "form-intake", stepNumber: 1, firstStep: true, assignees: [null] },
|
||||
{ key: Date.now() + 1, completed: false, stepType: "approval-by", stepNumber: 2, finalStep: true, assignees: [null] },
|
||||
],
|
||||
};
|
||||
setWorkflows((prev) => [...prev, newWorkflow]);
|
||||
@@ -253,7 +255,7 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<Tip text="Please fill workflow name and associated entity to start configuring workflow." />
|
||||
<Tip text="Please fill in workflow name and associated entity to start configuring workflow." />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -79,11 +79,10 @@ const STATUS_OPTIONS = [
|
||||
|
||||
interface Props {
|
||||
user: User,
|
||||
teachers: TeacherUser[],
|
||||
userEntitiesWithLabel: Entity[],
|
||||
}
|
||||
|
||||
export default function ApprovalWorkflows({ user, teachers, userEntitiesWithLabel }: Props) {
|
||||
export default function ApprovalWorkflows({ user, userEntitiesWithLabel }: Props) {
|
||||
|
||||
const ENTITY_OPTIONS = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user