feat(new-webui): Integrate aggregation results into query timeline UI.#941
Conversation
## Walkthrough
This update refactors the timeline and aggregation logic in the log viewer web UI. It standardizes time range handling using Dayjs tuples, introduces a new hook for fetching aggregation results, synchronizes timeline configuration with queries, and updates the global search state to track and update timeline configuration independently.
## Changes
| File(s) | Change Summary |
|-----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx | Changed `onTimelineZoom` parameter from custom type to `[Dayjs, Dayjs]` tuple; `timelineBuckets` is now non-nullable; updated zoom handler and removed null checks. |
| components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx | Refactored to use live aggregation data via new hook; timeline config now based on current time range; zoom handler updates config and can trigger new query; improved input disabling logic. |
| components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/useAggregationResults.ts | Added new hook to fetch aggregation results using current aggregation job ID and reactive cursor. |
| components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.ts | Updated `computeTimelineConfig` to accept `[dayjs.Dayjs, dayjs.Dayjs]` and use UTC time calculations. |
| components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx | Added `timelineConfig` and `updateTimelineConfig` to search state; initialized and updated timeline configuration in store. |
| components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx | Now synchronizes timeline configuration with query submission by recalculating and updating timeline config before submitting a query. |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant SubmitButton
participant SearchStore
participant Timeline
participant AggregationHook
User->>SubmitButton: Click submit
SubmitButton->>SearchStore: updateTimelineConfig(newConfig)
SubmitButton->>SearchStore: submitQuery({ timeRangeBucketSizeMillis: newConfig.bucketDurationMillis })
SearchStore->>AggregationHook: aggregationJobId updated
AggregationHook->>Timeline: Fetch aggregation results
Timeline->>User: Display updated timelinesequenceDiagram
participant User
participant Timeline
participant SearchStore
participant AggregationHook
User->>Timeline: Zooms timeline
Timeline->>SearchStore: updateTimelineConfig(zoomedConfig)
Timeline->>SearchStore: updateTimeRange(zoomedRange)
alt If query string is not empty
Timeline->>SearchStore: submitQuery()
end
SearchStore->>AggregationHook: aggregationJobId updated
AggregationHook->>Timeline: Fetch new aggregation results
Possibly related PRs
Suggested reviewers
|
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx (1)
40-41:⚠️ Potential issueAdd missing dependency to prevent stale closures.
The
useCallbackdependency array is missingupdateTimelineConfig. This could lead to stale closures where the callback references an outdated version of the function.}, [queryString, - timeRange]); + timeRange, + updateTimelineConfig]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx(3 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx(1 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx(2 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/useAggregationResults.ts(1 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.ts(2 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Preferfalse == <expression>rather than!<expression>.
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsxcomponents/log-viewer-webui/client/src/components/ResultsTimeline/index.tsxcomponents/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.tscomponents/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsxcomponents/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsxcomponents/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/useAggregationResults.ts
🧬 Code Graph Analysis (4)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx (6)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/useAggregationResults.ts (1)
useAggregationResults(32-32)components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.ts (1)
computeTimelineConfig(95-95)components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/TimeRangeInput/utils.tsx (1)
TIME_RANGE_OPTION(70-70)components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx (1)
SEARCH_STATE_DEFAULT(102-102)components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/search-requests.ts (1)
handleQuerySubmit(104-104)components/log-viewer-webui/client/src/pages/SearchPage/SearchState/typings.ts (1)
SEARCH_UI_STATE(26-26)
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx (2)
components/log-viewer-webui/client/src/components/ResultsTimeline/typings.ts (1)
TimelineBucket(28-28)components/log-viewer-webui/client/src/components/ResultsTimeline/datetime/index.ts (1)
convertZoomTimestampToUtcDatetime(60-60)
components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx (3)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.ts (1)
computeTimelineConfig(95-95)components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/TimeRangeInput/utils.tsx (2)
TIME_RANGE_OPTION_DAYJS_MAP(71-71)DEFAULT_TIME_RANGE(68-68)components/log-viewer-webui/client/src/components/ResultsTimeline/typings.ts (1)
TimelineConfig(29-29)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/useAggregationResults.ts (3)
components/log-viewer-webui/client/src/api/socket/useCursor.tsx (1)
useCursor(79-79)components/log-viewer-webui/client/src/components/ResultsTimeline/typings.ts (1)
TimelineBucket(28-28)components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx (1)
SEARCH_STATE_DEFAULT(102-102)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: lint-check (macos-latest)
🔇 Additional comments (13)
components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx (1)
29-36: Excellent integration of timeline configuration with query submission.The synchronization of timeline configuration updates with query submission ensures consistency between the UI state and the submitted query parameters. The use of
bucketDuration.asMilliseconds()for the bucket size is properly aligned with the timeline configuration.components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.ts (2)
3-3: Good addition of UTC plugin for consistent timezone handling.The addition of the UTC plugin ensures all timeline calculations are performed in UTC, which is crucial for consistent time handling across different user timezones.
Also applies to: 11-11
44-48:Details
✅ Verification successful
Verify function signature change is coordinated across the codebase.
The function signature change from separate Unix millisecond parameters to a Dayjs tuple is a breaking change. The implementation correctly converts the Dayjs objects to UTC milliseconds.
🏁 Script executed:
#!/bin/bash # Description: Verify all callers of computeTimelineConfig have been updated to use the new signature. # Test: Search for function calls to computeTimelineConfig. Expect: All calls use Dayjs tuple. rg -A 3 "computeTimelineConfig\(" --type ts --type tsxLength of output: 84
To ensure we catch all usages, let’s re-run the search without relying on ripgrep’s built-in file types:
🏁 Script executed:
#!/bin/bash # Description: Find all calls to computeTimelineConfig in .ts and .tsx files rg "computeTimelineConfig\(" -A 3 --glob '*.ts' --glob '*.tsx'Length of output: 1732
All
computeTimelineConfigcallers updated to use the new Dayjs tuple signatureI’ve verified every usage of
computeTimelineConfigacross the codebase:
- components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx
- components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx
- components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx
All call sites now pass a
[Dayjs, Dayjs]tuple. No further changes needed.components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/useAggregationResults.ts (1)
12-30: Well-implemented custom hook with proper patterns.The hook correctly:
- Uses the useCursor pattern for reactive data fetching
- Handles the null case when no aggregation job is active
- Includes proper dependency array with aggregationJobId
- Returns a clear null or cursor result
The implementation follows React hooks best practices and integrates well with the existing architecture.
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx (3)
46-47: Good API simplification and type consistency.The changes to use Dayjs tuples instead of TimeRange objects and non-nullable timelineBuckets simplify the component API and align with the broader refactoring efforts.
176-178: Proper implementation of zoom callback with Dayjs tuple.The zoom handler correctly creates a Dayjs tuple using
convertZoomTimestampToUtcDatetimefor consistent time handling, maintaining the UTC-based approach throughout the timeline system.
166-166: Excellent adherence to coding guidelines.The boolean expression
false === isInputDisabledcorrectly follows the project's coding guideline to preferfalse == <expression>rather than!<expression>.components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx (3)
1-14: Import organization looks good.The new imports are well-organized and necessary for the timeline integration functionality.
22-32: State management properly integrated.Good use of the search store and aggregation results hook to get the necessary data and update functions.
60-66: Timeline data integration implemented correctly.Good implementation:
- Proper input disabling during query states
- Safe fallback to empty array when aggregation results are not available
- Correct prop passing to the ResultsTimeline component
components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx (3)
11-27: Timeline configuration properly initialized.Good implementation of the timeline config initialization using the computed value from the default time range. This ensures consistency between the time range and timeline configuration.
50-72: Excellent documentation and interface design.The documentation clearly explains the purpose and update policy for
timelineConfig. This helps maintain consistency by ensuring it's only updated during query submissions.
90-92: Update method correctly implemented.Simple and effective implementation of the timeline config update method.
| handleQuerySubmit({ | ||
| ignoreCase: false, | ||
| queryString: queryString, | ||
| timeRangeBucketSizeMillis: newTimelineConfig.bucketDuration.asMilliseconds(), | ||
| timestampBegin: newTimeRange[0].valueOf(), | ||
| timestampEnd: newTimeRange[1].valueOf(), | ||
| }); |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Consider using the actual case sensitivity preference.
The ignoreCase parameter is hardcoded to false. If the search UI has a case sensitivity toggle, this should use the actual user preference from the search state.
Let me check if there's a case sensitivity setting in the search state:
🏁 Script executed:
#!/bin/bash
# Description: Check for case sensitivity settings in the search state and UI
# Search for ignoreCase or case sensitivity related state
rg -A 5 "ignoreCase|caseSensitive|caseInsensitive" --glob "**/*SearchState*" --glob "**/*Search*"Length of output: 1987
Use actual case sensitivity preference for timeline zoom queries
The call to handleQuerySubmit in
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx (lines 47–53)
is hardcoding ignoreCase: false, which overrides the user’s case-sensitivity toggle in SearchView.jsx. To fix:
• Ensure this component receives the ignoreCase prop from the search state.
• Replace the hardcoded value with that prop when submitting the zoomed query.
Suggested diff:
--- a/components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx
+++ b/components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx
@@ 47,7
- handleQuerySubmit({
- ignoreCase: false,
+ handleQuerySubmit({
+ ignoreCase: ignoreCase,
queryString: queryString,
timeRangeBucketSizeMillis: newTimelineConfig.bucketDuration.asMilliseconds(),
timestampBegin: newTimeRange[0].valueOf(),This will align the timeline-zoom search with the UI’s case-sensitivity setting.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| handleQuerySubmit({ | |
| ignoreCase: false, | |
| queryString: queryString, | |
| timeRangeBucketSizeMillis: newTimelineConfig.bucketDuration.asMilliseconds(), | |
| timestampBegin: newTimeRange[0].valueOf(), | |
| timestampEnd: newTimeRange[1].valueOf(), | |
| }); | |
| handleQuerySubmit({ | |
| ignoreCase: ignoreCase, | |
| queryString: queryString, | |
| timeRangeBucketSizeMillis: newTimelineConfig.bucketDuration.asMilliseconds(), | |
| timestampBegin: newTimeRange[0].valueOf(), | |
| timestampEnd: newTimeRange[1].valueOf(), | |
| }); |
🤖 Prompt for AI Agents
In
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/index.tsx
around lines 47 to 53, the ignoreCase parameter in handleQuerySubmit is
hardcoded to false, ignoring the user's case sensitivity preference. To fix
this, modify the component to accept the ignoreCase value from the search state
as a prop and replace the hardcoded false with this prop when calling
handleQuerySubmit, ensuring the timeline zoom queries respect the user's case
sensitivity setting.
| const newTimeRange: [Dayjs, Dayjs] = | ||
| [convertZoomTimestampToUtcDatetime(min), | ||
| convertZoomTimestampToUtcDatetime(max)]; |
There was a problem hiding this comment.
| const newTimeRange: [Dayjs, Dayjs] = | |
| [convertZoomTimestampToUtcDatetime(min), | |
| convertZoomTimestampToUtcDatetime(max)]; | |
| const newTimeRange: [Dayjs, Dayjs] = [ | |
| convertZoomTimestampToUtcDatetime(min), | |
| convertZoomTimestampToUtcDatetime(max) | |
| ]; |
| */ | ||
| const SubmitButton = () => { | ||
| const {searchUiState, timeRange, queryString} = useSearchStore(); | ||
| const {searchUiState, timeRange, queryString, updateTimelineConfig} = useSearchStore(); |
There was a problem hiding this comment.
purely stylish - we can address all in another PR
instead of retrieving the states / actions here, we can write
const handleSubmitButtonClick = useCallback(() => {
const {queryString, timeRange, updateTimelineConfig} = useSearchStore.get();
// Update timeline to match range picker selection.
const newTimelineConfig = computeTimelineConfig(timeRange);
updateTimelineConfig(newTimelineConfig);
handleQuerySubmit({
ignoreCase: false,
queryString: queryString,
timeRangeBucketSizeMillis: newTimelineConfig.bucketDuration.asMilliseconds(),
timestampBegin: timeRange[0].valueOf(),
timestampEnd: timeRange[1].valueOf(),
});
}, []);
so the definition of handleSubmitButtonClick won't change at all
There was a problem hiding this comment.
k i will do in later PR
| const SubmitButton = () => { | ||
| const {searchUiState, timeRange, queryString} = useSearchStore(); | ||
| const {searchUiState, timeRange, queryString, updateTimelineConfig} = useSearchStore(); | ||
| const isQueryStringEmpty = queryString === SEARCH_STATE_DEFAULT.queryString; |
There was a problem hiding this comment.
let's move this right above the return
| * @return | ||
| */ | ||
| const SearchResultsTimeline = () => { | ||
| const { |
There was a problem hiding this comment.
ditto - see the Zustand state / action retrieval styling sugguestion
There was a problem hiding this comment.
will do this also in future PR
junhaoliao
left a comment
There was a problem hiding this comment.
for the title, how about
feat(new-webui): Integrate aggregation results into query timeline UI.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx(3 hunks)components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Preferfalse == <expression>rather than!<expression>.
components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsxcomponents/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx
🧬 Code Graph Analysis (2)
components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx (2)
components/log-viewer-webui/client/src/pages/SearchPage/SearchResults/SearchResultsTimeline/utils.ts (1)
computeTimelineConfig(95-95)components/log-viewer-webui/client/src/pages/SearchPage/SearchState/index.tsx (1)
SEARCH_STATE_DEFAULT(101-101)
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx (2)
components/log-viewer-webui/client/src/components/ResultsTimeline/typings.ts (1)
TimelineBucket(28-28)components/log-viewer-webui/client/src/components/ResultsTimeline/datetime/index.ts (1)
convertZoomTimestampToUtcDatetime(60-60)
🪛 GitHub Actions: clp-lint
components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx
[error] 44-44: ESLint error: Trailing spaces not allowed (@stylistic/no-trailing-spaces).
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx
[warning] 178-178: ESLint warning: Missing trailing comma (@stylistic/comma-dangle).
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: lint-check (macos-latest)
🔇 Additional comments (6)
components/log-viewer-webui/client/src/components/ResultsTimeline/index.tsx (2)
16-16: LGTM: Proper TypeScript type import.Adding the explicit
Dayjstype import is necessary for the type annotations used in the component props and improves type safety.
46-47: LGTM: Improved type safety and simplified API.The changes standardize time range handling using
[Dayjs, Dayjs]tuples and remove nullable handling fortimelineBuckets. This simplifies the component interface and aligns with the broader refactoring effort described in the PR objectives.components/log-viewer-webui/client/src/pages/SearchPage/SearchControls/SearchButton/SubmitButton.tsx (4)
22-22: LGTM: Necessary store integration for timeline functionality.Adding
updateTimelineConfigto the destructured store variables enables the component to synchronize timeline configuration with query submission, which aligns with the PR objectives.
28-30: LGTM: Proper timeline configuration synchronization.The implementation correctly computes and updates the timeline configuration before query submission, ensuring synchronization between the time range picker and timeline buckets. This aligns with the PR objectives to integrate aggregation results into the timeline UI.
35-35: LGTM: Consistent use of updated timeline configuration.Using the bucket duration from the newly computed timeline configuration ensures the query submission parameters remain synchronized with the timeline settings.
39-41: LGTM: Correct dependency array update.Adding
updateTimelineConfigto the dependency array is necessary since it's now used within the callback, ensuring the callback updates correctly when the function reference changes.
Description
Pr connects server to timeline UI.
There are some changes from old webUI in that the timeline is always visible. I think this looks better.
Checklist
breaking change.
Validation performed
Tested query. Timeline rendered as expected
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Chores