Skip to content

Commit 312f026

Browse files
authored
feat(new-webui): Adds search job ID and number of search results to search page. (#962)
1 parent 1b2d550 commit 312f026

File tree

10 files changed

+173
-8
lines changed

10 files changed

+173
-8
lines changed

components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SearchButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import SubmitButton from "./SubmitButton";
1010
* @return
1111
*/
1212
const SearchButton = () => {
13-
const store = useSearchStore();
13+
const searchUiState = useSearchStore((state) => state.searchUiState);
1414

1515
return (
16-
(store.searchUiState === SEARCH_UI_STATE.QUERYING) ?
16+
(searchUiState === SEARCH_UI_STATE.QUERYING) ?
1717
<CancelButton/> :
1818
<SubmitButton/>
1919
);

components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/search-requests.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
CLP_STORAGE_ENGINES,
1212
SETTINGS_STORAGE_ENGINE,
1313
} from "../../../config";
14-
import useSearchStore from "../SearchState/index";
14+
import useSearchStore, {SEARCH_STATE_DEFAULT} from "../SearchState/";
1515
import {SEARCH_UI_STATE} from "../SearchState/typings";
1616
import {unquoteString} from "./utils";
1717

@@ -54,7 +54,7 @@ const handleClearResults = () => {
5454
const handleQuerySubmit = (payload: QueryJobCreationSchema) => {
5555
const store = useSearchStore.getState();
5656

57-
// Buttons to submit a query should be disabled while an existing query is in progress.
57+
// User should NOT be able to submit a new query while an existing query is in progress.
5858
if (
5959
store.searchUiState !== SEARCH_UI_STATE.DEFAULT &&
6060
store.searchUiState !== SEARCH_UI_STATE.DONE
@@ -64,9 +64,6 @@ const handleQuerySubmit = (payload: QueryJobCreationSchema) => {
6464
return;
6565
}
6666

67-
handleClearResults();
68-
69-
7067
if (CLP_STORAGE_ENGINES.CLP_S === SETTINGS_STORAGE_ENGINE) {
7168
try {
7269
payload.queryString = unquoteString(payload.queryString, '"', "\\");
@@ -84,6 +81,10 @@ const handleQuerySubmit = (payload: QueryJobCreationSchema) => {
8481
}
8582
}
8683

84+
handleClearResults();
85+
86+
store.updateNumSearchResultsTable(SEARCH_STATE_DEFAULT.numSearchResultsTable);
87+
store.updateNumSearchResultsTimeline(SEARCH_STATE_DEFAULT.numSearchResultsTimeline);
8788
store.updateSearchUiState(SEARCH_UI_STATE.QUERY_ID_PENDING);
8889

8990
submitQuery(payload)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {useMemo} from "react";
2+
3+
import {
4+
GetProps,
5+
Typography,
6+
} from "antd";
7+
8+
import useSearchStore from "../SearchState/index";
9+
import {SEARCH_UI_STATE} from "../SearchState/typings";
10+
11+
12+
const {Text} = Typography;
13+
type TextTypes = GetProps<typeof Text>["type"];
14+
15+
16+
/**
17+
* Displays the number of search results.
18+
*
19+
* @return
20+
*/
21+
const Results = () => {
22+
const numSearchResultsTimeline = useSearchStore((state) => state.numSearchResultsTimeline);
23+
const numSearchResultsTable = useSearchStore((state) => state.numSearchResultsTable);
24+
const searchUiState = useSearchStore((state) => state.searchUiState);
25+
26+
// Number of results is the maximum of the number of results in the timeline and table. The
27+
// timeline may have more results since the table results are capped. Having two sources may
28+
// provide more timely updates to the user.
29+
const numResults = useMemo(
30+
() => Math.max(numSearchResultsTimeline, numSearchResultsTable),
31+
[
32+
numSearchResultsTimeline,
33+
numSearchResultsTable,
34+
]
35+
);
36+
37+
let textType: TextTypes;
38+
switch (searchUiState) {
39+
case SEARCH_UI_STATE.QUERYING:
40+
textType = "warning";
41+
break;
42+
case SEARCH_UI_STATE.DEFAULT:
43+
case SEARCH_UI_STATE.QUERY_ID_PENDING:
44+
textType = "secondary";
45+
break;
46+
case SEARCH_UI_STATE.DONE:
47+
textType = "success";
48+
break;
49+
default:
50+
textType = "secondary";
51+
}
52+
53+
return (
54+
<Text
55+
strong={true}
56+
type={textType}
57+
>
58+
{numResults}
59+
</Text>
60+
);
61+
};
62+
63+
export default Results;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.status {
2+
margin-left: 2px;
3+
margin-top: 4px;
4+
}
5+
6+
.badge {
7+
padding-bottom: 4px;
8+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import {Typography} from "antd";
2+
3+
import useSearchStore from "../SearchState/index";
4+
import {SEARCH_UI_STATE} from "../SearchState/typings";
5+
import styles from "./index.module.css";
6+
import Results from "./Results";
7+
8+
9+
const {Text} = Typography;
10+
11+
/**
12+
* Displays the search job ID and the number of results found.
13+
*
14+
* @return
15+
*/
16+
const SearchQueryStatus = () => {
17+
const {
18+
searchJobId,
19+
searchUiState,
20+
} = useSearchStore();
21+
22+
return (
23+
<div className={styles["status"]}>
24+
{(searchUiState === SEARCH_UI_STATE.QUERYING ||
25+
searchUiState === SEARCH_UI_STATE.DONE) && (
26+
<Text type={"secondary"}>
27+
Search job #
28+
{searchJobId}
29+
{" "}
30+
found
31+
{" "}
32+
</Text>
33+
)}
34+
<Results/>
35+
<Text type={"secondary"}> results</Text>
36+
</div>
37+
);
38+
};
39+
40+
41+
export default SearchQueryStatus;

components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTable/index.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66

77
import {Table} from "antd";
88

9+
import useSearchStore from "../../SearchState/index";
910
import {
1011
SearchResult,
1112
searchResultsTableColumns,
@@ -20,10 +21,22 @@ import {useSearchResults} from "./useSearchResults";
2021
* @return
2122
*/
2223
const SearchResultsTable = () => {
24+
const {updateNumSearchResultsTable} = useSearchStore();
2325
const searchResults = useSearchResults();
2426
const [tableHeight, setTableHeight] = useState<number>(0);
2527
const containerRef = useRef<HTMLDivElement>(null);
2628

29+
useEffect(() => {
30+
const num = searchResults ?
31+
searchResults.length :
32+
0;
33+
34+
updateNumSearchResultsTable(num);
35+
}, [
36+
searchResults,
37+
updateNumSearchResultsTable,
38+
]);
39+
2740
// Antd table requires a fixed height for virtual scrolling. The effect sets a fixed height
2841
// based on the window height, container top, and fixed padding.
2942
useEffect(() => {

components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {useEffect} from "react";
2+
13
import {Card} from "antd";
24
import {Dayjs} from "dayjs";
35
import {TimelineConfig} from "src/components/ResultsTimeline/typings";
@@ -24,10 +26,22 @@ const SearchResultsTimeline = () => {
2426
timelineConfig,
2527
searchUiState,
2628
updateTimelineConfig,
29+
updateNumSearchResultsTimeline,
2730
} = useSearchStore();
2831

2932
const aggregationResults = useAggregationResults();
3033

34+
useEffect(() => {
35+
const numSearchResultsTimeline = aggregationResults?.reduce(
36+
(acc, curr) => acc + curr.count,
37+
0
38+
) ?? 0;
39+
40+
updateNumSearchResultsTimeline(numSearchResultsTimeline);
41+
}, [
42+
aggregationResults,
43+
updateNumSearchResultsTimeline,
44+
]);
3145

3246
const handleTimelineZoom = (newTimeRange: [Dayjs, Dayjs]) => {
3347
const newTimelineConfig: TimelineConfig = computeTimelineConfig(newTimeRange);

components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {SEARCH_UI_STATE} from "./typings";
1616
*/
1717
const SEARCH_STATE_DEFAULT = Object.freeze({
1818
aggregationJobId: null,
19+
numSearchResultsTable: 0,
20+
numSearchResultsTimeline: 0,
1921
queryIsCaseSensitive: false,
2022
queryString: "",
2123
searchJobId: null,
@@ -32,6 +34,16 @@ interface SearchState {
3234
*/
3335
aggregationJobId: string | null;
3436

37+
/**
38+
* The number of search table results.
39+
*/
40+
numSearchResultsTable: number;
41+
42+
/**
43+
* The number of timeline results.
44+
*/
45+
numSearchResultsTimeline: number;
46+
3547
/**
3648
* Whether the query is case sensitive.
3749
*/
@@ -70,6 +82,8 @@ interface SearchState {
7082
timelineConfig: TimelineConfig;
7183

7284
updateAggregationJobId: (id: string | null) => void;
85+
updateNumSearchResultsTable: (num: number) => void;
86+
updateNumSearchResultsTimeline: (num: number) => void;
7387
updateQueryIsCaseSensitive: (newValue: boolean) => void;
7488
updateQueryString: (query: string) => void;
7589
updateSearchJobId: (id: string | null) => void;
@@ -84,6 +98,12 @@ const useSearchStore = create<SearchState>((set) => ({
8498
updateAggregationJobId: (id) => {
8599
set({aggregationJobId: id});
86100
},
101+
updateNumSearchResultsTable: (num) => {
102+
set({numSearchResultsTable: num});
103+
},
104+
updateNumSearchResultsTimeline: (num) => {
105+
set({numSearchResultsTimeline: num});
106+
},
87107
updateQueryIsCaseSensitive: (newValue: boolean) => {
88108
set({queryIsCaseSensitive: newValue});
89109
},

components/log-viewer-webui/client/src/pages/SearchPage/SearchState/useResultsMetadata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ const useResultsMetadata = () => {
4848
return resultsMetadata;
4949
};
5050

51+
5152
export {useResultsMetadata};

components/log-viewer-webui/client/src/pages/SearchPage/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import styles from "./index.module.css";
22
import SearchControls from "./SearchControls";
3+
import SearchQueryStatus from "./SearchQueryStatus";
34
import SearchResultsTable from "./SearchResults/SearchResultsTable";
45
import SearchResultsTimeline from "./SearchResults/SearchResultsTimeline";
56
import {useUiUpdateOnDoneSignal} from "./SearchState/useUpdateStateWithMetadata";
@@ -15,7 +16,10 @@ const SearchPage = () => {
1516

1617
return (
1718
<div className={styles["searchPageContainer"]}>
18-
<SearchControls/>
19+
<div>
20+
<SearchControls/>
21+
<SearchQueryStatus/>
22+
</div>
1923
<SearchResultsTimeline/>
2024
<SearchResultsTable/>
2125
</div>

0 commit comments

Comments
 (0)