Every dashboard project I have taken on was described to me as a front end problem. Charts, filters, a date range picker, maybe a CSV export. Then you open the codebase and find out the charts were never the problem.
I work as a fractional CTO on a React, TypeScript and GraphQL app at Bettershop Consulting that puts an admin dashboard and analytics in front of Amazon sellers. On paper it is plumbing. In practice it is one of the least forgiving things I have built, because a seller reads a number on that screen and then reprices a product, kills an ad campaign, or spends real money on inventory.
That changes the bar completely. A pretty chart that is subtly stale or quietly double counts a return is worse than no chart, because it launders a bad number into a confident decision. Nothing crashes. Nobody files a bug. The seller just loses money slowly and never connects it to your software.
Here is what building analytics for people who act on it taught me, and why I now believe most dashboard work is backend work wearing a front end costume.
The product is an opinion, not a viewer
Amazon hands sellers a firehose across a spread of surfaces: orders, returns, fees, advertising, inventory, settlements. The raw material is all there and almost none of it is decision ready.
A seller does not want another table of rows. They want the two or three numbers that tell them what to do this week. Which products are actually profitable after every fee. Which ad spend is buying real orders. What is about to stock out.
So the product is not a viewer, it is an opinion. Its job is to take a mess of source data and turn it into a small number of trustworthy answers. Once you accept that framing, the center of gravity moves out of the front end entirely and into the layer that ingests, reconciles and models the data. By the time a chart renders, all the hard thinking should already be finished.
GraphQL earns its place here, but not for the reason people cite
Reaching for GraphQL is often cargo cult. On this app it earns its keep for a concrete reason: what the client needs varies wildly per screen, and the underlying data is deeply relational.
A profitability view needs products joined to orders joined to fees joined to returns. An advertising view needs campaigns joined to keywords joined to attributed sales. An inventory view needs stock joined to velocity joined to inbound shipments. With a pile of REST endpoints you either over fetch, dragging whole objects across the wire to use one field, or you sprout a bespoke endpoint per screen until the API is a graveyard of one off routes. GraphQL lets each screen ask for exactly the graph it needs in one round trip, and the type system doubles as a contract the TypeScript front end can trust.
But the query language is not the real win. The real win is the discipline it forces on you. A schema makes you name your domain precisely. What is a product. What is an order line versus an order. What exactly is profit, and which fees live inside it. Writing that schema is writing down the business, and getting it right is most of the battle. A sloppy schema produces a sloppy dashboard no matter how good the React looks on top.
One definition of profit, everywhere
The single most important rule on this project: a number means the same thing on every screen.
Profit on the overview card, profit in the product detail view and profit in an exported report have to be the identical calculation. Not three implementations that agree today and drift apart the first time someone adds a fee type. Computed once, surfaced everywhere.
This is the same instinct I carry from leading a sports tech platform, where a leaderboard position is one authoritative object rendered on three different screens. Here it is one authoritative margin calculation surfaced in a dozen places. The failure mode is identical too: the moment two views disagree about a number, the user stops trusting all of them, and a dashboard nobody trusts is worse than no dashboard because you still pay to run it.
The hard parts hide in the data, not the UI
Charts are the easy half. The work that decides whether this product is any good lives underneath, in reconciliation nobody ever demos.
- Fees are a maze. Amazon's fee structure is layered and it changes. A profit number that ignores a referral fee, an FBA fee, a storage fee or a return processing cost is not slightly off, it is telling a seller that a losing product is a winner.
- Returns arrive late. A sale looks profitable until the return lands weeks later. If you book revenue and forget to claw back returns, the picture is systematically rosier than reality. A sale has to be modeled as provisional until the return window resolves.
- Currencies and marketplaces multiply. A seller across several Amazon marketplaces has data in different currencies under different fee regimes. Rolling that into one comparable view without silently mixing units is fiddly, exact work.
- Time zones move the numbers. "Today's sales" depends on whose midnight you mean. Pick the wrong boundary and every daily figure is quietly shifted. That bug survives for months because nothing breaks, the numbers are just wrong.
None of this shows up in a screenshot. All of it decides whether the dashboard can be trusted.
Making a data heavy front end feel instant
Once the data is honest, the UI still has to be fast, because a seller checking numbers between other tasks will not sit through a spinner.
The big trap is the classic N+1 hiding behind a clean looking GraphQL request. Ask for a hundred products and each product's fees, and with naive resolvers that becomes a hundred and one database trips. Batching at the data loader level and careful resolver design is what keeps a rich screen from being a slow one. This is the tax GraphQL charges for the flexibility it gives you, and it is worth paying, but you do have to pay it deliberately.
The client side has its own version of the same discipline. Cache aggressively. Fetch the shape a screen needs rather than everything. Never let one expensive widget block the whole view from painting.
Never render a confident number you are not sure about
The honesty rule carries into the UI. A dashboard should not show a final looking number while it is still loading or partially failed.
If one data source is lagging, the correct move is to say so on that tile. Not to render a plausible zero. A blank that admits "still loading" is safer than a figure that looks settled and is not, because the user cannot tell the difference and will act on either. I hold AR overlays and voice agents to the same standard: signal uncertainty, never fake confidence. The systems that get people in trouble are not the ones that fail loudly, they are the ones that fail while looking fine.
What a fractional CTO actually adds here
Bettershop already had a capable team and a real product. Stepping in on something like that is not about writing the most code. It is about protecting the few decisions that are expensive to get wrong.
On a seller analytics app those decisions are almost entirely about data integrity. The definition of profit. How returns and fees are handled. Where the single source of truth lives. What the app is allowed to display when it is unsure. My most valuable contribution most weeks is insisting those ledger level questions get answered precisely before anyone argues about chart colors.
What I would tell past me
- The dashboard is the data model wearing a chart. Get the schema and the reconciliation right and the UI is easy. Get them wrong and no amount of front end polish saves it.
- One number, one definition, computed once. Otherwise trust erodes across the whole product, not just the screen that is wrong.
- Model the messy reality up front. Late returns, layered fees, multiple currencies and time zone boundaries are the product. They are not edge cases to bolt on in v2.
- Signal uncertainty. A tile that admits it is degraded beats a confident number that is quietly wrong.
Software people act on has to be honest before it is beautiful, and honest is much harder. That is the whole job on a seller analytics dashboard, and it is the standard I hold the AI systems I build now to as well.
If you want more of how I think about this kind of work, I write about it at nabeelbaghoor.com.
Top comments (0)