Work on workflows table
This commit is contained in:
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -10,6 +10,10 @@ export interface ApprovalWorkflow {
|
||||
}
|
||||
|
||||
export type StepType = "form-intake" | "approval-by";
|
||||
export const StepTypeLabel: Record<StepType, string> = {
|
||||
"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<ApprovalWorkflowStatus, string> = {
|
||||
approved: "Approved",
|
||||
pending: "Pending",
|
||||
rejected: "Rejected",
|
||||
};
|
||||
@@ -1,4 +1,11 @@
|
||||
export type Module = "reading" | "listening" | "writing" | "speaking" | "level";
|
||||
export const ModuleTypeLabels: Record<Module, string> = {
|
||||
reading: "Reading",
|
||||
listening: "Listening",
|
||||
writing: "Writing",
|
||||
speaking: "Speaking",
|
||||
level: "Level",
|
||||
};
|
||||
|
||||
export interface Step {
|
||||
min: number;
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
|
||||
@@ -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) => (
|
||||
<span className={clsx("rounded-full px-3 py-1 text-sm font-medium inline-block text-left w-[110px]", StatusClassNames[info.getValue()])}>
|
||||
<span className="font-medium">
|
||||
{info.getValue()}
|
||||
</span>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("modules", {
|
||||
header: "MODULES",
|
||||
cell: (info) => (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{info.getValue().map((module: Module, index: number) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-block rounded-full px-3 py-1 text-sm font-medium bg-purple-100 border border-purple-300 text-purple-900"
|
||||
>
|
||||
{ModuleTypeLabels[module]}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("status", {
|
||||
header: "STATUS",
|
||||
cell: (info) => (
|
||||
<span className={clsx("inline-block rounded-full px-3 py-1 text-sm font-medium text-left w-[110px]", StatusClassNames[info.getValue()])}>
|
||||
{ApprovalWorkflowStatusLabel[info.getValue()]}
|
||||
</span>
|
||||
),
|
||||
}),
|
||||
/* 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 (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{approvers.map((approver: string, index: number) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-block rounded-full px-3 py-1 text-sm font-medium bg-gray-100 border border-gray-300 text-gray-900"
|
||||
>
|
||||
{approver}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("steps", {
|
||||
header: "CURRENT STEP",
|
||||
cell: (info) => {
|
||||
const steps = info.row.original.steps;
|
||||
const currentStep = steps.find((step) => !step.completed);
|
||||
|
||||
return (
|
||||
<span className="font-medium">
|
||||
{currentStep
|
||||
? `Step ${currentStep.stepNumber}: ${StepTypeLabel[currentStep.stepType!]}`
|
||||
: "Completed"}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
}),
|
||||
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() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table className="bg-mti-purple-ultralight/40 w-full rounded-xl">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th className="px-4 py-4 text-left" key={header.id}>
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody className="px-2">
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<tr
|
||||
className={clsx(
|
||||
"even:bg-mti-purple-ultralight/40 rounded-lg py-2 odd:bg-white"
|
||||
)}
|
||||
key={row.id}>
|
||||
<div className="px-6 pb-4 bg-purple-100 rounded-2xl">
|
||||
<table
|
||||
className="w-full table-auto border-separate border-spacing-y-2"
|
||||
style={{ tableLayout: "auto" }}
|
||||
>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th key={header.id} className="px-2 py-3 text-left text-purple-900">
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<tr key={row.id}>
|
||||
{/*
|
||||
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 (
|
||||
<td key={cell.id} className={cellClasses}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<td className="w-fit items-center px-4 py-2" key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Layout>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user