add an error modal for failed requests
This commit is contained in:
parent
1ee9c93230
commit
4bd02923c9
5 changed files with 130 additions and 37 deletions
26
frontend/src/ErrorModal.tsx
Normal file
26
frontend/src/ErrorModal.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from "@nextui-org/react";
|
||||
|
||||
interface ErrorModalProps {
|
||||
error: string | null;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function ErrorModal({ error, onClose }: ErrorModalProps) {
|
||||
return (
|
||||
<Modal backdrop="blur" placement="auto" isOpen={error != null} onClose={onClose}>
|
||||
<ModalContent>
|
||||
<>
|
||||
<ModalHeader>Error</ModalHeader>
|
||||
<ModalBody>
|
||||
<p>{error}</p>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="danger" variant="light" onPress={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -2,49 +2,63 @@ import {useState} from "react";
|
|||
import {Button, Popover, PopoverContent, PopoverTrigger} from "@nextui-org/react";
|
||||
import axios from "axios";
|
||||
import PowerIcon from "./assets/PowerIcon.tsx";
|
||||
import {ErrorModal} from "./ErrorModal.tsx";
|
||||
import {makePostRequest} from "./requests.ts";
|
||||
|
||||
|
||||
export function RebootButton() {
|
||||
const [isRebooting, setIsRebooting] = useState(false);
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const reboot = async () => {
|
||||
setIsPopoverOpen(false);
|
||||
setIsRebooting(true);
|
||||
setError(null); // Clear any previous errors
|
||||
|
||||
// hardcode URL for now
|
||||
await axios.post("https://panel.mordor.ee/api/reboot").catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
const errorMessage = await makePostRequest("https://panel.mordor.ee/api/reboot");
|
||||
if (errorMessage) {
|
||||
setError(errorMessage);
|
||||
setIsRebooting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let isOnline = false;
|
||||
while (!isOnline) {
|
||||
// sleep for 5 seconds
|
||||
await new Promise(r => setTimeout(r, 5000));
|
||||
await axios.get("https://panel.mordor.ee/api/online", {timeout: 2000}).then((res) => {
|
||||
// machine might not have started rebooting yet.
|
||||
// if this is the case, the response will be 200 OK with "rebooting" as the body.
|
||||
// if the machine is online, the response will be 200 OK with "online" as the body.
|
||||
if (res.status === 200 && res.data === "online")
|
||||
try {
|
||||
const res = await axios.get("http://panel.mordor.ee/api/online", {timeout: 2000});
|
||||
if (res.status === 200 && res.data === "online") {
|
||||
isOnline = true;
|
||||
}).catch((err) => {
|
||||
}
|
||||
} catch (err) {
|
||||
// this is expected when rebooting
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setIsRebooting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return <Popover placement="top" backdrop="blur" isOpen={isPopoverOpen}
|
||||
onOpenChange={(open) => setIsPopoverOpen(open)}>
|
||||
<PopoverTrigger>
|
||||
<Button startContent={<PowerIcon/>} className="w-52" color="danger" isLoading={isRebooting} variant="shadow" size="lg">Reboot</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<div className="flex flex-col gap-3 items-center p-3">
|
||||
<p>Really reboot?</p>
|
||||
<Button color="danger" onPress={reboot} variant="shadow" className="w-0">Yes</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
return (
|
||||
<>
|
||||
<Popover placement="top" backdrop="blur" isOpen={isPopoverOpen}
|
||||
onOpenChange={(open) => setIsPopoverOpen(open)}>
|
||||
<PopoverTrigger>
|
||||
<Button startContent={<PowerIcon/>} className="w-52" color="danger" isLoading={isRebooting}
|
||||
variant="shadow" size="lg">
|
||||
Reboot
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<div className="flex flex-col gap-3 items-center p-3">
|
||||
<p>Really reboot?</p>
|
||||
<Button color="danger" onPress={reboot} variant="shadow" className="w-0">Yes</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<ErrorModal error={error} onClose={() => setError(null)}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
import {useState} from "react";
|
||||
import axios from "axios";
|
||||
import {Button} from "@nextui-org/react";
|
||||
import KodiIcon from "./assets/KodiIcon.tsx";
|
||||
import {ErrorModal} from "./ErrorModal.tsx";
|
||||
import {makePostRequest} from "./requests.ts";
|
||||
|
||||
export function RestartKodiButton() {
|
||||
const [isRestarting, setIsRestarting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const restart = async () => {
|
||||
setIsRestarting(true);
|
||||
// hardcode URL for now
|
||||
await axios.post("https://panel.mordor.ee/api/restart-kodi").catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
setError(null);
|
||||
setError(await makePostRequest("http://panel.mordor.ee/api/restart-kodi"));
|
||||
setIsRestarting(false);
|
||||
}
|
||||
|
||||
return <Button startContent={<KodiIcon/>} className="w-52" color="primary" variant="shadow" isLoading={isRestarting}
|
||||
size="lg" onPress={restart}>Restart Kodi</Button>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
startContent={<KodiIcon/>}
|
||||
className="w-52"
|
||||
color="primary"
|
||||
variant="shadow"
|
||||
isLoading={isRestarting}
|
||||
size="lg"
|
||||
onPress={restart}
|
||||
>
|
||||
Restart Kodi
|
||||
</Button>
|
||||
<ErrorModal error={error} onClose={() => setError(null)}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,34 @@
|
|||
import {useState} from "react";
|
||||
import {Button} from "@nextui-org/react";
|
||||
import axios from "axios";
|
||||
import PipeWireIcon from "./assets/PipeWireIcon.tsx";
|
||||
import {makePostRequest} from "./requests.ts";
|
||||
import {ErrorModal} from "./ErrorModal.tsx";
|
||||
|
||||
export function RestartPipeWireButton() {
|
||||
const [isRestarting, setIsRestarting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const restart = async () => {
|
||||
setIsRestarting(true);
|
||||
await axios.post("https://panel.mordor.ee/api/restart-pipewire").catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
setError(null);
|
||||
setError(await makePostRequest("http://panel.mordor.ee/api/restart-pipewire"));
|
||||
setIsRestarting(false);
|
||||
}
|
||||
|
||||
return <Button startContent={<PipeWireIcon/>} className="w-52" color="primary" variant="shadow" isLoading={isRestarting} size="lg" onPress={restart}>Restart PipeWire</Button>
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
startContent={<PipeWireIcon/>}
|
||||
className="w-52"
|
||||
color="primary"
|
||||
variant="shadow"
|
||||
isLoading={isRestarting}
|
||||
size="lg"
|
||||
onPress={restart}
|
||||
>
|
||||
Restart PipeWire
|
||||
</Button>
|
||||
<ErrorModal error={error} onClose={() => setError(null)}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
24
frontend/src/requests.ts
Normal file
24
frontend/src/requests.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import axios from "axios";
|
||||
|
||||
export const makePostRequest = async (url: string): Promise<string | null> => {
|
||||
try {
|
||||
const response = await axios.post(url);
|
||||
if (response.status !== 200) {
|
||||
return response.data;
|
||||
}
|
||||
return null;
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
if (axios.isAxiosError(err)) {
|
||||
if (err.response) {
|
||||
return err.response.data;
|
||||
} else if (err.request) {
|
||||
return "No response from server. Please try again.";
|
||||
} else {
|
||||
return err.message;
|
||||
}
|
||||
} else {
|
||||
return (err as Error).message;
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue