Add ledctl locate/off LED controls to drive detail modal

This commit is contained in:
2026-03-07 04:57:35 +00:00
parent 51e6b49830
commit 927a5ccf3a
5 changed files with 119 additions and 2 deletions

View File

@@ -353,11 +353,35 @@ function TableView({ enclosure, onSelect, selectedSerial, t }) {
}
function DriveDetail({ slot, onClose, t }) {
const [ledLoading, setLedLoading] = useState(false);
const [ledState, setLedState] = useState(null); // "locate" | "off" | null
if (!slot) return null;
const d = slot.drive || {};
const healthStatus = d.health_status || "healthy";
const c = t.health[healthStatus] || t.health.healthy;
const setLed = async (state) => {
setLedLoading(true);
try {
const res = await fetch(`${API_BASE}/api/drives/${slot.device}/led`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ state }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.detail || `HTTP ${res.status}`);
}
setLedState(state);
} catch (err) {
console.error("LED control failed:", err);
setLedState(null);
} finally {
setLedLoading(false);
}
};
const smartFields = [
["SMART Status", d.smart_healthy === true ? "PASSED" : d.smart_healthy === false ? "FAILED" : "Unknown"],
["Temperature", d.temperature_c != null ? `${d.temperature_c}\u00B0C` : "\u2014"],
@@ -504,7 +528,40 @@ function DriveDetail({ slot, onClose, t }) {
})}
</div>
<div style={{ padding: "12px 24px 20px", textAlign: "right" }}>
<div style={{ padding: "12px 24px 20px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<button
onClick={() => setLed("locate")}
disabled={ledLoading}
style={{
background: ledState === "locate" ? t.health.warning.bg : t.btnBg,
border: `1px solid ${ledState === "locate" ? t.health.warning.border : t.cardBorder}`,
borderRadius: 10, padding: "8px 16px", fontSize: 13, fontWeight: 600,
cursor: ledLoading ? "wait" : "pointer",
color: ledState === "locate" ? t.health.warning.text : t.textSecondary,
transition: "all 0.15s", opacity: ledLoading ? 0.6 : 1,
}}
onMouseEnter={(e) => { if (!ledLoading) e.currentTarget.style.background = ledState === "locate" ? t.health.warning.bg : t.btnHover; }}
onMouseLeave={(e) => { e.currentTarget.style.background = ledState === "locate" ? t.health.warning.bg : t.btnBg; }}
>
{ledLoading && ledState !== "locate" ? "..." : "Locate"}
</button>
<button
onClick={() => setLed("off")}
disabled={ledLoading}
style={{
background: t.btnBg, border: `1px solid ${t.cardBorder}`,
borderRadius: 10, padding: "8px 16px", fontSize: 13, fontWeight: 600,
cursor: ledLoading ? "wait" : "pointer",
color: t.textSecondary, transition: "all 0.15s",
opacity: ledLoading ? 0.6 : 1,
}}
onMouseEnter={(e) => { if (!ledLoading) e.currentTarget.style.background = t.btnHover; }}
onMouseLeave={(e) => { e.currentTarget.style.background = t.btnBg; }}
>
{ledLoading && ledState === "locate" ? "..." : "LED Off"}
</button>
</div>
<button
onClick={onClose}
style={{