- Added confirmation modal to sidebar's quick power switch

This commit is contained in:
maziggy
2025-12-31 11:40:14 +01:00
parent 9f665a9382
commit 16c21cf1fa
+78 -55
View File
@@ -1,7 +1,9 @@
import { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Plug, Power, PowerOff, Loader2, Wifi, WifiOff, Zap } from 'lucide-react';
import { api } from '../api/client';
import type { SmartPlug } from '../api/client';
import { ConfirmModal } from './ConfirmModal';
interface SwitchbarPopoverProps {
onClose: () => void;
@@ -9,6 +11,7 @@ interface SwitchbarPopoverProps {
function SwitchItem({ plug }: { plug: SmartPlug }) {
const queryClient = useQueryClient();
const [confirmAction, setConfirmAction] = useState<'on' | 'off' | null>(null);
// Fetch current status
const { data: status, isLoading: statusLoading } = useQuery({
@@ -29,66 +32,86 @@ function SwitchItem({ plug }: { plug: SmartPlug }) {
const isReachable = status?.reachable ?? false;
const isPending = controlMutation.isPending;
const handleConfirm = () => {
if (confirmAction) {
controlMutation.mutate(confirmAction);
setConfirmAction(null);
}
};
return (
<div className="flex items-center justify-between py-2 px-3 hover:bg-bambu-dark-tertiary rounded-lg transition-colors">
<div className="flex items-center gap-2">
<div className={`p-1.5 rounded ${isReachable ? (isOn ? 'bg-bambu-green/20' : 'bg-bambu-dark') : 'bg-red-500/20'}`}>
<Plug className={`w-4 h-4 ${isReachable ? (isOn ? 'text-bambu-green' : 'text-bambu-gray') : 'text-red-400'}`} />
</div>
<div>
<p className="text-sm text-white font-medium">{plug.name}</p>
<div className="flex items-center gap-1 text-xs">
{statusLoading ? (
<Loader2 className="w-3 h-3 text-bambu-gray animate-spin" />
) : isReachable ? (
<>
<Wifi className="w-3 h-3 text-bambu-green" />
<span className={isOn ? 'text-bambu-green' : 'text-bambu-gray'}>{status?.state || 'Unknown'}</span>
{status?.energy?.power !== null && status?.energy?.power !== undefined && (
<>
<span className="text-bambu-gray mx-1">|</span>
<Zap className="w-3 h-3 text-yellow-400" />
<span className="text-yellow-400">{Math.round(status.energy.power)}W</span>
</>
)}
</>
) : (
<>
<WifiOff className="w-3 h-3 text-red-400" />
<span className="text-red-400">Offline</span>
</>
)}
<>
<div className="flex items-center justify-between py-2 px-3 hover:bg-bambu-dark-tertiary rounded-lg transition-colors">
<div className="flex items-center gap-2">
<div className={`p-1.5 rounded ${isReachable ? (isOn ? 'bg-bambu-green/20' : 'bg-bambu-dark') : 'bg-red-500/20'}`}>
<Plug className={`w-4 h-4 ${isReachable ? (isOn ? 'text-bambu-green' : 'text-bambu-gray') : 'text-red-400'}`} />
</div>
<div>
<p className="text-sm text-white font-medium">{plug.name}</p>
<div className="flex items-center gap-1 text-xs">
{statusLoading ? (
<Loader2 className="w-3 h-3 text-bambu-gray animate-spin" />
) : isReachable ? (
<>
<Wifi className="w-3 h-3 text-bambu-green" />
<span className={isOn ? 'text-bambu-green' : 'text-bambu-gray'}>{status?.state || 'Unknown'}</span>
{status?.energy?.power !== null && status?.energy?.power !== undefined && (
<>
<span className="text-bambu-gray mx-1">|</span>
<Zap className="w-3 h-3 text-yellow-400" />
<span className="text-yellow-400">{Math.round(status.energy.power)}W</span>
</>
)}
</>
) : (
<>
<WifiOff className="w-3 h-3 text-red-400" />
<span className="text-red-400">Offline</span>
</>
)}
</div>
</div>
</div>
<div className="flex gap-1">
<button
onClick={() => setConfirmAction('on')}
disabled={!isReachable || isPending}
className={`p-1.5 rounded transition-colors ${
isOn
? 'bg-bambu-green text-white'
: 'bg-bambu-dark text-bambu-gray hover:text-white'
} disabled:opacity-50 disabled:cursor-not-allowed`}
title="Turn On"
>
{isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <Power className="w-4 h-4" />}
</button>
<button
onClick={() => setConfirmAction('off')}
disabled={!isReachable || isPending}
className={`p-1.5 rounded transition-colors ${
!isOn && isReachable
? 'bg-bambu-dark-tertiary text-white'
: 'bg-bambu-dark text-bambu-gray hover:text-white'
} disabled:opacity-50 disabled:cursor-not-allowed`}
title="Turn Off"
>
{isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <PowerOff className="w-4 h-4" />}
</button>
</div>
</div>
<div className="flex gap-1">
<button
onClick={() => controlMutation.mutate('on')}
disabled={!isReachable || isPending}
className={`p-1.5 rounded transition-colors ${
isOn
? 'bg-bambu-green text-white'
: 'bg-bambu-dark text-bambu-gray hover:text-white'
} disabled:opacity-50 disabled:cursor-not-allowed`}
title="Turn On"
>
{isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <Power className="w-4 h-4" />}
</button>
<button
onClick={() => controlMutation.mutate('off')}
disabled={!isReachable || isPending}
className={`p-1.5 rounded transition-colors ${
!isOn && isReachable
? 'bg-bambu-dark-tertiary text-white'
: 'bg-bambu-dark text-bambu-gray hover:text-white'
} disabled:opacity-50 disabled:cursor-not-allowed`}
title="Turn Off"
>
{isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <PowerOff className="w-4 h-4" />}
</button>
</div>
</div>
{confirmAction && (
<ConfirmModal
title={`Turn ${confirmAction === 'on' ? 'On' : 'Off'} Smart Plug`}
message={`Are you sure you want to turn ${confirmAction === 'on' ? 'on' : 'off'} "${plug.name}"?`}
confirmText={confirmAction === 'on' ? 'Turn On' : 'Turn Off'}
variant={confirmAction === 'off' ? 'warning' : 'default'}
onConfirm={handleConfirm}
onCancel={() => setConfirmAction(null)}
/>
)}
</>
);
}