import { Group, Tabs, Center, Space, Box, SegmentedControl, Stack, Button, Skeleton, } from "@mantine/core"; import { IconChartAreaFilled, IconChartLine, IconCheckbox, IconGraph, IconSquare, IconTable, } from "@tabler/icons-react"; import { FC, Suspense, useCallback, useMemo, useState } from "react"; import { useAppDispatch, useAppSelector } from "../../state/hooks"; import { addQueryToHistory, GraphDisplayMode, GraphResolution, removePanel, setExpr, setShowTree, setVisualizer, } from "../../state/queryPageSlice"; import TimeInput from "./TimeInput"; import RangeInput from "./RangeInput"; import ExpressionInput from "./ExpressionInput"; import Graph from "./Graph"; import ResolutionInput from "./ResolutionInput"; import TableTab from "./TableTab"; import TreeView from "./TreeView"; import ErrorBoundary from "../../components/ErrorBoundary"; import ASTNode from "../../promql/ast"; import serializeNode from "../../promql/serialize"; export interface PanelProps { idx: number; metricNames: string[]; } // TODO: This is duplicated everywhere, unify it. const iconStyle = { width: "0.9rem", height: "0.9rem" }; const QueryPanel: FC = ({ idx, metricNames }) => { // Used to indicate to the selected display component that it should retrigger // the query, even if the expression has not changed (e.g. when the user presses // the "Execute" button or hits again). const [retriggerIdx, setRetriggerIdx] = useState(0); const panel = useAppSelector((state) => state.queryPage.panels[idx]); const dispatch = useAppDispatch(); const [selectedNode, setSelectedNode] = useState<{ id: string; node: ASTNode; } | null>(null); const expr = useMemo( () => selectedNode !== null ? serializeNode(selectedNode.node) : panel.expr, [selectedNode, panel.expr] ); const onSelectRange = useCallback( (start: number, end: number) => dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, range: (end - start) * 1000, endTime: end * 1000, }, }) ), // TODO: How to have panel.visualizer in the dependencies, but not re-create // the callback every time it changes by the callback's own update? This leads // to extra renders of the plot further down. [dispatch, idx, panel.visualizer] ); return ( { setRetriggerIdx((idx) => idx + 1); dispatch(setExpr({ idx, expr })); if (!metricNames.includes(expr) && expr.trim() !== "") { dispatch(addQueryToHistory(expr)); } }} treeShown={panel.showTree} setShowTree={(showTree: boolean) => { dispatch(setShowTree({ idx, showTree })); if (!showTree) { setSelectedNode(null); } }} removePanel={() => { dispatch(removePanel(idx)); }} /> {panel.expr.trim() !== "" && panel.showTree && ( {Array.from(Array(20), (_, i) => ( ))} } > )} dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, activeTab: v as "table" | "graph", }, }) ) } keepMounted={false} > }> Table }> Graph dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, range }, }) ) } /> dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, endTime: time }, }) ) } /> { dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, resolution: res, }, }) ); }} /> dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, displayMode: value as GraphDisplayMode, }, }) ) } value={panel.visualizer.displayMode} data={[ { value: GraphDisplayMode.Lines, label: (
Unstacked
), }, { value: GraphDisplayMode.Stacked, label: (
Stacked
), }, { value: GraphDisplayMode.Heatmap, label: (
Heatmap
), }, ]} /> {/* */} {/* dispatch( setVisualizer({ idx, visualizer: { ...panel.visualizer, showExemplars: event.currentTarget.checked, }, }) ) } color={"rgba(34,139,230,.1)"} size="md" label="Show exemplars" thumbIcon={ panel.visualizer.showExemplars ? ( ) : ( ) } /> */}
); }; export default QueryPanel;