diff --git a/frontend/src/components/LogDetail/LogDetail.interfaces.ts b/frontend/src/components/LogDetail/LogDetail.interfaces.ts index 399e1dffb2..2a2dd56855 100644 --- a/frontend/src/components/LogDetail/LogDetail.interfaces.ts +++ b/frontend/src/components/LogDetail/LogDetail.interfaces.ts @@ -1,6 +1,7 @@ import { DrawerProps } from 'antd'; import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC'; import { ActionItemProps } from 'container/LogDetailedView/ActionItem'; +import { IField } from 'types/api/logs/fields'; import { ILog } from 'types/api/logs/log'; import { VIEWS } from './constants'; @@ -9,6 +10,7 @@ export type LogDetailProps = { log: ILog | null; selectedTab: VIEWS; isListViewPanel?: boolean; + listViewPanelSelectedFields?: IField[] | null; } & Pick & Partial> & Pick; diff --git a/frontend/src/components/LogDetail/index.tsx b/frontend/src/components/LogDetail/index.tsx index 0794ead980..650d474736 100644 --- a/frontend/src/components/LogDetail/index.tsx +++ b/frontend/src/components/LogDetail/index.tsx @@ -6,10 +6,13 @@ import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd'; import { RadioChangeEvent } from 'antd/lib'; import cx from 'classnames'; import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator'; +import { LOCALSTORAGE } from 'constants/localStorage'; import ContextView from 'container/LogDetailedView/ContextView/ContextView'; import JSONView from 'container/LogDetailedView/JsonView'; import Overview from 'container/LogDetailedView/Overview'; import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils'; +import { useOptionsMenu } from 'container/OptionsMenu'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useNotifications } from 'hooks/useNotifications'; import { @@ -21,9 +24,10 @@ import { TextSelect, X, } from 'lucide-react'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { useCopyToClipboard } from 'react-use'; import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData'; +import { DataSource, StringOperators } from 'types/common/queryBuilder'; import { VIEW_TYPES, VIEWS } from './constants'; import { LogDetailProps } from './LogDetail.interfaces'; @@ -36,6 +40,7 @@ function LogDetail({ onClickActionItem, selectedTab, isListViewPanel = false, + listViewPanelSelectedFields, }: LogDetailProps): JSX.Element { const [, copyToClipboard] = useCopyToClipboard(); const [selectedView, setSelectedView] = useState(selectedTab); @@ -45,6 +50,19 @@ function LogDetail({ const [contextQuery, setContextQuery] = useState(); const [filters, setFilters] = useState(null); const [isEdit, setIsEdit] = useState(false); + const { initialDataSource, stagedQuery } = useQueryBuilder(); + + const listQuery = useMemo(() => { + if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null; + + return stagedQuery.builder.queryData.find((item) => !item.disabled) || null; + }, [stagedQuery]); + + const { options } = useOptionsMenu({ + storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS, + dataSource: initialDataSource || DataSource.LOGS, + aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP, + }); const isDarkMode = useIsDarkMode(); @@ -192,6 +210,8 @@ function LogDetail({ onAddToQuery={onAddToQuery} onClickActionItem={onClickActionItem} isListViewPanel={isListViewPanel} + selectedOptions={options} + listViewPanelSelectedFields={listViewPanelSelectedFields} /> )} {selectedView === VIEW_TYPES.JSON && } diff --git a/frontend/src/container/LogDetailedView/Overview.tsx b/frontend/src/container/LogDetailedView/Overview.tsx index cf98ac11a9..9054217c33 100644 --- a/frontend/src/container/LogDetailedView/Overview.tsx +++ b/frontend/src/container/LogDetailedView/Overview.tsx @@ -12,9 +12,11 @@ import { Typography, } from 'antd'; import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC'; +import { OptionsQuery } from 'container/OptionsMenu/types'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { ChevronDown, ChevronRight, Search } from 'lucide-react'; import { ReactNode, useState } from 'react'; +import { IField } from 'types/api/logs/fields'; import { ILog } from 'types/api/logs/log'; import { ActionItemProps } from './ActionItem'; @@ -23,6 +25,8 @@ import TableView from './TableView'; interface OverviewProps { logData: ILog; isListViewPanel?: boolean; + selectedOptions: OptionsQuery; + listViewPanelSelectedFields?: IField[] | null; } type Props = OverviewProps & @@ -34,6 +38,8 @@ function Overview({ onAddToQuery, onClickActionItem, isListViewPanel = false, + selectedOptions, + listViewPanelSelectedFields, }: Props): JSX.Element { const [isWrapWord, setIsWrapWord] = useState(true); const [isSearchVisible, setIsSearchVisible] = useState(false); @@ -200,6 +206,8 @@ function Overview({ fieldSearchInput={fieldSearchInput} onClickActionItem={onClickActionItem} isListViewPanel={isListViewPanel} + selectedOptions={selectedOptions} + listViewPanelSelectedFields={listViewPanelSelectedFields} /> ), @@ -213,6 +221,7 @@ function Overview({ Overview.defaultProps = { isListViewPanel: false, + listViewPanelSelectedFields: null, }; export default Overview; diff --git a/frontend/src/container/LogDetailedView/TableView.tsx b/frontend/src/container/LogDetailedView/TableView.tsx index 810c946fb0..58b3779eda 100644 --- a/frontend/src/container/LogDetailedView/TableView.tsx +++ b/frontend/src/container/LogDetailedView/TableView.tsx @@ -6,17 +6,15 @@ import { LinkOutlined } from '@ant-design/icons'; import { Color } from '@signozhq/design-tokens'; import { Button, Space, Spin, Tooltip, Tree, Typography } from 'antd'; import { ColumnsType } from 'antd/es/table'; -import getLocalStorageApi from 'api/browser/localstorage/get'; -import setLocalStorageApi from 'api/browser/localstorage/set'; import cx from 'classnames'; import AddToQueryHOC, { AddToQueryHOCProps, } from 'components/Logs/AddToQueryHOC'; import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC'; import { ResizeTable } from 'components/ResizeTable'; -import { LOCALSTORAGE } from 'constants/localStorage'; import { OPERATORS } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; +import { OptionsQuery } from 'container/OptionsMenu/types'; import { useIsDarkMode } from 'hooks/useDarkMode'; import history from 'lib/history'; import { fieldSearchFilter } from 'lib/logs/fieldSearch'; @@ -29,12 +27,14 @@ import { generatePath } from 'react-router-dom'; import { Dispatch } from 'redux'; import AppActions from 'types/actions'; import { SET_DETAILED_LOG_DATA } from 'types/actions/logs'; +import { IField } from 'types/api/logs/fields'; import { ILog } from 'types/api/logs/log'; import { ActionItemProps } from './ActionItem'; import FieldRenderer from './FieldRenderer'; import { filterKeyForField, + findKeyPath, flattenObject, jsonToDataNodes, recursiveParseJSON, @@ -47,7 +47,9 @@ const RESTRICTED_FIELDS = ['timestamp']; interface TableViewProps { logData: ILog; fieldSearchInput: string; + selectedOptions: OptionsQuery; isListViewPanel?: boolean; + listViewPanelSelectedFields?: IField[] | null; } type Props = TableViewProps & @@ -60,6 +62,8 @@ function TableView({ onAddToQuery, onClickActionItem, isListViewPanel = false, + selectedOptions, + listViewPanelSelectedFields, }: Props): JSX.Element | null { const dispatch = useDispatch>(); const [isfilterInLoading, setIsFilterInLoading] = useState(false); @@ -71,21 +75,31 @@ function TableView({ >({}); useEffect(() => { - const pinnedAttributesFromLocalStorage = getLocalStorageApi( - LOCALSTORAGE.PINNED_ATTRIBUTES, - ); + const pinnedAttributes: Record = {}; - if (pinnedAttributesFromLocalStorage) { - try { - const parsedPinnedAttributes = JSON.parse(pinnedAttributesFromLocalStorage); - setPinnedAttributes(parsedPinnedAttributes); - } catch (e) { - console.error('Error parsing pinned attributes from local storgage'); - } + if (isListViewPanel) { + listViewPanelSelectedFields?.forEach((val) => { + const path = findKeyPath(logData, val.name, ''); + if (path) { + pinnedAttributes[path] = true; + } + }); } else { - setPinnedAttributes({}); + selectedOptions.selectColumns.forEach((val) => { + const path = findKeyPath(logData, val.key, ''); + if (path) { + pinnedAttributes[path] = true; + } + }); } - }, []); + + setPinnedAttributes(pinnedAttributes); + }, [ + logData, + selectedOptions.selectColumns, + listViewPanelSelectedFields, + isListViewPanel, + ]); const flattenLogData: Record | null = useMemo( () => (logData ? flattenObject(logData) : null), @@ -103,19 +117,6 @@ function TableView({ } }; - const togglePinAttribute = (record: DataType): void => { - if (record) { - const newPinnedAttributes = { ...pinnedAttributes }; - newPinnedAttributes[record.key] = !newPinnedAttributes[record.key]; - setPinnedAttributes(newPinnedAttributes); - - setLocalStorageApi( - LOCALSTORAGE.PINNED_ATTRIBUTES, - JSON.stringify(newPinnedAttributes), - ); - } - }; - const onClickHandler = ( operator: string, fieldKey: string, @@ -201,11 +202,8 @@ function TableView({ 'pin-attribute-icon', pinnedAttributes[record?.key] ? 'pinned' : '', )} - onClick={(): void => { - togglePinAttribute(record); - }} > - + {pinnedAttributes[record?.key] && } ); @@ -380,6 +378,7 @@ function TableView({ TableView.defaultProps = { isListViewPanel: false, + listViewPanelSelectedFields: null, }; interface DataType { diff --git a/frontend/src/container/LogDetailedView/utils.tsx b/frontend/src/container/LogDetailedView/utils.tsx index f5d7d7e325..8418e13893 100644 --- a/frontend/src/container/LogDetailedView/utils.tsx +++ b/frontend/src/container/LogDetailedView/utils.tsx @@ -267,3 +267,27 @@ export const removeEscapeCharacters = (str: string): string => export function removeExtraSpaces(input: string): string { return input.replace(/\s+/g, ' ').trim(); } + +export function findKeyPath( + obj: AnyObject, + targetKey: string, + currentPath = '', +): string | null { + let finalPath = null; + Object.keys(obj).forEach((key) => { + const value = obj[key]; + const newPath = currentPath ? `${currentPath}.${key}` : key; + + if (key === targetKey) { + finalPath = newPath; + } + + if (typeof value === 'object' && value !== null) { + const result = findKeyPath(value, targetKey, newPath); + if (result) { + finalPath = result; + } + } + }); + return finalPath; +} diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx index fef68ee60f..3835386cd3 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx @@ -245,6 +245,7 @@ function LogsPanelComponent({ onAddToQuery={onAddToQuery} onClickActionItem={onAddToQuery} isListViewPanel + listViewPanelSelectedFields={widget?.selectedLogFields} /> );