From 8f1797071b981266fa98c39ec9f8a2acabf99418 Mon Sep 17 00:00:00 2001 From: Artur Sarlo Date: Tue, 20 Jan 2026 22:50:04 +0000 Subject: [PATCH 1/2] Fix AdHoc view to always be rendered regardeless of wrapper conditions --- .../src/components/profiles/ProfilesViews.jsx | 23 +++++++++++++------ .../profiles/ProfilesViewsWrapper.jsx | 7 ++++-- .../components/profiles/ProfilesWrapper.jsx | 10 ++++++-- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/gprofiler/frontend/src/components/profiles/ProfilesViews.jsx b/src/gprofiler/frontend/src/components/profiles/ProfilesViews.jsx index e49dde6d..1dff2603 100644 --- a/src/gprofiler/frontend/src/components/profiles/ProfilesViews.jsx +++ b/src/gprofiler/frontend/src/components/profiles/ProfilesViews.jsx @@ -170,12 +170,17 @@ const ProfilesViews = () => { }, [searchedData, framesSelected]); useEffect(() => { - if (!_.isEmpty(flameGraphData[0]) && flameGraphData[0]?.children?.length === 0) { - setIsFGEmpty(true); - } else { - setIsFGEmpty(false); + // Only update isFGEmpty for views that depend on flamegraph data + // Only adhoc view doesn't depend on flamegraph data and manages its own empty state + const viewsDependingOnFgData = [PROFILES_VIEWS.flamegraph, PROFILES_VIEWS.table, PROFILES_VIEWS.service, PROFILES_VIEWS.html]; + if (viewsDependingOnFgData.includes(viewMode)) { + if (!_.isEmpty(flameGraphData[0]) && flameGraphData[0]?.children?.length === 0) { + setIsFGEmpty(true); + } else { + setIsFGEmpty(false); + } } - }, [flameGraphData, setIsFGEmpty]); + }, [flameGraphData, setIsFGEmpty, viewMode]); useEffect(() => { if (searchValue) { @@ -244,14 +249,18 @@ const ProfilesViews = () => { [setHoverData, searchedData] ); + // Always render adhoc view when selected, regardless of isFGEmpty state + // since adhoc view has its own empty state handling + if (viewMode === PROFILES_VIEWS.adhoc) { + return ; + } + return viewMode === PROFILES_VIEWS.service ? ( ) : viewMode === PROFILES_VIEWS.table ? ( ) : viewMode === PROFILES_VIEWS.html ? ( - ) : viewMode === PROFILES_VIEWS.adhoc ? ( - ) : !isFGEmpty ? ( { const { viewMode } = useContext(SelectorsContext); const isTableView = viewMode === PROFILES_VIEWS.table; const isFlameGraphView = viewMode === PROFILES_VIEWS.flamegraph; + const isAdhocView = viewMode === PROFILES_VIEWS.adhoc; + // Adhoc view doesn't depend on flamegraph data + const shouldDisplayView = isFgDisplayed || isAdhocView; return ( @@ -44,13 +47,13 @@ const ProfilesViewsWrapper = () => {
- {isFgDisplayed && } + {shouldDisplayView && }
{isFlameGraphView && ( { setInstanceType, } = useContext(FgContext); + const { viewMode } = useContext(SelectorsContext); + const { data, error, lastWeekDataError, lastWeekData, isLastWeekDataLoading, loading } = useGetFgData({}); const { metricsData, @@ -108,12 +111,15 @@ const ProfilesWrapper = () => { } }, [instanceTypeData, setInstanceTypeLoading, setInstanceType, instanceTypeDataLoading]); + // Adhoc view doesn't depend on flamegraph data, so always show it when selected + const shouldShowView = isFgDisplayed || viewMode === PROFILES_VIEWS.adhoc; + return ( <> {loading || isLastWeekDataLoading ? ( ) : ( - <>{!isFgDisplayed ? : } + <>{!shouldShowView ? : } )} ); From 9943b791137aa607c5761bfc70e54aa5b0ec5472 Mon Sep 17 00:00:00 2001 From: Artur Sarlo Date: Tue, 20 Jan 2026 23:08:37 +0000 Subject: [PATCH 2/2] Make AdHoc report list collapsible at AdHoc view --- .../views/adhoc/AdhocProfilingView.jsx | 122 +++++++++++------- 1 file changed, 74 insertions(+), 48 deletions(-) diff --git a/src/gprofiler/frontend/src/components/profiles/views/adhoc/AdhocProfilingView.jsx b/src/gprofiler/frontend/src/components/profiles/views/adhoc/AdhocProfilingView.jsx index 1f1cdda0..164d026f 100644 --- a/src/gprofiler/frontend/src/components/profiles/views/adhoc/AdhocProfilingView.jsx +++ b/src/gprofiler/frontend/src/components/profiles/views/adhoc/AdhocProfilingView.jsx @@ -15,7 +15,7 @@ */ import { useContext, useEffect, useState } from 'react'; -import { Box, Typography, Select, MenuItem, FormControl, InputLabel, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Button } from '@mui/material'; +import { Box, Typography, Select, MenuItem, FormControl, InputLabel, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Button, IconButton, Collapse } from '@mui/material'; import { SelectorsContext } from '@/states'; import { FilterTagsContext } from '@/states/filters/FiltersTagsContext'; import useFetchWithRequest from '@/api/useFetchWithRequest'; @@ -24,12 +24,15 @@ import { stringify } from 'query-string'; import { getStartEndDateTimeFromSelection } from '@/api/utils'; import { format } from 'date-fns'; import Flexbox from '@/components/common/layout/Flexbox'; +import Icon from '@/components/common/icon/Icon'; +import { ICONS_NAMES } from '@/components/common/icon/iconsData'; const AdhocProfilingView = () => { const { selectedService, timeSelection, selectedHost } = useContext(SelectorsContext); const { activeFilterTag } = useContext(FilterTagsContext); const [selectedFile, setSelectedFile] = useState(null); const [selectedFileContent, setSelectedFileContent] = useState(null); + const [isTableExpanded, setIsTableExpanded] = useState(true); const timeParams = getStartEndDateTimeFromSelection(timeSelection); @@ -99,65 +102,88 @@ const AdhocProfilingView = () => { return ( - + 0 ? 'grey.100' : 'transparent', + p: filesData && filesData.length > 0 ? 2 : 0, + borderRadius: 1, + cursor: filesData && filesData.length > 0 ? 'pointer' : 'default', + '&:hover': filesData && filesData.length > 0 ? { + backgroundColor: 'grey.200', + } : {} + }} + onClick={() => filesData && filesData.length > 0 && setIsTableExpanded(!isTableExpanded)} + > Adhoc Profiling {filesData && filesData.length > 0 && ( - - Select Flamegraph File - - + <> + + Select Flamegraph File + + + + + + )} {filesData && filesData.length > 0 ? ( - - - - - Timestamp - Hostname - Size - Action - - - - {filesData.map((file) => ( - handleRowClick(file)} - selected={selectedFile?.filename === file.filename} - sx={{ cursor: 'pointer', '&:hover': { backgroundColor: 'action.hover' } }} - > - {format(new Date(file.timestamp), 'yyyy-MM-dd HH:mm:ss')} - {file.hostname || 'N/A'} - {(file.size / 1024).toFixed(2)} KB - - - + + +
+ + + Timestamp + Hostname + Size + Action - ))} - -
-
+ + + {filesData.map((file) => ( + handleRowClick(file)} + selected={selectedFile?.filename === file.filename} + sx={{ cursor: 'pointer', '&:hover': { backgroundColor: 'action.hover' } }} + > + {format(new Date(file.timestamp), 'yyyy-MM-dd HH:mm:ss')} + {file.hostname || 'N/A'} + {(file.size / 1024).toFixed(2)} KB + + + + + ))} + + + + {contentLoading && Loading flamegraph content...} {contentError && Error loading flamegraph content: {contentError.message}} {selectedFileContent ? ( -