import { useState } from 'react'; import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; import { Plug, Power, PowerOff, Loader2, Trash2, Settings2, Thermometer, Clock, Wifi, WifiOff, Edit2, Bell, Calendar } from 'lucide-react'; import { api } from '../api/client'; import type { SmartPlug, SmartPlugUpdate } from '../api/client'; import { Card, CardContent } from './Card'; import { Button } from './Button'; import { ConfirmModal } from './ConfirmModal'; interface SmartPlugCardProps { plug: SmartPlug; onEdit: (plug: SmartPlug) => void; } export function SmartPlugCard({ plug, onEdit }: SmartPlugCardProps) { const queryClient = useQueryClient(); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [showPowerOnConfirm, setShowPowerOnConfirm] = useState(false); const [showPowerOffConfirm, setShowPowerOffConfirm] = useState(false); const [isExpanded, setIsExpanded] = useState(false); // Fetch current status const { data: status, isLoading: statusLoading, refetch: refetchStatus } = useQuery({ queryKey: ['smart-plug-status', plug.id], queryFn: () => api.getSmartPlugStatus(plug.id), refetchInterval: 30000, // Refresh every 30 seconds }); // Fetch printers for linking const { data: printers } = useQuery({ queryKey: ['printers'], queryFn: api.getPrinters, }); const linkedPrinter = printers?.find(p => p.id === plug.printer_id); // Control mutation const controlMutation = useMutation({ mutationFn: (action: 'on' | 'off' | 'toggle') => api.controlSmartPlug(plug.id, action), onSuccess: () => { refetchStatus(); }, }); // Update mutation const updateMutation = useMutation({ mutationFn: (data: SmartPlugUpdate) => api.updateSmartPlug(plug.id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['smart-plugs'] }); // Also invalidate printer-specific smart plug queries to keep PrintersPage in sync if (plug.printer_id) { queryClient.invalidateQueries({ queryKey: ['smartPlugByPrinter', plug.printer_id] }); } }, }); // Delete mutation const deleteMutation = useMutation({ mutationFn: () => api.deleteSmartPlug(plug.id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['smart-plugs'] }); }, }); const isOn = status?.state === 'ON'; const isReachable = status?.reachable ?? false; const isPending = controlMutation.isPending; return ( <> {/* Header Row */} {plug.name} {plug.ip_address} {/* Status indicator */} {statusLoading ? ( ) : isReachable ? ( {status?.state || 'Unknown'} ) : ( Offline )} {/* Linked Printer */} {linkedPrinter && ( Linked to: {linkedPrinter.name} )} {/* Feature Badges */} {(plug.power_alert_enabled || plug.schedule_enabled) && ( {plug.power_alert_enabled && ( Alerts )} {plug.schedule_enabled && ( {plug.schedule_on_time && plug.schedule_off_time ? `${plug.schedule_on_time} - ${plug.schedule_off_time}` : plug.schedule_on_time ? `On ${plug.schedule_on_time}` : `Off ${plug.schedule_off_time}`} )} )} {/* Quick Controls */} setShowPowerOnConfirm(true)} className="flex-1" > {isPending ? : } On setShowPowerOffConfirm(true)} className="flex-1" > {isPending ? : } Off {/* Toggle Settings Panel */} setIsExpanded(!isExpanded)} className="w-full flex items-center justify-between py-2 text-sm text-bambu-gray hover:text-white transition-colors" > Automation Settings {isExpanded ? '-' : '+'} {/* Expanded Settings */} {isExpanded && ( {/* Enabled Toggle */} Enabled Enable automation for this plug updateMutation.mutate({ enabled: e.target.checked })} className="sr-only peer" /> {/* Auto On */} Auto On Turn on when print starts updateMutation.mutate({ auto_on: e.target.checked })} className="sr-only peer" /> {/* Auto Off */} Auto Off Turn off when print completes (one-shot) updateMutation.mutate({ auto_off: e.target.checked })} className="sr-only peer" /> {/* Delay Mode */} {plug.auto_off && ( Turn Off Delay Mode updateMutation.mutate({ off_delay_mode: 'time' })} className={`flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-sm transition-colors ${ plug.off_delay_mode === 'time' ? 'bg-bambu-green text-white' : 'bg-bambu-dark text-bambu-gray hover:text-white' }`} > Time updateMutation.mutate({ off_delay_mode: 'temperature' })} className={`flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-sm transition-colors ${ plug.off_delay_mode === 'temperature' ? 'bg-bambu-green text-white' : 'bg-bambu-dark text-bambu-gray hover:text-white' }`} > Temp {plug.off_delay_mode === 'time' ? ( Delay (minutes) updateMutation.mutate({ off_delay_minutes: parseInt(e.target.value) || 5 })} className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none" /> ) : ( Temperature threshold (C) updateMutation.mutate({ off_temp_threshold: parseInt(e.target.value) || 70 })} className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none" /> Turns off when nozzle cools below this temperature )} )} {/* Action Buttons */} onEdit(plug)} className="flex-1" > Edit setShowDeleteConfirm(true)} className="text-red-400 hover:text-red-300" > )} {/* Delete Confirmation */} {showDeleteConfirm && ( { deleteMutation.mutate(); setShowDeleteConfirm(false); }} onCancel={() => setShowDeleteConfirm(false)} /> )} {/* Power On Confirmation */} {showPowerOnConfirm && ( { controlMutation.mutate('on'); setShowPowerOnConfirm(false); }} onCancel={() => setShowPowerOnConfirm(false)} /> )} {/* Power Off Confirmation */} {showPowerOffConfirm && ( { controlMutation.mutate('off'); setShowPowerOffConfirm(false); }} onCancel={() => setShowPowerOffConfirm(false)} /> )} > ); }
{plug.ip_address}
Enabled
Enable automation for this plug
Auto On
Turn on when print starts
Auto Off
Turn off when print completes (one-shot)
Turn Off Delay Mode
Turns off when nozzle cools below this temperature