import { useState, useEffect } from 'react'; import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; import { X, Save, Loader2, Wifi, WifiOff, CheckCircle, Bell, Clock } from 'lucide-react'; import { api } from '../api/client'; import type { SmartPlug, SmartPlugCreate, SmartPlugUpdate } from '../api/client'; import { Button } from './Button'; interface AddSmartPlugModalProps { plug?: SmartPlug | null; onClose: () => void; } export function AddSmartPlugModal({ plug, onClose }: AddSmartPlugModalProps) { const queryClient = useQueryClient(); const isEditing = !!plug; const [name, setName] = useState(plug?.name || ''); const [ipAddress, setIpAddress] = useState(plug?.ip_address || ''); const [username, setUsername] = useState(plug?.username || ''); const [password, setPassword] = useState(plug?.password || ''); const [printerId, setPrinterId] = useState(plug?.printer_id || null); const [testResult, setTestResult] = useState<{ success: boolean; state?: string | null; device_name?: string | null } | null>(null); const [error, setError] = useState(null); // Power alert settings const [powerAlertEnabled, setPowerAlertEnabled] = useState(plug?.power_alert_enabled || false); const [powerAlertHigh, setPowerAlertHigh] = useState(plug?.power_alert_high?.toString() || ''); const [powerAlertLow, setPowerAlertLow] = useState(plug?.power_alert_low?.toString() || ''); // Schedule settings const [scheduleEnabled, setScheduleEnabled] = useState(plug?.schedule_enabled || false); const [scheduleOnTime, setScheduleOnTime] = useState(plug?.schedule_on_time || ''); const [scheduleOffTime, setScheduleOffTime] = useState(plug?.schedule_off_time || ''); // Fetch printers for linking const { data: printers } = useQuery({ queryKey: ['printers'], queryFn: api.getPrinters, }); // Fetch existing plugs to check for conflicts const { data: existingPlugs } = useQuery({ queryKey: ['smart-plugs'], queryFn: api.getSmartPlugs, }); // Close on Escape key useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [onClose]); // Test connection mutation const testMutation = useMutation({ mutationFn: () => api.testSmartPlugConnection(ipAddress, username || null, password || null), onSuccess: (result) => { setTestResult(result); setError(null); // Auto-fill name from device if empty if (!name && result.device_name) { setName(result.device_name); } }, onError: (err: Error) => { setTestResult(null); setError(err.message); }, }); // Create mutation const createMutation = useMutation({ mutationFn: (data: SmartPlugCreate) => api.createSmartPlug(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['smart-plugs'] }); onClose(); }, onError: (err: Error) => { setError(err.message); }, }); // Update mutation const updateMutation = useMutation({ mutationFn: (data: SmartPlugUpdate) => api.updateSmartPlug(plug!.id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['smart-plugs'] }); onClose(); }, onError: (err: Error) => { setError(err.message); }, }); // Filter out printers that already have a plug assigned (except current plug's printer) const availablePrinters = printers?.filter(p => { const hasPlug = existingPlugs?.some(ep => ep.printer_id === p.id && ep.id !== plug?.id); return !hasPlug; }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); setError(null); if (!name.trim()) { setError('Name is required'); return; } if (!ipAddress.trim()) { setError('IP address is required'); return; } const data = { name: name.trim(), ip_address: ipAddress.trim(), username: username.trim() || null, password: password.trim() || null, printer_id: printerId, // Power alerts power_alert_enabled: powerAlertEnabled, power_alert_high: powerAlertHigh ? parseFloat(powerAlertHigh) : null, power_alert_low: powerAlertLow ? parseFloat(powerAlertLow) : null, // Schedule schedule_enabled: scheduleEnabled, schedule_on_time: scheduleOnTime || null, schedule_off_time: scheduleOffTime || null, }; if (isEditing) { updateMutation.mutate(data); } else { createMutation.mutate(data); } }; const isPending = createMutation.isPending || updateMutation.isPending; return (
e.stopPropagation()} > {/* Header */}

{isEditing ? 'Edit Smart Plug' : 'Add Smart Plug'}

{/* Form */}
{error && (
{error}
)} {/* IP Address */}
{ setIpAddress(e.target.value); setTestResult(null); }} placeholder="192.168.1.100" className="flex-1 px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />
{/* Test Result */} {testResult && (
{testResult.success ? ( <>

Connected!

{testResult.device_name && `Device: ${testResult.device_name} - `} State: {testResult.state}

) : ( <> Connection failed )}
)} {/* Name */}
setName(e.target.value)} placeholder="Living Room Plug" className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />
{/* Authentication (optional) */}
setUsername(e.target.value)} placeholder="admin" className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />
setPassword(e.target.value)} placeholder="********" className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />

Leave empty if your Tasmota device doesn't require authentication

{/* Link to Printer */}

Linking enables automatic on/off when prints start/complete

{/* Power Alerts */}
Power Alerts
{powerAlertEnabled && (
setPowerAlertHigh(e.target.value)} placeholder="e.g. 200" min="0" max="5000" className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />
setPowerAlertLow(e.target.value)} placeholder="e.g. 10" min="0" max="5000" className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />

Get notified when power consumption crosses these thresholds. Leave empty to disable that direction.

)}
{/* Schedule */}
Daily Schedule
{scheduleEnabled && (
setScheduleOnTime(e.target.value)} className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />
setScheduleOffTime(e.target.value)} className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" />

Automatically turn the plug on/off at these times daily. Leave empty to skip that action.

)}
{/* Actions */}
); }