From c0a994814694dcdf27da4c57f47e8d43535cb17f Mon Sep 17 00:00:00 2001 From: Yunus M Date: Tue, 2 Sep 2025 11:05:52 +0530 Subject: [PATCH] feat: handle active log flow (#8946) * feat: handle active log flow * feat: show live logs in logs explorer view * feat: enable live logs in logs explorer * feat: show live time option only in logs list view * chore: pass showLiveLogs as false in test cases * fix: handle live logs data format to open in log details * fix: use current query state for frequency chart in live logs view * fix: encode filter expression, show live option only in list view --- .../CustomTimePicker.styles.scss | 25 ++ .../CustomTimePicker/CustomTimePicker.tsx | 59 ++-- .../CustomTimePickerPopoverContent.tsx | 47 ++- .../LiveLogs/ListViewPanel/index.tsx | 6 +- .../LiveLogsContainer.styles.scss | 26 ++ .../LiveLogs/LiveLogsContainer/index.tsx | 282 +++++++++++------- .../LiveLogsList/LiveLogsList.styles.scss | 55 ++++ .../container/LiveLogs/LiveLogsList/index.tsx | 81 +++-- .../container/LiveLogs/LiveLogsList/types.ts | 8 +- .../LiveLogs/LiveLogsListChart/index.tsx | 36 ++- .../LiveLogs/LiveLogsListChart/types.ts | 1 + .../LiveLogsPauseResume.tsx | 90 ++++++ .../LogsExplorerChart.interfaces.ts | 1 + .../src/container/LogsExplorerChart/index.tsx | 8 +- .../ColumnView/ColumnView.tsx | 25 +- .../__tests__/LogsExplorerList.test.tsx | 2 + .../LogsActionsContainer.tsx | 174 +++++++++++ .../src/container/LogsExplorerViews/index.tsx | 193 +++--------- .../tests/LogsExplorerViews.test.tsx | 2 + .../ToolbarActions/LeftToolbarActions.tsx | 1 - .../ToolbarActions/RightToolbarActions.tsx | 10 +- .../src/container/Toolbar/Toolbar.styles.scss | 2 +- frontend/src/container/Toolbar/Toolbar.tsx | 15 + .../TopNav/DateTimeSelectionV2/index.tsx | 85 ++---- frontend/src/pages/LiveLogs/LiveLogs.tsx | 25 ++ frontend/src/pages/LiveLogs/index.tsx | 27 +- frontend/src/pages/LogsExplorer/index.tsx | 124 ++++---- frontend/src/periscope.scss | 17 ++ frontend/src/providers/EventSource.tsx | 15 +- 29 files changed, 944 insertions(+), 498 deletions(-) create mode 100644 frontend/src/container/LiveLogs/LiveLogsContainer/LiveLogsContainer.styles.scss create mode 100644 frontend/src/container/LiveLogs/LiveLogsList/LiveLogsList.styles.scss create mode 100644 frontend/src/container/LiveLogs/LiveLogsPauseResume/LiveLogsPauseResume.tsx create mode 100644 frontend/src/container/LogsExplorerViews/LogsActionsContainer.tsx create mode 100644 frontend/src/pages/LiveLogs/LiveLogs.tsx diff --git a/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss b/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss index 376e24d0b05a..d97688b0cfe6 100644 --- a/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss +++ b/frontend/src/components/CustomTimePicker/CustomTimePicker.styles.scss @@ -174,6 +174,31 @@ cursor: pointer; } +.time-input-prefix { + .live-dot-icon { + width: 6px; + height: 6px; + border-radius: 50%; + background-color: var(--bg-forest-500); + animation: ripple 1s infinite; + + margin-right: 4px; + margin-left: 4px; + } +} + +@keyframes ripple { + 0% { + box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4); + } + 70% { + box-shadow: 0 0 0 6px rgba(245, 158, 11, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); + } +} + .time-input-suffix-icon-badge { display: flex; align-items: center; diff --git a/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx b/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx index e9da51d14679..8882c6f60a83 100644 --- a/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx +++ b/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx @@ -59,7 +59,9 @@ interface CustomTimePickerProps { customDateTimeVisible?: boolean; setCustomDTPickerVisible?: Dispatch>; onCustomDateHandler?: (dateTimeRange: DateTimeRangeType) => void; - handleGoLive?: () => void; + showLiveLogs?: boolean; + onGoLive?: () => void; + onExitLiveLogs?: () => void; } function CustomTimePicker({ @@ -76,7 +78,9 @@ function CustomTimePicker({ customDateTimeVisible, setCustomDTPickerVisible, onCustomDateHandler, - handleGoLive, + onGoLive, + onExitLiveLogs, + showLiveLogs, }: CustomTimePickerProps): JSX.Element { const [ selectedTimePlaceholderValue, @@ -165,9 +169,13 @@ function CustomTimePicker({ }; useEffect(() => { - const value = getSelectedTimeRangeLabel(selectedTime, selectedValue); - setSelectedTimePlaceholderValue(value); - }, [selectedTime, selectedValue]); + if (showLiveLogs) { + setSelectedTimePlaceholderValue('Live'); + } else { + const value = getSelectedTimeRangeLabel(selectedTime, selectedValue); + setSelectedTimePlaceholderValue(value); + } + }, [selectedTime, selectedValue, showLiveLogs]); const hide = (): void => { setOpen(false); @@ -338,6 +346,28 @@ function CustomTimePicker({ return ''; }; + const getInputPrefix = (): JSX.Element => { + if (showLiveLogs) { + return ( +
+
+
+ ); + } + + return ( +
+ {inputValue && inputStatus === 'success' ? ( + + ) : ( + + + + )} +
+ ); + }; + return (
@@ -357,7 +387,8 @@ function CustomTimePicker({ setCustomDTPickerVisible={defaultTo(setCustomDTPickerVisible, noop)} onCustomDateHandler={defaultTo(onCustomDateHandler, noop)} onSelectHandler={handleSelect} - handleGoLive={defaultTo(handleGoLive, noop)} + onGoLive={defaultTo(onGoLive, noop)} + onExitLiveLogs={defaultTo(onExitLiveLogs, noop)} options={items} selectedTime={selectedTime} activeView={activeView} @@ -392,17 +423,7 @@ function CustomTimePicker({ onBlur={handleBlur} onChange={handleInputChange} data-1p-ignore - prefix={ -
- {inputValue && inputStatus === 'success' ? ( - - ) : ( - - - - )} -
- } + prefix={getInputPrefix()} suffix={
{!!isTimezoneOverridden && activeTimezoneOffset && ( @@ -439,6 +460,8 @@ CustomTimePicker.defaultProps = { customDateTimeVisible: false, setCustomDTPickerVisible: noop, onCustomDateHandler: noop, - handleGoLive: noop, + onGoLive: noop, onCustomTimeStatusUpdate: noop, + onExitLiveLogs: noop, + showLiveLogs: false, }; diff --git a/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx b/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx index d28d3f1b8bad..94b452074162 100644 --- a/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx +++ b/frontend/src/components/CustomTimePicker/CustomTimePickerPopoverContent.tsx @@ -6,6 +6,7 @@ import logEvent from 'api/common/logEvent'; import cx from 'classnames'; import DatePickerV2 from 'components/DatePickerV2/DatePickerV2'; import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats'; +import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal'; import { @@ -16,7 +17,14 @@ import { import dayjs from 'dayjs'; import { Clock, PenLine } from 'lucide-react'; import { useTimezone } from 'providers/Timezone'; -import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; +import { + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { useLocation } from 'react-router-dom'; import { getCustomTimeRanges } from 'utils/customTimeRangeUtils'; @@ -32,12 +40,13 @@ interface CustomTimePickerPopoverContentProps { lexicalContext?: LexicalContext, ) => void; onSelectHandler: (label: string, value: string) => void; - handleGoLive: () => void; + onGoLive: () => void; selectedTime: string; activeView: 'datetime' | 'timezone'; setActiveView: Dispatch>; isOpenedFromFooter: boolean; setIsOpenedFromFooter: Dispatch>; + onExitLiveLogs: () => void; } interface RecentlyUsedDateTimeRange { @@ -56,12 +65,13 @@ function CustomTimePickerPopoverContent({ setCustomDTPickerVisible, onCustomDateHandler, onSelectHandler, - handleGoLive, + onGoLive, selectedTime, activeView, setActiveView, isOpenedFromFooter, setIsOpenedFromFooter, + onExitLiveLogs, }: CustomTimePickerPopoverContentProps): JSX.Element { const { pathname } = useLocation(); @@ -69,6 +79,19 @@ function CustomTimePickerPopoverContent({ pathname, ]); + const url = new URLSearchParams(window.location.search); + + let panelTypeFromURL = url.get(QueryParams.panelTypes); + + try { + panelTypeFromURL = JSON.parse(panelTypeFromURL as string); + } catch { + // fallback → leave as-is + } + + const isLogsListView = + panelTypeFromURL !== 'table' && panelTypeFromURL !== 'graph'; // we do not select list view in the url + const { timezone } = useTimezone(); const activeTimezoneOffset = timezone.offset; @@ -76,6 +99,12 @@ function CustomTimePickerPopoverContent({ RecentlyUsedDateTimeRange[] >([]); + const handleExitLiveLogs = useCallback((): void => { + if (isLogsExplorerPage) { + onExitLiveLogs(); + } + }, [isLogsExplorerPage, onExitLiveLogs]); + useEffect(() => { if (!customDateTimeVisible) { const customTimeRanges = getCustomTimeRanges(); @@ -107,6 +136,7 @@ function CustomTimePickerPopoverContent({ className="time-btns" key={option.label + option.value} onClick={(): void => { + handleExitLiveLogs(); onSelectHandler(option.label, option.value); }} > @@ -140,12 +170,17 @@ function CustomTimePickerPopoverContent({ ); } + const handleGoLive = (): void => { + onGoLive(); + setIsOpen(false); + }; + return ( <>
{!customDateTimeVisible && (
- {isLogsExplorerPage && ( + {isLogsExplorerPage && isLogsListView && ( @@ -155,6 +190,7 @@ function CustomTimePickerPopoverContent({ type="text" key={option.label + option.value} onClick={(): void => { + handleExitLiveLogs(); onSelectHandler(option.label, option.value); }} className={cx( @@ -169,7 +205,6 @@ function CustomTimePickerPopoverContent({ ))}
)} -
{ if (e.key === 'Enter' || e.key === ' ') { + handleExitLiveLogs(); onCustomDateHandler([dayjs(range.from), dayjs(range.to)]); setIsOpen(false); } }} key={range.value} onClick={(): void => { + handleExitLiveLogs(); onCustomDateHandler([dayjs(range.from), dayjs(range.to)]); setIsOpen(false); }} diff --git a/frontend/src/container/LiveLogs/ListViewPanel/index.tsx b/frontend/src/container/LiveLogs/ListViewPanel/index.tsx index f517241cdd6c..777a0e82b7c8 100644 --- a/frontend/src/container/LiveLogs/ListViewPanel/index.tsx +++ b/frontend/src/container/LiveLogs/ListViewPanel/index.tsx @@ -13,7 +13,7 @@ import { useCallback } from 'react'; import { DataSource, StringOperators } from 'types/common/queryBuilder'; import { popupContainer } from 'utils/selectPopupContainer'; -import { SpinnerWrapper, Wrapper } from './styles'; +import { SpinnerWrapper } from './styles'; function ListViewPanel(): JSX.Element { const { config } = useOptionsMenu({ @@ -42,7 +42,7 @@ function ListViewPanel(): JSX.Element { }, [config]); return ( - +