Software · Systems · Research
Interface sounds
Ambient music
a.
Adjie Workspace
Interface sounds
Ambient music

Scripted introduction. The project study is Adjie AI’s prepared answer; use Ask AI for your own questions.

How do you model a blood-bank workflow without collapsing different events into one status?

Adjie AI

Issuing a bag does not mean it was used. BDRS keeps service, per-bag crossmatch, physical outcome and transfusion events distinct, then brings them together in one case workspace.

Blood-bank operations / 2026

BDRS

A case is more
than its status.

A blood-bank system of record: requests, per-bag crossmatch, issue and outcomes connected in one case workspace.

BDRS · public product evidence
BDRS · public product evidence
The synthetic case workstation at phone width.
The synthetic case workstation at phone width.
My contribution
Product owner · sole developer · QA and release
Built with
Laravel 13 · PHP 8.4 · Inertia 3 · React 19 · Tailwind 4 · SQLite
The problem

Start with the work.

The original process spanned 12 Excel workbooks and a Word document. Producing the monthly report meant manually transcribing between files; the lifecycle of a request and each blood unit needed a shared record.

What I built

I designed and built a domain-led system with one case workstation, separate state machines for service, crossmatch and transfusion, and controlled finalisation. Product decisions, full-stack implementation, testing and release were my responsibility, with AI-assisted implementation.

01 / Domain model

Connected events.
Independent meaning.

Stock entersReceipt → confirmation → inventory
A case proceedsRequest → crossmatch → issue → physical outcome
When recordedTransfusion episode → reaction

Receipt confirmation brings units into inventory; unconfirmed deliveries are not stock.

An explanation of the domain model, not a clinical decision tool.

02 / Engineering decisions

Make the rules
impossible to miss.

  1. 01

    One case. Separate truths.

    Issuing a bag does not mean it was used. A used bag does not imply a recorded transfusion episode. Each event keeps its own meaning.

  2. 02

    Compatibility belongs to the pairing.

    Crossmatch is per bag, never a single verdict for the request. An incompatible result leaves the request line short.

  3. 03

    A refusal must be actionable.

    Named readiness checks explain what is unresolved and where to resolve it. Warnings remain distinct from blockers.

  4. 04

    Enforce the boundary on the server.

    Staff handle daily transactions; administration and correction require the super-admin role. Policies enforce the distinction beyond visible buttons.

03 / Refusal as a useful state

Explain what
still needs work.

The readiness surface names the unresolved checks and provides the resolving action. The capture shows a synthetic case, including its actual notification stack.

Finalisation evaluates seven blockers and two warnings; the open-episode check runs again inside the transaction.

Synthetic case: named blockers explain why finalisation is refused.
Synthetic case: named blockers explain why finalisation is refused.
04 / Inventory

Available is not always usable.

Stock is derived by the domain service. The interface keeps availability and expiry visible together.

Inventory by component and blood group, with expiry.
Inventory by component and blood group, with expiry.
The synthetic case workstation at phone width.
The synthetic case workstation at phone width.
05 / A smaller surface

The same case.
A different layout.

At phone width, the case keeps its actions and context. The public fixture makes the boundary explicit: DEMO patients, DEMO bags and E2E staff.

Domain modelling, traceable corrections and release discipline are part of the product—not work left behind the interface.

Evidence / verification

The public record.

Recorded release
19 September 2026 · owner UAT passed
Unit / feature suite
2,672 passed · 74 skipped · 3 incomplete
End-to-end suite
44 passed
Handover
Operator manual + technical maintenance package
Want to go deeper?