Cacao Ixchel -- Inventory & Order System
Client: Marco Aju, Cacao Ixchel (Coban, Guatemala) What you're building: A full-stack inventory and order management system for a small bean-to-bar chocolate maker. Marco sources cacao from six farms, processes it into single-origin chocolate bars, and sells to about thirty specialty shops in the US, Germany, and Japan. The system tracks inventory from bean to bar, manages orders with allocation to prevent double-selling, and provides a simple interface for workshop staff.
Tech stack
- Next.js with App Router (TypeScript)
- PostgreSQL (relational database)
- Prisma (ORM and migrations)
- React Testing Library + Vitest (testing)
- Tailwind CSS (styling)
- Sentry (error tracking)
- Vercel (deployment)
File structure
src/
app/
page.tsx # Landing/inventory dashboard (server component)
orders/
page.tsx # Order status list (server component)
new/
page.tsx # Order creation form (client component)
api/
inventory/
route.ts # GET all inventory, filterable by stage/farm
[id]/
route.ts # GET single item, PUT update stage
orders/
route.ts # GET all orders, POST create order
[id]/
status/
route.ts # PUT update order status
components/ # Shared React components
lib/
db.ts # Database connection
validators.ts # Input validation functions
prisma/
schema.prisma # Database schema
migrations/ # Migration files
seed.ts # Seed data script
materials/
first-contact.md # Marco's initial email
api-contract.md # REST API contract specification
seed-data.json # Seed data for development
templates/ # Pipeline templates (PRD, design decisions, tickets)
public/
Tickets
- T1: Project setup -- Initialize Next.js with App Router, TypeScript, Tailwind CSS. Set up PostgreSQL and Prisma. Install dependencies. Acceptance:
npm run devstarts without errors, Prisma connects to the database. - T2: Database schema -- Create tables for farms, batches, products, orders with foreign keys. Write Prisma schema and run initial migration. Acceptance: migration applies cleanly, tables exist with correct columns and relationships.
- T3: Seed data -- Populate the database with six farms, batches at various stages, products, and sample orders. Acceptance:
npx prisma db seedruns without errors, query for available products returns results. - T4: Inventory API routes -- GET /api/inventory (list, filterable), GET /api/inventory/:id (with traceability), PUT /api/inventory/:id (stage update). Acceptance: routes return correct status codes (200, 400, 404), input validation rejects invalid data.
- T5: Order API routes -- POST /api/orders (with allocation, prevents double-selling), GET /api/orders (list with status), PUT /api/orders/:id/status. Acceptance: creating an order for already-allocated product returns 409, whitespace-only customer name returns 400.
- T6: Inventory dashboard -- Server component showing inventory by stage and farm. Acceptance: page renders with data from the database, shows available vs allocated counts.
- T7: Order form -- Client component with validation, product selection showing available stock. Acceptance: form validates required fields, submit creates order via API, inventory updates.
- T8: Order status page -- Mixed server/client component showing order list with status updates. Acceptance: page shows all orders, status dropdown updates via API.
Verification targets
- Vitest: all tests passing
- React Testing Library: component tests use role-based queries (getByRole, getByLabelText), not CSS selectors
- CSP: Content-Security-Policy header present without
unsafe-inlineorunsafe-eval - CORS: configured for application origin only
- CSRF: tokens present on forms that modify data
- Lighthouse: all categories >= 90
- Sentry: connected, no unhandled errors in production
- Structured logging: JSON entries with request IDs, log levels, contextual fields
- No hydration mismatch warnings in browser console
Commit convention
Conventional commits, one ticket per commit. Format: feat(T2): create database schema with farm-batch-product-order relationships