3.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
# Development
pnpm dev # Next.js + Turbopack at localhost:3000
pnpm lint # ESLint
pnpm type-check # tsc --noEmit
# Tests
pnpm test # Unit tests (Vitest, jsdom)
pnpm test:watch # Unit tests in watch mode
pnpm test:integration # Integration tests (Vitest, node + real DB)
pnpm test:e2e # E2E tests (Playwright, headless)
pnpm test:e2e:ui # E2E tests with interactive UI
pnpm test:all # All test suites
# Run a single test file
pnpm test -- tests/unit/po-line-items-editor.test.tsx
pnpm test:integration -- tests/integration/create-po.test.ts
# Database
pnpm db:migrate # Create + apply migration (dev)
pnpm db:migrate:deploy # Apply migrations (CI/prod)
pnpm db:seed # Populate sample data
pnpm db:studio # Prisma GUI at localhost:5555
pnpm db:reset # Drop + recreate + seed (dev)
Architecture
Overview
Internal purchase order management system for a maritime company. Full-stack Next.js 15 App Router app with Prisma + PostgreSQL, NextAuth v5 credentials auth, and Tailwind CSS v4.
Key design decisions:
- Server Components for all data-fetching pages; Client Components only where interactivity is needed
- Server Actions for all mutations (form submissions, approvals, etc.)
- Prisma
Decimalfields cannot be passed directly to Client Components — convert withNumber()in the Server Component before passing as props (seepo-detail.tsx→lineItemsForEditorpattern) - File storage toggles automatically: Cloudflare R2 in production,
.dev-uploads/directory in development - Email toggles automatically: Resend in production, console log in development
PO Lifecycle (State Machine)
lib/po-state-machine.ts enforces all status transitions. The canonical flow:
DRAFT → SUBMITTED → MGR_REVIEW → MGR_APPROVED → SENT_FOR_PAYMENT → PAID_DELIVERED → CLOSED
↓↑
EDITS_REQUESTED / REJECTED / VENDOR_ID_PENDING
Every status change is validated against the state machine and recorded as a POAction row (audit trail).
Role-Based Permissions
lib/permissions.ts defines hasPermission(role, permission) and requirePermission(role, permission). Roles: TECHNICAL, MANNING, ACCOUNTS, MANAGER, SUPERUSER, AUDITOR, ADMIN.
Pattern: Server Actions call requirePermission() at the top before any DB write.
Key Directories
app/(portal)/— All authenticated pages (portal layout with sidebar)app/api/po/[id]/export/— PDF and XLSX export endpointlib/validations/po.ts— Zod schemas for PO forms; exportsTC_FIXED_LINEandTC_DEFAULTSlib/po-state-machine.ts— All valid status transitions with required roleslib/notifier.ts— Email dispatch (Resend in prod, console in dev)lib/storage.ts— File upload/download (R2 in prod, local in dev)components/po/— PO-specific components (line items editor, status badge, etc.)tests/integration/helpers.ts—makeSession(),makePoForm(),fd()for integration test setup
GST Calculation
totalAmount = sum(quantity × unitPrice × (1 + gstRate)) for each line item. The gstRate is stored as a decimal on POLineItem (e.g., 0.18 = 18%). This applies in Server Actions when computing totalPrice per line and the PO totalAmount.
Environment Variables
NEXTAUTH_SECRET # Required always
NEXTAUTH_URL # Required always (e.g., http://localhost:3000)
DATABASE_URL # PostgreSQL connection string
# Optional in dev (defaults to local storage + console email):
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET_NAME, R2_PUBLIC_URL
RESEND_API_KEY, EMAIL_FROM, EMAIL_FROM_NAME