From f30a58afaf554265999795459f7fe10d4b79e835 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Thu, 1 Aug 2024 13:13:02 +0200 Subject: [PATCH] Completely refactor DataTable, show query infos and warnings Signed-off-by: Julius Volz --- web/ui/mantine-ui/src/api/api.ts | 1 + .../mantine-ui/src/pages/query/DataTable.tsx | 372 ++++++------------ .../mantine-ui/src/pages/query/QueryPanel.tsx | 4 +- .../mantine-ui/src/pages/query/TableTab.tsx | 131 ++++++ 4 files changed, 255 insertions(+), 253 deletions(-) create mode 100644 web/ui/mantine-ui/src/pages/query/TableTab.tsx diff --git a/web/ui/mantine-ui/src/api/api.ts b/web/ui/mantine-ui/src/api/api.ts index 163a1aae38..baef0b72c5 100644 --- a/web/ui/mantine-ui/src/api/api.ts +++ b/web/ui/mantine-ui/src/api/api.ts @@ -7,6 +7,7 @@ export type SuccessAPIResponse = { status: "success"; data: T; warnings?: string[]; + infos?: string[]; }; export type ErrorAPIResponse = { diff --git a/web/ui/mantine-ui/src/pages/query/DataTable.tsx b/web/ui/mantine-ui/src/pages/query/DataTable.tsx index 4825ca1cc9..5b0276b52f 100644 --- a/web/ui/mantine-ui/src/pages/query/DataTable.tsx +++ b/web/ui/mantine-ui/src/pages/query/DataTable.tsx @@ -1,17 +1,8 @@ -import { - FC, - ReactNode, - useEffect, - useId, - useLayoutEffect, - useState, -} from "react"; +import { FC, ReactNode, useState } from "react"; import { Table, Alert, - Skeleton, Box, - LoadingOverlay, SegmentedControl, ScrollArea, Group, @@ -26,7 +17,6 @@ import { RangeSamples, } from "../../api/responseTypes/query"; import SeriesName from "./SeriesName"; -import { useAPIQuery } from "../../api/api"; import classes from "./DataTable.module.css"; import dayjs from "dayjs"; import timezone from "dayjs/plugin/timezone"; @@ -35,13 +25,10 @@ import HistogramChart from "./HistogramChart"; import { Histogram } from "../../types/types"; import { bucketRangeString } from "./HistogramHelpers"; import { useSettings } from "../../state/settingsSlice"; -import { useAppDispatch, useAppSelector } from "../../state/hooks"; -import { setVisualizer } from "../../state/queryPageSlice"; -import TimeInput from "./TimeInput"; dayjs.extend(timezone); -const maxFormattableSeries = 1000; -const maxDisplayableSeries = 10000; +const maxFormattableSeries = 10; +const maxDisplayableSeries = 100; const limitSeries = ( series: S[], @@ -54,256 +41,139 @@ const limitSeries = ( }; export interface DataTableProps { - panelIdx: number; - retriggerIdx: number; + data: InstantQueryResult; + limitResults: boolean; + setLimitResults: (limit: boolean) => void; } -const DataTable: FC = ({ panelIdx, retriggerIdx }) => { +const DataTable: FC = ({ + data, + limitResults, + setLimitResults, +}) => { const [scale, setScale] = useState("exponential"); - const [limitResults, setLimitResults] = useState(true); - const [responseTime, setResponseTime] = useState(0); - - const { expr, visualizer } = useAppSelector( - (state) => state.queryPage.panels[panelIdx] - ); - const dispatch = useAppDispatch(); - - const { endTime, range } = visualizer; - - const { data, error, isFetching, isLoading, refetch } = - useAPIQuery({ - key: useId(), - path: "/query", - params: { - query: expr, - time: `${(endTime !== null ? endTime : Date.now()) / 1000}`, - }, - enabled: expr !== "", - recordResponseTime: setResponseTime, - }); - - useEffect(() => { - expr !== "" && refetch(); - }, [retriggerIdx, refetch, expr, endTime]); - - useLayoutEffect(() => { - setLimitResults(true); - }, [data, isFetching]); - const { useLocalTime } = useSettings(); - // Show a skeleton only on the first load, not on subsequent ones. - if (isLoading) { - return ( - - {Array.from(Array(5), (_, i) => ( - - ))} - - ); - } - - if (error) { - return ( - } - > - {error.message} - - ); - } - - if (data === undefined) { - return No data queried yet; - } - - const { result, resultType } = data.data; - - if (result.length === 0) { - return ( - }> - This query returned no data. - - ); - } - + const { result, resultType } = data; const doFormat = result.length <= maxFormattableSeries; return ( - - {isLoading ? ( - - {Array.from(Array(5), (_, i) => ( - - ))} - - ) : data === undefined ? ( - No data queried yet - ) : result.length === 0 ? ( - }> - This query returned no data. - - ) : ( - <> - - - dispatch( - setVisualizer({ - idx: panelIdx, - visualizer: { ...visualizer, endTime: time }, - }) - ) - } - /> - - Load time: {responseTime}ms   Result series: {result.length} - - + + {limitResults && + ["vector", "matrix"].includes(resultType) && + result.length > maxDisplayableSeries && ( + } + title="Showing limited results" + > + Fetched {data.result.length} metrics, only displaying first{" "} + {maxDisplayableSeries} for performance reasons. + setLimitResults(false)}> + Show all results + + + )} - {limitResults && - ["vector", "matrix"].includes(resultType) && - result.length > maxDisplayableSeries && ( + {!doFormat && ( + } + > + Showing more than {maxFormattableSeries} series, turning off label + formatting to improve rendering performance. + + )} + + + + + {resultType === "vector" ? ( + limitSeries(result, limitResults).map((s, idx) => ( + + + + + + {s.value && s.value[1]} + {s.histogram && ( + + + + + + Count: {s.histogram[1].count} + + + Sum: {s.histogram[1].sum} + + + + x-axis scale: + + + + {histogramTable(s.histogram[1])} + + )} + + + )) + ) : resultType === "matrix" ? ( + limitSeries(result, limitResults).map((s, idx) => ( + + + + + + {s.values && + s.values.map((v, idx) => ( +
+ {v[1]}{" "} + + @ {v[0]} + +
+ ))} +
+
+ )) + ) : resultType === "scalar" ? ( + + Scalar value + {result[1]} + + ) : resultType === "string" ? ( + + String value + {result[1]} + + ) : ( } - title="Showing limited results" > - Fetched {data.data.result.length} metrics, only displaying first{" "} - {maxDisplayableSeries} for performance reasons. - setLimitResults(false)}> - Show all results - + Invalid result value type )} - - {!doFormat && ( - } - > - Showing more than {maxFormattableSeries} series, turning off label - formatting for performance reasons. - - )} - - - , - }} - styles={{ loader: { width: "100%", height: "100%" } }} - /> - -
- - {resultType === "vector" ? ( - limitSeries(result, limitResults).map( - (s, idx) => ( - - - - - - {s.value && s.value[1]} - {s.histogram && ( - - - - - - Count:{" "} - {s.histogram[1].count} - - - Sum: {s.histogram[1].sum} - - - - x-axis scale: - - - - {histogramTable(s.histogram[1])} - - )} - - - ) - ) - ) : resultType === "matrix" ? ( - limitSeries(result, limitResults).map( - (s, idx) => ( - - - - - - {s.values && - s.values.map((v, idx) => ( -
- {v[1]}{" "} - - @ {v[0]} - -
- ))} -
-
- ) - ) - ) : resultType === "scalar" ? ( - - Scalar value - - {result[1]} - - - ) : resultType === "string" ? ( - - String value - {result[1]} - - ) : ( - } - > - Invalid result value type - - )} -
-
-
- - )} + + +
); }; diff --git a/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx b/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx index 6e2814f128..fce74d55b1 100644 --- a/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx +++ b/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx @@ -23,12 +23,12 @@ import { setExpr, setVisualizer, } from "../../state/queryPageSlice"; -import DataTable from "./DataTable"; import TimeInput from "./TimeInput"; import RangeInput from "./RangeInput"; import ExpressionInput from "./ExpressionInput"; import Graph from "./Graph"; import ResolutionInput from "./ResolutionInput"; +import TableTab from "./TableTab"; export interface PanelProps { idx: number; @@ -84,7 +84,7 @@ const QueryPanel: FC = ({ idx, metricNames }) => { - + = ({ panelIdx, retriggerIdx }) => { + const [responseTime, setResponseTime] = useState(0); + const [limitResults, setLimitResults] = useState(true); + + const { expr, visualizer } = useAppSelector( + (state) => state.queryPage.panels[panelIdx] + ); + const dispatch = useAppDispatch(); + + const { endTime, range } = visualizer; + + const { data, error, isFetching, refetch } = useAPIQuery({ + key: useId(), + path: "/query", + params: { + query: expr, + time: `${(endTime !== null ? endTime : Date.now()) / 1000}`, + }, + enabled: expr !== "", + recordResponseTime: setResponseTime, + }); + + useEffect(() => { + expr !== "" && refetch(); + }, [retriggerIdx, refetch, expr, endTime]); + + useLayoutEffect(() => { + setLimitResults(true); + }, [data, isFetching]); + + return ( + + + + dispatch( + setVisualizer({ + idx: panelIdx, + visualizer: { ...visualizer, endTime: time }, + }) + ) + } + /> + {!isFetching && data !== undefined && ( + + Load time: {responseTime}ms   Result series:{" "} + {data.data.result.length} + + )} + + {isFetching ? ( + + {Array.from(Array(5), (_, i) => ( + + ))} + + ) : error !== null ? ( + } + > + {error.message} + + ) : data === undefined ? ( + No data queried yet + ) : ( + <> + {data.data.result.length === 0 && ( + } + > + This query returned no data. + + )} + + {data.warnings?.map((w, idx) => ( + } + > + {w} + + ))} + + {data.infos?.map((w, idx) => ( + } + > + {w} + + ))} + + + )} + + ); +}; + +export default TableTab;