BisbeCustomers

CRM

How this is built

The stack, how the parts fit together, and how access is controlled — in plain English.

The stack

Language
TypeScript, end to end — the screens, the server code and the checks are all one language. A change that breaks a promise between two of them stops at the compiler rather than in front of a rep.
Screens
React, rendered on the server by Next.js, so a screen is up when a thumb lands on it rather than still deciding. Nothing on this app writes from the browser.
Server
Node.js. Every save runs on the server as an action belonging to the screen that owns it, and each one checks who is asking again before it does anything — not only on the way in.
Records
PostgreSQL, hosted on Supabase, reached with the same AIU sign-in as the rest of the suite. Who may read or write a row is decided by the database itself, row by row, rather than by the screen that happens to be asking.
Hosting
Vercel.
What gets checked
Around 800 automated checks run before anything about this app ships. Separately, every screen is photographed and read back, so a claim about what is on a screen is measured rather than remembered.
Look and feel
The shared AIU design system — the same header, type, theme and material as every other AIU app, so the suite reads as one product.

Screens read through one layer

No screen talks to an outside system directly. Everything goes through one typed layer, and six services sit behind it — stock, orders, shipping, direct sales, text messages and email.

Every screen reads the company’s records out of the database, under the permissions of whoever is signed in. The company is invented — that part has not changed — but its records are ordinary rows read the ordinary way, not a sample file switched on for a viewing and not a copy held in the browser. An account without the invitation reads nothing at all, and that refusal comes from the database rather than from the screen deciding to be careful.

Two of the six services — stock and orders — answer for real against those records. The other four are declared, named on this page, and refuse when called rather than returning plausible-looking numbers. Pointing one at a real provider later is a new implementation of the same contract plus a setting on the account. The screens do not change, and neither does anything a rep sees.

When the app drafts something — an order read out of a text message, a follow-up after a visit — it never sends it. It queues it as a suggestion, you read it, and you approve it with one tap. The suggestion and your decision on it are both saved, together with the handle that undoes what was done.

Screens, a few endpoints, and one contract

Settings shows which services are wired to your own account.

From the outside this app is its screens plus a small number of endpoints that require a signed-in, invited account — saving a visit, acting on the drafts a visit produces, and rebuilding the sample company. Everything else that writes does so through an action belonging to the screen that owns it, each one re-checking the account before it runs.

The integration contract is the layer above that: six services, the operations each one exposes, and a button that sends a real request to any of them while you watch.

Every action saves

A save that reports success and then is not there next time is the failure this closes.

  • A visit write-up, approving or dismissing something in the queue, ticking a to-do, putting an outreach draft in front of someone, and adding a new contact — each one writes a record through the permissions of the person who pressed the button, decided row by row by the database. An account without the invitation can no more write than it can read.
  • Placing an order is the one step that does not work that way, and it is worth being exact about. The records behind the order service are not reachable from a browser at all, so that write runs inside the server-side layer instead — which checks who is asking, and refuses if they were never invited, before it writes anything. Building a quote is not itself a save: it becomes an order, and the order is the record that gets written.
  • Several of those used to be held in the running server’s own memory or in one browser tab. So a visit saved on one request could be gone on the next, and approving a suggestion then reloading put it back. That holding pattern is deleted rather than patched.
  • Pressing save twice does not make two visits or two orders. The second attempt lands on the first one’s record and returns it, which matters most on the one action that turns into money.
  • An action that changed nothing says so. A to-do switch that matched no record reports the refusal rather than reporting success and leaving the screen to imply otherwise.

Stock is live, not a number typed in last week

The one thing a notebook cannot do, and the reason the app is worth opening.

  1. 1Building a quote reads what is actually available, piece by piece.
  2. 2Approving the order holds those pieces.
  3. 3Shipping takes them off the books.
  4. 4Cancelling puts them back.

So the number on the screen is the number in the room, and a quote cannot promise stock that is already spoken for.