Modern purchase request workflow application built with Next.js 14 (App Router), TypeScript, Tailwind CSS, and shadcn/ui. It models a pragmatic approval process using a finite state machine and provides a clean UI to create, review, and advance requests.
The core business flow is expressed as a small, explicit finite state machine (FSM) to guarantee valid transitions and auditable history.
- States: Draft → Pending Approval → Approved | Rejected
- Actions:
- Draft → Pending Approval: "Submit for Approval"
- Pending Approval → Approved: "Approve"
- Pending Approval → Rejected: "Reject"
- Pending Approval → Draft: "Withdraw"
- Rejected → Draft: "Revise"
Transitions are declared in lib/types.ts as WORKFLOW_TRANSITIONS, and validated by getValidTransitions(currentStatus). Enforcement happens in useRequestStore.changeStatus(...) to prevent illegal state changes, and each change is appended to the request history for traceability.
Mermaid diagram of the FSM:
stateDiagram-v2
[*] --> Draft
Draft --> Pending_Approval: Submit for Approval
Pending_Approval --> Approved: Approve
Pending_Approval --> Rejected: Reject
Pending_Approval --> Draft: Withdraw
Rejected --> Draft: Revise
Key business guarantees:
- Only declared transitions are allowed (no implicit jumps between states).
- Every mutation creates an auditable
historyentry (actor, action, timestamp, optional comment). - Requests can be revised after rejection and resubmitted.
- Framework: Next.js 14 (App Router) with React 18 and TypeScript
- Styling: Tailwind CSS + tailwindcss-animate
- UI Kit: shadcn/ui (Radix UI primitives) and lucide-react icons
- State Management: Zustand stores for requests and material catalog
- Utilities: date-fns, uuid, clsx/cva
Architecture highlights:
app/uses the App Router for pages and routing (e.g.,app/requests, dynamic routes for edit/view).store/requestStore.tscontains request CRUD, status transitions, and consistent mock data generation for a rich demo without a backend.store/materialStore.tsprovides a filterable material catalog used by the Items table selector.components/workflow/WorkflowModal.tsxrenders the actionable workflow view, surfacing valid transitions returned bygetValidTransitionsand writing back to the store.components/RequestForm.tsx,components/ItemsTable.tsx, andcomponents/MaterialSelector.tsxcompose the main creation/edit experience.
Selected files:
lib/types.ts: Types,RequestStatus,WORKFLOW_TRANSITIONS,getValidTransitions.store/requestStore.ts: Authoritative transition enforcement inchangeStatuswith history logging.components/workflow/WorkflowModal.tsx: Shows current status, valid actions, activity/notes/files tabs.
app/
requests/
[id]/edit/page.tsx
[id]/view/page.tsx
new/page.tsx
page.tsx
components/
ItemsTable.tsx
MaterialSelector.tsx
RequestForm.tsx
RequestTable.tsx
workflow/WorkflowModal.tsx
ui/ ... (shadcn/ui components)
lib/
types.ts
store/
requestStore.ts
materialStore.ts
Prerequisites:
- Node.js 18+
- npm 9+
Install dependencies:
npm installRun in development mode:
npm run devBuild for production:
npm run buildStart the production server (after build):
npm startAvailable scripts (see package.json):
dev: Start Next.js in developmentbuild: Create production buildstart: Run the production serverlint: Run Next.js ESLint
- The app uses deterministic mock data for requests and materials to provide a stable, realistic demo without a backend.
requestStore.tsbootstraps a large, repeatable dataset and appends history entries on each mutation.materialStore.tsgenerates a catalog with categories/subcategories and exposes a filter for the selector dialog.
This project is configured for Netlify deployments using the official Next.js plugin.
Netlify configuration (netlify.toml):
- Build command:
npm run build - Publish directory:
.next - Node runtime:
NODE_VERSION=18 - Plugin:
@netlify/plugin-nextjs
Deploy options:
- Connect the Git repository to Netlify
- Netlify will auto-detect Next.js and use the provided
netlify.toml. - Ensure the Node version is 18 in the build environment (already specified).
- Manual via CLI (optional)
# If not installed
npm install -g netlify-cli
# Login and link
netlify login
netlify init
# Build and deploy a production build
npm run build
netlify deploy --build --prodPost-deploy:
- The Next.js plugin handles serverless functions and routing for the App Router.
- If environment variables are needed later, define them in Netlify UI under Site settings → Build & deploy → Environment.
- TypeScript across the codebase for safer refactors.
- ESLint with Next.js rules (
npm run lint). - No external backend or secrets are required by default. Add environment variables only when integrating external APIs.
- Create a feature branch.
- Write clear commit messages (e.g., "feat: add X", "fix: correct Y").
- Ensure
npm run buildandnpm run lintpass. - Open a PR with a concise, context-rich description.
If you have questions or need guidance on extending the FSM (e.g., additional states like "On Hold" or multi-step approvals), see lib/types.ts and useRequestStore.changeStatus as the authoritative places to add transitions and guards.