ReactTypeScriptViteGraphQLSupabaseWeb Serial APIRadix UIDockernginxKubernetesClaude Code26 min read
How I replaced a legacy warehouse tool by designing, building, deploying, and supporting an Arabic operational product end to end: collector trips, a live platform scale in the browser, photo evidence for every weighing, and a staged move from Supabase into the company's core API.
Written by
Ahmed Amr
Role
Product Designer and Full-stack Product Builder
Year
2026
In this article28
Loading case study
At the entrance to a warehouse, a van or truck drives onto a platform scale.
The number on that scale is not a decorative metric. It is the beginning of a
chain of operational facts: how much used cooking oil arrived, how much should
enter inventory, what needs to be inspected, and what the company can later
move or export.
The old Warehouse Center was making that chain difficult to trust. It had been
started roughly two years earlier, had accumulated reliability and
implementation problems, and was difficult for a new frontend engineer to
continue. When the company no longer had a frontend engineer, I proposed a
different path: rebuild the product myself.
I worked with a product manager who understood the operation on the ground. I
owned the product from workflow and interface design through frontend,
backend, database, API, hardware integration, deployment, and support. The
result replaced the old application and became the Warehouse Center used in
the operation.
The project tested whether a product designer with a computer-science
background, an established design system, and an AI-assisted development
workflow could take responsibility for a consequential internal product. The
responsibility was not only to prototype it, but to connect it to real data
and physical hardware, ship it, deploy it, and support it in daily use.
Role
Product Designer and Full-stack Product Builder
Scope
Operational workflow modelling, Product and interaction design, Frontend implementation, Backend, data modelling, and server-side functions, Core-system API integration, Platform-scale and camera integration, Laboratory quality workflows, Deployment and production support
Team
Owned end to end, in close collaboration with a product manager and the engineers responsible for the core API and infrastructure
Synthetic records; the interface is the production screen.
The warehouse was a hub, not a room with inventory
Tagaddod collects used cooking oil from households and businesses such as
restaurants. Collectors bring those loads to a warehouse, but receiving the oil
is only one part of what happens there.
Warehouse teams need to know which collector trips are running, which vehicles
are expected to arrive, and what quantity each trip is likely to return. Some
B2C trips begin with a warehouse visit because the collector needs to pick up
the gifts exchanged with household customers. Warehouses also send and receive
oil between one another and ship it to port. Quality teams inspect samples
before material moves towards export, while managers need a dependable view of
inventory and custody.
The warehouse therefore sits between several worlds:
customer and field-collection activity;
vehicles, containers, tanks, and physical measurements;
receiving and quality decisions;
inventory, inter-warehouse transfers, and port shipments;
the company's existing operational and database systems.
A failure in the interface does not remain inside the interface. It can delay a
vehicle, create doubt about a received quantity, or make the recorded inventory
disagree with what is physically present.
01
Field operationWhat is expected to arrive
Collector trips
B2B and B2C collections
Gift pickup
Vehicle and route status
02
ReceivingWhat physically arrived
Trip lookup
Manual verification
First weight
Unload
Second weight
Photo evidence
03
Quality and custodyWhether the material can move forward
Samples
Laboratory results
Tanks
Permits and records
04
Inventory and movementWhat the company can account for
Warehouse balance
Received quantity
Transfers
Port shipments
Movement history
05
Connected systemsWhere the facts come from and go
Core GraphQL API
Supabase for what the core does not hold
Platform scale over Web Serial
Weighbridge camera
Email and cron
The product had to preserve one operational truth as oil moved from a collector's trip into warehouse inventory and onward.
Why I chose to rebuild it
The previous Warehouse Center had already consumed significant effort. That
alone was not a reason to preserve it.
Its foundation made new work difficult. A frontend engineer who joined later
encountered the same problems while trying to continue the application. When
that engineer left for another opportunity, the company had no frontend person
available to take on another rebuild.
Waiting would have kept a critical operation dependent on the old system.
Adding another layer to the same foundation would have moved the immediate
deadline while preserving the underlying risk.
I had graduated from computer science and had already built experience with
AI-assisted coding. I was not claiming the depth of a senior engineer across
every part of the stack. I did, however, understand how software systems fit
together, and I could combine that understanding with product design,
knowledge of our design system, and direct access to the operational problem.
The question was whether that combination could close the gap responsibly.
Continue the legacy product
Each new requirement inherited implementation problems from an application that another frontend engineer had already found difficult to extend.
Rebuild around the operation
The product model, receiving states, data boundaries, and interface could be defined together, then connected deliberately to the company's existing systems.
The rebuild cost more responsibility up front, but it gave the operation a foundation I could understand and support end to end.
I started with the operation, not the old screens
The product manager became my closest source of operational context. He knew
how the work happened on the ground: which teams touched a trip, when a vehicle
arrived, which quantities mattered, and where exceptions appeared.
We translated that knowledge into jobs rather than pages.
For a warehouse manager, the job was to find an arriving trip, compare the
expected and reported quantities, verify what actually arrived, and close the
receipt with confidence.
For a quality specialist, the job was to connect a sample or inspection to the
right material and decide whether it met the required standard.
For an inventory controller, the job was to account for containers and
material as they entered, moved through, or left the warehouse.
This gave me a small set of stable objects: warehouse, trip, request, load,
scale record, quality record, tank, transfer, port shipment, sediment request,
and custody permit. The screens could change. Those relationships had to
remain coherent.
01
Send and receiveMaterial arriving and leaving
Collector trips
Inter-warehouse transfers
Port shipments
Scale workflow
Manual confirmation
02
QualityWhether the material meets the standard
Trips
Transfer requests
Port shipments
Sediments
Tanks
03
CustodyWho holds what
Transfer permits
Permit tables
Request creation
04
NotificationsWhat leaves the app
Transfer emails
Shipment reminders
Sediment emails
Daily stakeholder digest
The product as shipped, grouped by the job each area serves. Every area reads and writes the same objects.
A receiving screen had to answer two questions at once
When a vehicle arrives, the warehouse worker needs to answer both "Is this the
right trip?" and "What should happen next?"
I used a master-detail structure for that reason. A list of trips stays
visible on one side, with a filter drawer for date, channel, and trip status, a
sort control, and a search by trip identifier scoped to the worker's own
warehouse. Each card carries two statuses: where the trip is in the field
workflow, and where the warehouse is in receiving it. Selecting a trip opens
its context without sending the worker into a separate part of the product.
The detail view brings together the collector, vehicle, channels, dates, and
the quantities that matter: what the requests expected, and what the warehouse
has received. Its tabs are overview, receiving, orders, and logs. The receiving
tab only appears while there is something to receive, which means a trip that
has started or ended. Once a trip is received, its history lives in the logs.
This was more than a layout preference. Warehouse work is interruptible. A
worker may need to check another arriving vehicle, compare two statuses, or
return to a receipt after unloading. Preserving the list and the current
context reduces the cost of those interruptions.
Not every load should be received in the same way
There are two receiving cases. They share the same trip context, quantity
summary, final status, and log, but they establish the received quantity in
different ways.
The rule that chooses between them comes from two facts, neither of which the
interface invents. The core API classifies each channel of a trip as a scale
flow or a default flow. The warehouse record says whether that warehouse has a
scale. The client shows the scale workflow only when both agree. A trip at a
warehouse without a scale falls back to manual confirmation even when the API
would otherwise ask for weighing, and while a trip is still running only the
scale-capable channels are surfaced, because a manual flow has nothing to do
before the truck is back.
Case 01: manual confirmation
The manual path is a quantity verification, not a manually typed scale
reading. When the trip carries an expected quantity, the form shows it
read-only and asks one question: is the sent quantity correct?
Answering yes receives that amount at once. Answering no opens an editable
field so the worker can enter what actually arrived. The value must be above
zero, and it is rounded to whole kilograms because the core system stores an
integer.
Completing the action creates the load in the core system, moves the quantity
from the collector to the warehouse, records who completed it and when, and
leaves no scale record because no weighing took place.
Case 02: digital platform scale
The scale path is used when measurement must be verified by the warehouse's
connected platform scale. A loaded vehicle is weighed, the oil is unloaded,
and the vehicle is weighed again. The difference produces the net received
amount. If new empty containers were issued during the trip, their weight also
needs to be removed from the calculation.
Placing both processes behind one generic "Receive" button would have hidden
the rules that make each one trustworthy. I kept one shared entry point and
gave each verification method its own explicit flow.
One receiving area, two verification paths
Chose
Route each channel of a trip into either manual confirmation or a three-step scale workflow, decided by the API's flow classification and the warehouse's scale flag, while preserving a shared trip context and log.
Trade-off
The product carries more states and conditional logic than a single form, but the interface mirrors how the quantity is actually verified instead of asking the worker to translate the operation into a generic input.
Keeping the decision on the server and the warehouse record, rather than in
the interface, meant a second warehouse with a different setup needed no new
screens.
The scale turned a form into a state machine
The first version of the interface simulated the scale. That let me design the
full sequence, exercise failure states, and test the calculation before the
product depended on a physical device. The simulation is still there, behind
an environment flag, so anyone can run the workflow without hardware.
The flow became a small state machine:
The scale is disconnected, connecting, or connected. A badge shows which,
and the connection is monitored so a cable pulled mid-weighing is reported
and re-acquired when it comes back.
Each load runs through idle, weighing, and captured. Only one load can hold
the scale at a time, but the worker can queue the next load while the
previous one is mid-flow.
The worker captures the first weight while the loaded vehicle is on the
scale. If the trip is still running, the core system records that weight
as a pre-trip reading.
The worker captures the second weight as soon as the vehicle is back and
unloaded. An earlier version of the client waited for the trip to be
formally ended first. Operators asked for that gate to go, and it went.
The confirmation step shows the calculation, not only its result: the
difference between the two weights, less any new empty containers issued
during the trip. The empties deduction is only offered when the first
weight was pre-trip, because that is the only case in which the core
system accepts it. If the two readings are identical, the server refuses,
and the interface shows the server's own reason rather than a generic one.
The worker receives the trip, and the quantity enters inventory.
Every step needed a visible status, an actionable error, and a route back to a
valid state. "Scale not connected" had to tell the worker to check the device
and reconnect. A rejected weight had to say why. The confirmation step had to
show the arithmetic.
01
ConnectEstablish a trustworthy reading
Disconnected
Connecting
Connected
Replug detected
Reconnect
02
LoadOne load holds the scale at a time
Idle
Weighing
Captured
Next load queued
03
First weightLoaded vehicle
Live value
Capture
Pre-trip flag
Photo evidence
04
Second weightVehicle after unloading
Live value
Capture
Server validation
Photo evidence
05
Confirm and receiveTurn measurement into inventory
Gross difference
New empties
Net quantity
Receive trip
Log
The interface makes the physical sequence explicit, and leaves the rules that decide validity with the core system.
Synthetic records; the interface is the production screen.
Reading a scale from a browser
A convincing simulated reading was not the goal. The production product had to
read the actual platform scale.
I integrated the device through the browser's serial-port interface. The
warehouse PC runs Windows, and the scale's indicator reaches it over a serial
cable through a USB adapter. When the worker connects, the browser offers the
ports that match a short list of common adapter makers, opens the chosen one at
the scale's speed and framing, and starts reading. The scale sends a fixed
eight-byte frame that begins with an equals sign and carries its digits in
reverse order. The reader aligns on that byte, decodes the number, and puts it
on screen. When a vehicle moves onto the scale, the number in the active
receipt changes.
That integration changed how I thought about "frontend" work. The interface was
not displaying a value that already existed in a database. It was
participating in the creation of an operational record from a physical event.
It also created a new class of failure. The browser may not have permission to
use the port. The cable or device may be unavailable. A reading can change
while the vehicle settles. The trip can be in the wrong state even when the
hardware is working perfectly.
The product needed to distinguish those problems. Treating all of them as
"something went wrong" would send warehouse workers back to verbal workarounds
and manual correction. And, as it turned out, the simulation could not tell me
which of those problems a real warehouse would produce first.
The day the scale read zero
The scale worked on my laptop. In the warehouse, on the Windows PC at the
weighbridge, the reading stayed at zero. The operators had found a workaround
of their own: unplug the adapter and move it to a different USB port until the
number came back. That is not a workaround a product should depend on.
I could not reproduce it remotely, so I went to the site, pulled a development
build onto the warehouse PC, and worked next to the scale with a loaded vehicle
on it. The problems came out one at a time.
The scale was silent. The laptop's driver had been raising two control signals
on the serial port automatically, and the Windows driver did not. The scale
would not transmit until those signals were asserted explicitly, so the
connection now asserts them after every open, and the read buffer was enlarged
at the same time.
Then the bytes arrived, but the reading was still zero. I added logging of the
raw bytes and could see the answer. The first read after opening the port
caught a partial frame, and the parser was slicing fixed eight-byte chunks from
that offset forever after, so every chunk was misaligned. The fix was to stop
trusting offsets and instead scan for the equals byte that begins each frame,
discarding whatever precedes it.
Then reconnecting failed. After a cable was unplugged and replugged, Windows
held the port for a while and refused to open it again. Opening now retries
with increasing delays. When every attempt fails, the browser is told to forget
the port, so the next connect asks the operating system for a fresh handle
instead of reusing a locked one. That is what removed the need to switch USB
ports.
Then two smaller things. The forget call had been declared inside a try block
and was unreachable from the catch, so it had never actually run. And a reset
that fired on every re-render was zeroing the display while a load was being
added, so the operator saw the weight drop for no reason.
By the end of the day the number on the warehouse PC matched the number on the
scale's own display, and it stayed there through a cable pull. The monitor
that watches for the device to come back and reconnects to it came out of the
same session.
Debug on the floor, not from the office
Chose
Take the development build to the warehouse and fix the serial integration next to the scale, with a vehicle on it.
Trade-off
It cost a day of the operation's attention and mine, but every one of those failures was invisible from a laptop, and guessing at them from logs would have taken weeks of round trips.
The assistant that wrote most of the serial code could not see that a signal
was not being asserted. A person standing next to a zero could. The
combination fixed it in a day.
Evidence for every weighing
A weight is a claim about a physical event. The workflow attaches evidence to
it in two ways, and neither can block the weight from being saved.
The first is manual. At each weighing step, the worker can take a photo from
the device in front of them. The file is size-checked and uploaded straight to
storage, and its address is kept with the scale record.
The second is automatic. A camera watches the platform, and a server-side
function asks it for a still at the moment each weight is captured. The camera
sits behind digest authentication and will not talk to a browser directly, so
the function performs the handshake itself, fetches the image, stores it, and
returns the address. The client calls that function and moves on.
Both paths are deliberately non-blocking. A camera failure never prevents a
weight entry from being saved; the record simply has no picture. The stills
appear later in the logs beside each weighing, where they can be opened at
full size.
Photos are evidence, not a gate
Chose
Capture a manual photo and an automatic camera still at every weight, stored beside the scale record, without ever making the weight wait for either.
Trade-off
Some weighings have no picture when the camera or the network fails, but the alternative was a receiving flow that stops for reasons the worker cannot fix while a vehicle is on the scale.
From Supabase to the core system, one module at a time
The new product needed data before every connection to the core system was
ready. The core API existed and already owned trips, but it did not yet expose
the operations the warehouse needed, and I did not want the interface to wait
on that.
So the product launched on Supabase. Its data model was quick to establish,
easy to inspect, and legible to an AI coding assistant. Trips were mirrored
from the core API into Supabase tables and read from there. Quality
submissions, tanks, and everything else lived in Supabase outright. That let
the warehouse start using the product while the deeper integration was
worked out with the engineers who own the core API.
Then it moved, module by module, as the core API grew the operations each one
needed. The first cutover took quality submissions and tanks. The second took
the trips themselves, with a full receiving pipeline of mutations for creating
loads, recording each scale step, and receiving the trip. The mirroring went
away with it. Inter-warehouse transfers followed, then port shipments, then
the daily digest.
Some things stay on Supabase by design. The raw laboratory measurements behind
each computed quality value, the photos and camera stills, the audit log,
notes, the sediments module, and the scheduled emails all live there, because
the core system has no place for them and they are not part of its truth.
The client for the core API is a small hand-written wrapper: one request
function that attaches the bearer token and the worker's language as headers,
and a normaliser that turns the server's enums and casing into what the
interface expects. In development a Vite proxy fronts the API. In production
nginx does the same, so the browser never has to negotiate CORS with a
different origin.
01
LaunchEverything on Supabase
Trips mirrored from the core API
Quality on Supabase
Tanks on Supabase
02
First cutoverQuality and tanks
Quality mutation
Tank inspections
Raw measurements stay
03
Second cutoverTrips and receiving
Trip list and detail
Create load
First, second, third scale
Receive trip
04
Later cutoversTransfers, shipments, digest
Send and receive lifecycle
Port shipments and returns
Digest reads the core API
05
Stays localWhat the core does not hold
Photos and stills
Audit log and notes
Sediments
Scheduled emails
Supabase was a staging ground, drained as the core API caught up. What it still holds is what the core system has no place for.
Use an intermediate data foundation without creating a second operation
Chose
Launch on Supabase, then move each module onto the company's core API as it gained the operations that module needed, leaving on Supabase only what the core has no place for.
Trade-off
The staged approach meant temporary data boundaries, a mirroring layer that later had to be removed, and several migrations instead of one, but it let product behaviour become concrete and reach the warehouse before core-system access determined every design decision.
The important boundary was ownership of truth. Supabase accelerated the build;
the API integration made the Warehouse Center part of the existing company
system rather than a separate warehouse ledger.
The rest of the product
The receiving flow is the spine of this story, but it was one of several
modules that shipped, and the others are where much of the daily use happens.
The laboratory form
Quality specialists test a sample by weighing an empty beaker, the beaker with
the sample, the beaker after boiling, and the beaker with the sediments that
remain, and by titrating with a measured volume of potassium hydroxide. The
form takes those raw measurements and computes free fatty acids, moisture, and
impurities as they are entered, with optional fields for sulphur, chlorine,
phosphorus, and unsaponifiable matter, and a classification of the sample.
The computed values go to the core API, which is the system of record for
quality. The raw measurements go to Supabase, so a result can always be traced
back to what was on the bench. Submission is deferred to an explicit action
rather than firing on every keystroke, and every submission is audit-logged.
The same form serves several sources through one switcher: collector trips,
transfer requests, port shipments, and sediments, with scrap ready to be
switched back on. Two changes came straight from the laboratory. The upper
limit on the parts-per-million fields was too low for real samples and was
raised, and the order of the classification options was reversed to match how
specialists read them.
Tanks
Each storage tank shows its latest inspection and a quality summary. Metrics
are weighted across the tested quantity on the server, and a card shows how
much is ready to ship. Inspections can be recorded for several tanks at once,
and a records tab lists past inspections by quarter.
Inter-warehouse transfers
A transfer has a sending side and a receiving side, and the same scale
workflow runs on both. Quality is split by viewer: the sender sees its
outbound loads, the receiver sees inbound, and each card's inspection badge is
derived from the loads that viewer is responsible for. One early fix made
requests visible to the receiving warehouse, which had only been able to see
what it sent.
Port shipments
Shipments to port carry sends and returns. A return can be redirected to a
different warehouse, and that warehouse then sees the return in its own
quality view. Reminder emails go out on the shipping date.
Sediments
Sediment requests from providers come with their own inspection form, filters,
and email notification. This module still runs on Supabase.
Custody permits
The custody area lists transfer permits in a table, with creation drawers for
transfers, port shipments, and sediment requests. Three further permit types
are shown as coming soon rather than hidden, so the shape of the area is
visible before it is complete.
Emails and the daily digest
Seven server-side functions handle what leaves the app: the camera capture,
transfer and shipment emails with their reminders, sediment emails, and a
daily stakeholder digest. The digest renders the same quantity and quality
chapters as an in-app preview, reads only the core API in two-week chunks to
stay under the CDN's gateway timeout, and is scheduled by a database cron at a
fixed local hour. That schedule is written in UTC, so it has to be re-pointed
at each daylight-saving switch, which the migration says in its first line so
the next person knows.
Shipping it, and the week the old bundle would not go away
Uploading a product to a warehouse is not the same as building one. The app is
built in a Docker stage and served by nginx from a second, smaller image. A
Jenkins pipeline running on a Kubernetes agent builds the image, pushes it,
applies the manifests, waits for the rollout to finish, and invalidates the CDN
cache. A colleague on the infrastructure side bootstrapped that pipeline on
launch day; I have maintained it since. Staging deploys from one branch and
production from another, so every merge to the main branch is a release.
The most instructive incident was not a bug in the product. After a deploy,
operators kept seeing the previous version. Their browsers had cached the entry
document and, in some cases, a service worker left behind by an earlier
mock-data tool was serving a stale bundle outright.
The fix was a set of cache rules rather than a code change. The entry document
and the version file are served with no-store, so a browser always asks for
them again. Hashed assets are served as immutable for a year, because their
names change when their contents do. The app unregisters any leftover service
worker at startup. And the pipeline now waits for the full rollout and
invalidates the CDN before reporting success, so nobody is told a deploy is
live while a stale copy is still being served.
Make the entry document uncacheable and everything else immutable
Chose
Serve the HTML shell and version file with no-store, serve hashed assets as immutable, and unregister stale service workers on startup.
Trade-off
Every visit re-fetches a small document that rarely changes, but the alternative was operators working on a version I had already replaced, with no way to tell.
The design system relationship, stated precisely
Warehouse Center was the first consequential test of the coded Tagaddod design
system and the AI workflow around it, and it is worth being exact about how
the two relate.
The product does not import the component library. It carries its own copy of
the token layer and implements its own components against it, built on Radix
primitives, so the colours, spacing, type, and reading direction are the design
system's while the component code is the product's. A trip card that combines
a collector, vehicle, channels, quantities, and two kinds of status, a scale
panel with live measurement states, and the custody and quality views were all
composed under the same token, accessibility, and RTL rules. That is a weaker
coupling than importing the library. It kept the product free to move quickly,
at the cost of re-adopting fixes by hand.
The feedback moved in both directions. The design system's tokens and rules
made the first version faster and more consistent. Warehouse Center exposed
the screen-level knowledge an AI agent still needed: how to compose
master-detail layouts, who owns scrolling, where filters live, how an RTL
navigation shell behaves, and what to do when a domain component does not
exist yet.
Those lessons informed the MCP, design guide, and agent skills that later made
the library easier for product managers, designers, and engineers to use.
A component preview
A library story could prove that a component rendered, changed theme, and supported RTL in isolation.
An operational proving ground
Warehouse Center tested whether those tokens and rules could hold up under live hardware, dense operational state, custom workflows, existing APIs, and daily Arabic use in one product.
The product did not simply consume the design system; it taught the system what production required.
What AI accelerated, and what remained my responsibility
Claude Code helped me move across a surface area that would normally be divided
between several people. It could turn a defined flow into React components,
connect data, write a server-side function, investigate errors, and help
iterate on unfamiliar integration work.
That did not remove the need for product or technical judgement. An assistant
could not decide which warehouse record was authoritative, whether the second
weight should be allowed before a trip ended, or whether a generated interface
matched the way people worked on the floor. It could not walk into the
warehouse and notice that a serial signal was not being asserted. The day the
scale read zero was fixed by an assistant and a person standing next to the
hardware, and neither would have managed it alone.
I used specifications and constraints to keep the work grounded. Component
documentation told the agent which APIs existed. Design tokens prevented local
visual invention. Arabic and RTL rules made direction part of implementation,
not a final flip. Product states and validation rules turned operational
knowledge into conditions the application could enforce.
Then I ran the product, tested the workflows, connected real systems, and fixed
what failed.
The same loop built infrastructure, not only screens. Late in the project the
app gained an offline mock environment: every network call intercepted at the
three modules all traffic passes through, a synthetic bilingual seed for every
screen, a build plugin that fails the build if a mock module ever leaks into
production, and a smoke test that drives the real services through the mocks.
That smoke test caught two real bugs on its first run, including an encoding
call that crashed on an Arabic inspector name.
The useful capability was not autonomous code generation. It was the ability
to hold product design, implementation, and verification in one tight loop.
Operators changed the rules
The product reached the warehouse and fully replaced the previous application.
That adoption mattered more than whether every screen matched its first design.
Real use exposed issues that no specification had anticipated, and because I
owned the product across design and implementation, the path from observation
to fix was short. We did not need to collect every problem, explain it across
several handoffs, and wait for a later sprint before responding. I could
inspect the failing state, correct it, and return the change to use.
Most of those fixes were the operation correcting the product:
Operators wanted to capture the second weight as soon as the truck was back,
not after the trip was formally ended. The gate went.
The add-load bar disappeared while a previous load was mid-flow, forcing an
operator to finish or delete it before queueing the next. It now stays.
A failed weight showed a generic message. It now shows the server's own
reason.
The laboratory's parts-per-million fields had an upper limit too low for
real samples.
The classification options were in the wrong order for how specialists read
them.
Dates in the Arabic locale sometimes rendered in the Hijri calendar. They are
now always Gregorian.
Trips that had started at scale-equipped warehouses had no entry point into
receiving. They got one.
Searching by trip identifier searched every warehouse. It is now scoped to
the operator's own.
That operating model had limits; one person should not become the permanent
unreviewed owner of every critical system. In this project, however, continuity
was valuable. The same person who understood why a workflow existed could trace
how it had been expressed in the interface and code.
What changed in practice
The old Warehouse Center stopped being the product people had to work around.
The replacement became the system used in the warehouse operation.
Warehouse workers could connect an arriving trip to the correct receiving
method, move through Arabic workflows, read the live platform scale in the
browser, attach evidence to every weighing, and turn the result into a
traceable receipt. Quality specialists, tank managers, and the teams handling
transfers, shipments, and custody worked in the same product. It was connected
to the company's core API instead of remaining a standalone prototype, and it
was deployed and kept current by a pipeline I could operate myself.
The project also provided production evidence for a broader organisational
idea: AI-assisted products did not have to look and behave like unrelated
templates. When the design system, MCP, skills, and product rules were present,
an independently built application could remain recognisably part of the same
product family as designer-led work.
Outcome
Warehouse Center fully replaced the previous internal application
Receiving, laboratory quality, tanks, transfers, port shipments, sediments, and custody permits shipped as one Arabic product
The web product read the physical platform scale through a serial-port integration, fixed against the real hardware in the warehouse
Every weighing carries photo evidence, captured without ever blocking the weight
Trips, receiving, quality, tanks, transfers, and shipments moved from Supabase onto the company's core API, module by module
An Arabic, RTL interface became the daily operational product rather than a translated secondary experience
Production use fed layout, component, and fallback knowledge back into the design-system and agent workflow
The company did not maintain a controlled before-and-after time study or a reliable baseline for defect rates. Adoption, full replacement, direct operational use, and support observations are presented as the evidence; I am not converting them into an invented efficiency percentage.
My role
I proposed the rebuild and worked with the product manager to translate the
warehouse operation into product requirements and executable states.
I modelled the workflows and data, designed the Arabic interface, implemented
the frontend and backend, established the initial Supabase foundation, wrote
the server-side functions, and moved the product onto the company's core API
module by module. I integrated the physical scale and the camera through the
browser, took the build to the warehouse to fix what only the real hardware
could show, maintained the deployment pipeline, supported the rollout, and
fixed what appeared in use. Late in the project I built the offline mock
environment so the product could be prototyped and tested without the backend.
The design-system work sat underneath the build. I used its tokens, rules,
documentation, MCP, and skills, then carried what Warehouse Center taught me
back into that system.
What I would measure next
Time from a vehicle arriving to a confirmed warehouse receipt, separated by
manual and scale-based flows
Failed or repeated scale captures by cause: connection, permission, unstable
reading, validation, or trip state
Share of weighings with a camera still and a manual photo attached
Differences between expected, collector-reported, measured, and accepted
quantities
Inventory corrections and the point in the workflow where each originated
Time from a reported production issue to a verified fix in warehouse use
Reports of a stale version after a deploy
Completion and rejection patterns for quality inspections and transfers
The number on the screen became part of the operation
Warehouse Center began as a response to a difficult legacy application and a
missing frontend role. It became a test of a larger possibility: whether design,
code, AI assistance, and operational knowledge could be held together closely
enough for one person to ship a real internal product.
The answer was not that AI made engineering trivial. The answer was that clear
product models, executable design rules, technical foundations, and immediate
contact with real use made the boundary more navigable.
The decisive moment was not when the interface looked complete. It was when a
vehicle moved onto the warehouse scale, the zero on the warehouse PC became a
reading, the workflow responded, and the resulting record joined the rest of
the company's operation.
That is where the product stopped being a prototype.