diff --git a/esp/src/src-react/components/Metrics.tsx b/esp/src/src-react/components/Metrics.tsx index 0cb9a7aff41..3d1f91cc2bd 100644 --- a/esp/src/src-react/components/Metrics.tsx +++ b/esp/src/src-react/components/Metrics.tsx @@ -15,7 +15,7 @@ import { HolyGrail } from "../layouts/HolyGrail"; import { AutosizeComponent, AutosizeHpccJSComponent } from "../layouts/HpccJSAdapter"; import { DockPanel, DockPanelItem, ResetableDockPanel } from "../layouts/DockPanel"; import { LayoutStatus, MetricGraph, MetricGraphWidget, isGraphvizWorkerResponse, layoutCache } from "../util/metricGraph"; -import { pushUrl } from "../util/history"; +import { pushUrl as _pushUrl } from "../util/history"; import { debounce } from "../util/throttle"; import { ErrorBoundary } from "../util/errorBoundary"; import { ShortVerticalDivider } from "./Common"; @@ -45,6 +45,7 @@ interface MetricsProps { queryId?: string; parentUrl?: string; selection?: string; + fullscreen?: boolean; } export const Metrics: React.FunctionComponent = ({ @@ -52,7 +53,8 @@ export const Metrics: React.FunctionComponent = ({ querySet = "", queryId = "", parentUrl = `/workunits/${wuid}/metrics`, - selection + selection, + fullscreen = false }) => { const [_uiState, _setUIState] = React.useState({ ...defaultUIState }); const [selectedMetricsSource, setSelectedMetricsSource] = React.useState(""); @@ -63,7 +65,6 @@ export const Metrics: React.FunctionComponent = ({ const [showMetricOptions, setShowMetricOptions] = React.useState(false); const [dockpanel, setDockpanel] = React.useState(); const [trackSelection, setTrackSelection] = React.useState(true); - const [fullscreen, setFullscreen] = React.useState(false); const [hotspots, setHotspots] = React.useState(""); const [lineage, setLineage] = React.useState([]); const [selectedLineage, setSelectedLineage] = React.useState(); @@ -96,10 +97,24 @@ export const Metrics: React.FunctionComponent = ({ }).catch(err => logger.error(err)); }, [wuid]); + const pushUrl = React.useCallback((selection?: string, fullscreen?: boolean) => { + const selectionStr = selection?.length ? `/${selection}` : ""; + const fullscreenStr = fullscreen ? "?fullscreen" : ""; + _pushUrl(`${parentUrl}${selectionStr}${fullscreenStr}`); + }, [parentUrl]); + + const pushSelectionUrl = React.useCallback((selection: string) => { + pushUrl(selection, fullscreen); + }, [fullscreen, pushUrl]); + + const pushFullscreenUrl = React.useCallback((fullscreen: boolean) => { + pushUrl(selection, fullscreen); + }, [pushUrl, selection]); + const onHotspot = React.useCallback(() => { setSelectedMetricsSource("hotspot"); - pushUrl(`${parentUrl}/${selection}`); - }, [parentUrl, selection]); + pushSelectionUrl(selection); + }, [pushSelectionUrl, selection]); // Timeline --- const timeline = useConst(() => new WUTimelineNoFetch() @@ -114,11 +129,11 @@ export const Metrics: React.FunctionComponent = ({ timeline.selection([]); setSelectedMetricsSource("scopesTable"); setScopeFilter(`name:${row[7].__hpcc_id}`); - pushUrl(`${parentUrl}/${row[7].id}`); + pushSelectionUrl(row[7].id); } }, true) ; - }, [parentUrl, timeline]); + }, [pushSelectionUrl, timeline]); React.useEffect(() => { if (view.showTimeline) { @@ -142,10 +157,10 @@ export const Metrics: React.FunctionComponent = ({ .on("selectionChanged", () => { const selection = metricGraphWidget.selection().filter(id => metricGraph.item(id)).map(id => metricGraph.item(id).id); setSelectedMetricsSource("metricGraphWidget"); - pushUrl(`${parentUrl}/${selection.join(",")}`); + pushSelectionUrl(selection.join(",")); }, true) ; - }, [metricGraph, metricGraphWidget, parentUrl]); + }, [metricGraph, metricGraphWidget, pushSelectionUrl]); React.useEffect(() => { metricGraph.load(metrics); @@ -301,8 +316,8 @@ export const Metrics: React.FunctionComponent = ({ const scopesSelectionChanged = React.useCallback((source: SelectedMetricsSource, selection: IScope[]) => { setSelectedMetricsSource(source); - pushUrl(`${parentUrl}/${selection.map(row => row.__lparam?.id ?? row.id).join(",")}`); - }, [parentUrl]); + pushSelectionUrl(selection.map(row => row.__lparam?.id ?? row.id).join(",")); + }, [pushSelectionUrl]); const scopesTable = useConst(() => new ScopesTable() .multiSelect(true) @@ -520,9 +535,9 @@ export const Metrics: React.FunctionComponent = ({ { key: "divider_2", itemType: ContextualMenuItemType.Divider, onRender: () => }, { key: "fullscreen", title: nlsHPCC.MaximizeRestore, iconProps: { iconName: fullscreen ? "ChromeRestore" : "FullScreen" }, - onClick: () => setFullscreen(!fullscreen) + onClick: () => pushFullscreenUrl(!fullscreen) } - ], [dot, formatColumns, fullscreen, metrics, wuid]); + ], [dot, formatColumns, fullscreen, metrics, pushFullscreenUrl, wuid]); const setShowMetricOptionsHook = React.useCallback((show: boolean) => { setShowMetricOptions(show); diff --git a/esp/src/src-react/components/WorkunitDetails.tsx b/esp/src/src-react/components/WorkunitDetails.tsx index f30bbf974ae..1b890826fce 100644 --- a/esp/src/src-react/components/WorkunitDetails.tsx +++ b/esp/src/src-react/components/WorkunitDetails.tsx @@ -40,7 +40,7 @@ interface WorkunitDetailsProps { parentUrl?: string; tab?: string; state?: { outputs?: string, metrics?: string, resources?: string, helpers?: string, eclsummary?: string }; - queryParams?: { outputs?: StringStringMap, inputs?: StringStringMap, resources?: StringStringMap, helpers?: StringStringMap, logs?: StringStringMap }; + queryParams?: { summary?: StringStringMap, outputs?: StringStringMap, inputs?: StringStringMap, metrics?: StringStringMap, resources?: StringStringMap, helpers?: StringStringMap, logs?: StringStringMap }; } export const WorkunitDetails: React.FunctionComponent = ({ @@ -180,7 +180,7 @@ export const WorkunitDetails: React.FunctionComponent = ({
- + @@ -205,7 +205,7 @@ export const WorkunitDetails: React.FunctionComponent = ({ }> - + diff --git a/esp/src/src-react/components/WorkunitSummary.tsx b/esp/src/src-react/components/WorkunitSummary.tsx index 8e74c0b008c..a519dd1a36c 100644 --- a/esp/src/src-react/components/WorkunitSummary.tsx +++ b/esp/src/src-react/components/WorkunitSummary.tsx @@ -9,6 +9,7 @@ import { useConfirm } from "../hooks/confirm"; import { useWorkunit, useWorkunitExceptions } from "../hooks/workunit"; import { ReflexContainer, ReflexElement, ReflexSplitter } from "../layouts/react-reflex"; import { pushUrl, replaceUrl } from "../util/history"; +import { HolyGrail } from "../layouts/HolyGrail"; import { ShortVerticalDivider } from "./Common"; import { TableGroup } from "./forms/Groups"; import { PublishQueryForm } from "./forms/PublishQuery"; @@ -26,10 +27,12 @@ interface MessageBarContent { interface WorkunitSummaryProps { wuid: string; + fullscreen?: boolean; } export const WorkunitSummary: React.FunctionComponent = ({ - wuid + wuid, + fullscreen = false }) => { const [workunit, , , , refresh] = useWorkunit(wuid, true); @@ -171,6 +174,13 @@ export const WorkunitSummary: React.FunctionComponent = ({ }, ], [_protected, canDelete, canDeschedule, canReschedule, canSave, description, jobname, refresh, refreshSavings, setShowDeleteConfirm, showMessageBar, workunit, wuid]); + const rightButtons = React.useMemo((): ICommandBarItemProps[] => [ + { + key: "fullscreen", title: nlsHPCC.MaximizeRestore, iconProps: { iconName: fullscreen ? "ChromeRestore" : "FullScreen" }, + onClick: () => pushUrl(`/workunits/${wuid}${fullscreen ? "" : "?fullscreen"}`) + } + ], [fullscreen, wuid]); + const serviceNames = React.useMemo(() => { return workunit?.ServiceNames?.Item?.join("\n") || ""; }, [workunit?.ServiceNames?.Item]); @@ -190,68 +200,70 @@ export const WorkunitSummary: React.FunctionComponent = ({ }, 0) || 0; }, [exceptions]); - return <> - - -
- - - - {messageBarContent && - - {messageBarContent.message} - - } - - - -
- -
-
- 0 ? Math.round((potentialSavings / totalCosts) * 10000) / 100 : 0}%)`, readonly: true }, - "compileCost": { label: nlsHPCC.CompileCost, type: "string", value: `${formatCost(workunit?.CompileCost)}`, readonly: true }, - "executeCost": { label: nlsHPCC.ExecuteCost, type: "string", value: `${formatCost(workunit?.ExecuteCost)}`, readonly: true }, - "fileAccessCost": { label: nlsHPCC.FileAccessCost, type: "string", value: `${formatCost(workunit?.FileAccessCost)}`, readonly: true }, - "protected": { label: nlsHPCC.Protected, type: "checkbox", value: _protected }, - "cluster": { label: nlsHPCC.Cluster, type: "string", value: workunit?.Cluster, readonly: true }, - "totalClusterTime": { label: nlsHPCC.TotalClusterTime, type: "string", value: workunit?.TotalClusterTime ? workunit?.TotalClusterTime : "0.00", readonly: true }, - "abortedBy": { label: nlsHPCC.AbortedBy, type: "string", value: workunit?.AbortBy, readonly: true }, - "abortedTime": { label: nlsHPCC.AbortedTime, type: "string", value: workunit?.AbortTime, readonly: true }, - "ServiceNamesCustom": { label: nlsHPCC.Services, type: "string", value: serviceNames, readonly: true, multiline: true }, - }} onChange={(id, value) => { - switch (id) { - case "jobname": - setJobname(value); - break; - case "description": - setDescription(value); - break; - case "protected": - setProtected(value); - break; - default: - logger.debug(`${id}: ${value}`); - } - }} /> -
-
-
- - - - -
- - - - - ; + return + + +
+ + + + {messageBarContent && + + {messageBarContent.message} + + } + + + +
+ +
+
+ 0 ? Math.round((potentialSavings / totalCosts) * 10000) / 100 : 0}%)`, readonly: true }, + "compileCost": { label: nlsHPCC.CompileCost, type: "string", value: `${formatCost(workunit?.CompileCost)}`, readonly: true }, + "executeCost": { label: nlsHPCC.ExecuteCost, type: "string", value: `${formatCost(workunit?.ExecuteCost)}`, readonly: true }, + "fileAccessCost": { label: nlsHPCC.FileAccessCost, type: "string", value: `${formatCost(workunit?.FileAccessCost)}`, readonly: true }, + "protected": { label: nlsHPCC.Protected, type: "checkbox", value: _protected }, + "cluster": { label: nlsHPCC.Cluster, type: "string", value: workunit?.Cluster, readonly: true }, + "totalClusterTime": { label: nlsHPCC.TotalClusterTime, type: "string", value: workunit?.TotalClusterTime ? workunit?.TotalClusterTime : "0.00", readonly: true }, + "abortedBy": { label: nlsHPCC.AbortedBy, type: "string", value: workunit?.AbortBy, readonly: true }, + "abortedTime": { label: nlsHPCC.AbortedTime, type: "string", value: workunit?.AbortTime, readonly: true }, + "ServiceNamesCustom": { label: nlsHPCC.Services, type: "string", value: serviceNames, readonly: true, multiline: true }, + }} onChange={(id, value) => { + switch (id) { + case "jobname": + setJobname(value); + break; + case "description": + setDescription(value); + break; + case "protected": + setProtected(value); + break; + default: + logger.debug(`${id}: ${value}`); + } + }} /> +
+
+
+ + + + +
+ + + + + } + />; }; diff --git a/esp/src/src-react/routes.tsx b/esp/src/src-react/routes.tsx index bee1c306fd9..e73d78d9823 100644 --- a/esp/src/src-react/routes.tsx +++ b/esp/src/src-react/routes.tsx @@ -25,7 +25,7 @@ const workunitsChildren: Route[] = [ }, { path: "/:Wuid", action: (ctx, params) => import("./components/WorkunitDetails").then(_ => { - return <_.WorkunitDetails wuid={params.Wuid as string} parentUrl={params.parentUrl as string} />; + return <_.WorkunitDetails wuid={params.Wuid as string} parentUrl={params.parentUrl as string} queryParams={{ summary: parseSearch(ctx.search) as any }} />; }) }, {