Skip to content

Add device-admin iframe for reboot/shutdown management in SystemSettings#245

Open
gokugiant wants to merge 1 commit intomasterfrom
fix/TASK-FR014_frontend_reboot_iframe
Open

Add device-admin iframe for reboot/shutdown management in SystemSettings#245
gokugiant wants to merge 1 commit intomasterfrom
fix/TASK-FR014_frontend_reboot_iframe

Conversation

@gokugiant
Copy link
Contributor

Add an iframe instead of the not working reboot buttons to the admin panel.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SystemSettings admin panel to replace non-functional Raspberry Pi reboot/shutdown buttons with a device-admin embedded iframe (and a direct-link fallback) for system power management.

Changes:

  • Adds a device-admin “System Management” section with an embedded iframe for reboot/shutdown.
  • Adds a fallback UI that links out to device-admin in a new tab when the iframe is unavailable.
  • Removes the previous Raspberry Pi reboot/shutdown toggle + buttons.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +44
const [deviceAdminUrl] = useState(
() => `http://${hostIP}/admin/panel/boot/?mode=minimal&nav=hidden`,
);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hostIP from ConnectionSettingsSlice already includes the protocol prefix (e.g. http://hostname). Building deviceAdminUrl as http://${hostIP}/... produces an invalid URL like http://http://hostname/.... Build this URL from hostIP directly (or via new URL('/admin/panel/boot/', hostIP)) and avoid hardcoding the scheme so https setups work too.

Suggested change
const [deviceAdminUrl] = useState(
() => `http://${hostIP}/admin/panel/boot/?mode=minimal&nav=hidden`,
);
const [deviceAdminUrl] = useState(() => {
const url = new URL("/admin/panel/boot/", hostIP);
url.searchParams.set("mode", "minimal");
url.searchParams.set("nav", "hidden");
return url.toString();
});

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +45
const [deviceAdminUrl] = useState(
() => `http://${hostIP}/admin/panel/boot/?mode=minimal&nav=hidden`,
);
const [deviceAdminLoaded, setDeviceAdminLoaded] = useState(false);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deviceAdminUrl is stored in state with an initializer, so it will not update if the connection settings (hostIP) change after first render. Since this value is derived from Redux state, compute it from hostIP on each render (or use useMemo with [hostIP]) instead of useState.

Copilot uses AI. Check for mistakes.
Comment on lines +279 to +302
{deviceAdminLoaded ? (
<Box
sx={{
border: "1px solid #e0e0e0",
borderRadius: 1,
overflow: "hidden",
mb: 2,
}}
>
<iframe
src={deviceAdminUrl}
style={{
width: "100%",
height: "300px",
border: "none",
borderRadius: "4px",
}}
title="Device Admin Panel - Reboot/Shutdown"
onLoad={() => setDeviceAdminLoaded(true)}
onError={() => setDeviceAdminLoaded(false)}
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
/>
</Box>
) : (
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iframe is only rendered when deviceAdminLoaded is already true, but deviceAdminLoaded starts as false and is only set to true by the iframe’s onLoad. As a result, the iframe can never load and the UI will stay stuck on the fallback link. Render the iframe initially (e.g., always render it and show a loading/fallback overlay, or use a separate deviceAdminError flag).

Suggested change
{deviceAdminLoaded ? (
<Box
sx={{
border: "1px solid #e0e0e0",
borderRadius: 1,
overflow: "hidden",
mb: 2,
}}
>
<iframe
src={deviceAdminUrl}
style={{
width: "100%",
height: "300px",
border: "none",
borderRadius: "4px",
}}
title="Device Admin Panel - Reboot/Shutdown"
onLoad={() => setDeviceAdminLoaded(true)}
onError={() => setDeviceAdminLoaded(false)}
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
/>
</Box>
) : (
<Box
sx={{
border: "1px solid #e0e0e0",
borderRadius: 1,
overflow: "hidden",
mb: 2,
}}
>
<iframe
src={deviceAdminUrl}
style={{
width: "100%",
height: "300px",
border: "none",
borderRadius: "4px",
}}
title="Device Admin Panel - Reboot/Shutdown"
onLoad={() => setDeviceAdminLoaded(true)}
onError={() => setDeviceAdminLoaded(false)}
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
/>
</Box>
{!deviceAdminLoaded && (

Copilot uses AI. Check for mistakes.
Comment on lines +296 to +333
title="Device Admin Panel - Reboot/Shutdown"
onLoad={() => setDeviceAdminLoaded(true)}
onError={() => setDeviceAdminLoaded(false)}
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
/>
</Box>
) : (
<Box
sx={{
border: "1px solid #424242",
borderRadius: 1,
p: 2,
mb: 2,
backgroundColor: "#2a2a2a",
display: "flex",
flexDirection: "column",
gap: 1,
}}
>
<Typography variant="body2" sx={{ color: "#b0b0b0", mb: 1 }}>
Device admin panel not available. Please use the direct link:
</Typography>
<Link
href={deviceAdminUrl}
target="_blank"
rel="noopener noreferrer"
sx={{
display: "flex",
alignItems: "center",
gap: 0.5,
color: "#64b5f6",
"&:hover": { color: "#90caf9" },
}}
>
Reboot/Shutdown in device-admin <OpenInNew fontSize="small" />
</Link>
</Box>
)}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the conditional unmounts the iframe when deviceAdminLoaded flips to false, any transient load error (or a later navigation error) will permanently remove the iframe and there’s no retry path besides a full rerender. Consider keeping the iframe mounted and toggling visibility, and provide an explicit “Retry” action that resets the error/loading state.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants