diff --git a/frontend/src/components/ResultGrid.tsx b/frontend/src/components/ResultGrid.tsx index 36e4a7b..de486d6 100644 --- a/frontend/src/components/ResultGrid.tsx +++ b/frontend/src/components/ResultGrid.tsx @@ -1,9 +1,8 @@ import React, { useEffect, useState } from 'react'; -import { DataGridPremium, GridColDef, GridRenderCellParams } from '@mui/x-data-grid-premium'; -import IconButton from '@mui/material/IconButton'; -import InfoIcon from '@mui/icons-material/Info'; -import { OpenAPI, SamplesService } from '../../openapi'; +import { DataGridPremium, GridColDef } from '@mui/x-data-grid-premium'; +import RunDetails from './RunDetails'; import './SampleImage.css'; +import { OpenAPI, SamplesService } from '../../openapi'; // Extend your image info interface if needed. interface ImageInfo { @@ -97,38 +96,44 @@ interface ResultGridProps { activePgroup: string; } -// Helper function to safely get the number of images. -const getNumberOfImages = (run: ExperimentParameters): number => { - const params = run.beamline_parameters; - if (params.rotation && params.rotation.numberOfImages != null) { - return params.rotation.numberOfImages; - } else if (params.gridScan && params.gridScan.numberOfImages != null) { - return params.gridScan.numberOfImages; - } - return 0; -}; - -// Helper function to determine the experiment type. -const getExperimentType = (run: ExperimentParameters): string => { - const params = run.beamline_parameters; - if (params.rotation && params.rotation.numberOfImages != null && params.rotation.omegaStep != null) { - const numImages = params.rotation.numberOfImages; - const omegaStep = params.rotation.omegaStep; - if ([1, 2, 4].includes(numImages) && omegaStep === 90) { - return "Characterization"; - } - return "Rotation"; - } else if (params.gridScan) { - return "Grid Scan"; - } - return "Rotation"; -}; const ResultGrid: React.FC = ({ activePgroup }) => { const [rows, setRows] = useState([]); const [basePath, setBasePath] = useState(''); + // Helper function to safely get the number of images. + const getNumberOfImages = (run: ExperimentParameters): number => { + const params = run.beamline_parameters; + if (params.rotation && params.rotation.numberOfImages != null) { + return params.rotation.numberOfImages; + } else if (params.gridScan && params.gridScan.numberOfImages != null) { + return params.gridScan.numberOfImages; + } + return 0; + }; + + // Helper function to determine the experiment type. + const getExperimentType = (run: ExperimentParameters): string => { + const params = run.beamline_parameters; + if ( + params.rotation && + params.rotation.numberOfImages != null && + params.rotation.omegaStep != null + ) { + const numImages = params.rotation.numberOfImages; + const omegaStep = params.rotation.omegaStep; + if ([1, 2, 4].includes(numImages) && omegaStep === 90) { + return 'Characterization'; + } + return 'Rotation'; + } else if (params.gridScan) { + return 'Grid Scan'; + } + return 'Rotation'; + }; + useEffect(() => { + // Set OpenAPI.BASE depending on environment mode const mode = import.meta.env.MODE; OpenAPI.BASE = mode === 'test' @@ -141,22 +146,16 @@ const ResultGrid: React.FC = ({ activePgroup }) => { console.error('OpenAPI.BASE is not set. Falling back to a default value.'); OpenAPI.BASE = 'https://default-url.com'; } - - console.log('Environment Mode:', mode); - console.log('Resolved OpenAPI.BASE:', OpenAPI.BASE); - setBasePath(`${OpenAPI.BASE}/`); }, []); useEffect(() => { - console.log('Fetching sample results for active_pgroup:', activePgroup); + // Fetch sample details and construct rows SamplesService.getSampleResultsSamplesResultsGet(activePgroup) .then((response: SampleResult[]) => { - console.log('Response received:', response); const treeRows: TreeRow[] = []; response.forEach((sample) => { - // Add the sample row. const sampleRow: TreeRow = { id: `sample-${sample.sample_id}`, hierarchy: [sample.sample_id], @@ -169,7 +168,6 @@ const ResultGrid: React.FC = ({ activePgroup }) => { }; treeRows.push(sampleRow); - // Add experiment run rows. if (sample.experiment_runs) { sample.experiment_runs.forEach((run) => { const experimentType = getExperimentType(run); @@ -197,25 +195,22 @@ const ResultGrid: React.FC = ({ activePgroup }) => { }); }, [activePgroup]); - // Define the grid columns, including the new processing results column. + // Define the grid columns const columns: GridColDef[] = [ { field: 'sample_name', headerName: 'Sample Name', width: 200, - renderCell: (params) => (params.row.type === 'sample' ? params.value : null), }, { field: 'puck_name', headerName: 'Puck Name', width: 150, - renderCell: (params) => (params.row.type === 'sample' ? params.value : null), }, { field: 'dewar_name', headerName: 'Dewar Name', width: 150, - renderCell: (params) => (params.row.type === 'sample' ? params.value : null), }, { field: 'experimentType', @@ -230,87 +225,77 @@ const ResultGrid: React.FC = ({ activePgroup }) => { renderCell: (params) => (params.row.type === 'run' ? params.value : null), }, { - field: 'processingResults', - headerName: 'Processing Results', - width: 180, + field: 'images', + headerName: 'Images', + width: 300, renderCell: (params) => { - if (params.row.type === 'run') { + const images = params.row.images; + if (images && images.length) { return ( - { - // Placeholder for processing results details. - console.log('Clicked processing details for run', params.row.run_number); - }} - > - - +
+ {images.map((image: ImageInfo) => ( + {image.comment + ))} +
); } return null; }, }, - { - field: 'images', - headerName: 'Images', - width: 300, - renderCell: (params) => { - const imageList: ImageInfo[] = params.row.images; - - if (!imageList || imageList.length === 0) { - return null; - } - - return ( -
- {imageList.map((img) => { - const url = `${basePath}${img.filepath}`; - return ( -
- {img.comment -
- ); - })} -
- ); - }, - }, ]; + const getDetailPanelContent = (params: any) => { + if (params.row.type === 'run') { + return ; + } + return null; + }; + + const getDetailPanelHeight = (params: any) => { + if (params.row.type === 'run') return 300; + return 0; + }; return ( -
- row.hierarchy} - defaultGroupingExpansionDepth={-1} - getRowId={(row) => row.id} - sx={{ - '& .MuiDataGrid-cell': { - overflow: 'visible', - }, - '& .MuiDataGrid-rendererContainer': { - overflow: 'visible', - position: 'relative', - }, - }} - /> + row.id} + autoHeight + treeData + getTreeDataPath={(row: TreeRow) => { + if (row.type === 'run') { + // Include sample_id to make the path globally unique + return [`Sample-${row.sample_id}`, `Run-${row.run_number}`]; + } + // If it's a sample row, it will be at the root + return [`Sample-${row.sample_id}`]; + }} + + defaultGroupingExpansionDepth={-1} + disableColumnMenu + getDetailPanelContent={getDetailPanelContent} + getDetailPanelHeight={getDetailPanelHeight} + sx={{ + '& .MuiDataGrid-cell': { + overflow: 'visible', + }, + }} + /> -
); }; -export default ResultGrid; \ No newline at end of file +export default ResultGrid; + diff --git a/frontend/src/components/RunDetails.tsx b/frontend/src/components/RunDetails.tsx index 60003f3..9f4215e 100644 --- a/frontend/src/components/RunDetails.tsx +++ b/frontend/src/components/RunDetails.tsx @@ -1,6 +1,14 @@ -import {SimpleTreeView, TreeItem} from "@mui/x-tree-view"; -import React from "react"; - +import React from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + IconButton, + Typography, + Grid, +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import { SimpleTreeView, TreeItem } from '@mui/x-tree-view'; interface ExperimentParameters { id: number; @@ -12,73 +20,120 @@ interface ExperimentParameters { manufacturer: string; model: string; type: string; - serial_number: string; - detector_distance_mm: number; - beam_center_x_px: number; - beam_center_y_px: number; - pixel_size_x_um: number; - pixel_size_y_um: number; - number_of_images: number; - exposure_time_s: number; + serialNumber: string; + detectorDistance_mm: number; + beamCenterX_px: number; + beamCenterY_px: number; + pixelSizeX_um: number; + pixelSizeY_um: number; }; - // Add additional fields if needed. + // Include additional parameters as needed. }; + // Optionally, add fields for images and processing results. + images?: Array<{ + id: number; + filepath: string; + comment?: string; + }>; + processingResults?: any; } - - Detector Details}> - Detector Details}> - - - - - - - - - - +interface RunDetailsProps { + run: ExperimentParameters; + onClose: () => void; +} +const RunDetails: React.FC = ({ run }) => { + const { beamline_parameters } = run; + const { synchrotron, beamline, detector } = beamline_parameters; + return ( +
+ + Run {run.run_number} Details + + + Beamline: {beamline} | Synchrotron: {synchrotron} + - - \ No newline at end of file + + Detector Details}> + + + + + + + + + + + + Associated Images + + {run.images && run.images.length > 0 ? ( + + {run.images.map((img) => ( + + {img.comment + + ))} + + ) : ( + No images available. + )} + + + Processing Results + + {run.processingResults ? ( + + {JSON.stringify(run.processingResults, null, 2)} + + ) : ( + + Processing details and results go here. + + )} +
+ ); +}; + +export default RunDetails; \ No newline at end of file