chore: adjust discover (#151)

* fix: add time range to suggestions's request

* fix: adjust DatePicker's display

* chore: hide search info automatically in discover

* chore: adjust discover's histogram

* chore: update release notes

---------

Co-authored-by: yaojiping <yaojiping@infini.ltd>
Co-authored-by: Hardy <luohoufu@infinilabs.com>
This commit is contained in:
yaojp123 2025-02-19 22:09:34 +08:00 committed by GitHub
parent 1cd1f98af4
commit 932a2a46e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 121 additions and 20 deletions

View File

@ -18,9 +18,11 @@ Information about release notes of INFINI Console is provided here.
### Bug fix ### Bug fix
- Fixed the error when querying empty metric data (#144) - Fixed the error when querying empty metric data (#144)
- Fixed empty host when setup step finishes (#147) - Fixed empty host when setup step finishes (#147)
- Fixed the error of obtaining suggestions of field's value in discover
### Improvements ### Improvements
- Update agent config with cluster name (#148) - Update agent config with cluster name (#148)
- Optimize UI of histogram and datepicker in discover (#151)
- Support viewing logs for cluster, node, index health change events (#150) - Support viewing logs for cluster, node, index health change events (#150)
## 1.28.2 (2025-02-15) ## 1.28.2 (2025-02-15)

View File

@ -18,12 +18,13 @@ title: "版本历史"
### Bug fix ### Bug fix
- 修复指标数据为空时的查询错误 (#144) - 修复指标数据为空时的查询错误 (#144)
- 修复初始化结束步骤中主机显示为错误的问题 (#147) - 修复初始化结束步骤中主机显示为错误的问题 (#147)
- 修复数据探索中获取字段值建议的错误
### Improvements ### Improvements
- 优化下发给 Agent 的配置,增加集群名称 (#148) - 优化下发给 Agent 的配置,增加集群名称 (#148)
- 优化柱状图和时间选择器的 UI (#151)
- 集群,节点,索引健康状态变更支持查看日志 (#150) - 集群,节点,索引健康状态变更支持查看日志 (#150)
## 1.28.2 (2025-02-15) ## 1.28.2 (2025-02-15)
### Features ### Features

View File

@ -215,7 +215,7 @@ const DatePicker = (props) => {
isMinimum ? styles.minimum : "" isMinimum ? styles.minimum : ""
} ${className}`} } ${className}`}
> >
<Button.Group className={styles.RangeBox}> <Button.Group className={styles.RangeBox} style={{ width: onRefresh ? 'calc(100% - 64px)' : 'calc(100% - 32px)'}}>
{!isMinimum && ( {!isMinimum && (
<Button <Button
className={`${styles.iconBtn} common-ui-datepicker-backward`} className={`${styles.iconBtn} common-ui-datepicker-backward`}

View File

@ -42,8 +42,8 @@
align-items: center; align-items: center;
margin-left: 4px !important; margin-left: 4px !important;
.play { .play {
min-width: 30px; min-width: 32px;
max-width: 30px; max-width: 32px;
padding: 0; padding: 0;
font-size: 14px; font-size: 14px;
color: #1890ff; color: #1890ff;

View File

@ -121,6 +121,9 @@ function FilterBarUI(props: Props) {
onCancel={() => setIsAddFilterPopoverOpen(false)} onCancel={() => setIsAddFilterPopoverOpen(false)}
key={JSON.stringify(newFilter)} key={JSON.stringify(newFilter)}
services={props.services} services={props.services}
dateRangeFrom={props.dateRangeFrom}
dateRangeTo={props.dateRangeTo}
timeField={props.timeField}
/> />
</div> </div>
</EuiFlexItem> </EuiFlexItem>

View File

@ -313,6 +313,9 @@ class FilterEditorUI extends Component<Props, State> {
onChange={this.onParamsChange} onChange={this.onParamsChange}
data-test-subj="phraseValueInput" data-test-subj="phraseValueInput"
services={this.props.services} services={this.props.services}
dateRangeFrom={this.props.dateRangeFrom}
dateRangeTo={this.props.dateRangeTo}
timeField={this.props.timeField}
/> />
); );
case 'phrases': case 'phrases':
@ -323,6 +326,9 @@ class FilterEditorUI extends Component<Props, State> {
values={this.state.params} values={this.state.params}
onChange={this.onParamsChange} onChange={this.onParamsChange}
services={this.props.services} services={this.props.services}
dateRangeFrom={this.props.dateRangeFrom}
dateRangeTo={this.props.dateRangeTo}
timeField={this.props.timeField}
/> />
); );
case 'range': case 'range':

View File

@ -82,17 +82,30 @@ export class PhraseSuggestorUI<
protected updateSuggestions = debounce(async (query: string = "") => { protected updateSuggestions = debounce(async (query: string = "") => {
if (this.abortController) this.abortController.abort(); if (this.abortController) this.abortController.abort();
this.abortController = new AbortController(); this.abortController = new AbortController();
const { indexPattern, field } = this.props as PhraseSuggestorProps; const { indexPattern, field, dateRangeFrom, dateRangeTo, timeField } = this.props as PhraseSuggestorProps;
if (!field || !this.isSuggestingValues()) { if (!field || !this.isSuggestingValues()) {
return; return;
} }
this.setState({ isLoading: true }); this.setState({ isLoading: true });
const boolFilter = []
if (dateRangeFrom && dateRangeTo && timeField) {
boolFilter.push({
"range": {
[timeField]: {
"gte": dateRangeFrom,
"lte": dateRangeTo
}
}
})
}
const suggestions = await this.props.services.data.autocomplete.getValueSuggestions( const suggestions = await this.props.services.data.autocomplete.getValueSuggestions(
{ {
indexPattern, indexPattern,
field, field,
query, query,
boolFilter,
signal: this.abortController.signal, signal: this.abortController.signal,
} }
); );

View File

@ -50,8 +50,7 @@ class PhraseValueInputUI extends PhraseSuggestorUI<Props> {
} }
private renderWithSuggestions() { private renderWithSuggestions() {
let { suggestions } = this.state; const suggestions = Array.isArray(this.state.suggestions) ? this.state.suggestions : []
suggestions = suggestions || [];
const { value, intl, onChange } = this.props; const { value, intl, onChange } = this.props;
// there are cases when the value is a number, this would cause an exception // there are cases when the value is a number, this would cause an exception
const valueAsStr = String(value); const valueAsStr = String(value);

View File

@ -59,7 +59,7 @@
@include euiBreakpoint("m", "l", "xl") { @include euiBreakpoint("m", "l", "xl") {
.kbnQueryBar__datePickerWrapper { .kbnQueryBar__datePickerWrapper {
// sass-lint:disable-block no-important // sass-lint:disable-block no-important
max-width: 340px; max-width: 400px;
flex-grow: 0 !important; flex-grow: 0 !important;
flex-basis: auto !important; flex-basis: auto !important;
margin-right: -$euiSizeXS; margin-right: -$euiSizeXS;

View File

@ -264,7 +264,7 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) {
return ( return (
<NoDataPopover storage={storage} showNoDataPopover={props.indicateNoData}> <NoDataPopover storage={storage} showNoDataPopover={props.indicateNoData}>
<EuiFlexGroup responsive={false} gutterSize="s"> <EuiFlexGroup responsive={false} gutterSize="s">
{renderHistogram()} {/* {renderHistogram()} */}
{renderDatePicker()} {renderDatePicker()}
<EuiFlexItem grow={false}>{button}</EuiFlexItem> <EuiFlexItem grow={false}>{button}</EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>

View File

@ -484,6 +484,9 @@ class SearchBarUI extends Component<SearchBarProps, State> {
filters={this.props.filters!} filters={this.props.filters!}
onFiltersUpdated={this.props.onFiltersUpdated} onFiltersUpdated={this.props.onFiltersUpdated}
indexPatterns={this.props.indexPatterns!} indexPatterns={this.props.indexPatterns!}
dateRangeFrom={this.state.dateRangeFrom}
dateRangeTo={this.state.dateRangeTo}
timeField={this.props.timeSetting?.timeField}
services={this.props.services} services={this.props.services}
/> />
</div> </div>

View File

@ -1122,7 +1122,7 @@ const Discover = (props) => {
getVisualizations={() => visRef?.current?.getVisualizations()} getVisualizations={() => visRef?.current?.getVisualizations()}
searchInfo={{ searchInfo={{
took, took,
hits, total: hits,
...timeChartProps, ...timeChartProps,
}} }}
selectedQueriesId={selectedQueriesId} selectedQueriesId={selectedQueriesId}
@ -1160,6 +1160,10 @@ const Discover = (props) => {
} }
}} }}
showLayoutListIcon={false} showLayoutListIcon={false}
histogramProps={{
histogramData,
timefilterUpdateHandler
}}
// viewLayout={viewLayout} // viewLayout={viewLayout}
// onViewLayoutChange={(layout) => { // onViewLayoutChange={(layout) => {
// if (layout) { // if (layout) {

View File

@ -0,0 +1,29 @@
import { DiscoverHistogram } from "@/components/vendor/discover/public/application/components/histogram/histogram";
import { Icon, Popover } from "antd"
import { useEffect, useRef, useState } from "react";
import styles from "./index.less";
export default (props) => {
const { histogramData, timefilterUpdateHandler } = props
const [visible, setVisible] = useState(false)
return (
<Popover
visible={visible}
placement="left"
title={null}
overlayClassName={styles.histogram}
content={(
<DiscoverHistogram
chartData={histogramData}
timefilterUpdateHandler={timefilterUpdateHandler}
/>
)}>
<Icon type="bar-chart" style={{color: '#006BB4', cursor: 'pointer'}} onClick={() => {
setVisible(!visible)
}}/>
</Popover>
)
}

View File

@ -0,0 +1,9 @@
.histogram {
z-index: 1;
:global {
.ant-popover-inner-content {
width: 400px;
padding: 0;
}
}
}

View File

@ -5,9 +5,9 @@ import InsightConfig, { ISearchConfig } from "../InsightConfig";
import styles from './index.less'; import styles from './index.less';
import { create, list, remove, update } from "../services/queries"; import { create, list, remove, update } from "../services/queries";
import FullScreen from "../FullScreen"; import FullScreen from "../FullScreen";
import ModeHandler from "../ModeHandler";
import { Icon, message } from "antd"; import { Icon, message } from "antd";
import SearchInfo from "../SearchInfo"; import SearchInfo from "../SearchInfo";
import Histogram from "../Histogram";
import ViewLayout from "../ViewLayout"; import ViewLayout from "../ViewLayout";
export interface IQueries { export interface IQueries {
@ -72,7 +72,8 @@ export default forwardRef((props: IProps, ref: any) => {
onSearchConfigChange, onSearchConfigChange,
showLayoutListIcon, showLayoutListIcon,
viewLayout, viewLayout,
onViewLayoutChange onViewLayoutChange,
histogramProps = {}
} = props; } = props;
const { const {
@ -183,6 +184,7 @@ export default forwardRef((props: IProps, ref: any) => {
return ( return (
<div className={styles.bar}> <div className={styles.bar}>
<SearchInfo {...searchInfo} loading={searchLoading}/> <SearchInfo {...searchInfo} loading={searchLoading}/>
{ histogramProps?.histogramData && <Histogram {...histogramProps}/>}
<SaveQueries <SaveQueries
tags={tags} tags={tags}
onTagsChange={setTags} onTagsChange={setTags}

View File

@ -30,7 +30,7 @@ export interface IProps {
* selected interval * selected interval
*/ */
stateInterval: string; stateInterval: string;
hits: number; total: number;
took?: number; took?: number;
} }
@ -39,7 +39,7 @@ export default ({
dateFormat, dateFormat,
timeRange, timeRange,
stateInterval, stateInterval,
hits, total,
took, took,
}: IProps) => { }: IProps) => {
const [interval, setInterval] = useState(stateInterval); const [interval, setInterval] = useState(stateInterval);
@ -69,7 +69,7 @@ export default ({
> >
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<div style={{ fontSize: 12}}> <div style={{ fontSize: 12}}>
Found <span style={{fontWeight: "bold" }}>{hits}</span>{" "} Found <span style={{fontWeight: "bold" }}>{total}</span>{" "}
records {took && ( records {took && (
<span style={{marginLeft: 5 }}> <span style={{marginLeft: 5 }}>
({took} milliscond) ({took} milliscond)

View File

@ -1,17 +1,40 @@
import { Icon, Popover } from "antd" import { Icon, Popover } from "antd"
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import Info, { IProps } from "./Info"; import Info, { IProps } from "./Info";
import styles from './index.scss'; import styles from './index.scss';
export default (props: IProps & { loading: boolean }) => { export default (props: IProps & { loading: boolean }) => {
const [showResultCount, setShowResultCount] = useState(true); const { loading, total } = props
if (typeof props.hits !== 'number' || props.hits <= 0) return null; const [showResultCount, setShowResultCount] = useState(true);
const timerRef = useRef(null)
const autoHiddenRef = useRef(true)
useEffect(() => {
if (timerRef.current) {
clearTimeout(timerRef.current)
}
if (showResultCount) {
timerRef.current = setTimeout(() => {
if (autoHiddenRef.current) {
setShowResultCount(false)
}
}, 3000);
}
}, [showResultCount])
useEffect(() => {
if (loading) {
autoHiddenRef.current = true
}
}, [loading])
if (typeof total !== 'number' || total <= 0) return null;
return ( return (
<Popover <Popover
visible={!props.loading && showResultCount} visible={!loading && showResultCount}
placement="left" placement="left"
title={null} title={null}
overlayClassName={styles.searchInfo} overlayClassName={styles.searchInfo}
@ -21,7 +44,14 @@ export default (props: IProps & { loading: boolean }) => {
dateFormat={"YYYY-MM-DD H:mm"} dateFormat={"YYYY-MM-DD H:mm"}
/> />
)}> )}>
<Icon type="info-circle" style={{color: '#006BB4', cursor: 'pointer'}} onClick={() => setShowResultCount(!showResultCount)}/> <Icon type="info-circle" style={{color: '#006BB4', cursor: 'pointer'}} onClick={() => {
if (showResultCount) {
autoHiddenRef.current = true
} else {
autoHiddenRef.current = false
}
setShowResultCount(!showResultCount)
}}/>
</Popover> </Popover>
) )
} }