From 66d23b414018049273335f019b9363bc93e08c5a Mon Sep 17 00:00:00 2001 From: Joao Correia Date: Fri, 17 Jan 2025 18:44:52 +0000 Subject: [PATCH] Add local test data, implement [id].tsx for approval workflows --- src/demo/approval_workflows.json | 43 +++++++++++- src/hooks/useApprovalWorkflows.tsx | 2 +- src/pages/approval-workflows/[id].tsx | 68 ++++++++++++++++++ .../index.tsx} | 70 +++++++++++++++---- src/utils/approval.workflows.be.ts | 8 +++ 5 files changed, 175 insertions(+), 16 deletions(-) create mode 100644 src/pages/approval-workflows/[id].tsx rename src/pages/{approval-workflows.tsx => approval-workflows/index.tsx} (71%) create mode 100644 src/utils/approval.workflows.be.ts diff --git a/src/demo/approval_workflows.json b/src/demo/approval_workflows.json index 0637a088..d0244fb8 100644 --- a/src/demo/approval_workflows.json +++ b/src/demo/approval_workflows.json @@ -1 +1,42 @@ -[] \ No newline at end of file +[ + { + "id": "local-test-id-1", + "name": "name-1", + "module": "reading", + "status": "approved", + "approvers": "prof-1", + "step": "Concluded" + }, + { + "id": "local-test-id-2", + "name": "name-2", + "module": "reading", + "status": "pending", + "approvers": "prof-2", + "step": "Concluded" + }, + { + "id": "local-test-id-3", + "name": "name-3", + "module": "listening", + "status": "rejected", + "approvers": "prof-3", + "step": "Concluded" + }, + { + "id": "local-test-id-4", + "name": "name-4", + "module": "writing", + "status": "approved", + "approvers": "prof-4", + "step": "Concluded" + }, + { + "id": "local-test-id-5", + "name": "name-5", + "module": "reading", + "status": "approved", + "approvers": "prof-5", + "step": "Concluded" + } +] \ No newline at end of file diff --git a/src/hooks/useApprovalWorkflows.tsx b/src/hooks/useApprovalWorkflows.tsx index c0cc273f..9be0459c 100644 --- a/src/hooks/useApprovalWorkflows.tsx +++ b/src/hooks/useApprovalWorkflows.tsx @@ -5,7 +5,7 @@ import { useCallback, useEffect, useState } from "react"; import approvalWorkflowsData from '../demo/approval_workflows.json' // to test locally export default function useApprovalWorkflows() { - const [approvalWorkflows, setApprovalWorkflows] = useState(approvalWorkflowsData); + const [approvalWorkflows, setApprovalWorkflows] = useState(approvalWorkflowsData as ApprovalWorkflow[]); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); diff --git a/src/pages/approval-workflows/[id].tsx b/src/pages/approval-workflows/[id].tsx new file mode 100644 index 00000000..622d6d4e --- /dev/null +++ b/src/pages/approval-workflows/[id].tsx @@ -0,0 +1,68 @@ +import Layout from "@/components/High/Layout"; +import Select from "@/components/Low/Select"; +import useApprovalWorkflows from "@/hooks/useApprovalWorkflows"; +import useUser from "@/hooks/useUser"; +import useUsers from "@/hooks/useUsers"; +import { ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel } from "@/interfaces/approval.workflow"; +import { GroupWithUsers, User } from "@/interfaces/user"; +import { sessionOptions } from "@/lib/session"; +import { redirect } from "@/utils"; +import { requestUser } from "@/utils/api"; +import { getApprovalWorkflow } from "@/utils/approval.workflows.be"; +import { shouldRedirectHome } from "@/utils/navigation.disabled"; +import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; +import axios 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 { toast, ToastContainer } from "react-toastify"; + +import approvalWorkflowsData from '../../demo/approval_workflows.json' // to test locally + +export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => { + 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 { id } = params as { id: string }; + + const approvalWorkflow = approvalWorkflowsData.find(workflow => workflow.id === id); // await getApprovalWorkflow(id); + if (!approvalWorkflow) + return redirect("/approval-workflows") + + return { + props: { user }, + }; +}, sessionOptions); + +export default function Home() { + const { user } = useUser({ redirectTo: "/login" }); + + return ( + <> + + Approval Workflows | EnCoach + + + + + + {user && ( + +

Approval Workflows

+ + +
+ )} + + ); +} diff --git a/src/pages/approval-workflows.tsx b/src/pages/approval-workflows/index.tsx similarity index 71% rename from src/pages/approval-workflows.tsx rename to src/pages/approval-workflows/index.tsx index 795a2262..e7688193 100644 --- a/src/pages/approval-workflows.tsx +++ b/src/pages/approval-workflows/index.tsx @@ -9,11 +9,15 @@ import { redirect } from "@/utils"; import { requestUser } from "@/utils/api"; import { shouldRedirectHome } from "@/utils/navigation.disabled"; import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; +import axios 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 { ToastContainer } from "react-toastify"; +import { BsTrash } from "react-icons/bs"; +import { FaRegEdit } from "react-icons/fa"; +import { toast, ToastContainer } from "react-toastify"; const columnHelper = createColumnHelper(); @@ -30,14 +34,11 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { }, sessionOptions); const StatusClassNames: { [key in ApprovalWorkflowStatus]: string } = { - approved: "bg-mti-green-light", - pending: "bg-mti-orange-light", - rejected: "bg-mti-red-light", + 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", }; -const escapedURL = process.env.NEXT_PUBLIC_WEBSITE_URL || "".replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -const fromHomepage = [new RegExp(`^${escapedURL}`), /\/contact$/]; - type CustomStatus = ApprovalWorkflowStatus | "all" | "pending"; const STATUS_OPTIONS = [ @@ -65,12 +66,12 @@ const STATUS_OPTIONS = [ export default function ApprovalWorkflows() { const [filteredApprovalWorkflows, setFilteredApprovalWorkflows] = useState([]); - const [selectedApprovalWorkflow, setSelectedApprovalWorkflow] = useState(); + /* const [selectedApprovalWorkflow, setSelectedApprovalWorkflow] = useState(); */ - const [statusFilter, setStatusFilter] = useState("pending"); + const [statusFilter, setStatusFilter] = useState("all"); const { user } = useUser({ redirectTo: "/login" }); - + const { approvalWorkflows/* , reload */ } = useApprovalWorkflows(); useEffect(() => { @@ -83,6 +84,32 @@ export default function ApprovalWorkflows() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [approvalWorkflows, statusFilter]); + 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", @@ -99,7 +126,7 @@ export default function ApprovalWorkflows() { columnHelper.accessor("status", { header: "Status", cell: (info) => ( - + {ApprovalWorkflowStatusLabel[info.getValue()]} ), @@ -112,6 +139,22 @@ export default function ApprovalWorkflows() { header: "Step", cell: (info) => info.getValue(), }), + { + header: "Actions", + id: "actions", + cell: ({ row }: { row: { original: ApprovalWorkflow } }) => { + return ( +
+
deleteApprovalWorkflow(row.original.id, row.original.name)}> + +
+ + + +
+ ); + }, + }, ]; const table = useReactTable({ @@ -165,11 +208,10 @@ export default function ApprovalWorkflows() { {table.getRowModel().rows.map((row) => ( setSelectedApprovalWorkflow(row.original)} key={row.id}> + {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} diff --git a/src/utils/approval.workflows.be.ts b/src/utils/approval.workflows.be.ts new file mode 100644 index 00000000..0173ac48 --- /dev/null +++ b/src/utils/approval.workflows.be.ts @@ -0,0 +1,8 @@ +import { ApprovalWorkflow } from "@/interfaces/approval.workflow"; +import client from "@/lib/mongodb"; + +const db = client.db(process.env.MONGODB_DB); + +export const getApprovalWorkflow = async (id: string) => { + return await db.collection("approval-workflows").findOne({ id }); +};