Step 1: Design the customer page
Marco's shops need to see their own orders and nothing else. The customer portal page shows:
- A header identifying the customer (shop name)
- An order list with status (confirmed, in production, shipped, delivered)
- For each order, the items with batch provenance -- which batch the bars came from and which farm supplied the cacao
They should not see: other customers' orders, Marco's internal inventory, pricing data beyond their own orders, or batch details like quality scores.
This is a server-rendered page. The data is customer-specific and changes with every order -- static generation doesn't work here because the data would be stale the moment an order status changes. Server-side rendering fetches the current data on each request.
Step 2: Build the customer order page
Direct Claude to build the customer page. It fetches from GET /customers/:id/orders and displays the order list with provenance information.
Specify the constraints before Claude generates the page:
- Server component (data fetch happens on the server)
- Fetches only the authenticated customer's data
- Displays batch and farm provenance for each item
- No links to other customers' data or admin views
AI generates pages that work functionally but may expose data the customer shouldn't see -- like a link to the full order list or a navigation item pointing to the admin dashboard. Review the generated page for any data or navigation that leaks outside the customer's scope.
Step 3: Build the traceability view
This is Marco's admin view, not the customer portal. When Marco clicks a batch in his dashboard, he sees the full trace chain:
- The farm that supplied the cacao
- Other batches from the same farm
- All orders that include items from this batch
- All customers who received those orders
This is the Munich incident workflow. Marco sees the batch, finds Finca Rosario, sees that Munich, Portland, and Tokyo all received bars from the same batch. Within minutes, not hours.
Direct Claude to build the traceability view using the GET /batches/:id/orders and GET /farms/:id/batches endpoints. The trace should work in both directions.
Step 4: Configure CORS
If the customer portal runs on a different subdomain from the admin dashboard, the browser blocks cross-origin requests by default. CORS must explicitly allow the customer portal's domain.
Review the CORS configuration. It should allow:
- The customer portal subdomain
- The admin dashboard origin (same as before)
It should not allow all origins. AI commonly generates CORS middleware with origin: '*' -- that opens the API to any domain. Restrict it to the specific domains that need access.
Step 5: Test the frontend
Load the customer page for one of the seed data customers. Verify:
- The page shows only that customer's orders
- Each order shows items with batch provenance (batch code, farm name)
- No other customer's data is visible
- No admin navigation or internal data leaks through
Navigate the traceability view for batch 2024-H2-03 (the Munich incident batch). Verify the trace shows Finca Rosario, the products from this batch, the orders, and the customers (Munich, Tokyo).
Run the Performance profiler on the traceability view. The JOIN-heavy data fetch behind this page might create long tasks on the main thread. Check whether the page interaction feels responsive.
Open the chat with Marco. Send him a screenshot or link to the customer portal. Marco responds: he wants to know if the shops can see where their bars came from. "My customers in Japan care about provenance. If they can see the farm name, that's good." The provenance information is already in the display -- point Marco to it.
Check: Load the customer page for a specific customer. Does it show only their orders? Navigate the traceability view for the Munich batch. Does the trace go from batch to farm to all affected orders?