Add real-time consumer panel and paginated transaction results

- Implement WebSocket endpoint (/ws/fraud-results) in API for real-time fraud detection updates
- Add Redis pub/sub listener in API to broadcast consumer results to connected WebSocket clients
- Update consumer to publish fraud detection results to Redis 'fraud_results' channel with transaction details
- Add WebSocket connection logic in frontend with auto-reconnect (exponential backoff)
- Display real-time consumer status panel with fraud score color coding and decision status
- Implement client-side transaction results pagination (10/25/50 items per page)
- Add pagination controls: Previous/Next buttons, page indicator, page size selector
- Newest-first transaction sorting (DESC by timestamp)
- Add comprehensive styling for consumer panel and pagination controls
- All frontend components tested and builds successfully

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Pat TeruelandCopilot committed 2026-05-01 19:29:31 +08:00
1 parent d4751b9061
commit 0387900954
5 files changed
+515 -7

No files matched your search

+28 -2
View File
@@ -122,8 +122,12 @@ class DecisionEngine:
return decision
def store_result(self, transaction_id: str, decision: str,
layer1_score: float, layer2_score: float = None) -> None:
"""Store decision result in Redis for retrieval"""
layer1_score: float, layer2_score: float = None,
transaction_data: dict = None) -> None:
"""Store decision result in Redis and publish to pub/sub channel"""
import json
from datetime import datetime
key = f"result:{transaction_id}"
value = {
"decision": decision,
@@ -136,3 +140,25 @@ class DecisionEngine:
self.redis_client.setex(key, 3600, str(value))
logger.info(f"[RESULT STORAGE] Stored decision in Redis: {key} = {decision}")
# Publish to pub/sub channel for real-time WebSocket updates
try:
# Build message with transaction details for frontend
message = {
"transaction_id": transaction_id,
"decision": decision,
"fraud_score": round(layer2_score, 4) if layer2_score is not None else round(layer1_score, 4),
"layer1_score": round(layer1_score, 4),
"timestamp": datetime.utcnow().isoformat() + "Z"
}
# Add transaction data if provided (amount, merchant)
if transaction_data:
message["amount"] = transaction_data.get("amount")
message["merchant"] = transaction_data.get("merchant")
# Publish to Redis pub/sub channel
self.redis_client.publish("fraud_results", json.dumps(message))
logger.info(f"[RESULT STORAGE] Published result to pub/sub channel: {transaction_id[:8]}... → {decision}")
except Exception as e:
logger.error(f"[RESULT STORAGE] Failed to publish to pub/sub: {str(e)}")
+2 -1
View File
@@ -193,7 +193,8 @@ def process_transaction(transaction, components):
transaction_id=transaction_id,
decision=decision,
layer1_score=layer1_score,
layer2_score=layer2_score
layer2_score=layer2_score,
transaction_data=transaction # Pass full transaction for pub/sub message
)
# ─────────────────────────────────────────────────────────────────────────