Skip to content
Open
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
2 changes: 1 addition & 1 deletion frontend/src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const Layout = () => {
<Navbar
logo="theme"
containerWidth={false}
position="static"
position="sticky"
leftSlot={
<Stack
direction="row"
Expand Down
23 changes: 16 additions & 7 deletions frontend/unified/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { ChevronsLeft, List, Table } from "lucide-react";

import WorkflowsDataFetcher from "./components/WorkflowsDataFetcher/WorkflowsDataFetcher";

import { Beamline, WorkflowsViewMode, InstrumentSession } from "./types";
import {
Beamline,
WorkflowsViewMode,
InstrumentSession,
ScanNodeProcessed,
} from "./types";
import {
apolloClientWorkflows,
apolloClientUlimsScans,
Expand All @@ -22,7 +27,10 @@ type AppProps = {
};

export const App: React.FC<AppProps> = ({ session }: AppProps) => {
const [selectedScanIds, setSelectedScanIds] = useState<number[]>([]);
const [selectedScanIds, setSelectedScanIds] = useState<ScanNodeProcessed[]>(
[]
);

const [showParamConfigDrawer, setShowParamConfigDrawer] =
useState<boolean>(false);
const [workflowsViewMode, setWorkflowsViewMode] = useState<WorkflowsViewMode>(
Expand All @@ -48,7 +56,7 @@ export const App: React.FC<AppProps> = ({ session }: AppProps) => {
}, [sessionRef]);

return (
<Box sx={{ display: "flex" }}>
<Box sx={{ display: "flex", height: "100%" }}>
<Drawer
variant="permanent"
sx={{
Expand All @@ -63,8 +71,8 @@ export const App: React.FC<AppProps> = ({ session }: AppProps) => {
<ApolloProvider client={apolloClientUlimsScans}>
<Sidebar
sessionRef={sessionRef}
scanIds={selectedScanIds}
updateSelectedScanIds={setSelectedScanIds}
scanData={selectedScanIds}
updateScanData={setSelectedScanIds}
/>
</ApolloProvider>
</Drawer>
Expand All @@ -84,17 +92,18 @@ export const App: React.FC<AppProps> = ({ session }: AppProps) => {
visit={selectedVisit}
beamline={beamline}
startTime={session.startTime}
scanIds={selectedScanIds}
scanIds={selectedScanIds.map((x) => x.scanNumber)}
/>
</ApolloProvider>
</Suspense>
</Box>
</Drawer>
<Box sx={{ flexGrow: 1 }}>
<Box sx={{ flexGrow: 1, overflow: "auto" }}>
<ApolloProvider client={apolloClientWorkflows}>
<WorkflowsDataFetcher
visit={selectedVisit}
workflowsViewMode={workflowsViewMode}
scanData={selectedScanIds}
/>
</ApolloProvider>
</Box>
Expand Down
22 changes: 19 additions & 3 deletions frontend/unified/src/beamline.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,25 @@ const BEAMLINE_CONFIG: BeamlineConfig[] = [
scanRequest: (v) => v.data?.scan_parameters?.scan_request,
components: [
{
title: "Sample name",
getter: (v) => v.data?.sample_information?.name,
formatter: FORMATTER.NONE,
groupTitle: "Experiment",
children: [
{
title: "Sample name",
getter: (v) => v.data?.sample_information?.name,
formatter: FORMATTER.NONE,
},
{
title: "Experiment type",
getter: (v) => {
const req = v?.scanRequest?.processingRequest?.request;
if (req) {
return Object.keys(req);
}
return null;
},
formatter: (v) => v.join(", "),
},
],
},
{
groupTitle: "Times",
Expand Down
117 changes: 117 additions & 0 deletions frontend/unified/src/components/MetadataDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Typography, Stack, Skeleton, Grid } from "@mui/material";
import type { ScanNodeProcessed } from "../types";
import { getBeamlineConfig, isMetadataComponent } from "../beamline.config";

type MetadataDisplayProp = {
selectedScanData: ScanNodeProcessed[];
condensed?: boolean;
};
export function MetadataDisplay({
selectedScanData,
condensed,
}: MetadataDisplayProp) {
const firstScan = selectedScanData.at(0);

if (!firstScan) {
return <Typography>No scans selected</Typography>;
}

const schema = firstScan.dataSchema;

const config = getBeamlineConfig(schema);
const title = makeTitle(selectedScanData);
const multipleSelected = selectedScanData.length > 1;

let maxColumns = 1;
let components = config?.components;
if (condensed) {
// If condensed, limit the data display to the first two groups,
// and place each group into its own row
const columns = config?.components.map((x) => {
if (!isMetadataComponent(x)) {
return x.children.length;
} else {
return 1;
}
});
maxColumns = columns ? Math.max(...columns) : 1;
components = components?.slice(0, 2);
}

return (
<Stack spacing={2}>
<Typography variant="h5">{!title ? <Skeleton /> : title}</Typography>
<Grid container spacing={2} columns={maxColumns}>
{components &&
firstScan &&
!multipleSelected &&
components.map((x) => {
if (isMetadataComponent(x)) {
const val = x.getter(firstScan);
return (
<Grid size={1}>
<SingleMetadata
label={x?.title ? x.title : ""}
value={val ? x.formatter(val) : "-"}
/>
</Grid>
);
} else {
return (
// Draw containers
<Grid container spacing={0} size={maxColumns}>
{x.children.map((y) => {
const val = y.getter(firstScan);
return (
<Grid size={1}>
<SingleMetadata
label={y?.title ? y.title : ""}
value={val ? y.formatter(val) : "-"}
/>
</Grid>
);
})}
</Grid>
);
}
})}
</Grid>
</Stack>
);
}

type SingleMetadataProp = {
label: string;
value: string;
};
export function SingleMetadata({ label, value }: SingleMetadataProp) {
return (
<Stack>
<Typography
variant="overline"
color="text.tertiary"
sx={{ overflow: "auto" }}
>
{label}
</Typography>
<Typography variant="body2" sx={{ overflow: "auto" }}>
{value}
</Typography>
</Stack>
);
}

export function makeTitle(scanData: ScanNodeProcessed[]): string | null {
if (scanData.length === 0) {
return null;
} else if (scanData.length === 1) {
const scanNumber = scanData.at(0)?.scanNumber;
if (scanNumber) {
return `${scanNumber}`;
} else {
return null;
}
} else {
return `Selected ${scanData.length} files`;
}
}
11 changes: 8 additions & 3 deletions frontend/unified/src/components/ScanSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ const columns = [

type ScanSelectorProp = {
sessionRef: string;
onScanSelect: (scans: ScanNodeProcessed[]) => void;
updateScanData: (scans: ScanNodeProcessed[]) => void;
};
export function ScanSelector({ sessionRef, onScanSelect }: ScanSelectorProp) {
export function ScanSelector({ sessionRef, updateScanData }: ScanSelectorProp) {
const tableContainerRef = useRef<HTMLDivElement>(null); //we can get access to the underlying TableContainer element and react to its scroll events

const FETCH_SIZE = 30;
Expand Down Expand Up @@ -218,7 +218,7 @@ export function ScanSelector({ sessionRef, onScanSelect }: ScanSelectorProp) {
const selectedData = selectedIdx
.filter((x) => rowSelection[x])
.map((x) => table.getRow(x).original);
onScanSelect(selectedData);
updateScanData(selectedData);

// `onScanSelect` should not be placed into dependency list as it causes an infinite loop
}, [table.getState().rowSelection]);
Expand All @@ -244,5 +244,10 @@ export function ScanSelector({ sessionRef, onScanSelect }: ScanSelectorProp) {
prevDesc.current = isDescSort(sorting);
}, [sorting, client, fetchMore, sessionRef]);

// Clear row selection when session ref changes
useEffect(() => {
setRowSelection({});
}, [sessionRef]);

return <MaterialReactTable table={table} />;
}
39 changes: 30 additions & 9 deletions frontend/unified/src/components/ScanSelectorCombined.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { Stack, ToggleButtonGroup, ToggleButton } from "@mui/material";
import { useState } from "react";
import { ScanSelector } from "./ScanSelector";
import { ScanSelector as ScanSelectorLegacy } from "./ScanSelectorLegacy";
import { ScanNodeProcessed } from "../types";

type ScanSelectorCombinedProps = {
sessionRef: string;
scanIds: number[];
setScanIds: (v: number[]) => void;
scanData: ScanNodeProcessed[];
updateScanData: (v: ScanNodeProcessed[]) => void;
};
export const ScanSelectorCombined = ({
sessionRef,
scanIds,
setScanIds,
scanData,
updateScanData,
}: ScanSelectorCombinedProps) => {
const [selectorType, setSelectorType] = useState("legacy");

Expand All @@ -35,13 +36,33 @@ export const ScanSelectorCombined = ({
</ToggleButtonGroup>

{selectorType === "new" ? (
<ScanSelector
onScanSelect={(v) => setScanIds(v.map((x) => x.scanNumber))}
sessionRef={sessionRef}
/>
<ScanSelector updateScanData={updateScanData} sessionRef={sessionRef} />
) : (
<ScanSelectorLegacy scanIds={scanIds} setScanIds={setScanIds} />
<ScanSelectorLegacy
scanIds={scanData.map((x) => x.scanNumber)}
setScanIds={(v) => updateScanData(generateScanData(v))}
/>
)}
</Stack>
);
};

// Generates dummy detailed scan data when using the legacy
// scan selector
const generateScanData = (scanIds: number[]): ScanNodeProcessed[] => {
return scanIds.map((x) => {
return {
__typename: "Dataset",
scanNumber: x,
dataSchema: "",
description: null,
id: -1,
title: "Title",
beamline: null,
startTime: null,
endTime: null,
scanRequest: null,
data: {},
};
});
};
13 changes: 7 additions & 6 deletions frontend/unified/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Navbar } from "@diamondlightsource/sci-react-ui";
import { Box, Typography, Stack } from "@mui/material";
import { ScanSelectorCombined } from "./ScanSelectorCombined";
import { ScanNodeProcessed } from "../types";

type SidebarProps = {
scanIds: number[];
updateSelectedScanIds: (_: number[]) => void;
scanData: ScanNodeProcessed[];
updateScanData: (_: ScanNodeProcessed[]) => void;
sessionRef: string;
};

export const Sidebar: React.FC<SidebarProps> = ({
scanIds,
updateSelectedScanIds,
scanData,
updateScanData,
sessionRef,
}: SidebarProps) => {
return (
Expand All @@ -37,8 +38,8 @@ export const Sidebar: React.FC<SidebarProps> = ({
This is to ensure infinite scroll works correctly. */}
<Box sx={{ height: "100%", minHeight: 0 }}>
<ScanSelectorCombined
scanIds={scanIds}
setScanIds={updateSelectedScanIds}
scanData={scanData}
updateScanData={updateScanData}
sessionRef={sessionRef}
/>
</Box>
Expand Down
Loading
Loading