medOS ultra

Blood Component Preparation

Reference for the blood-component separation/preparation workflow.

12 min read diagramsUpdated 2026-05-01docs/uat/blood-component-preparation-guide.md

เอกสารตรวจสอบระบบ ระบบงานธนาคารเลือด — คู่มือแนะนำเส้นทางการทดสอบ UAT checklist → route → component → how to verify


Quick Reference

UAT # รายละเอียด Route In-App Guide
รับผลิตภัณฑ์โลหิตเข้าคลัง /blood-bank/blood-receive UAT Guide §6
แยกส่วนประกอบโลหิต /blood-bank/separate-components UAT Guide §9
พิมพ์ฉลากผลิตภัณฑ์โลหิต /blood-bank/blood-sticker UAT Guide §12
กำจัดผลิตภัณฑ์โลหิต /blood-bank/blood-discard UAT Guide §20
แสดงคลังจัดเก็บ + แจ้งเตือน /blood-bank/blood-receive (tab 2) + /blood-bank/inventory-sources-new UAT Guide §6

In-app UAT guide: Navigate to /blood-bank/uat-guide for the interactive guide with clickable links to every page.


๑. รับผลิตภัณฑ์โลหิตเข้าคลัง (Receive Blood Products)

Route

/blood-bank/blood-receive

Components

Component Path Purpose
Page wrapper web/src/containers/blood-bank/blood-receive/page.tsx Entry point
Module web/packages/diagnostics-kit/src/blood-bank/components/blood-receive/BloodReceiveModule.tsx Provider + main
Main UI .../blood-receive/BloodReceiveMain.tsx Header, realtime badge, tab container
Tab router .../blood-receive/BloodReceiveTabContent.tsx 5-tab navigation
Receipt tab .../blood-receive/components/tabs/BloodReceiveTab.tsx REST-based receipt list (424 lines)
Inventory tab .../blood-receive/components/tabs/BloodInventoryTab.tsx Supabase inventory (1028 lines)
Red Cross intake .../blood-receive/components/RedCrossBloodReceiptPage.tsx NBC barcode scanning (414 lines)
Add product dialog .../blood-receive/components/dialogs/BloodProductDialog.tsx New product form
Realtime hook .../blood-receive/hooks/useBloodBankRealtime.ts 13-table Supabase subscription
Inventory hook .../blood-receive/hooks/useBloodInventorySupabase.ts Primary data source

How to Test

  1. Navigate to /blood-bank/blood-receive
  2. Verify realtime badge — top-right shows “Realtime” (green) or “Offline” (grey)
  3. Tab 1 “การรับเลือดเข้าคลัง” — shows incoming blood transactions from REST API
  4. Tab 2 “คลังเลือด” (Inventory) — primary inventory view from Supabase
    • Click “+ เพิ่มผลิตภัณฑ์” button → BloodProductDialog opens
    • Fill: bag number, blood group, Rh, component type, volume, expiry
    • Save → creates entry in both MongoDB (REST) and Supabase (dual-write)
    • New bag appears in table immediately via realtime subscription
  5. Verify the bag shows correct status: รับเข้า (received) or อยู่ในคลัง (in_stock)

Data Flow

BloodProductDialog → createBloodTransactionAsync() (REST/MongoDB)
                   → createSeparationEntriesBatch() (Supabase)
                   → createInventoryMovement() (shelf placement)
                   → useBloodBankRealtime() → auto-refresh table

Database Tables

  • blood_separation_entries (Supabase) — primary inventory table
  • blood_inventory_movements (Supabase) — shelf tracking
  • blood_storage_locations (Supabase) — physical locations

๒. แยกส่วนประกอบโลหิต (Component Separation)

Route

/blood-bank/separate-components

Components

Component Path Purpose
Page wrapper web/src/containers/blood-bank/separate-components/page.tsx Entry point
Main module web/packages/diagnostics-kit/src/blood-bank/components/separate-components/BloodBankSeparateComponentsModule.tsx Two-tab layout (385 lines)
Donor separation (new) .../separate-components/DonorSeparationView.tsx Supabase-driven separation (730 lines)
Column definitions .../separate-components/utils/CustomColumnTable.tsx 43 columns for 15 component types
Discard confirm .../separate-components/components/BloodComponentDiscardConfirm.tsx Discard workflow (618 lines)
Guide .../separate-components/BloodSeparationGuide.tsx Bento intro card

How to Test

  1. Navigate to /blood-bank/separate-components
  2. Read the guide card — explains the two tabs (Supabase vs legacy REST)
  3. Click “ผู้บริจาค พร้อมแยก” tab (Tab 0) — shows donors in workflow_state = 'bb-blood-receive'
  4. Click “แยกส่วนประกอบ” on a donor row → Separation dialog opens
  5. In dialog:
    • Check component types: PRC, FFP, PC, Cryo, LPRC, SDP, LDPRC, LPPC, LDPPC, Autologous, Tube, etc. (16 types total)
    • Enter volume (ml) for each selected component
    • Volume validation — bottom shows “ปริมาตรรวม: XXX ml / 450 ml (Whole Blood)”
    • If total exceeds 450 ml → red warning “⚠ เกินปริมาตร Whole Blood”
    • Select operator name (autocomplete from staff list)
    • Click “บันทึก + พิมพ์สติกเกอร์”
  6. Result:
    • blood_separation_entries rows created (one per component, status = quarantine)
    • Donor moves to workflow_state = 'bb-separate-components'
    • Sticker window opens automatically
  7. Click “เข้าคลัง” to release bags from quarantine → in_stock
  8. Click “แก้ไข” to edit volume, status, expiry, notes after separation

16 Supported Component Types

Code Name Shelf Life
PRC Packed Red Cell 35 days
LPRC Leukocyte-Poor PRC 42 days
LDPRC Leukodepleted PRC 42 days
FFP Fresh Frozen Plasma 365 days
PC Platelet Concentrate 5 days
Cryo Cryoprecipitate 365 days
SDP Single Donor Platelet 5 days
LPPC Leukocyte-Poor PC 5 days
LDPPC Leukodepleted Pool PC 5 days
SDP LD SDP Leukodepleted 5 days
SDP-PAS SDP in PAS 7 days
SDP LD-PAS SDP LD in PAS 7 days
LPPC-PAS LPPC in PAS 7 days
LDPPC-PAS LDPPC in PAS 7 days
Autologous Autologous Blood 35 days
Tube Tube Sample 7 days

Data Flow

Separation dialog → createSeparationEntriesBatch() (Supabase)
                  → updateBloodDonorSupabase() (workflow_state)
                  → openStickerWindow() + writeBagStickerContent()
                  → refetch donors + entries

๓. พิมพ์ฉลากผลิตภัณฑ์โลหิต (Label Printing)

Route

/blood-bank/blood-sticker

Components

Component Path Purpose
Page wrapper web/src/containers/blood-bank/blood-sticker/page.tsx Entry point
Module web/packages/diagnostics-kit/src/blood-bank/components/blood-sticker/BloodStickerModule.tsx Provider wrapper
Main UI .../blood-sticker/BloodStickerMain.tsx Header, kind picker, filters (134 lines)
Kind picker .../blood-sticker/components/BloodStickerKindPicker.tsx 6 sticker type tiles (185 lines)
Table .../blood-sticker/components/tabs/BloodStickerTab.tsx Data table + print actions (354 lines)
Print dialog .../blood-sticker/components/dialogs/DialogPrintSticker.tsx Confirmation modal (82 lines)
Print engine .../blood-bank/components/util/printer.tsx All print logic (1057 lines)
Data hook .../blood-sticker/hooks/useBloodTransactions.ts Supabase query with kind filters (220 lines)
Context .../blood-sticker/contexts/BloodStickerContext.tsx URL param parsing (62 lines)

6 Sticker Kinds

Kind URL Param Filter Use Case
Tag ?kind=tag Default inventory Standard bag label
Donation ?kind=donation Received/in_stock by donor Post-screening label
Crossmatch ?kind=crossmatch Reserved/crossmatched by request Patient-reserved bag
Dispense ?kind=dispense Issued/dispensed today Today’s issue slips
Pickup ?kind=pickup By pickup receipt number Ward collection ticket
Red Cross ?kind=redcross Source = Red Cross/NBC NBC-supplied intake

How to Test

  1. Navigate to /blood-bank/blood-sticker
  2. Pick a sticker kind — click one of the 6 tiles (Tag, Donation, etc.)
  3. Table loads with bags matching that kind’s filter
    • If no real data: mock rows appear with demo banner “⚡ Mock data”
  4. Single print: Click print icon on any row → DialogPrintSticker → “ยืนยัน” → sticker prints
  5. Bulk print: Check multiple rows → count badge appears → click “Print Stickers (N)” → confirm
  6. Print output:
    • If Jasper backend available: PDF sent to printer or opened in browser
    • If backend unavailable: HTML5 mock preview opens (90mm × 55mm label with barcode)
Row action / bulk select
  → printForKind(rows)
    → handlePrintStickerBloodBag(rows, isRedCross)
      → Maps fields to col1..col20 (component, bag#, group, Rh, dates, volume, barcode, QR)
      → globalPrintEForm({ reportTemplateId: '07edf961-...' })
        → Jasper/PDF backend → PDF blob → printer or browser
      → On 404: openMockStickerPreview() → HTML5 fallback with window.print()

Sticker Label Fields

  • Component type (PRC, FFP, etc.)
  • Bag type + suffix (TB/DB/QB)
  • Bag number (barcode)
  • Blood group + Rh (with 🔴 for negative)
  • Donation date / Expiry date
  • Volume (ml)
  • Storage temperature
  • QR code content

๔. กำจัดผลิตภัณฑ์โลหิต (Blood Product Disposal)

Route

/blood-bank/blood-discard

Components

Component Path Purpose
Page wrapper web/src/containers/blood-bank/blood-discard/page.tsx Entry point (21 lines)
Discard form web/packages/diagnostics-kit/src/blood-bank/components/separate-components/components/BloodComponentDiscardConfirm.tsx Full discard workflow (618 lines)

How to Test

  1. Navigate to /blood-bank/blood-discard

  2. Header: “ทิ้ง / จำกัดผลิตภัณฑ์โลหิตที่ใช้ไม่ได้”

  3. Eligible bags — auto-fetched from Supabase where status IN (expired, infectOrImmuno, damaged)

  4. Select bags to discard (checkbox selection)

  5. Fill discard form:

    Field Options
    เหตุผล (Reason) หมดอายุ, ปนเปื้อน, ผลตรวจเป็นบวก, ถุงเสียหาย, เลือดแข็งตัว, เม็ดเลือดแดงแตก, ไขมันในเลือดสูง, อื่นๆ
    วิธีกำจัด (Method) เผาทำลาย, นึ่งฆ่าเชื้อ, ใช้สารเคมี, อื่นๆ
    พยาน (Witness) Free text — witness_1, witness_2
    หมายเหตุ (Notes) Multiline text
  6. Confirm → writes to blood_discard_records + updates bag status to destroyed

Data Flow

getComponentsForDiscard() → blood_separation_entries WHERE status IN ['expired','infectOrImmuno','damaged']
  ↓
saveDiscardRecord() → INSERT blood_discard_records (bag_number, reason, method, witness, notes)
  ↓
updateComponentStatus() → UPDATE blood_separation_entries SET status='destroyed'

Database Tables

  • blood_discard_records — detailed discard log with witness tracking
  • blood_separation_entries — bag status updated to destroyed + discard_reason, discarded_at, discarded_by columns

๕. แสดงคลังจัดเก็บ + แจ้งเตือนขั้นต่ำ (Inventory Display + Alerts)

Routes

/blood-bank/blood-receive          (Tab 2: คลังเลือด)
/blood-bank/inventory-sources-new  (Split view: NBC vs In-house)

Components

Component Path Purpose
Inventory tab .../blood-receive/components/tabs/BloodInventoryTab.tsx Primary inventory (1028 lines)
Stock alerts .../blood-receive/components/BloodStockAlerts.tsx Min threshold config + warnings
Shelf visualization .../blood-receive/components/BloodInventoryShelves.tsx Visual shelf layout
Inventory sources web/packages/diagnostics-kit/src/blood-bank/components/inventory-sources-new/InventorySourcesNewModule.tsx NBC vs in-house split view
Status config .../blood-receive/utils/bloodStatusConfig.ts State machine (valid transitions)

How to Test

A. Inventory Display (/blood-bank/blood-receive → Tab 2)

  1. Navigate to /blood-bank/blood-receive → click “คลังเลือด” tab

  2. Filters available:

    • Date range (start/end)
    • Search (bag number, patient HN/name, donor ID)
    • Status dropdown (16 statuses)
    • Blood group (A / B / AB / O)
    • Rh (Positive / Negative)
    • Component type (WB / PRC / LPRC / FFP / SFFP / PC / Cryo / Tube)
  3. Table columns: bag number, component type, blood group, Rh, bag type, volume, donation date, expiry, status, reserved patient

  4. Status labels (Thai):

    Status Thai Color
    received รับเข้า #2196F3 (blue)
    in_stock อยู่ในคลัง #4CAF50 (green)
    ready พร้อมจ่าย #4CAF50 (green)
    reserved จองไว้ #3B82F6 (blue)
    crossmatched จับคู่แล้ว #F59E0B (amber)
    issued จ่ายไป Ward #22C55E (green)
    expired หมดอายุ #F44336 (red)
    quarantine กักกัน #C62828 (dark red)
    destroyed ทำลายแล้ว #9E9E9E (grey)
  5. Row actions: View detail, Dispatch to ward, Send to separation, Discard, Change status

  6. Export: XLSX export with 21 columns + Thai date formatting

B. Stock Alerts (same page, above table)

  1. BloodStockAlerts component renders above the inventory table

  2. Default minimums per blood group:

    Blood Group Min Stock Color
    O+ 10 Red
    O- 3 Red
    A+ 5 Orange
    A- 2 Orange
    B+ 5 Purple
    B- 2 Purple
    AB+ 3 Cyan
    AB- 2 Cyan
  3. Slider UI — adjust minimum thresholds per blood group

  4. Warning display — visual indicators when current stock < minimum

  5. Persistence: Config saved to blood_stock_config table (Supabase), falls back to defaults if table doesn’t exist

C. Inventory Sources (/blood-bank/inventory-sources-new)

  1. Navigate to /blood-bank/inventory-sources-new
  2. Split view: NBC (Red Cross) bags vs In-house donor bags
  3. Filters: ABO group (segmented toggles), Rh, source, status
  4. Days-to-expiry calculation shown per bag
  5. Aggregated counts by blood group

E2E Test Coverage

Playwright spec: web/e2e/blood-component-preparation-uat.spec.ts

# Run against local dev server
E2E_BASE_URL=http://localhost:5173 pnpm exec playwright test \
  --config=playwright.fast.config.ts e2e/blood-component-preparation-uat.spec.ts

# Run against Thailand demo
E2E_BASE_URL=https://his-thailand.vercel.app pnpm exec playwright test \
  --config=playwright.fast.config.ts e2e/blood-component-preparation-uat.spec.ts

# Run with video recording
E2E_BASE_URL=http://localhost:5173 pnpm exec playwright test \
  --config=playwright.fast.config.ts e2e/blood-component-preparation-uat.spec.ts \
  --video on

Test Matrix

Test UAT # What it verifies
page loads with tab navigation Blood-receive page renders with 5 tabs
inventory tab renders with filters and table Filter controls, status chips, table data
add product dialog can be opened Receipt entry form opens correctly
realtime status indicator is present WiFi/Realtime badge shows connection status
page loads with separation guide Guide card renders with tab instructions
donor list tab loads Supabase donor list or empty state
separation dialog has all 16 component types All component checkboxes + volume inputs
volume validation shows total vs whole blood Volume summary + 450ml limit warning
page loads with sticker kind picker 6 sticker kind tiles render
sticker kind options are available Tag, Donation, Crossmatch, etc. visible
sticker table shows data or mock rows Table with real or mock data
print dialog can be triggered DialogPrintSticker opens with confirm
bulk print selection works Multi-select checkboxes + count badge
page loads with discard interface Discard page title visible
discard reasons dropdown has correct options หมดอายุ, ปนเปื้อน, ถุงเสียหาย options
discard method options are available เผาทำลาย, นึ่งฆ่าเชื้อ, สารเคมี
shows bags eligible for disposal Table/list of expired/damaged bags
inventory page loads with blood group filters A/B/AB/O filter toggles
blood receive inventory tab shows stock data Status chips, table or empty state
stock alerts component renders Min threshold UI or inventory filters
inventory filters by blood group work Dropdown filter → filtered results
inventory shows product counts by type and status PRC/FFP/WB labels + Thai status labels

Screenshots Output

All screenshots saved to web/playwright/screenshots/uat{1-5}-*.png

Ask Anything