Catalogue and cart
Six categories, facet filters, product images held in the browser, and a cart that survives a reload and a closed tab.
Two surfaces over one store: catalogue, cart and checkout for the customer, and products, stock, orders and discounts for whoever runs it. It seeds itself the moment you open it, so there is no account to make and nothing to wait for.
Opens storefront.taiphamsolutions.com in a new tab. No account, no signup, and nothing you do in it leaves your browser.
The storefront is the shopping half: six categories, facet filters, product images, and a cart that survives a reload. The back office is the other half of the same database, reached through a persona switch in the menu.
Checkout is the part that had to be built properly. It runs as one IndexedDB transaction that re-reads stock, refuses to oversell, numbers the order from a counter, writes an inventory ledger entry, and empties the cart. Race two tabs for the last unit and exactly one wins. The loser rolls back completely, order number included.
Stock is a denormalised count with a ledger behind it that explains every movement, and the inventory page sums the ledger and raises a banner if the two ever disagree. Orders snapshot title, SKU and unit price at the moment of sale, so editing a product later cannot rewrite what someone was charged.
Open the back office in a second tab, change a price, and watch the storefront tab repaint without a reload.
24 products across 6 categories, 10 customers, and about 90 days of order history, seeded on first open.
Six categories, facet filters, product images held in the browser, and a cart that survives a reload and a closed tab.
Products and variants, stock, orders and fulfilment, customers, discount codes, and the inventory ledger that accounts for the count.
A write on one surface reaches the other with no reload, no websocket of our own, and no cache to invalidate.
Export the whole database, product images included, as one JSON file, and restore it in a single all-or-nothing transaction.
Every step below happens inside the demo. There is no point in it where the work leaves the browser and comes back.
Categories and facets narrow the catalogue. Prices, stock and images all come from the same records the staff side edits.
Lines are held against variants rather than titles, so a rename upstream cannot silently change what is in the basket.
Stock is re-read, an oversell is refused, the order takes the next number from a counter, a ledger entry is written, and the cart empties. All of it or none of it.
It appears in the back office with its lines snapshotted, and moves through fulfilment from there.
The inventory page sums the ledger against the stored count and says so if they have drifted, rather than quietly trusting the number.
There is no server behind any of this, which decides both columns. The left one is what that constraint made worth building properly. The right one is what it rules out.
The write-up sells harder than the application does, so it is on the page rather than in a repository nobody can open.
A demo exists to prove something on the offer list rather than to be impressive on its own. These are the ones this one argues for.
Each one is a different kind of problem, and all of them are in the Demos menu at the top of the page.
This one was built to a constraint nobody would choose. Yours would have a server, real accounts, and the parts of the domain that are actually hard. Tell us what the software has to do and we will tell you what it takes.
Bring the messy version of the problem. That is the conversation we enjoy most.