am_i_up_ui/src/components/DiagnosticsCard.tsx

197 lines
4.2 KiB
TypeScript

import React, { useState } from "react";
import { Button, ListGroup, Collapse } from "react-bootstrap";
import {
Dash,
ChevronDown,
ChevronRight,
CircleFill,
} from "react-bootstrap-icons";
import { Container, Row, Col, Table, Card } from "react-bootstrap";
import { atom, useAtomValue } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query";
const diagQueryFn = async () => {
const resp = await fetch("api/diagnostics");
if (!resp.ok) {
throw new Error("Network response was not ok");
}
return resp.json();
};
const diagQueryAtom = atomWithQuery(() => ({
queryKey: ["diagnostics"],
queryFn: diagQueryFn,
}));
const diagAtom = atom((get) => {
const diag = get(diagQueryAtom);
if (diag.isPending) {
return "Loading...";
}
if (diag.isError) {
return "Error loading!";
}
if (!diag.data.status) {
return "Failed to get diagnostic information.";
}
return diag.data.message;
});
export function DiagnosticsCard() {
const diagnostics = useAtomValue(diagAtom);
const diagContent = () => {
if (typeof diagnostics === "string" || diagnostics instanceof String) {
return <pre> {diagnostics} </pre>;
}
return <TreeView data={diagnostics} />;
};
return (
<Card className="padded">
<Card.Title>Diagnostics</Card.Title>
<Card.Body>{diagContent()}</Card.Body>
</Card>
);
}
function TreeView(props: Any) {
const data = props.data;
return (
<ListGroup>
{data.children.map((node) => (
<SingleNode key={node.id} node={node} />
))}
</ListGroup>
);
}
function LevelIndicator(props: Any) {
const level = props.level;
if (level == "ERROR") {
return <CircleFill color="red" />;
}
if (level == "WARN") {
return <CircleFill color="orange" />;
}
if (level == "OK") {
return <CircleFill color="green" />;
}
return <CircleFill color="grey" />;
}
function MessageText(props: Any) {
if (props.message === "") {
return <div></div>;
}
return <div>Message: {props.message}</div>;
}
function ValuesTable(props: Any) {
const values = props.values;
if (Object.keys(values).length === 0) {
return <div></div>;
}
return (
<Table>
<thead>
<th>Key</th>
<th>Value</th>
</thead>
<tbody>
{Object.entries(values).map(([key, value]) => (
<tr key={key}>
<td>{key}</td>
<td>{value}</td>
</tr>
))}
</tbody>
</Table>
);
}
function StatusContent(props: Any) {
const status = props.status;
console.log(status);
return (
<div>
<p>{status.name}</p>
<p>
<MessageText message={status.message} />
</p>
<p>
<ValuesTable values={status.values} />
</p>
</div>
);
}
function DirectionIcon(props: Any) {
let icon = <ChevronRight />;
if (props.isOpen) {
icon = <ChevronDown />;
}
if (!props.hasChildren) {
icon = <Dash />;
}
return (
<Button variant="link" onClick={props.onClick}>
{icon}
</Button>
);
}
function ChildStatuses(props: Any) {
const isOpen = props.isOpen;
const node = props.node;
return (
<Collapse in={isOpen}>
<div>
<ListGroup>
{node.children.map((child) => (
<SingleNode key={child.id} node={child} />
))}
</ListGroup>
</div>
</Collapse>
);
}
function SingleNode(props: Any) {
const node = props.node;
const [isOpen, setIsOpen] = useState(false);
const hasChildren = node.children && node.children.length > 0;
const openFunction = () => {
if (hasChildren) {
setIsOpen(!isOpen);
}
};
return (
<ListGroup.Item>
<Container>
<Row>
<Col xs={1}>
<DirectionIcon
isOpen={isOpen}
hasChildren={hasChildren}
onClick={openFunction}
/>
</Col>
<Col xs={1}>
<LevelIndicator level={node.status.level} />
</Col>
<Col xs={"auto"}>
<StatusContent status={node.status} />
</Col>
</Row>
<Row>
<ChildStatuses isOpen={isOpen} node={node} />
</Row>
</Container>
</ListGroup.Item>
);
}