Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nine-dragons-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---

Adds "Relative Time" switch to TimePicker component (if relative time is supported by parent). When enabled, searches will work similar to Live Tail but be relative to the option selected.
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Ignore artifacts:
dist
coverage
tests
.volumes
3 changes: 2 additions & 1 deletion packages/app/.stylelintignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.next
.storybook
node_modules
coverage
coverage
playwright-report
2 changes: 2 additions & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"ky": "^0.30.0",
"ky-universal": "^0.10.1",
"lodash": "^4.17.21",
"ms": "^2.1.3",
"next": "^14.2.32",
"next-query-params": "^4.1.0",
"next-runtime-env": "1",
Expand Down Expand Up @@ -125,6 +126,7 @@
"@types/intercom-web": "^2.8.18",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.14.186",
"@types/ms": "^0.7.31",
"@types/object-hash": "^2.2.1",
"@types/pluralize": "^0.0.29",
"@types/react": "18.3.1",
Expand Down
6 changes: 3 additions & 3 deletions packages/app/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8080',
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8081',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
/* Take screenshot on failure */
Expand All @@ -49,8 +49,8 @@ export default defineConfig({
/* Run your local dev server before starting the tests */
webServer: {
command:
'NEXT_PUBLIC_IS_LOCAL_MODE=true NEXT_TELEMETRY_DISABLED=1 yarn run dev',
port: 8080,
'NEXT_PUBLIC_IS_LOCAL_MODE=true NEXT_TELEMETRY_DISABLED=1 PORT=8081 yarn run dev',
port: 8081,
reuseExistingServer: !process.env.CI,
timeout: 180 * 1000,
stdout: 'pipe',
Expand Down
80 changes: 52 additions & 28 deletions packages/app/src/DBSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Link from 'next/link';
import router from 'next/router';
import {
parseAsBoolean,
parseAsInteger,
parseAsJson,
parseAsString,
parseAsStringEnum,
Expand Down Expand Up @@ -43,7 +44,6 @@ import {
Flex,
Grid,
Group,
Input,
Menu,
Modal,
Paper,
Expand Down Expand Up @@ -91,14 +91,22 @@ import {
useSource,
useSources,
} from '@/source';
import { parseTimeQuery, useNewTimeQuery } from '@/timeQuery';
import {
parseRelativeTimeQuery,
parseTimeQuery,
useNewTimeQuery,
} from '@/timeQuery';
import { QUERY_LOCAL_STORAGE, useLocalStorage, usePrevious } from '@/utils';

import { SQLPreview } from './components/ChartSQLPreview';
import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar';
import PatternTable from './components/PatternTable';
import { DBSearchHeatmapChart } from './components/Search/DBSearchHeatmapChart';
import SourceSchemaPreview from './components/SourceSchemaPreview';
import {
getRelativeTimeOptionLabel,
LIVE_TAIL_DURATION_MS,
} from './components/TimePicker/utils';
import { useTableMetadata } from './hooks/useMetadata';
import { useSqlSuggestions } from './hooks/useSqlSuggestions';
import {
Expand Down Expand Up @@ -417,7 +425,7 @@ function useLiveUpdate({
onTimeRangeSelect: (
start: Date,
end: Date,
displayedTimeInputValue?: string | undefined,
displayedTimeInputValue?: string | null,
) => void;
pause: boolean;
}) {
Expand All @@ -426,7 +434,7 @@ function useLiveUpdate({
const [refreshOnVisible, setRefreshOnVisible] = useState(false);

const refresh = useCallback(() => {
onTimeRangeSelect(new Date(Date.now() - interval), new Date(), 'Live Tail');
onTimeRangeSelect(new Date(Date.now() - interval), new Date(), null);
}, [onTimeRangeSelect, interval]);

// When the user comes back to the app after switching tabs, we immediately refresh the list.
Expand Down Expand Up @@ -664,8 +672,10 @@ function DBSearchPage() {
parseAsString,
);

const [_isLive, setIsLive] = useQueryState('isLive', parseAsBoolean);
const isLive = _isLive ?? true;
const [isLive, setIsLive] = useQueryState(
'isLive',
parseAsBoolean.withDefault(true),
);

useEffect(() => {
if (analysisMode === 'delta' || analysisMode === 'pattern') {
Expand Down Expand Up @@ -727,7 +737,7 @@ function DBSearchPage() {
const [displayedTimeInputValue, setDisplayedTimeInputValue] =
useState('Live Tail');

const { from, to, isReady, searchedTimeRange, onSearch, onTimeRangeSelect } =
const { isReady, searchedTimeRange, onSearch, onTimeRangeSelect } =
useNewTimeQuery({
initialDisplayValue: 'Live Tail',
initialTimeRange: defaultTimeRange,
Expand All @@ -736,18 +746,6 @@ function DBSearchPage() {
updateInput: !isLive,
});

// If live tail is null, but time range exists, don't live tail
// If live tail is null, and time range is null, let's live tail
useEffect(() => {
if (_isLive == null && isReady) {
if (from == null && to == null) {
setIsLive(true);
} else {
setIsLive(false);
}
}
}, [_isLive, setIsLive, from, to, isReady]);

// Sync url state back with form state
// (ex. for history navigation)
// TODO: Check if there are any bad edge cases here
Expand Down Expand Up @@ -1014,9 +1012,29 @@ function DBSearchPage() {
// State for collapsing all expanded rows when resuming live tail
const [collapseAllRows, setCollapseAllRows] = useState(false);

const [interval, setInterval] = useQueryState(
'liveInterval',
parseAsInteger.withDefault(LIVE_TAIL_DURATION_MS),
);

const updateRelativeTimeInputValue = useCallback((interval: number) => {
const label = getRelativeTimeOptionLabel(interval);
if (label) {
setDisplayedTimeInputValue(label);
}
}, []);

useEffect(() => {
if (isReady && isLive) {
updateRelativeTimeInputValue(interval);
}
// we only want this to run on initial mount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updateRelativeTimeInputValue, isReady]);

useLiveUpdate({
isLive,
interval: 1000 * 60 * 15,
interval,
refreshFrequency: 4000,
onTimeRangeSelect,
pause: isAnyQueryFetching || !queryReady || !isTabVisible,
Expand All @@ -1043,13 +1061,12 @@ function DBSearchPage() {

const handleResumeLiveTail = useCallback(() => {
setIsLive(true);
setDisplayedTimeInputValue('Live Tail');
updateRelativeTimeInputValue(interval);
// Trigger collapsing all expanded rows
setCollapseAllRows(true);
// Reset the collapse trigger after a short delay
setTimeout(() => setCollapseAllRows(false), 100);
onSearch('Live Tail');
}, [onSearch, setIsLive]);
}, [interval, updateRelativeTimeInputValue, setIsLive]);

const dbSqlRowTableConfig = useMemo(() => {
if (chartConfig == null) {
Expand Down Expand Up @@ -1508,14 +1525,21 @@ function DBSearchPage() {
inputValue={displayedTimeInputValue}
setInputValue={setDisplayedTimeInputValue}
onSearch={range => {
if (range === 'Live Tail') {
setIsLive(true);
} else {
setIsLive(false);
}
setIsLive(false);
onSearch(range);
}}
onRelativeSearch={rangeMs => {
const _range = parseRelativeTimeQuery(rangeMs);
setIsLive(true);
setInterval(rangeMs);
onTimeRangeSelect(_range[0], _range[1], null);
}}
showLive={analysisMode === 'results'}
isLiveMode={isLive}
// Default to relative time mode if the user has made changes to interval and reloaded.
defaultRelativeTimeMode={
isLive && interval !== LIVE_TAIL_DURATION_MS
}
/>
<Button
data-testid="search-submit-button"
Expand Down
Loading
Loading