localgreenchain/components/vertical-farm/AlertPanel.tsx
Claude 2f7f22ca22
Add vertical farming UI components and pages
Components:
- FarmCard: Farm summary display with status and metrics
- ZoneGrid: Multi-level zone layout visualization
- ZoneDetailCard: Individual zone details with environment readings
- EnvironmentGauge: Real-time environmental parameter display
- BatchProgress: Crop batch progress tracking with health scores
- RecipeSelector: Growing recipe browser and selector
- AlertPanel: Environment alerts display and management
- GrowthStageIndicator: Visual growth stage progress tracker
- ResourceUsageChart: Energy/water usage analytics visualization

Pages:
- /vertical-farm: Dashboard with farm listing and stats
- /vertical-farm/register: Multi-step farm registration form
- /vertical-farm/[farmId]: Farm detail view with zones and alerts
- /vertical-farm/[farmId]/zones: Zone management with batch starting
- /vertical-farm/[farmId]/batches: Batch management and harvesting
- /vertical-farm/[farmId]/analytics: Farm analytics and performance metrics
2025-11-22 18:35:57 +00:00

117 lines
4.5 KiB
TypeScript

import { EnvironmentAlert } from '../../lib/vertical-farming/types';
interface AlertPanelProps {
alerts: EnvironmentAlert[];
onAcknowledge?: (alert: EnvironmentAlert) => void;
title?: string;
}
export default function AlertPanel({ alerts, onAcknowledge, title = 'Environment Alerts' }: AlertPanelProps) {
const activeAlerts = alerts.filter(a => !a.acknowledged);
const acknowledgedAlerts = alerts.filter(a => a.acknowledged);
const alertStyles: Record<string, { bg: string; border: string; icon: string }> = {
low: { bg: 'bg-blue-50', border: 'border-blue-400', icon: 'arrow-down' },
high: { bg: 'bg-orange-50', border: 'border-orange-400', icon: 'arrow-up' },
critical_low: { bg: 'bg-red-50', border: 'border-red-500', icon: 'exclamation' },
critical_high: { bg: 'bg-red-50', border: 'border-red-500', icon: 'exclamation' },
sensor_fault: { bg: 'bg-gray-50', border: 'border-gray-500', icon: 'warning' },
};
const formatParameter = (param: string) => {
const names: Record<string, string> = {
temperature: 'Temperature',
humidity: 'Humidity',
co2: 'CO2 Level',
ec: 'EC (Nutrient)',
ph: 'pH Level',
ppfd: 'Light (PPFD)',
};
return names[param] || param;
};
if (alerts.length === 0) {
return (
<div className="bg-green-50 rounded-lg p-4 text-center">
<p className="text-green-700 font-medium">All systems operating within normal parameters</p>
</div>
);
}
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-semibold text-gray-900">{title}</h3>
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
activeAlerts.some(a => a.type.includes('critical'))
? 'bg-red-100 text-red-800'
: activeAlerts.length > 0
? 'bg-yellow-100 text-yellow-800'
: 'bg-green-100 text-green-800'
}`}>
{activeAlerts.length} Active
</span>
</div>
{activeAlerts.length > 0 && (
<div className="space-y-2">
{activeAlerts.map((alert, idx) => {
const style = alertStyles[alert.type] || alertStyles.low;
return (
<div
key={`${alert.parameter}-${alert.timestamp}-${idx}`}
className={`rounded-lg p-4 border-l-4 ${style.bg} ${style.border}`}
>
<div className="flex justify-between items-start">
<div>
<div className="flex items-center gap-2">
<span className={`font-semibold ${
alert.type.includes('critical') ? 'text-red-700' : 'text-gray-900'
}`}>
{alert.type.includes('critical') && '!! '}
{formatParameter(alert.parameter)}
{alert.type.includes('low') ? ' LOW' : ' HIGH'}
</span>
</div>
<p className="text-sm text-gray-700 mt-1">
Current: <span className="font-medium">{alert.value}</span>
{' | '}
Threshold: <span className="font-medium">{alert.threshold}</span>
</p>
<p className="text-xs text-gray-500 mt-1">
{new Date(alert.timestamp).toLocaleString()}
</p>
</div>
{onAcknowledge && (
<button
onClick={() => onAcknowledge(alert)}
className="px-3 py-1 text-sm bg-white border border-gray-300 rounded hover:bg-gray-50 transition"
>
Acknowledge
</button>
)}
</div>
</div>
);
})}
</div>
)}
{acknowledgedAlerts.length > 0 && (
<details className="text-sm">
<summary className="cursor-pointer text-gray-600 hover:text-gray-900">
{acknowledgedAlerts.length} acknowledged alert(s)
</summary>
<div className="mt-2 space-y-1 pl-4">
{acknowledgedAlerts.map((alert, idx) => (
<p key={idx} className="text-gray-500">
{formatParameter(alert.parameter)} {alert.type}: {alert.value}
<span className="text-gray-400"> - {new Date(alert.timestamp).toLocaleTimeString()}</span>
</p>
))}
</div>
</details>
)}
</div>
);
}