Blood Component Preparation
Reference for the blood-component separation/preparation workflow.
เอกสารตรวจสอบระบบ ระบบงานธนาคารเลือด — คู่มือแนะนำเส้นทางการทดสอบ 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-guidefor 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
- Navigate to
/blood-bank/blood-receive - Verify realtime badge — top-right shows “Realtime” (green) or “Offline” (grey)
- Tab 1 “การรับเลือดเข้าคลัง” — shows incoming blood transactions from REST API
- 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
- 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 tableblood_inventory_movements(Supabase) — shelf trackingblood_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
- Navigate to
/blood-bank/separate-components - Read the guide card — explains the two tabs (Supabase vs legacy REST)
- Click “ผู้บริจาค พร้อมแยก” tab (Tab 0) — shows donors in
workflow_state = 'bb-blood-receive' - Click “แยกส่วนประกอบ” on a donor row → Separation dialog opens
- 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 “บันทึก + พิมพ์สติกเกอร์”
- Result:
blood_separation_entriesrows created (one per component, status =quarantine)- Donor moves to
workflow_state = 'bb-separate-components' - Sticker window opens automatically
- Click “เข้าคลัง” to release bags from quarantine →
in_stock - 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
- Navigate to
/blood-bank/blood-sticker - Pick a sticker kind — click one of the 6 tiles (Tag, Donation, etc.)
- Table loads with bags matching that kind’s filter
- If no real data: mock rows appear with demo banner “⚡ Mock data”
- Single print: Click print icon on any row → DialogPrintSticker → “ยืนยัน” → sticker prints
- Bulk print: Check multiple rows → count badge appears → click “Print Stickers (N)” → confirm
- 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)
Print Handler Chain
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
-
Navigate to
/blood-bank/blood-discard -
Header: “ทิ้ง / จำกัดผลิตภัณฑ์โลหิตที่ใช้ไม่ได้”
-
Eligible bags — auto-fetched from Supabase where status IN (
expired,infectOrImmuno,damaged) -
Select bags to discard (checkbox selection)
-
Fill discard form:
Field Options เหตุผล (Reason) หมดอายุ, ปนเปื้อน, ผลตรวจเป็นบวก, ถุงเสียหาย, เลือดแข็งตัว, เม็ดเลือดแดงแตก, ไขมันในเลือดสูง, อื่นๆ วิธีกำจัด (Method) เผาทำลาย, นึ่งฆ่าเชื้อ, ใช้สารเคมี, อื่นๆ พยาน (Witness) Free text — witness_1, witness_2 หมายเหตุ (Notes) Multiline text -
Confirm → writes to
blood_discard_records+ updates bag status todestroyed
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 trackingblood_separation_entries— bag status updated todestroyed+discard_reason,discarded_at,discarded_bycolumns
๕. แสดงคลังจัดเก็บ + แจ้งเตือนขั้นต่ำ (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)
-
Navigate to
/blood-bank/blood-receive→ click “คลังเลือด” tab -
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)
-
Table columns: bag number, component type, blood group, Rh, bag type, volume, donation date, expiry, status, reserved patient
-
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) -
Row actions: View detail, Dispatch to ward, Send to separation, Discard, Change status
-
Export: XLSX export with 21 columns + Thai date formatting
B. Stock Alerts (same page, above table)
-
BloodStockAlerts component renders above the inventory table
-
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 -
Slider UI — adjust minimum thresholds per blood group
-
Warning display — visual indicators when current stock < minimum
-
Persistence: Config saved to
blood_stock_configtable (Supabase), falls back to defaults if table doesn’t exist
C. Inventory Sources (/blood-bank/inventory-sources-new)
- Navigate to
/blood-bank/inventory-sources-new - Split view: NBC (Red Cross) bags vs In-house donor bags
- Filters: ABO group (segmented toggles), Rh, source, status
- Days-to-expiry calculation shown per bag
- 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