localgreenchain/lib/realtime/events.ts
Claude 7098335ce7
Add real-time updates system with Socket.io
Implement Agent 6: Real-Time Updates feature for LocalGreenChain:

- Add Socket.io server with room-based subscriptions
- Create client-side hooks (useSocket, useLiveFeed, usePlantUpdates)
- Add SocketProvider context for application-wide state
- Implement UI components:
  - ConnectionStatus: Shows WebSocket connection state
  - LiveFeed: Real-time event feed display
  - NotificationToast: Toast notifications with auto-dismiss
  - LiveChart: Real-time data visualization
- Add event type definitions and formatting utilities
- Create socket API endpoint for WebSocket initialization
- Add socket stats endpoint for monitoring
- Extend tailwind with fadeIn/slideIn animations

Integrates with existing EventStream SSE system for fallback.
2025-11-23 03:51:51 +00:00

273 lines
9.4 KiB
TypeScript

/**
* Real-Time Event Definitions for LocalGreenChain
*
* Defines all real-time event types and utilities for formatting events.
*/
import type { TransparencyEventType, TransparencyEvent, LiveFeedItem } from './types';
/**
* Event categories for grouping and filtering
*/
export enum EventCategory {
PLANT = 'plant',
TRANSPORT = 'transport',
DEMAND = 'demand',
FARM = 'farm',
AGENT = 'agent',
BLOCKCHAIN = 'blockchain',
SYSTEM = 'system',
AUDIT = 'audit',
}
/**
* Real-time event types enum for client use
*/
export enum RealtimeEvent {
// Plant events
PLANT_REGISTERED = 'plant.registered',
PLANT_CLONED = 'plant.cloned',
PLANT_TRANSFERRED = 'plant.transferred',
PLANT_UPDATED = 'plant.updated',
// Transport events
TRANSPORT_STARTED = 'transport.started',
TRANSPORT_COMPLETED = 'transport.completed',
TRANSPORT_VERIFIED = 'transport.verified',
// Demand events
DEMAND_CREATED = 'demand.created',
DEMAND_MATCHED = 'demand.matched',
SUPPLY_COMMITTED = 'supply.committed',
// Farm events
FARM_REGISTERED = 'farm.registered',
FARM_UPDATED = 'farm.updated',
BATCH_STARTED = 'batch.started',
BATCH_HARVESTED = 'batch.harvested',
// Agent events
AGENT_ALERT = 'agent.alert',
AGENT_TASK_COMPLETED = 'agent.task_completed',
AGENT_ERROR = 'agent.error',
// Blockchain events
BLOCKCHAIN_BLOCK_ADDED = 'blockchain.block_added',
BLOCKCHAIN_VERIFIED = 'blockchain.verified',
BLOCKCHAIN_ERROR = 'blockchain.error',
// System events
SYSTEM_HEALTH = 'system.health',
SYSTEM_ALERT = 'system.alert',
SYSTEM_METRIC = 'system.metric',
// Audit events
AUDIT_LOGGED = 'audit.logged',
AUDIT_ANOMALY = 'audit.anomaly',
}
/**
* Map event types to their categories
*/
export const EVENT_CATEGORIES: Record<TransparencyEventType, EventCategory> = {
'plant.registered': EventCategory.PLANT,
'plant.cloned': EventCategory.PLANT,
'plant.transferred': EventCategory.PLANT,
'plant.updated': EventCategory.PLANT,
'transport.started': EventCategory.TRANSPORT,
'transport.completed': EventCategory.TRANSPORT,
'transport.verified': EventCategory.TRANSPORT,
'demand.created': EventCategory.DEMAND,
'demand.matched': EventCategory.DEMAND,
'supply.committed': EventCategory.DEMAND,
'farm.registered': EventCategory.FARM,
'farm.updated': EventCategory.FARM,
'batch.started': EventCategory.FARM,
'batch.harvested': EventCategory.FARM,
'agent.alert': EventCategory.AGENT,
'agent.task_completed': EventCategory.AGENT,
'agent.error': EventCategory.AGENT,
'blockchain.block_added': EventCategory.BLOCKCHAIN,
'blockchain.verified': EventCategory.BLOCKCHAIN,
'blockchain.error': EventCategory.BLOCKCHAIN,
'system.health': EventCategory.SYSTEM,
'system.alert': EventCategory.SYSTEM,
'system.metric': EventCategory.SYSTEM,
'audit.logged': EventCategory.AUDIT,
'audit.anomaly': EventCategory.AUDIT,
};
/**
* Event display configuration
*/
interface EventDisplay {
title: string;
icon: string;
color: string;
}
/**
* Map event types to display properties
*/
export const EVENT_DISPLAY: Record<TransparencyEventType, EventDisplay> = {
'plant.registered': { title: 'Plant Registered', icon: '🌱', color: 'green' },
'plant.cloned': { title: 'Plant Cloned', icon: '🧬', color: 'green' },
'plant.transferred': { title: 'Plant Transferred', icon: '🔄', color: 'blue' },
'plant.updated': { title: 'Plant Updated', icon: '📝', color: 'gray' },
'transport.started': { title: 'Transport Started', icon: '🚚', color: 'yellow' },
'transport.completed': { title: 'Transport Completed', icon: '✅', color: 'green' },
'transport.verified': { title: 'Transport Verified', icon: '🔍', color: 'blue' },
'demand.created': { title: 'Demand Created', icon: '📊', color: 'purple' },
'demand.matched': { title: 'Demand Matched', icon: '🎯', color: 'green' },
'supply.committed': { title: 'Supply Committed', icon: '📦', color: 'blue' },
'farm.registered': { title: 'Farm Registered', icon: '🏭', color: 'green' },
'farm.updated': { title: 'Farm Updated', icon: '🔧', color: 'gray' },
'batch.started': { title: 'Batch Started', icon: '🌿', color: 'green' },
'batch.harvested': { title: 'Batch Harvested', icon: '🥬', color: 'green' },
'agent.alert': { title: 'Agent Alert', icon: '⚠️', color: 'yellow' },
'agent.task_completed': { title: 'Task Completed', icon: '✔️', color: 'green' },
'agent.error': { title: 'Agent Error', icon: '❌', color: 'red' },
'blockchain.block_added': { title: 'Block Added', icon: '🔗', color: 'blue' },
'blockchain.verified': { title: 'Blockchain Verified', icon: '✓', color: 'green' },
'blockchain.error': { title: 'Blockchain Error', icon: '⛓️‍💥', color: 'red' },
'system.health': { title: 'Health Check', icon: '💓', color: 'green' },
'system.alert': { title: 'System Alert', icon: '🔔', color: 'yellow' },
'system.metric': { title: 'Metric Update', icon: '📈', color: 'blue' },
'audit.logged': { title: 'Audit Logged', icon: '📋', color: 'gray' },
'audit.anomaly': { title: 'Anomaly Detected', icon: '🚨', color: 'red' },
};
/**
* Get the category for an event type
*/
export function getEventCategory(type: TransparencyEventType): EventCategory {
return EVENT_CATEGORIES[type];
}
/**
* Get display properties for an event type
*/
export function getEventDisplay(type: TransparencyEventType): EventDisplay {
return EVENT_DISPLAY[type] || { title: type, icon: '📌', color: 'gray' };
}
/**
* Format a description for an event
*/
export function formatEventDescription(event: TransparencyEvent): string {
const { type, data, source } = event;
switch (type) {
case 'plant.registered':
return `${data.name || 'A plant'} was registered by ${source}`;
case 'plant.cloned':
return `${data.name || 'A plant'} was cloned from ${data.parentName || 'parent'}`;
case 'plant.transferred':
return `${data.name || 'A plant'} was transferred to ${data.newOwner || 'new owner'}`;
case 'plant.updated':
return `${data.name || 'A plant'} information was updated`;
case 'transport.started':
return `Transport started from ${data.from || 'origin'} to ${data.to || 'destination'}`;
case 'transport.completed':
return `Transport completed: ${data.distance || '?'} km traveled`;
case 'transport.verified':
return `Transport verified on blockchain`;
case 'demand.created':
return `Demand signal created for ${data.product || 'product'}`;
case 'demand.matched':
return `Demand matched with ${data.supplier || 'a supplier'}`;
case 'supply.committed':
return `Supply committed: ${data.quantity || '?'} units`;
case 'farm.registered':
return `Vertical farm "${data.name || 'Farm'}" registered`;
case 'farm.updated':
return `Farm settings updated`;
case 'batch.started':
return `Growing batch started: ${data.cropType || 'crops'}`;
case 'batch.harvested':
return `Batch harvested: ${data.yield || '?'} kg`;
case 'agent.alert':
return data.message || 'Agent alert triggered';
case 'agent.task_completed':
return `Task completed: ${data.taskName || 'task'}`;
case 'agent.error':
return `Agent error: ${data.error || 'unknown error'}`;
case 'blockchain.block_added':
return `New block added to chain`;
case 'blockchain.verified':
return `Blockchain integrity verified`;
case 'blockchain.error':
return `Blockchain error: ${data.error || 'unknown error'}`;
case 'system.health':
return `System health: ${data.status || 'OK'}`;
case 'system.alert':
return data.message || 'System alert';
case 'system.metric':
return `Metric: ${data.metricName || 'metric'} = ${data.value || '?'}`;
case 'audit.logged':
return `Audit: ${data.action || 'action'} by ${data.actor || 'unknown'}`;
case 'audit.anomaly':
return `Anomaly: ${data.description || 'unusual activity detected'}`;
default:
return `Event from ${source}`;
}
}
/**
* Convert a TransparencyEvent to a LiveFeedItem
*/
export function toFeedItem(event: TransparencyEvent): LiveFeedItem {
const display = getEventDisplay(event.type);
return {
id: event.id,
event,
timestamp: new Date(event.timestamp).getTime(),
formatted: {
title: display.title,
description: formatEventDescription(event),
icon: display.icon,
color: display.color,
},
};
}
/**
* Get events by category
*/
export function getEventsByCategory(category: EventCategory): TransparencyEventType[] {
return Object.entries(EVENT_CATEGORIES)
.filter(([, cat]) => cat === category)
.map(([type]) => type as TransparencyEventType);
}
/**
* Check if an event type belongs to a category
*/
export function isEventInCategory(type: TransparencyEventType, category: EventCategory): boolean {
return EVENT_CATEGORIES[type] === category;
}
/**
* Priority to numeric value for sorting
*/
export function priorityToNumber(priority: string): number {
switch (priority) {
case 'CRITICAL': return 4;
case 'HIGH': return 3;
case 'NORMAL': return 2;
case 'LOW': return 1;
default: return 0;
}
}
/**
* Sort events by priority and time
*/
export function sortEvents(events: TransparencyEvent[]): TransparencyEvent[] {
return [...events].sort((a, b) => {
const priorityDiff = priorityToNumber(b.priority) - priorityToNumber(a.priority);
if (priorityDiff !== 0) return priorityDiff;
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
});
}