TLDR: If the people in the meeting make decisions per PO, your dashboard row has to be a PO. Grouping by product looks tidier. It's lying.
The Setup
I've been building a supply-chain dashboard for an ecommerce business (the business my client and my mentor run) anchored around their weekly L10 — their Level-10 leadership meeting where my mentor interrogates inventory every single time.
One of the key cards on the dashboard is "Orders in Progress" — what purchase orders are open, how many units are incoming, when do they land.
Sounds simple.
What I Built First
I aggregated.
getOpenPOsByProduct() — a Map keyed by productId. Every open PO line for a given product collapsed into one summed row. One number for quantity. One ETA.
Fewer rows. Cleaner. Tidier.
I liked it.
The Problem (Which I Caused Entirely)
my mentor has two open POs for 500mL bottles.
One lands in 12 days. One lands in 38 days.
My card showed him one row: "1,200 units, ETA in 25 days."
That's technically not wrong. It's also completely useless to him.
His weekly questions — I mined 49 L10 transcripts to find this — run in almost exactly the same order every meeting: how many do we have → when's more coming → are we going to run out → did the PO go in → should we reorder. He's asking about specific POs. "Did that PO go in?" "When does that one land?"
I had collapsed exactly the thing he was trying to interrogate.
The Fix
My own screenshot annotation from the call told me plainly: "split out ALL POs by PO, do not aggregate the results by product canonicals."
Renamed getOpenPOsByProduct() to getOpenPOLines() in src/queries/purchase-orders.ts. Swapped the type from OpenPOByProduct to OpenPOLine.
New row shape:
poId,poNumber,productId,name,sku,quantity,eta,daysUntil
No Map. No collapsing. One row per open PO line item.
Two POs for the same product → two rows, each with its own quantity and ETA.
The render (orders-in-progress.tsx) keys on ${poId}-${productId}. PO number shows inline as a dimmed · PO-0042 suffix after the product name so same-product rows are immediately distinguishable at a glance.
The header total still sums across everything. That part was right.
Shipped to main at 3c03c13, tsc clean, autodeploys.
Why This One Sticks With Me
I collapsed the rows because tidier felt like progress.
It wasn't. It was just fewer pixels.
The rule I keep coming back to: match the granularity of the row to the granularity of the decision. Before you group things by a shared key, ask — do the things I'm about to collapse carry independent attributes someone actually acts on?
my mentor acts on individual POs. Not on the product they contain.
Aggregating destroyed exactly the signal the meeting runs on.
This same logic showed up that same week when we split is_reportable and is_orderable into two separate flags — one field was doing two different jobs and hiding one of them. Same smell, different layer.
When you find yourself grouping "to keep it clean," ask who's reading it and what they're deciding. Clean to you is noise to them if you've already collapsed the thing they're trying to see.
P.S. If you're building for a recurring leadership meeting, mine the transcripts first. 49 L10s told me exactly what my mentor asks every single time. The feature list basically wrote itself.