2024-03-07 04:16:54 -08:00
|
|
|
import {
|
|
|
|
Group,
|
|
|
|
Tabs,
|
|
|
|
Center,
|
|
|
|
Space,
|
|
|
|
Box,
|
|
|
|
SegmentedControl,
|
|
|
|
Stack,
|
|
|
|
} from "@mantine/core";
|
|
|
|
import {
|
|
|
|
IconChartAreaFilled,
|
|
|
|
IconChartGridDots,
|
|
|
|
IconChartLine,
|
|
|
|
IconGraph,
|
|
|
|
IconTable,
|
|
|
|
} from "@tabler/icons-react";
|
2024-08-16 11:41:18 -07:00
|
|
|
import { FC, useCallback, useState } from "react";
|
2024-03-07 04:16:54 -08:00
|
|
|
import { useAppDispatch, useAppSelector } from "../../state/hooks";
|
|
|
|
import {
|
|
|
|
GraphDisplayMode,
|
2024-07-21 14:55:08 -07:00
|
|
|
GraphResolution,
|
2024-03-07 07:58:51 -08:00
|
|
|
removePanel,
|
2024-03-07 04:16:54 -08:00
|
|
|
setExpr,
|
|
|
|
setVisualizer,
|
|
|
|
} from "../../state/queryPageSlice";
|
|
|
|
import TimeInput from "./TimeInput";
|
|
|
|
import RangeInput from "./RangeInput";
|
|
|
|
import ExpressionInput from "./ExpressionInput";
|
2024-03-08 08:44:21 -08:00
|
|
|
import Graph from "./Graph";
|
2024-07-23 14:13:06 -07:00
|
|
|
import ResolutionInput from "./ResolutionInput";
|
2024-08-01 04:13:02 -07:00
|
|
|
import TableTab from "./TableTab";
|
2024-03-07 04:16:54 -08:00
|
|
|
|
|
|
|
export interface PanelProps {
|
|
|
|
idx: number;
|
2024-03-08 06:15:49 -08:00
|
|
|
metricNames: string[];
|
2024-03-07 04:16:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: This is duplicated everywhere, unify it.
|
|
|
|
const iconStyle = { width: "0.9rem", height: "0.9rem" };
|
|
|
|
|
2024-03-08 06:15:49 -08:00
|
|
|
const QueryPanel: FC<PanelProps> = ({ idx, metricNames }) => {
|
2024-03-07 04:16:54 -08:00
|
|
|
// 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 <Enter> again).
|
|
|
|
const [retriggerIdx, setRetriggerIdx] = useState<number>(0);
|
|
|
|
|
|
|
|
const panel = useAppSelector((state) => state.queryPage.panels[idx]);
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
|
2024-08-16 11:41:18 -07:00
|
|
|
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]
|
|
|
|
);
|
|
|
|
|
2024-03-07 04:16:54 -08:00
|
|
|
return (
|
2024-03-14 04:19:41 -07:00
|
|
|
<Stack gap="lg">
|
2024-03-07 04:16:54 -08:00
|
|
|
<ExpressionInput
|
|
|
|
initialExpr={panel.expr}
|
2024-03-08 06:15:49 -08:00
|
|
|
metricNames={metricNames}
|
2024-03-07 04:16:54 -08:00
|
|
|
executeQuery={(expr: string) => {
|
|
|
|
setRetriggerIdx((idx) => idx + 1);
|
|
|
|
dispatch(setExpr({ idx, expr }));
|
|
|
|
}}
|
2024-03-07 07:59:47 -08:00
|
|
|
removePanel={() => {
|
|
|
|
dispatch(removePanel(idx));
|
|
|
|
}}
|
2024-03-07 04:16:54 -08:00
|
|
|
/>
|
2024-07-22 07:31:18 -07:00
|
|
|
<Tabs
|
|
|
|
value={panel.visualizer.activeTab}
|
|
|
|
onChange={(v) =>
|
|
|
|
dispatch(
|
|
|
|
setVisualizer({
|
|
|
|
idx,
|
|
|
|
visualizer: {
|
|
|
|
...panel.visualizer,
|
|
|
|
activeTab: v as "table" | "graph",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
keepMounted={false}
|
|
|
|
>
|
2024-03-07 04:16:54 -08:00
|
|
|
<Tabs.List>
|
|
|
|
<Tabs.Tab value="table" leftSection={<IconTable style={iconStyle} />}>
|
|
|
|
Table
|
|
|
|
</Tabs.Tab>
|
|
|
|
<Tabs.Tab value="graph" leftSection={<IconGraph style={iconStyle} />}>
|
|
|
|
Graph
|
|
|
|
</Tabs.Tab>
|
|
|
|
</Tabs.List>
|
2024-03-07 07:58:51 -08:00
|
|
|
<Tabs.Panel pt="sm" value="table">
|
2024-08-01 04:13:02 -07:00
|
|
|
<TableTab panelIdx={idx} retriggerIdx={retriggerIdx} />
|
2024-03-07 04:16:54 -08:00
|
|
|
</Tabs.Panel>
|
|
|
|
<Tabs.Panel
|
2024-03-07 07:58:51 -08:00
|
|
|
pt="sm"
|
2024-03-07 04:16:54 -08:00
|
|
|
value="graph"
|
|
|
|
// style={{ border: "1px solid lightgrey", borderTop: "none" }}
|
|
|
|
>
|
|
|
|
<Group mt="xs" justify="space-between">
|
|
|
|
<Group>
|
|
|
|
<RangeInput
|
|
|
|
range={panel.visualizer.range}
|
|
|
|
onChangeRange={(range) =>
|
|
|
|
dispatch(
|
|
|
|
setVisualizer({
|
|
|
|
idx,
|
|
|
|
visualizer: { ...panel.visualizer, range },
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
<TimeInput
|
|
|
|
time={panel.visualizer.endTime}
|
|
|
|
range={panel.visualizer.range}
|
|
|
|
description="End time"
|
|
|
|
onChangeTime={(time) =>
|
|
|
|
dispatch(
|
|
|
|
setVisualizer({
|
|
|
|
idx,
|
|
|
|
visualizer: { ...panel.visualizer, endTime: time },
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
/>
|
2024-07-23 14:13:06 -07:00
|
|
|
<ResolutionInput
|
|
|
|
resolution={panel.visualizer.resolution}
|
|
|
|
range={panel.visualizer.range}
|
|
|
|
onChangeResolution={(res: GraphResolution) => {
|
|
|
|
dispatch(
|
|
|
|
setVisualizer({
|
|
|
|
idx,
|
|
|
|
visualizer: {
|
|
|
|
...panel.visualizer,
|
|
|
|
resolution: res,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
2024-07-21 09:26:11 -07:00
|
|
|
}}
|
|
|
|
/>
|
2024-03-07 04:16:54 -08:00
|
|
|
</Group>
|
|
|
|
|
|
|
|
<SegmentedControl
|
|
|
|
onChange={(value) =>
|
|
|
|
dispatch(
|
|
|
|
setVisualizer({
|
|
|
|
idx,
|
|
|
|
visualizer: {
|
|
|
|
...panel.visualizer,
|
|
|
|
displayMode: value as GraphDisplayMode,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
value={panel.visualizer.displayMode}
|
|
|
|
data={[
|
|
|
|
{
|
|
|
|
value: GraphDisplayMode.Lines,
|
|
|
|
label: (
|
|
|
|
<Center>
|
|
|
|
<IconChartLine style={iconStyle} />
|
|
|
|
<Box ml={10}>Unstacked</Box>
|
|
|
|
</Center>
|
|
|
|
),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
value: GraphDisplayMode.Stacked,
|
|
|
|
label: (
|
|
|
|
<Center>
|
|
|
|
<IconChartAreaFilled style={iconStyle} />
|
|
|
|
<Box ml={10}>Stacked</Box>
|
|
|
|
</Center>
|
|
|
|
),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
value: GraphDisplayMode.Heatmap,
|
|
|
|
label: (
|
|
|
|
<Center>
|
|
|
|
<IconChartGridDots style={iconStyle} />
|
|
|
|
<Box ml={10}>Heatmap</Box>
|
|
|
|
</Center>
|
|
|
|
),
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
{/* <Switch color="gray" defaultChecked label="Show exemplars" /> */}
|
|
|
|
{/* <Switch
|
|
|
|
checked={panel.visualizer.showExemplars}
|
|
|
|
onChange={(event) =>
|
|
|
|
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 ? (
|
|
|
|
<IconCheck
|
|
|
|
style={{ width: "0.9rem", height: "0.9rem" }}
|
|
|
|
color={"rgba(34,139,230,.1)"}
|
|
|
|
stroke={3}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<IconX
|
|
|
|
style={{ width: "0.9rem", height: "0.9rem" }}
|
|
|
|
color="rgba(34,139,230,.1)"
|
|
|
|
stroke={3}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
/> */}
|
|
|
|
</Group>
|
|
|
|
<Space h="lg" />
|
2024-03-08 08:44:21 -08:00
|
|
|
<Graph
|
|
|
|
expr={panel.expr}
|
|
|
|
endTime={panel.visualizer.endTime}
|
|
|
|
range={panel.visualizer.range}
|
|
|
|
resolution={panel.visualizer.resolution}
|
|
|
|
showExemplars={panel.visualizer.showExemplars}
|
|
|
|
displayMode={panel.visualizer.displayMode}
|
|
|
|
retriggerIdx={retriggerIdx}
|
2024-08-16 11:41:18 -07:00
|
|
|
onSelectRange={onSelectRange}
|
2024-03-08 08:44:21 -08:00
|
|
|
/>
|
2024-03-07 04:16:54 -08:00
|
|
|
</Tabs.Panel>
|
|
|
|
</Tabs>
|
|
|
|
</Stack>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default QueryPanel;
|