-
+
+
{t('statement.pages.detail.head.back')}
+
+ }
+ >
{error && }
{plans && plans.length > 0 && (
diff --git a/ui/lib/apps/Statement/pages/List/TimeRangeSelector.tsx b/ui/lib/apps/Statement/pages/List/TimeRangeSelector.tsx
index b6cb3255d4..2cf760603c 100644
--- a/ui/lib/apps/Statement/pages/List/TimeRangeSelector.tsx
+++ b/ui/lib/apps/Statement/pages/List/TimeRangeSelector.tsx
@@ -48,6 +48,15 @@ export const DEFAULT_TIME_RANGE: TimeRange = {
value: 30 * 60,
}
+export function stringifyTimeRange(timeRange?: TimeRange): string {
+ let t2 = timeRange ?? DEFAULT_TIME_RANGE
+ if (t2.type === 'absolute') {
+ return `${t2.type}_${t2.value[0]}_${t2.value[1]}`
+ } else {
+ return `${t2.type}_${t2.value}`
+ }
+}
+
// timePoints are descent array
function findNearTimePoint(timePoint: number, timePoints: number[]): number {
if (timePoints.length === 0) {
diff --git a/ui/lib/apps/Statement/pages/List/index.tsx b/ui/lib/apps/Statement/pages/List/index.tsx
index 28368a8c9c..4316f40d0e 100644
--- a/ui/lib/apps/Statement/pages/List/index.tsx
+++ b/ui/lib/apps/Statement/pages/List/index.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react'
+import React, { useState, useContext } from 'react'
import {
Space,
Tooltip,
@@ -22,6 +22,7 @@ import {
import { ScrollablePane } from 'office-ui-fabric-react/lib/ScrollablePane'
import { useTranslation } from 'react-i18next'
+import { CacheContext } from '@lib/utils/useCache'
import { Card, ColumnsSelector, Toolbar, MultiSelect } from '@lib/components'
import { StatementsTable } from '../../components'
@@ -39,6 +40,8 @@ const STMT_SHOW_FULL_SQL = 'statement.show_full_sql'
export default function StatementsOverview() {
const { t } = useTranslation()
+ const statementCacheMgr = useContext(CacheContext)
+
const [showSettings, setShowSettings] = useState(false)
const [visibleColumnKeys, setVisibleColumnKeys] = useLocalStorageState(
STMT_VISIBLE_COLUMN_KEYS,
@@ -49,7 +52,11 @@ export default function StatementsOverview() {
false
)
- const controller = useStatementTableController(visibleColumnKeys, showFullSQL)
+ const controller = useStatementTableController(
+ statementCacheMgr,
+ visibleColumnKeys,
+ showFullSQL
+ )
const {
queryOptions,
setQueryOptions,
diff --git a/ui/lib/apps/Statement/utils/useStatementTableController.ts b/ui/lib/apps/Statement/utils/useStatementTableController.ts
index afb26a33e7..dae7e097f2 100644
--- a/ui/lib/apps/Statement/utils/useStatementTableController.ts
+++ b/ui/lib/apps/Statement/utils/useStatementTableController.ts
@@ -7,8 +7,11 @@ import client, {
StatementModel,
StatementTimeRange,
} from '@lib/client'
-import { IColumnKeys } from '@lib/components'
+import { IColumnKeys, stringifyTimeRange } from '@lib/components'
+import { getSelectedFields } from '@lib/utils/tableColumnFactory'
+import { CacheMgr } from '@lib/utils/useCache'
import useOrderState, { IOrderOptions } from '@lib/utils/useOrderState'
+import useCacheItemIndex from '@lib/utils/useCacheItemIndex'
import {
calcValidStatementTimeRange,
@@ -16,7 +19,6 @@ import {
TimeRange,
} from '../pages/List/TimeRangeSelector'
import { derivedFields, statementColumns } from './tableColumns'
-import { getSelectedFields } from '@lib/utils/tableColumnFactory'
export const DEF_STMT_COLUMN_KEYS: IColumnKeys = {
digest_text: true,
@@ -70,9 +72,13 @@ export interface IStatementTableController {
downloadCSV: () => Promise
downloading: boolean
+
+ saveClickedItemIndex: (idx: number) => void
+ getClickedItemIndex: () => number
}
export default function useStatementTableController(
+ cacheMgr: CacheMgr | null,
visibleColumnKeys: IColumnKeys,
showFullSQL: boolean,
options?: IStatementQueryOptions,
@@ -121,8 +127,28 @@ export default function useStatementTableController(
const [errors, setErrors] = useState([])
+ useEffect(() => {
+ errors.length && setLoadingStatements(false)
+ }, [errors])
+
+ const selectedFields = useMemo(
+ () => getSelectedFields(visibleColumnKeys, derivedFields).join(','),
+ [visibleColumnKeys]
+ )
+
+ const cacheKey = useMemo(() => {
+ const { schemas, stmtTypes, searchText, timeRange } = queryOptions
+ const cacheKey = `${schemas.join(',')}_${stmtTypes.join(
+ ','
+ )}_${searchText}_${stringifyTimeRange(timeRange)}_${selectedFields}`
+ return cacheKey
+ }, [queryOptions, selectedFields])
+
function refresh() {
+ cacheMgr?.remove(cacheKey)
+
setErrors([])
+ setLoadingStatements(true)
setRefreshTimes((prev) => prev + 1)
}
@@ -177,11 +203,6 @@ export default function useStatementTableController(
queryStmtTypes()
}, [refreshTimes])
- const selectedFields = useMemo(
- () => getSelectedFields(visibleColumnKeys, derivedFields).join(','),
- [visibleColumnKeys]
- )
-
const tableColumns = useMemo(
() => statementColumns(statements, showFullSQL),
[statements, showFullSQL]
@@ -189,12 +210,16 @@ export default function useStatementTableController(
useEffect(() => {
async function queryStatementList() {
- if (allTimeRanges.length === 0) {
- setStatements([])
+ const cacheItem = cacheMgr?.get(cacheKey)
+ if (cacheItem) {
+ setStatements(cacheItem)
setLoadingStatements(false)
return
}
+ if (allTimeRanges.length === 0) {
+ return
+ }
setLoadingStatements(true)
try {
const res = await client
@@ -211,6 +236,7 @@ export default function useStatementTableController(
}
)
setStatements(res?.data || [])
+ cacheMgr?.set(cacheKey, res?.data || [])
setErrors([])
} catch (e) {
setErrors((prev) => prev.concat(e))
@@ -219,7 +245,14 @@ export default function useStatementTableController(
}
queryStatementList()
- }, [queryOptions, allTimeRanges, validTimeRange, selectedFields])
+ }, [
+ queryOptions,
+ allTimeRanges,
+ validTimeRange,
+ selectedFields,
+ cacheKey,
+ cacheMgr,
+ ])
const [downloading, setDownloading] = useState(false)
@@ -243,6 +276,10 @@ export default function useStatementTableController(
}
}
+ const { saveClickedItemIndex, getClickedItemIndex } = useCacheItemIndex(
+ cacheMgr
+ )
+
return {
queryOptions,
setQueryOptions,
@@ -265,5 +302,8 @@ export default function useStatementTableController(
downloadCSV,
downloading,
+
+ saveClickedItemIndex,
+ getClickedItemIndex,
}
}
diff --git a/ui/lib/apps/UserProfile/index.tsx b/ui/lib/apps/UserProfile/index.tsx
index 03b8f0b38b..07d9d525ff 100644
--- a/ui/lib/apps/UserProfile/index.tsx
+++ b/ui/lib/apps/UserProfile/index.tsx
@@ -42,7 +42,19 @@ import { getValueFormat } from '@baurine/grafana-value-formats'
import ReactMarkdown from 'react-markdown'
const DEFAULT_FORM_ITEM_STYLE = { width: 200 }
-const SHARE_SESSION_EXPIRY_HOURS = [0.25, 0.5, 1, 2, 3, 6, 12, 24]
+const SHARE_SESSION_EXPIRY_HOURS = [
+ 0.25,
+ 0.5,
+ 1,
+ 2,
+ 3,
+ 6,
+ 12,
+ 24,
+ 24 * 3,
+ 24 * 7,
+ 24 * 30,
+]
function ShareSessionButton() {
const { t } = useTranslation()
diff --git a/ui/lib/components/CardTable/index.module.less b/ui/lib/components/CardTable/index.module.less
index 8c57c451f3..af3857d6a5 100644
--- a/ui/lib/components/CardTable/index.module.less
+++ b/ui/lib/components/CardTable/index.module.less
@@ -51,6 +51,10 @@
cursor: pointer;
}
+.highlightRow {
+ border: 1px solid;
+}
+
.cardTableContent {
margin-left: -@padding-page;
margin-right: -@padding-page;
diff --git a/ui/lib/components/CardTable/index.tsx b/ui/lib/components/CardTable/index.tsx
index 0073f11c4c..6272c876f5 100644
--- a/ui/lib/components/CardTable/index.tsx
+++ b/ui/lib/components/CardTable/index.tsx
@@ -7,11 +7,12 @@ import {
DetailsList,
DetailsListLayoutMode,
IColumn,
+ IDetailsList,
IDetailsListProps,
SelectionMode,
} from 'office-ui-fabric-react/lib/DetailsList'
import { Sticky, StickyPositionType } from 'office-ui-fabric-react/lib/Sticky'
-import React, { useCallback, useMemo } from 'react'
+import React, { useCallback, useEffect, useMemo, useRef } from 'react'
import AnimatedSkeleton from '../AnimatedSkeleton'
import Card from '../Card'
@@ -96,9 +97,10 @@ export interface ICardTableProps extends IDetailsListProps {
itemIndex: number,
ev: React.MouseEvent
) => void
+ clickedRowIndex?: number
}
-function useRenderClickableRow(onRowClicked) {
+function useRenderClickableRow(onRowClicked, clickedRowIdx) {
return useCallback(
(props, defaultRender) => {
if (!props) {
@@ -106,14 +108,16 @@ function useRenderClickableRow(onRowClicked) {
}
return (
onRowClicked?.(props.item, props.itemIndex, ev)}
>
{defaultRender!(props)}
)
},
- [onRowClicked]
+ [onRowClicked, clickedRowIdx]
)
}
@@ -146,11 +150,15 @@ export default function CardTable(props: ICardTableProps) {
desc = true,
onChangeOrder,
onRowClicked,
+ clickedRowIndex,
columns,
items,
...restProps
} = props
- const renderClickableRow = useRenderClickableRow(onRowClicked)
+ const renderClickableRow = useRenderClickableRow(
+ onRowClicked,
+ clickedRowIndex || -1
+ )
const onColumnClick = usePersistFn(
(_ev: React.MouseEvent, column: IColumn) => {
@@ -207,6 +215,13 @@ export default function CardTable(props: ICardTableProps) {
return newItems
}, [visibleItemsCount, items, orderBy, finalColumns])
+ const tableRef = useRef(null)
+ useEffect(() => {
+ if ((clickedRowIndex ?? -1) > 0) {
+ tableRef.current?.scrollToIndex(clickedRowIndex!)
+ }
+ })
+
return (
diff --git a/ui/lib/components/TimeRangeSelector/index.tsx b/ui/lib/components/TimeRangeSelector/index.tsx
index dba1292bd6..d811bbae5a 100644
--- a/ui/lib/components/TimeRangeSelector/index.tsx
+++ b/ui/lib/components/TimeRangeSelector/index.tsx
@@ -56,6 +56,15 @@ export function calcTimeRange(timeRange?: TimeRange): [number, number] {
}
}
+export function stringifyTimeRange(timeRange?: TimeRange): string {
+ let t2 = timeRange ?? DEFAULT_TIME_RANGE
+ if (t2.type === 'absolute') {
+ return `${t2.type}_${t2.value[0]}_${t2.value[1]}`
+ } else {
+ return `${t2.type}_${t2.value}`
+ }
+}
+
export interface ITimeRangeSelectorProps {
value?: TimeRange
onChange?: (val: TimeRange) => void
diff --git a/ui/lib/utils/useCache.ts b/ui/lib/utils/useCache.ts
new file mode 100644
index 0000000000..a3506edd20
--- /dev/null
+++ b/ui/lib/utils/useCache.ts
@@ -0,0 +1,69 @@
+import { useRef, createContext } from 'react'
+
+type CacheItem = {
+ expireAt: number
+ data: any
+}
+
+type Cache = Record