import Layout from "@/components/High/Layout"; import Button from "@/components/Low/Button"; import Select from "@/components/Low/Select"; import useApprovalWorkflows from "@/hooks/useApprovalWorkflows"; import useUser from "@/hooks/useUser"; import { Module, ModuleTypeLabels } from "@/interfaces"; import { ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel, StepTypeLabel } from "@/interfaces/approval.workflow"; import { EntityWithRoles } from "@/interfaces/entity"; import { TeacherUser, User } from "@/interfaces/user"; import { sessionOptions } from "@/lib/session"; import { mapBy, redirect, serialize } from "@/utils"; import { requestUser } from "@/utils/api"; import { getEntitiesWithRoles } from "@/utils/entities.be"; import { shouldRedirectHome } from "@/utils/navigation.disabled"; import { findAllowedEntities } from "@/utils/permissions"; import { isAdmin } from "@/utils/users"; import { getUsers } from "@/utils/users.be"; import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; import axios, { all } from "axios"; import clsx from "clsx"; import { withIronSessionSsr } from "iron-session/next"; import Head from "next/head"; import Link from "next/link"; import { useEffect, useState } from "react"; import { BsTrash } from "react-icons/bs"; import { FaRegEdit } from "react-icons/fa"; import { IoIosAddCircleOutline } from "react-icons/io"; import { toast, ToastContainer } from "react-toastify"; const columnHelper = createColumnHelper(); export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") if (shouldRedirectHome(user) || !["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) return redirect("/") const entityIDS = mapBy(user.entities, "id"); const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDS); const allowedEntities = findAllowedEntities(user, entities, "view_approval_workflows"); return { props: serialize({ user, teachers: await getUsers({ type: "teacher" }) as TeacherUser[], allowedEntities, }), }; }, sessionOptions); const StatusClassNames: { [key in ApprovalWorkflowStatus]: string } = { approved: "bg-green-100 text-green-800 border border-green-300 before:content-[''] before:w-2 before:h-2 before:bg-green-500 before:rounded-full before:inline-block before:mr-2", pending: "bg-orange-100 text-orange-800 border border-orange-300 before:content-[''] before:w-2 before:h-2 before:bg-orange-500 before:rounded-full before:inline-block before:mr-2", rejected: "bg-red-100 text-red-800 border border-red-300 before:content-[''] before:w-2 before:h-2 before:bg-red-500 before:rounded-full before:inline-block before:mr-2", }; type CustomStatus = ApprovalWorkflowStatus | "all"; type CustomEntity = EntityWithRoles["label"] | "all"; const STATUS_OPTIONS = [ { label: "All", value: "all", filter: (x: ApprovalWorkflow) => true, }, { label: "Approved", value: "approved", filter: (x: ApprovalWorkflow) => x.status === "approved", }, { label: "Pending", value: "pending", filter: (x: ApprovalWorkflow) => x.status === "pending", }, { label: "Rejected", value: "rejected", filter: (x: ApprovalWorkflow) => x.status === "rejected", }, ]; interface Props { user: User, teachers: TeacherUser[], allowedEntities: EntityWithRoles[], } export default function ApprovalWorkflows({ user, teachers, allowedEntities }: Props) { console.log(user); console.log(teachers); console.log(allowedEntities); const ENTITY_OPTIONS = [ { label: "All", value: "all", filter: (x: ApprovalWorkflow) => true, }, ...allowedEntities .map(entity => ({ label: entity.label, value: entity.id, filter: (x: ApprovalWorkflow) => x.entityId === entity.id, })) .sort((a, b) => a.label.localeCompare(b.label)), ]; const [filteredApprovalWorkflows, setFilteredApprovalWorkflows] = useState([]); const [statusFilter, setStatusFilter] = useState("all"); const [entityFilter, setEntityFilter] = useState("all"); const { approvalWorkflows/* , reload */ } = useApprovalWorkflows(); useEffect(() => { const filters = []; if (statusFilter) { const filter = STATUS_OPTIONS.find((x) => x.value === statusFilter)?.filter; if (filter) filters.push(filter); } if (entityFilter) { const filter = ENTITY_OPTIONS.find((x) => x.value === entityFilter)?.filter; if (filter) filters.push(filter); } setFilteredApprovalWorkflows([...filters.reduce((d, f) => d.filter(f), approvalWorkflows)]); // eslint-disable-next-line react-hooks/exhaustive-deps }, [approvalWorkflows, statusFilter, entityFilter]); const deleteApprovalWorkflow = (id: string, name: string) => { if (!confirm(`Are you sure you want to delete this Approval Workflow?`)) return; axios .delete(`/api/approval-workflows/${id}`) .then(() => toast.success(`Deleted ${name} Approval Workflow.`)) .catch((reason) => { if (reason.response.status === 404) { toast.error("Approval Workflow not found!"); return; } if (reason.response.status === 403) { toast.error("You do not have permission to delete an Approval Workflow!"); return; } toast.error("Something went wrong, please try again later."); }) /* .finally(reload); */ }; const editApprovalWorkflow = (id: string) => { }; const columns = [ /* columnHelper.accessor("id", { header: "ID", cell: (info) => info.getValue(), }), */ columnHelper.accessor("name", { header: "NAME", cell: (info) => ( {info.getValue()} ), }), columnHelper.accessor("modules", { header: "MODULES", cell: (info) => (
{info.getValue().map((module: Module, index: number) => ( {ModuleTypeLabels[module]} ))}
), }), columnHelper.accessor("status", { header: "STATUS", cell: (info) => ( {ApprovalWorkflowStatusLabel[info.getValue()]} ), }), columnHelper.accessor("steps", { id: "currentApprovers", header: "CURRENT APPROVERS", cell: (info) => { const steps = info.row.original.steps; const currentStep = steps.find((step) => !step.completed); const approvers = currentStep?.assignees || []; return (
{approvers.map((approver: string, index: number) => ( {approver} ))}
); }, }), columnHelper.accessor("steps", { id: "currentStep", header: "CURRENT STEP", cell: (info) => { const steps = info.row.original.steps; const currentStep = steps.find((step) => !step.completed); return ( {currentStep ? `Step ${currentStep.stepNumber}: ${StepTypeLabel[currentStep.stepType!]}` : "Completed"} ); }, }), { header: "ACTIONS", id: "actions", cell: ({ row }: { row: { original: ApprovalWorkflow } }) => { return (
); }, }, ]; const table = useReactTable({ data: filteredApprovalWorkflows, columns: columns, getCoreRowModel: getCoreRowModel(), }); return ( <> Approval Workflows Panel | EnCoach {user && (

Approval Workflows

x.value === entityFilter)} onChange={(value) => setEntityFilter((value?.value as string) ?? undefined)} isClearable placeholder="Entity..." />
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {table.getRowModel().rows.map((row) => ( window.location.href = `/approval-workflows/${row.original.id}`} style={{ cursor: "pointer" }} className="hover:bg-purple-100" > {row.getVisibleCells().map((cell, cellIndex) => { const lastCellIndex = row.getVisibleCells().length - 1; let cellClasses = "px-4 py-2 bg-purple-50 border-y-2 border-purple-300"; if (cellIndex === 0) { cellClasses += " border-l-2 rounded-l-2xl"; } if (cellIndex === lastCellIndex) { cellClasses += " border-r-2 rounded-r-2xl"; } return ( ); })} ))}
{header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
)} ); }