-
Notifications
You must be signed in to change notification settings - Fork 18
feat(MenuBar): Add timestamp query input for jumping to log events by UTC timestamp (resolves #118). #359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(MenuBar): Add timestamp query input for jumping to log events by UTC timestamp (resolves #118). #359
Changes from all commits
4c0251c
a8e0ce2
dab80a8
81e884b
50ecffc
83b9942
3679406
3756990
c3136c1
f7a20a1
194bd09
43c7af8
839dc9a
45a8acc
00964b9
a6c173a
02d1589
40f8378
7ed2a41
b108747
fab2be3
e53f1e4
ca797e2
b8cee98
4778414
47b2d42
696f570
f598929
51c323b
4c4a779
905686f
e62ba63
2c993df
0af4e74
abfda18
2517f57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.timestamp-query-container { | ||
display: flex; | ||
} | ||
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,61 @@ | ||||||||||||||||||||
import { | ||||||||||||||||||||
useCallback, | ||||||||||||||||||||
useState, | ||||||||||||||||||||
} from "react"; | ||||||||||||||||||||
|
||||||||||||||||||||
import { | ||||||||||||||||||||
Box, | ||||||||||||||||||||
Divider, | ||||||||||||||||||||
} from "@mui/joy"; | ||||||||||||||||||||
|
||||||||||||||||||||
import CalendarTodayIcon from "@mui/icons-material/CalendarToday"; | ||||||||||||||||||||
|
||||||||||||||||||||
import useUiStore from "../../stores/uiStore"; | ||||||||||||||||||||
import {UI_ELEMENT} from "../../typings/states"; | ||||||||||||||||||||
import {isDisabled} from "../../utils/states"; | ||||||||||||||||||||
import MenuBarIconButton from "./MenuBarIconButton"; | ||||||||||||||||||||
import TimestampQueryInput from "./TimestampQueryInput"; | ||||||||||||||||||||
|
||||||||||||||||||||
import "./TimestampQueryContainer.css"; | ||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
/** | ||||||||||||||||||||
* Wraps the timestamp query input and toggles its visibility using a calendar button. | ||||||||||||||||||||
* | ||||||||||||||||||||
* @return | ||||||||||||||||||||
*/ | ||||||||||||||||||||
const TimestampQueryContainer = () => { | ||||||||||||||||||||
const [isInputVisible, setIsInputVisible] = useState(false); | ||||||||||||||||||||
const uiState = useUiStore((state) => state.uiState); | ||||||||||||||||||||
|
||||||||||||||||||||
const handleInputVisibilityToggle = useCallback(() => { | ||||||||||||||||||||
setIsInputVisible((prev) => !prev); | ||||||||||||||||||||
}, []); | ||||||||||||||||||||
|
||||||||||||||||||||
return ( | ||||||||||||||||||||
<Box className={"timestamp-query-container"}> | ||||||||||||||||||||
{false === isInputVisible && ( | ||||||||||||||||||||
<> | ||||||||||||||||||||
<Divider orientation={"vertical"}/> | ||||||||||||||||||||
<MenuBarIconButton | ||||||||||||||||||||
disabled={isDisabled(uiState, UI_ELEMENT.NAVIGATION_BAR)} | ||||||||||||||||||||
tooltipTitle={"Search by timestamp"} | ||||||||||||||||||||
onClick={handleInputVisibilityToggle} | ||||||||||||||||||||
> | ||||||||||||||||||||
Comment on lines
+41
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Optional: specify tooltip placement for consistency Aligns with other icon buttons. - tooltipTitle={"Search by timestamp"}
+ tooltipTitle={"Search by timestamp"}
+ tooltipPlacement={"bottom-start"} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||
<CalendarTodayIcon/> | ||||||||||||||||||||
</MenuBarIconButton> | ||||||||||||||||||||
<Divider orientation={"vertical"}/> | ||||||||||||||||||||
</> | ||||||||||||||||||||
)} | ||||||||||||||||||||
<div | ||||||||||||||||||||
className={`timestamp-query-input-wrapper ${isInputVisible ? | ||||||||||||||||||||
"expanded" : | ||||||||||||||||||||
""}`} | ||||||||||||||||||||
> | ||||||||||||||||||||
<TimestampQueryInput onInputCollapse={handleInputVisibilityToggle}/> | ||||||||||||||||||||
</div> | ||||||||||||||||||||
</Box> | ||||||||||||||||||||
); | ||||||||||||||||||||
}; | ||||||||||||||||||||
|
||||||||||||||||||||
export default TimestampQueryContainer; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.timestamp-query-input { | ||
display: flex; | ||
flex-direction: row; | ||
align-items: center; | ||
height: var(--ylv-menu-bar-height); | ||
} | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,87 @@ | ||||||||||||||||||||||||||||||||
import React, {useCallback} from "react"; | ||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||
Box, | ||||||||||||||||||||||||||||||||
Input, | ||||||||||||||||||||||||||||||||
Tooltip, | ||||||||||||||||||||||||||||||||
} from "@mui/joy"; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
import CollapseIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; | ||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
import SearchIcon from "@mui/icons-material/Search"; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
import useUiStore from "../../stores/uiStore"; | ||||||||||||||||||||||||||||||||
import useViewStore from "../../stores/viewStore"; | ||||||||||||||||||||||||||||||||
import {UI_ELEMENT} from "../../typings/states"; | ||||||||||||||||||||||||||||||||
import {isDisabled} from "../../utils/states"; | ||||||||||||||||||||||||||||||||
import {updateWindowUrlHashParams} from "../../utils/url"; | ||||||||||||||||||||||||||||||||
import {updateViewHashParams} from "../../utils/url/urlHash"; | ||||||||||||||||||||||||||||||||
import MenuBarIconButton from "./MenuBarIconButton"; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
import "./TimestampQueryInput.css"; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
interface TimestampQueryInputProps { | ||||||||||||||||||||||||||||||||
onInputCollapse: () => void; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Renders an input allowing the user to jump to the nearest log event at or before a specified UTC | ||||||||||||||||||||||||||||||||
* datetime. Collapses the input when requested. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @param props | ||||||||||||||||||||||||||||||||
* @param props.onInputCollapse | ||||||||||||||||||||||||||||||||
* @return | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
Comment on lines
+27
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Tighten JSDoc params Document the actual prop name directly; drop redundant “props”. - * @param props
- * @param props.onInputCollapse
+ * @param onInputCollapse 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||
const TimestampQueryInput = ({onInputCollapse}: TimestampQueryInputProps) => { | ||||||||||||||||||||||||||||||||
const uiState = useUiStore((state) => state.uiState); | ||||||||||||||||||||||||||||||||
const dateTimeString = useViewStore((state) => state.dateTimeString); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
const handleTimestampQuery = useCallback(() => { | ||||||||||||||||||||||||||||||||
const timestamp = new Date(`${dateTimeString}Z`).getTime(); | ||||||||||||||||||||||||||||||||
updateWindowUrlHashParams({timestamp: timestamp}); | ||||||||||||||||||||||||||||||||
updateViewHashParams(); | ||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
}, [dateTimeString]); | ||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
const handleKeyboardEnterPress = useCallback((ev: React.KeyboardEvent<HTMLInputElement>) => { | ||||||||||||||||||||||||||||||||
if ("Enter" === ev.key) { | ||||||||||||||||||||||||||||||||
handleTimestampQuery(); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
}, [handleTimestampQuery]); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
const handleDateTimeInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { | ||||||||||||||||||||||||||||||||
const {setDateTimeString} = useViewStore.getState(); | ||||||||||||||||||||||||||||||||
setDateTimeString(e.currentTarget.value); | ||||||||||||||||||||||||||||||||
}, []); | ||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||
<Box className={"timestamp-query-input"}> | ||||||||||||||||||||||||||||||||
<Tooltip title={"Jump to the nearest log event at / before this UTC time"}> | ||||||||||||||||||||||||||||||||
<Input | ||||||||||||||||||||||||||||||||
disabled={isDisabled(uiState, UI_ELEMENT.NAVIGATION_BAR)} | ||||||||||||||||||||||||||||||||
size={"sm"} | ||||||||||||||||||||||||||||||||
sx={{height: "100%"}} | ||||||||||||||||||||||||||||||||
title={"Timestamp to seek to in UTC"} | ||||||||||||||||||||||||||||||||
type={"datetime-local"} | ||||||||||||||||||||||||||||||||
value={dateTimeString} | ||||||||||||||||||||||||||||||||
endDecorator={ | ||||||||||||||||||||||||||||||||
<MenuBarIconButton | ||||||||||||||||||||||||||||||||
onClick={handleTimestampQuery} | ||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||
<SearchIcon/> | ||||||||||||||||||||||||||||||||
</MenuBarIconButton> | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
startDecorator={ | ||||||||||||||||||||||||||||||||
<MenuBarIconButton | ||||||||||||||||||||||||||||||||
onClick={onInputCollapse} | ||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||
<CollapseIcon/> | ||||||||||||||||||||||||||||||||
</MenuBarIconButton> | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
onChange={handleDateTimeInputChange} | ||||||||||||||||||||||||||||||||
onKeyDown={handleKeyboardEnterPress}/> | ||||||||||||||||||||||||||||||||
</Tooltip> | ||||||||||||||||||||||||||||||||
</Box> | ||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
export default TimestampQueryInput; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,19 @@ import { | |
} from "./index"; | ||
|
||
|
||
/** | ||
* Converts a timestamp to an ISO 8601 date-time string (without the 'Z' suffix) | ||
* | ||
* @param timestamp | ||
*/ | ||
const updateDateTimeString = (timestamp: number) => { | ||
const dateTimeString = new Date(timestamp).toISOString() | ||
.slice(0, -1); | ||
|
||
const {setDateTimeString} = useViewStore.getState(); | ||
setDateTimeString(dateTimeString); | ||
}; | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Determines the cursor for navigating log events based on URL hash parameters. | ||
* | ||
|
@@ -26,6 +39,7 @@ import { | |
* @param params.timestamp The timestamp from the URL hash. | ||
* @return `CursorType` object if a navigation action is needed, or `null` if no action is required. | ||
*/ | ||
// eslint-disable-next-line max-statements | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @coderabbitai create a refactoring issue request for this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain@junhaoliao I've created a refactoring issue for the 🐰
🏁 Script executed:
Length of output: 55 Perfect! I've successfully created refactoring issue #389 for the
You can view the issue at: #389 🐰
|
||
const getCursorFromHashParams = ({isPrettified, logEventNum, timestamp}: { | ||
isPrettified: boolean; logEventNum: number; timestamp: number; | ||
}): Nullable<CursorType> => { | ||
|
@@ -56,6 +70,8 @@ const getCursorFromHashParams = ({isPrettified, logEventNum, timestamp}: { | |
} | ||
|
||
if (timestamp !== URL_HASH_PARAMS_DEFAULT.timestamp) { | ||
updateDateTimeString(timestamp); | ||
|
||
return { | ||
code: CURSOR_CODE.TIMESTAMP, | ||
Comment on lines
72
to
76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Avoid redundant store updates. Micro-optimisation: only update dateTimeString if it differs from the computed value to prevent unnecessary re-renders. - if (timestamp !== URL_HASH_PARAMS_DEFAULT.timestamp) {
- updateDateTimeString(timestamp);
+ if (timestamp !== URL_HASH_PARAMS_DEFAULT.timestamp) {
+ // updateDateTimeString internally normalises to ISO (UTC, no 'Z')
+ // and can early-return if unchanged (see suggested guard).
+ updateDateTimeString(timestamp);
🤖 Prompt for AI Agents
|
||
args: {timestamp: timestamp}, | ||
|
Uh oh!
There was an error while loading. Please reload this page.