浏览代码

- Added confirmation modal to sidebar's quick power switch

maziggy 4 月之前
父节点
当前提交
16c21cf1fa
共有 1 个文件被更改,包括 78 次插入55 次删除
  1. 78 55
      frontend/src/components/SwitchbarPopover.tsx

+ 78 - 55
frontend/src/components/SwitchbarPopover.tsx

@@ -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>
 
-      <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 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>
+
+      {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)}
+        />
+      )}
+    </>
   );
 }