mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Add state and label/alertname filter mockups to Alerts page
Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
2f95bbe570
commit
627826783c
135
web/ui/mantine-ui/src/pages/AlertStateMultiSelect.tsx
Normal file
135
web/ui/mantine-ui/src/pages/AlertStateMultiSelect.tsx
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
CheckIcon,
|
||||||
|
CloseButton,
|
||||||
|
Combobox,
|
||||||
|
ComboboxChevron,
|
||||||
|
ComboboxClearButton,
|
||||||
|
Group,
|
||||||
|
Input,
|
||||||
|
Pill,
|
||||||
|
PillGroup,
|
||||||
|
PillsInput,
|
||||||
|
useCombobox,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import badgeClasses from "../Badge.module.css";
|
||||||
|
import { IconFilter } from "@tabler/icons-react";
|
||||||
|
import { IconFilterSearch } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
interface StatePillProps extends React.ComponentPropsWithoutRef<"div"> {
|
||||||
|
value: string;
|
||||||
|
onRemove?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StatePill({ value, onRemove, ...others }: StatePillProps) {
|
||||||
|
return (
|
||||||
|
<Pill
|
||||||
|
fw={600}
|
||||||
|
style={{ textTransform: "uppercase", fontWeight: 600 }}
|
||||||
|
className={
|
||||||
|
value === "inactive"
|
||||||
|
? badgeClasses.healthOk
|
||||||
|
: value === "pending"
|
||||||
|
? badgeClasses.healthWarn
|
||||||
|
: badgeClasses.healthErr
|
||||||
|
}
|
||||||
|
onRemove={onRemove}
|
||||||
|
{...others}
|
||||||
|
withRemoveButton={!!onRemove}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</Pill>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AlertStateMultiSelect() {
|
||||||
|
const combobox = useCombobox({
|
||||||
|
onDropdownClose: () => combobox.resetSelectedOption(),
|
||||||
|
onDropdownOpen: () => combobox.updateSelectedOptionIndex("active"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [values, setValues] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const handleValueSelect = (val: string) =>
|
||||||
|
setValues((current) =>
|
||||||
|
current.includes(val)
|
||||||
|
? current.filter((v) => v !== val)
|
||||||
|
: [...current, val]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleValueRemove = (val: string) =>
|
||||||
|
setValues((current) => current.filter((v) => v !== val));
|
||||||
|
|
||||||
|
const renderedValues = values.map((item) => (
|
||||||
|
<StatePill
|
||||||
|
value={item}
|
||||||
|
onRemove={() => handleValueRemove(item)}
|
||||||
|
key={item}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
const options = ["inactive", "pending", "firing"].map((value) => {
|
||||||
|
return (
|
||||||
|
<Combobox.Option
|
||||||
|
value={value}
|
||||||
|
key={value}
|
||||||
|
active={values.includes(value)}
|
||||||
|
>
|
||||||
|
<Group gap="sm">
|
||||||
|
{values.includes(value) ? <CheckIcon size={12} color="gray" /> : null}
|
||||||
|
<StatePill value={value} />
|
||||||
|
</Group>
|
||||||
|
</Combobox.Option>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Combobox
|
||||||
|
store={combobox}
|
||||||
|
onOptionSubmit={handleValueSelect}
|
||||||
|
withinPortal={false}
|
||||||
|
>
|
||||||
|
<Combobox.DropdownTarget>
|
||||||
|
<PillsInput
|
||||||
|
pointer
|
||||||
|
onClick={() => combobox.toggleDropdown()}
|
||||||
|
miw={200}
|
||||||
|
leftSection={<IconFilter size={14} />}
|
||||||
|
rightSection={
|
||||||
|
values.length > 0 ? (
|
||||||
|
<ComboboxClearButton onClear={() => setValues([])} />
|
||||||
|
) : (
|
||||||
|
<ComboboxChevron />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Pill.Group>
|
||||||
|
{renderedValues.length > 0 ? (
|
||||||
|
renderedValues
|
||||||
|
) : (
|
||||||
|
<Input.Placeholder>Filter by alert state</Input.Placeholder>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Combobox.EventsTarget>
|
||||||
|
<PillsInput.Field
|
||||||
|
type="hidden"
|
||||||
|
onBlur={() => combobox.closeDropdown()}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === "Backspace") {
|
||||||
|
event.preventDefault();
|
||||||
|
handleValueRemove(values[values.length - 1]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Combobox.EventsTarget>
|
||||||
|
</Pill.Group>
|
||||||
|
</PillsInput>
|
||||||
|
</Combobox.DropdownTarget>
|
||||||
|
|
||||||
|
<Combobox.Dropdown>
|
||||||
|
<Combobox.Options>{options}</Combobox.Options>
|
||||||
|
</Combobox.Dropdown>
|
||||||
|
</Combobox>
|
||||||
|
);
|
||||||
|
}
|
|
@ -7,14 +7,24 @@ import {
|
||||||
Badge,
|
Badge,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Box,
|
Box,
|
||||||
Switch,
|
Divider,
|
||||||
|
Button,
|
||||||
|
Fieldset,
|
||||||
|
Checkbox,
|
||||||
|
MultiSelect,
|
||||||
|
Pill,
|
||||||
|
Stack,
|
||||||
|
Input,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useSuspenseAPIQuery } from "../api/api";
|
import { useSuspenseAPIQuery } from "../api/api";
|
||||||
import { AlertingRulesMap } from "../api/responseTypes/rules";
|
import { AlertingRulesMap } from "../api/responseTypes/rules";
|
||||||
import badgeClasses from "../Badge.module.css";
|
import badgeClasses from "../Badge.module.css";
|
||||||
import RuleDefinition from "../RuleDefinition";
|
import RuleDefinition from "../RuleDefinition";
|
||||||
import { formatRelative, now } from "../lib/formatTime";
|
import { formatRelative, now } from "../lib/formatTime";
|
||||||
import { Fragment, useState } from "react";
|
import { Fragment } from "react";
|
||||||
|
import { AlertStateMultiSelect } from "./AlertStateMultiSelect";
|
||||||
|
import { useAppSelector } from "../state/hooks";
|
||||||
|
import { IconSearch } from "@tabler/icons-react";
|
||||||
|
|
||||||
export default function AlertsPage() {
|
export default function AlertsPage() {
|
||||||
const { data } = useSuspenseAPIQuery<AlertingRulesMap>({
|
const { data } = useSuspenseAPIQuery<AlertingRulesMap>({
|
||||||
|
@ -23,7 +33,9 @@ export default function AlertsPage() {
|
||||||
type: "alert",
|
type: "alert",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const [showAnnotations, setShowAnnotations] = useState(false);
|
const showAnnotations = useAppSelector(
|
||||||
|
(state) => state.settings.showAnnotations
|
||||||
|
);
|
||||||
|
|
||||||
const ruleStatsCount = {
|
const ruleStatsCount = {
|
||||||
inactive: 0,
|
inactive: 0,
|
||||||
|
@ -37,19 +49,20 @@ export default function AlertsPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Switch
|
<Group mb="md" mt="xs">
|
||||||
checked={showAnnotations}
|
<AlertStateMultiSelect />
|
||||||
label="Show annotations"
|
<Input
|
||||||
onChange={(event) => setShowAnnotations(event.currentTarget.checked)}
|
flex={1}
|
||||||
mb="md"
|
leftSection={<IconSearch size={14} />}
|
||||||
/>
|
placeholder="Filter by alert name or labels"
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
<Stack>
|
||||||
{data.data.groups.map((g, i) => (
|
{data.data.groups.map((g, i) => (
|
||||||
<Card
|
<Card
|
||||||
shadow="xs"
|
shadow="xs"
|
||||||
withBorder
|
withBorder
|
||||||
radius="md"
|
|
||||||
p="md"
|
p="md"
|
||||||
mb="md"
|
|
||||||
key={i} // TODO: Find a stable and definitely unique key.
|
key={i} // TODO: Find a stable and definitely unique key.
|
||||||
>
|
>
|
||||||
<Group mb="md" mt="xs" ml="xs" justify="space-between">
|
<Group mb="md" mt="xs" ml="xs" justify="space-between">
|
||||||
|
@ -162,7 +175,11 @@ export default function AlertsPage() {
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Tooltip label={a.activeAt}>
|
<Tooltip label={a.activeAt}>
|
||||||
<Box>
|
<Box>
|
||||||
{formatRelative(a.activeAt, now(), "")}
|
{formatRelative(
|
||||||
|
a.activeAt,
|
||||||
|
now(),
|
||||||
|
""
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
|
@ -198,6 +215,7 @@ export default function AlertsPage() {
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue