From 8f8d5e5640d8bcbeb728be5702465dc189a328dd Mon Sep 17 00:00:00 2001 From: Joao Correia Date: Wed, 22 Jan 2025 00:30:14 +0000 Subject: [PATCH] Work on workflows table --- src/demo/approval_workflows.json | 77 +++++++++++- src/interfaces/approval.workflow.ts | 10 +- src/interfaces/index.ts | 7 ++ src/pages/approval-workflows/[id].tsx | 10 +- src/pages/approval-workflows/index.tsx | 165 ++++++++++++++++++------- 5 files changed, 211 insertions(+), 58 deletions(-) diff --git a/src/demo/approval_workflows.json b/src/demo/approval_workflows.json index af95a5c8..ddf0e155 100644 --- a/src/demo/approval_workflows.json +++ b/src/demo/approval_workflows.json @@ -1,13 +1,12 @@ [ { "id": "kajhfakscbka-asacaca-acawesae", - "name": "name-1", + "name": "English Exam 1st Quarter 2025", "modules": [ "reading", "writing" ], "status": "pending", - "approvers": "prof-1", "steps": [ { "stepType": "form-intake", @@ -67,5 +66,79 @@ "assigneesType": "corporate" } ] + }, + { + "id": "aaaaaakscbka-asacaca-acawesae", + "name": "English Exam 2nd Quarter 2025", + "modules": [ + "reading", + "writing", + "level", + "speaking", + "listening" + ], + "status": "approved", + "steps": [ + { + "stepType": "form-intake", + "stepNumber": 1, + "completed": true, + "completedBy": "Prof. X", + "assignees": [ + "Prof. X", + "Prof. Y", + "Prof. Z" + ], + "assigneesType": "teacher" + }, + { + "stepType": "approval-by", + "stepNumber": 2, + "completed": true, + "completedBy": "Prof. Y", + "assignees": [ + "Prof. X", + "Prof. Y", + "Prof. Z" + ], + "assigneesType": "teacher" + }, + { + "stepType": "approval-by", + "stepNumber": 3, + "completed": true, + "completedBy": "Prof. Y", + "assignees": [ + "Prof. X", + "Prof. Y", + "Prof. Z" + ], + "assigneesType": "teacher" + }, + { + "stepType": "approval-by", + "stepNumber": 4, + "completed": true, + "completedBy": "Prof. Y", + "assignees": [ + "Prof. X", + "Prof. Y", + "Prof. Z" + ], + "assigneesType": "teacher" + }, + { + "stepType": "approval-by", + "stepNumber": 5, + "completed": true, + "completedBy": "Prof. Y", + "assignees": [ + "Dir. X", + "Dir. Y", + "Dir. Z" + ], + "assigneesType": "corporate" + } + ] } ] \ No newline at end of file diff --git a/src/interfaces/approval.workflow.ts b/src/interfaces/approval.workflow.ts index 845e071f..a896541d 100644 --- a/src/interfaces/approval.workflow.ts +++ b/src/interfaces/approval.workflow.ts @@ -10,6 +10,10 @@ export interface ApprovalWorkflow { } export type StepType = "form-intake" | "approval-by"; +export const StepTypeLabel: Record = { + "form-intake": "Form Intake", + "approval-by": "Approval", +}; type AssigneesType = TeacherUser["type"] | CorporateUser["type"] | MasterCorporateUser["type"]; @@ -38,8 +42,8 @@ export function getUserTypeLabel(type: AssigneesType | undefined): string { } export type ApprovalWorkflowStatus = "approved" | "pending" | "rejected"; -export const ApprovalWorkflowStatusLabel: {[key in ApprovalWorkflowStatus]: string} = { - approved: "Approved", +export const ApprovalWorkflowStatusLabel: Record = { + approved: "Approved", pending: "Pending", rejected: "Rejected", -}; +}; \ No newline at end of file diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 79248c34..4692f2f7 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,4 +1,11 @@ export type Module = "reading" | "listening" | "writing" | "speaking" | "level"; +export const ModuleTypeLabels: Record = { + reading: "Reading", + listening: "Listening", + writing: "Writing", + speaking: "Speaking", + level: "Level", +}; export interface Step { min: number; diff --git a/src/pages/approval-workflows/[id].tsx b/src/pages/approval-workflows/[id].tsx index 3b8e154d..c05d0f74 100644 --- a/src/pages/approval-workflows/[id].tsx +++ b/src/pages/approval-workflows/[id].tsx @@ -1,6 +1,6 @@ import Layout from "@/components/High/Layout"; import useUser from "@/hooks/useUser"; -import { ApprovalWorkflow, WorkflowStep } from "@/interfaces/approval.workflow"; +import { ApprovalWorkflow } from "@/interfaces/approval.workflow"; import { sessionOptions } from "@/lib/session"; import { redirect } from "@/utils"; import { requestUser } from "@/utils/api"; @@ -17,10 +17,8 @@ import approvalWorkflowsData from '../../demo/approval_workflows.json'; // to te import RequestedBy from "@/components/ApprovalWorkflows/RequestedBy"; import StartedOn from "@/components/ApprovalWorkflows/StartedOn"; import Status from "@/components/ApprovalWorkflows/Status"; -import { useState } from "react"; -import Button from "@/components/Low/Button"; -import WorkflowEditableStepComponent from "@/components/ApprovalWorkflows/WorkflowEditableStepComponent"; import WorkflowStepComponent from "@/components/ApprovalWorkflows/WorkflowStepComponent"; +import { useState } from "react"; export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => { const user = await requestUser(req, res); @@ -102,8 +100,8 @@ export default function Home({ workflow }: { workflow: ApprovalWorkflow }) { assigneesType={step.assigneesType} finalStep={index === steps.length - 1} currentStep={steps.findIndex(step => !step.completed) === index} - selected={index === selectedIndex} // Determine selected state - onClick={() => handleStepClick(index)} // Add click handler + selected={index === selectedIndex} + onClick={() => handleStepClick(index)} /> ))} diff --git a/src/pages/approval-workflows/index.tsx b/src/pages/approval-workflows/index.tsx index 7a3166e3..ec4a04dd 100644 --- a/src/pages/approval-workflows/index.tsx +++ b/src/pages/approval-workflows/index.tsx @@ -3,7 +3,8 @@ import Button from "@/components/Low/Button"; import Select from "@/components/Low/Select"; import useApprovalWorkflows from "@/hooks/useApprovalWorkflows"; import useUser from "@/hooks/useUser"; -import { ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel } from "@/interfaces/approval.workflow"; +import { Module, ModuleTypeLabels } from "@/interfaces"; +import { ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel, StepTypeLabel } from "@/interfaces/approval.workflow"; import { sessionOptions } from "@/lib/session"; import { redirect } from "@/utils"; import { requestUser } from "@/utils/api"; @@ -117,31 +118,74 @@ export default function ApprovalWorkflows() { cell: (info) => info.getValue(), }), */ columnHelper.accessor("name", { - header: "Name", - cell: (info) => info.getValue(), - }), - /* columnHelper.accessor("module", { - header: "Module", - cell: (info) => info.getValue(), - }), */ - columnHelper.accessor("status", { - header: "Status", + 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("approvers", { - header: "Approvers", - cell: (info) => info.getValue(), + columnHelper.accessor("steps", { + 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", { + 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"} + + ); + }, }), - columnHelper.accessor("step", { - header: "Step", - cell: (info) => info.getValue(), - }), */ { - header: "Actions", + header: "ACTIONS", id: "actions", cell: ({ row }: { row: { original: ApprovalWorkflow } }) => { return ( @@ -204,35 +248,62 @@ export default function ApprovalWorkflows() { - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - - ))} - - - {table.getRowModel().rows.map((row) => ( - +
+
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {/* + Might be an overkill way to add rounded borders to the rows, but couldn't figure out another way... + border round and margin does not seem to work properly on tr + Another way to do it was with grid but that puts the same width in all rows, which is inconvenient + Regardless, it works and all calcs are pretty simple so shouldnt be too inefficient + */} + {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())} +
+ + + + - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - - )}