Skip to content

alamrumman/Payload-CMS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

8 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”„ NovaCorp Dynamic Workflow Management System

A production-grade, fully dynamic multi-stage approval workflow engine built on Payload CMS v3, Next.js 15, TypeScript, and MongoDB.

Built as a backend engineering challenge for WeFrameTech β€” designed and implemented from scratch with zero prior Payload CMS experience.


πŸ“‹ Table of Contents


🎯 What It Does

NovaCorp's internal teams can create, assign, and track multi-stage approval workflows for any document type (Contracts, Blogs, Product Specs, etc.) directly from the Admin UI β€” no code changes required.

The Problem It Solves

Before this system, approvals were handled manually via email chains with no visibility, no audit trail, and no enforcement of who should approve what. Now:

  • Every document has a defined approval path
  • The right person is automatically notified and assigned
  • Every action is permanently logged and immutable
  • Steps are conditionally skipped based on document values
  • The full workflow history is visible inline inside the document
Sarah (editor) submits a contract for β‚Ή75,000
  ↓
Step 1 β†’ Legal Review
         Auto-assigned to Priya (legal)
         Priya sees Approve / Reject / Comment buttons
         ↓ Priya approves
Step 2 β†’ Manager Approval  [condition: amount > 10,000 βœ…]
         Auto-assigned to Arjun (manager)
         ↓ Arjun approves
Step 3 β†’ Director Sign-off
         Auto-assigned to Raj (director)
         ↓ Raj signs off
Contract status β†’ Executed βœ…
Audit trail preserved forever

For a small contract (β‚Ή5,000), Step 2 is automatically skipped β€” the system evaluates amount > 10000 and proceeds directly to Director Sign-off.


πŸ— Architecture and Diagrams

All workflow logic lives in a custom Workflow Engine that runs server-side, triggered by Payload's lifecycle hooks. The architecture is best understood through three core flows

Creation Flow

Screenshot 2026-03-09 194518

Reviewer's Flow

image

Approve/Reject Flow

image

Folder Structure

src/
β”œβ”€β”€ collections/
β”‚   β”œβ”€β”€ Users.ts            β†’ Auth system + role-based access control
β”‚   β”œβ”€β”€ Workflows.ts        β†’ Dynamic workflow definitions (unlimited steps)
β”‚   β”œβ”€β”€ WorkflowLogs.ts     β†’ Immutable audit trail (update/delete blocked)
β”‚   β”œβ”€β”€ Contracts.ts        β†’ Legal agreements with workflow support
β”‚   β”œβ”€β”€ Blogs.ts            β†’ Internal articles with workflow support
β”‚   └── Media.ts            β†’ File upload handling
β”‚
β”œβ”€β”€ plugins/
β”‚   └── WorkflowEngine.ts   β†’ Core plugin (the brain of the system)
β”‚
β”œβ”€β”€ components/
β”‚   └── WorkflowPanel.tsx   β†’ React UI injected into every document edit view
β”‚
└── payload.config.ts       β†’ Root config β€” wires everything together

Plugin Architecture

The WorkflowEngine is a Payload plugin that dynamically injects afterChange hooks into any collection you tell it to watch:

workflowEnginePlugin({
  watchedCollections: ['contracts', 'blogs', 'product-specs']
})

When a document is saved, the plugin:

1. Checks if a workflow is attached to the document
2. Iterates through workflow steps
3. Evaluates each step's condition against the document data
4. Skips steps whose conditions fail
5. Finds the first user matching the step's assigned role
6. Creates an immutable WorkflowLog entry
7. Updates the document status automatically
8. Fires a console notification (simulated email)

Condition Evaluator

Steps support simple field-based expressions:

amount > 10000      β†’ runs only for high-value contracts
amount <= 5000      β†’ runs only for small contracts
status == draft     β†’ runs only if document is still draft
estimatedCost >= 50000  β†’ runs for expensive product specs

The evaluator parses the expression, resolves the field value from the document, and returns true/false. Steps with failing conditions are automatically skipped and logged.

WorkflowPanel Component

A React component ('use client') injected via Payload's ui field type into every document's edit view. It:

  • Fetches live workflow status via /api/workflow-status
  • Shows current step, assigned user, and status badge
  • Renders Approve / Reject / Comment buttons only for the assigned user
  • Shows an observer message to all other users
  • Displays the full chronological audit trail with color-coded actions
  • Dynamically detects which collection it's in from the URL

Data Flow Diagram

Admin UI                    WorkflowEngine Plugin           MongoDB
─────────                   ─────────────────────           ───────
Create Contract        β†’    afterChange hook fires
Attach Workflow        β†’    Find workflow by ID         β†’   workflows collection
Save Document          β†’    Evaluate step conditions
                       β†’    Find user by role           β†’   users collection
                       β†’    Create WorkflowLog          β†’   workflow-logs collection
                       β†’    Update document status      β†’   contracts collection
                       β†’    Console notify assigned user

Assigned User opens doc β†’   WorkflowPanel fetches status ←  workflow-logs collection
Clicks Approve          β†’   POST /api/workflow-action
                        β†’   Log action                  β†’   workflow-logs collection
                        β†’   Evaluate next step
                        β†’   Assign next user            β†’   users collection
                        β†’   Update status               β†’   contracts collection

βš™οΈ Setup Instructions

Prerequisites

  • Node.js 18 or higher
  • MongoDB β€” local instance or MongoDB Atlas (free tier works)
  • npm or yarn

Step 1 β€” Clone the Repository

git clone https://github.com/alamrumman/workflow-system.git
cd workflow-system

Step 2 β€” Install Dependencies

npm install

This may take 2-3 minutes on first install due to Payload CMS dependencies.

Step 3 β€” Configure Environment Variables

Create a .env file in the project root:

# For local MongoDB
DATABASE_URL=mongodb://127.0.0.1/workflow-system

# For MongoDB Atlas
# DATABASE_URL=mongodb+srv://<username>:<password>@cluster.mongodb.net/workflow-system

# Any random string β€” used to sign auth tokens
PAYLOAD_SECRET=your-secret-key-change-this-in-production

Step 4 β€” Start the Development Server

npm run dev

Wait for the terminal to show:

βœ“ Ready in Xs

Step 5 β€” Create Your Admin Account

Open http://localhost:3000/admin in your browser. You'll see a "Create First User" screen. Create your admin account.

Step 6 β€” Seed Demo Data (Optional)

npm run seed

This automatically creates all 5 demo users and 2 sample workflows. See Demo Credentials below.

Step 7 β€” Create a Workflow and Test

  1. Go to Workflow Engine β†’ Workflows β†’ Create New
  2. Set name, applies-to collection, and add steps
  3. Go to Business Documents β†’ Contracts β†’ Create New
  4. Fill in the fields and select your workflow under "Attached Workflow"
  5. Save β€” the workflow fires automatically
  6. Check the Workflow Status panel at the bottom of the contract

πŸ‘₯ Demo Credentials

image
Name Email Password Role What They Do
Admin alamrumman0@gmail.com 12345 admin Manages system, creates workflows
Sarah sarah@novacorp.com sarah12345 editor Creates contracts and blog posts
Priya priya@novacorp.com priya12345 legal Reviews contracts for legal risk
Arjun arjun@novacorp.com arjun12345 manager Approves high-value contracts
Raj raj@novacorp.com raj12345 director Final sign-off on all contracts

πŸ“‹ Sample Workflows

Workflow 1 β€” Contract Approval Flow

Applies to: contracts

Step Name Assigned Role Condition Step Type
1 Legal Review legal (always runs) review
2 Manager Approval manager amount > 10000 approval
3 Director Sign-off director (always runs) sign-off

Scenario A β€” Small Contract (β‚Ή5,000):

Legal Review (Priya) β†’ approved
Manager Approval     β†’ SKIPPED (5000 is not > 10000)
Director Sign-off (Raj) β†’ approved
Status: Executed βœ…

Scenario B β€” Large Contract (β‚Ή75,000):

Legal Review (Priya)     β†’ approved
Manager Approval (Arjun) β†’ approved (75000 > 10000 βœ…)
Director Sign-off (Raj)  β†’ approved
Status: Executed βœ…

Scenario C β€” Rejected Contract:

Legal Review (Priya) β†’ rejected with comment: "Missing termination clause"
Status: Rejected ❌
Full audit trail preserved

Workflow 2 β€” Blog Publishing Flow

Applies to: blogs

Step Name Assigned Role Condition Step Type
1 Editorial Review editor (always runs) review
2 Manager Approval manager (always runs) approval
Blog submitted
Editorial Review (editor) β†’ approved
Manager Approval (Arjun)  β†’ approved
Status: Published βœ…

πŸš€ API Endpoints

POST /api/workflows/trigger

Manually trigger a workflow on any document.

Request Body:

{
  "workflowId": "64abc123...",
  "docId": "64def456...",
  "collection": "contracts"
}

Success Response:

{
  "success": true,
  "message": "Workflow triggered for contracts #64def456...",
  "workflow": "Contract Approval Flow",
  "firstStep": "Legal Review"
}

Error Responses:

{ "error": "Missing workflowId, docId, or collection" }   // 400
{ "error": "Document not found" }                          // 404
{ "error": "Workflow not found" }                          // 404

GET /api/workflow-status?docId=xxx

Returns the current workflow state and complete audit trail for any document.

Example Request:

GET /api/workflow-status?docId=64def456abc123

Success Response:

{
  "docId": "64def456abc123",
  "currentStep": "Manager Approval",
  "currentAction": "triggered",
  "totalLogs": 3,
  "logs": [
    {
      "stepName": "Manager Approval",
      "action": "triggered",
      "userEmail": "arjun@novacorp.com",
      "comment": null,
      "timestamp": "2026-03-07T12:30:00.000Z"
    },
    {
      "stepName": "Legal Review",
      "action": "approved",
      "userEmail": "priya@novacorp.com",
      "comment": "All clauses verified",
      "timestamp": "2026-03-07T11:45:00.000Z"
    },
    {
      "stepName": "Legal Review",
      "action": "triggered",
      "userEmail": "priya@novacorp.com",
      "comment": null,
      "timestamp": "2026-03-07T10:00:00.000Z"
    }
  ]
}

🌐 Deployment Guide

Deploy to Vercel (Recommended)

Payload CMS v3 is built on Next.js, making Vercel the simplest deployment target.

Step 1 β€” Set Up MongoDB Atlas

  1. Go to mongodb.com/atlas
  2. Create a free M0 cluster
  3. Create a database user: Database Access β†’ Add New Database User
  4. Allow all IPs: Network Access β†’ Add IP Address β†’ 0.0.0.0/0
  5. Get your connection string: Connect β†’ Drivers β†’ Copy connection string

It looks like:

mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/workflow-system

Step 2 β€” Push to GitHub

Make sure your repository is private (required by the task):

git add .
git commit -m "NovaCorp Workflow Management System"
git push origin main

Step 3 β€” Create Vercel Project

  1. Go to vercel.com and sign in
  2. Click Add New β†’ Project
  3. Import your GitHub repository
  4. Keep the default build settings (Vercel auto-detects Next.js)

Step 4 β€” Add Environment Variables

In the Vercel project settings before deploying:

Variable Value
DATABASE_URL Your MongoDB Atlas connection string
PAYLOAD_SECRET Any strong random string (min 32 chars)

Step 5 β€” Deploy

Click Deploy. Vercel will build and deploy automatically. First build takes 3-5 minutes.

Step 6 β€” Create Admin User on Live Site

Visit https://your-project.vercel.app/admin and create your first admin user.

Step 7 β€” Set Up Demo Data

Either create workflows and users manually in the admin panel, or update your local .env to point to Atlas and run:

npm run seed

This seeds your Atlas database directly.


Deploy to Render (Alternative)

  1. Go to render.com β†’ New Web Service
  2. Connect your GitHub repository
  3. Set build command: npm install && npm run build
  4. Set start command: npm start
  5. Add environment variables (DATABASE_URL, PAYLOAD_SECRET)
  6. Deploy

Environment Variables Reference

Variable Required Description
DATABASE_URL βœ… MongoDB connection string
PAYLOAD_SECRET βœ… Secret key for auth token signing (min 32 chars)

πŸ›  Tech Stack

Technology Version Purpose
Payload CMS v3.x Headless CMS, collections, admin UI
Next.js 15.x React framework (Payload v3 runtime)
TypeScript 5.x Type safety throughout
MongoDB 7.x Database
Mongoose 8.x MongoDB adapter for Payload
React 19.x Admin UI components

πŸ”‘ Key Design Decisions

Why a plugin instead of direct hooks? The plugin pattern makes the workflow engine completely reusable β€” adding a new collection to the workflow system requires only one line of config change. No duplicated hook logic across collections.

Why UI field for the WorkflowPanel? Payload v3 removed afterFields from admin.components. The ui field type is the correct v3 approach β€” it renders a custom React component inline within the collection edit form, giving full access to Payload's React context including useDocumentInfo and useAuth.

Why query params instead of URL params for the status endpoint? Payload v3's custom endpoint routing has issues with dynamic URL segments (:docId). Query parameters (?docId=xxx) are more reliable and work consistently across all environments.

Why is WorkflowLogs immutable? The audit trail must be trustworthy. By setting update: () => false and delete: () => false on the access control, the logs become a permanent, tamper-proof record of all workflow actions regardless of user role.


πŸ‘€ About

Built by Rumman Alam

  • GitHub: @alamrumman
  • Stack: Node.js Β· Express Β· React Β· MongoDB Β· TypeScript

This project was built in under 24 hours with zero prior Payload CMS experience as part of a backend engineering hiring challenge.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors