2024-02-23 08:37:56 -08:00
|
|
|
import { Alert, Badge, Card, Group, Table, Text, Tooltip } from "@mantine/core";
|
2024-02-21 02:13:48 -08:00
|
|
|
// import { useQuery } from "react-query";
|
2024-03-07 04:16:54 -08:00
|
|
|
import { formatRelative, humanizeDuration, now } from "../lib/formatTime";
|
2024-02-21 02:13:48 -08:00
|
|
|
import {
|
|
|
|
IconAlertTriangle,
|
|
|
|
IconBell,
|
|
|
|
IconDatabaseImport,
|
|
|
|
IconHourglass,
|
|
|
|
IconRefresh,
|
|
|
|
IconRepeat,
|
|
|
|
} from "@tabler/icons-react";
|
|
|
|
import { useSuspenseAPIQuery } from "../api/api";
|
2024-03-07 04:16:54 -08:00
|
|
|
import { RulesMap } from "../api/responseTypes/rules";
|
|
|
|
import badgeClasses from "../Badge.module.css";
|
|
|
|
import RuleDefinition from "../RuleDefinition";
|
2024-02-21 02:13:48 -08:00
|
|
|
|
|
|
|
const healthBadgeClass = (state: string) => {
|
|
|
|
switch (state) {
|
|
|
|
case "ok":
|
2024-02-23 08:37:56 -08:00
|
|
|
return badgeClasses.healthOk;
|
2024-02-21 02:13:48 -08:00
|
|
|
case "err":
|
2024-02-23 08:37:56 -08:00
|
|
|
return badgeClasses.healthErr;
|
2024-02-21 02:13:48 -08:00
|
|
|
case "unknown":
|
2024-02-23 08:37:56 -08:00
|
|
|
return badgeClasses.healthUnknown;
|
2024-02-21 02:13:48 -08:00
|
|
|
default:
|
|
|
|
return "orange";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-03-07 04:16:54 -08:00
|
|
|
export default function RulesPage() {
|
2024-03-07 12:00:43 -08:00
|
|
|
const { data } = useSuspenseAPIQuery<RulesMap>({ path: `/rules` });
|
2024-02-21 02:13:48 -08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
2024-02-23 08:37:56 -08:00
|
|
|
{data.data.groups.map((g, i) => (
|
2024-02-21 02:13:48 -08:00
|
|
|
<Card
|
|
|
|
shadow="xs"
|
|
|
|
withBorder
|
|
|
|
radius="md"
|
|
|
|
p="md"
|
|
|
|
mb="md"
|
2024-02-23 08:37:56 -08:00
|
|
|
key={i} // TODO: Find a stable and definitely unique key.
|
2024-02-21 02:13:48 -08:00
|
|
|
>
|
|
|
|
<Group mb="md" mt="xs" ml="xs" justify="space-between">
|
|
|
|
<Group align="baseline">
|
|
|
|
<Text fz="xl" fw={600} c="var(--mantine-primary-color-filled)">
|
|
|
|
{g.name}
|
|
|
|
</Text>
|
|
|
|
<Text fz="sm" c="gray.6">
|
|
|
|
{g.file}
|
|
|
|
</Text>
|
|
|
|
</Group>
|
|
|
|
<Group>
|
|
|
|
<Tooltip label="Last group evaluation" withArrow>
|
|
|
|
<Badge
|
|
|
|
variant="light"
|
2024-02-23 08:37:56 -08:00
|
|
|
className={badgeClasses.statsBadge}
|
2024-02-21 02:13:48 -08:00
|
|
|
styles={{ label: { textTransform: "none" } }}
|
|
|
|
leftSection={<IconRefresh size={12} />}
|
|
|
|
>
|
|
|
|
{formatRelative(g.lastEvaluation, now())}
|
|
|
|
</Badge>
|
|
|
|
</Tooltip>
|
|
|
|
<Tooltip label="Duration of last group evaluation" withArrow>
|
|
|
|
<Badge
|
|
|
|
variant="light"
|
2024-02-23 08:37:56 -08:00
|
|
|
className={badgeClasses.statsBadge}
|
2024-02-21 02:13:48 -08:00
|
|
|
styles={{ label: { textTransform: "none" } }}
|
|
|
|
leftSection={<IconHourglass size={12} />}
|
|
|
|
>
|
|
|
|
{humanizeDuration(parseFloat(g.evaluationTime) * 1000)}
|
|
|
|
</Badge>
|
|
|
|
</Tooltip>
|
|
|
|
<Tooltip label="Group evaluation interval" withArrow>
|
|
|
|
<Badge
|
|
|
|
variant="light"
|
2024-02-23 08:37:56 -08:00
|
|
|
className={badgeClasses.statsBadge}
|
2024-02-21 02:13:48 -08:00
|
|
|
styles={{ label: { textTransform: "none" } }}
|
|
|
|
leftSection={<IconRepeat size={12} />}
|
|
|
|
>
|
|
|
|
{humanizeDuration(parseFloat(g.interval) * 1000)}
|
|
|
|
</Badge>
|
|
|
|
</Tooltip>
|
|
|
|
</Group>
|
|
|
|
</Group>
|
|
|
|
<Table>
|
|
|
|
<Table.Tbody>
|
|
|
|
{g.rules.map((r) => (
|
2024-02-23 08:37:56 -08:00
|
|
|
// TODO: Find a stable and definitely unique key.
|
2024-02-21 02:13:48 -08:00
|
|
|
<Table.Tr key={r.name}>
|
2024-02-23 08:37:56 -08:00
|
|
|
<Table.Td p="md" py="xl" valign="top">
|
2024-02-21 02:13:48 -08:00
|
|
|
<Group gap="xs" wrap="nowrap">
|
|
|
|
{r.type === "alerting" ? (
|
|
|
|
<IconBell size={14} />
|
|
|
|
) : (
|
|
|
|
<IconDatabaseImport size={14} />
|
|
|
|
)}
|
|
|
|
<Text fz="sm" fw={600}>
|
|
|
|
{r.name}
|
|
|
|
</Text>
|
|
|
|
</Group>
|
|
|
|
<Group mt="md" gap="xs">
|
|
|
|
<Badge className={healthBadgeClass(r.health)}>
|
|
|
|
{r.health}
|
|
|
|
</Badge>
|
|
|
|
|
|
|
|
<Group gap="xs" wrap="wrap">
|
|
|
|
<Tooltip label="Last rule evaluation" withArrow>
|
|
|
|
<Badge
|
|
|
|
variant="light"
|
2024-02-23 08:37:56 -08:00
|
|
|
className={badgeClasses.statsBadge}
|
2024-02-21 02:13:48 -08:00
|
|
|
styles={{ label: { textTransform: "none" } }}
|
|
|
|
leftSection={<IconRefresh size={12} />}
|
|
|
|
>
|
|
|
|
{formatRelative(r.lastEvaluation, now())}
|
|
|
|
</Badge>
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
<Tooltip
|
|
|
|
label="Duration of last rule evaluation"
|
|
|
|
withArrow
|
|
|
|
>
|
|
|
|
<Badge
|
|
|
|
variant="light"
|
2024-02-23 08:37:56 -08:00
|
|
|
className={badgeClasses.statsBadge}
|
2024-02-21 02:13:48 -08:00
|
|
|
styles={{ label: { textTransform: "none" } }}
|
|
|
|
leftSection={<IconHourglass size={12} />}
|
|
|
|
>
|
|
|
|
{humanizeDuration(
|
|
|
|
parseFloat(r.evaluationTime) * 1000
|
|
|
|
)}
|
|
|
|
</Badge>
|
|
|
|
</Tooltip>
|
|
|
|
</Group>
|
|
|
|
</Group>
|
|
|
|
</Table.Td>
|
2024-02-23 08:37:56 -08:00
|
|
|
<Table.Td p="md" py="xl">
|
|
|
|
<RuleDefinition rule={r} />
|
2024-02-21 02:13:48 -08:00
|
|
|
{r.lastError && (
|
|
|
|
<Alert
|
|
|
|
color="red"
|
|
|
|
mt="sm"
|
|
|
|
title="Rule failed to evaluate"
|
|
|
|
icon={<IconAlertTriangle size={14} />}
|
|
|
|
>
|
|
|
|
<strong>Error:</strong> {r.lastError}
|
|
|
|
</Alert>
|
|
|
|
)}
|
|
|
|
</Table.Td>
|
|
|
|
</Table.Tr>
|
|
|
|
))}
|
|
|
|
</Table.Tbody>
|
|
|
|
</Table>
|
|
|
|
</Card>
|
|
|
|
))}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|