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.
273 lines
9.4 KiB
TypeScript
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();
|
|
});
|
|
}
|