Skip to content

Commit

Permalink
Add code comments (#437)
Browse files Browse the repository at this point in the history
* Add code comments

* Set rows per page to 25

* Update handle row change comment

* Revert radix value
  • Loading branch information
Dagonite authored Feb 7, 2025
1 parent 9fe72c2 commit 7cf65c4
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 44 deletions.
31 changes: 21 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,43 @@ import ValueEditor from './ValueEditor';
import GlobalStyles from './GlobalStyles';
import { clearFailedAuthRequestsQueue, retryFailedAuthRequests } from './api';

// Initialize Google Analytics
// Initialize Google Analytics with the given tracking ID
ReactGA.initialize('G-7XJBCP6P75');
// Track the initial page load event
ReactGA.send({ hitType: 'pageview', page: window.location.pathname });

const App: FC = () => {
// Need to call forceUpdate if SciGateway tells us to rerender but there's no
// forceUpdate in functional components, so this is the hooks equivalent. See
// https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
// Force update mechanism using React's useReducer hook
// This is used to trigger a re-render when necessary (e.g. when SciGateway requests it)
// There is no direct forceUpdate in functional components, so we increment a state variable instead
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, forceUpdate] = React.useReducer((x) => x + 1, 0);

function handler(e: Event): void {
// Attempt to re-render the plugin if we get told to
// Handle custom SciGateway events for triggering actions in the app
const action = (e as CustomEvent).detail;

// If SciGateway requests a plugin re-render, trigger a re-render
if ('scigateway:api:plugin_rerender'.match(action)) {
forceUpdate();
}

// If SciGateway invalidates the token, retry failed authentication requests
if ('scigateway:api:invalidate_token'.match(action)) {
retryFailedAuthRequests();
}

// If SciGateway requests signout, clear the authentication request queue
if ('scigateway:api:signout'.match(action)) {
clearFailedAuthRequestsQueue();
}
}

// Attach event listener for SciGateway events when the component mounts
React.useEffect(() => {
document.addEventListener('scigateway', handler);

// Remove event listener when the component unmounts
return () => {
document.removeEventListener('scigateway', handler);
};
Expand All @@ -48,20 +58,21 @@ const App: FC = () => {
<GlobalStyles>
<Router basename="/fia">
<Switch>
{/* Define application routes */}
<Route exact path="/">
<HomePage />
<HomePage /> {/* Renders the HomePage component on the root path */}
</Route>
<Route path="/instruments">
<Instruments />
<Instruments /> {/* Renders the Instruments page */}
</Route>
<Route path="/reduction-history/ALL">
<JobsAll />
<JobsAll /> {/* Displays all reduction jobs */}
</Route>
<Route path="/reduction-history/:instrumentName">
<JobsGeneral />
<JobsGeneral /> {/* Displays reduction jobs filtered by instrument name */}
</Route>
<Route path="/value-editor/:jobId">
<ValueEditor />
<ValueEditor /> {/* Opens the ValueEditor for a specific job */}
</Route>
</Switch>
</Router>
Expand Down
12 changes: 9 additions & 3 deletions src/ConfigSettings/ConfigSettingsLOQ.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,32 @@ import React from 'react';
import { Box, Button } from '@mui/material';
import { UploadFile, Edit } from '@mui/icons-material';

// Local data
// Local components
import ConfigSettingsGeneral from './ConfigSettingsGeneral';
import FileUploader from './FileUploader';

// API base URL for LOQ-specific requests
const fiaApiUrl = process.env.REACT_APP_FIA_REST_API_URL;
const instrument_url = `${fiaApiUrl}/extras/loq`;

const ConfigSettingsLOQ: React.FC = () => {
// Insert LOQ specifc buttons into the ConfigSettingsGeneral component
// File uploader logic for LOQ
const { selectedFile, uploadMessage, handleFileSelection, handleFileUpload } = FileUploader(instrument_url);

return (
// Render ConfigSettingsGeneral with additional LOQ-specific elements
<ConfigSettingsGeneral onFileUpload={handleFileUpload}>
{/* File upload button */}
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
<Button component="label" variant="contained" startIcon={<UploadFile />}>
Upload file...
<input type="file" multiple hidden onChange={handleFileSelection} />
</Button>
{/* Display upload message if a file is selected */}
{selectedFile && <Box sx={{ mt: 1, ml: 2 }}>{uploadMessage}</Box>}
</Box>

{/* Change script button -- disabled for now */}
{/* Change script button (currently disabled) */}
<Box>
<Button variant="contained" disabled startIcon={<Edit />}>
Change script...
Expand Down
5 changes: 4 additions & 1 deletion src/ConfigSettings/FileUploader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import axios from 'axios';
// React components
import { useState } from 'react';

// Axios components
import axios from 'axios';

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const FileUploader = (instrument_url: string) => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
Expand Down
5 changes: 5 additions & 0 deletions src/GlobalStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import React from 'react';
// Material UI components
import { ThemeProvider, StyledEngineProvider, Theme, createTheme } from '@mui/material/styles';

// Initialize a default theme
let theme: Theme = createTheme();

// Listen for theme changes from SciGateway and update the theme dynamically
document.addEventListener('scigateway', (e) => {
const action = (e as CustomEvent).detail;
// If the event contains theme options, update the theme
if (action.type === 'scigateway:api:send_themeoptions' && action.payload && action.payload.theme) {
theme = action.payload.theme;
}
});

// GlobalStyles component wraps the app in a theme provider
class GlobalStyles extends React.Component<{ children: React.ReactNode }> {
public constructor(props: { children: React.ReactNode }) {
super(props);
Expand All @@ -21,6 +25,7 @@ class GlobalStyles extends React.Component<{ children: React.ReactNode }> {
public render(): React.ReactElement {
return (
<StyledEngineProvider injectFirst>
{/* Apply the theme to all children components */}
<ThemeProvider theme={theme}>{this.props.children}</ThemeProvider>
</StyledEngineProvider>
);
Expand Down
13 changes: 13 additions & 0 deletions src/Instruments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import { instruments } from './InstrumentData';

const Instruments: React.FC = () => {
const theme = useTheme();
// State for tracking expanded instruments
const [expandedIds, setExpandedIds] = React.useState<number[]>([]);
// State for tracking favorite instruments
const [favoriteIds, setFavoriteIds] = React.useState<number[]>([]);

// Toggle expansion state of an instrument
const handleToggleExpand = (id: number, event?: React.MouseEvent): void => {
if (event) {
event.stopPropagation();
Expand All @@ -27,6 +30,7 @@ const Instruments: React.FC = () => {
);
};

// Toggle favorite state of an instrument
const handleToggleFavorite = (id: number, event: React.MouseEvent): void => {
event.stopPropagation();
setFavoriteIds((prevFavoriteIds) =>
Expand All @@ -36,6 +40,7 @@ const Instruments: React.FC = () => {
);
};

// Sort instruments based on favorite status
const sortedInstruments = [...instruments].sort((a, b) => {
if (favoriteIds.includes(a.id) && !favoriteIds.includes(b.id)) {
return -1;
Expand All @@ -48,6 +53,7 @@ const Instruments: React.FC = () => {

return (
<>
{/* Page title */}
<Typography variant="h3" component="h1" style={{ color: theme.palette.text.primary, padding: '20px' }}>
ISIS instruments
</Typography>
Expand All @@ -70,6 +76,7 @@ const Instruments: React.FC = () => {
>
<Box display="flex" alignItems="center" justifyContent="space-between">
<Box display="flex" alignItems="center">
{/* Expand button */}
<IconButton
aria-expanded={expandedIds.includes(instrument.id)}
aria-label="show more"
Expand All @@ -80,6 +87,7 @@ const Instruments: React.FC = () => {
/>
</IconButton>
<Box sx={{ marginLeft: 2 }}>
{/* Instrument name and type */}
<Typography
variant="h6"
component="h1"
Expand All @@ -97,6 +105,7 @@ const Instruments: React.FC = () => {
</Typography>
</Box>
</Box>
{/* Favorite button */}
<IconButton
aria-label="add to favorites"
onClick={(event) => handleToggleFavorite(instrument.id, event)}
Expand All @@ -111,6 +120,7 @@ const Instruments: React.FC = () => {
<Collapse in={expandedIds.includes(instrument.id)} timeout="auto" unmountOnExit>
<Box marginTop={2}>
<Box display="flex" justifyContent="space-between" alignItems="flex-start">
{/* Instrument description */}
<Typography
variant="body2"
paragraph
Expand All @@ -119,6 +129,7 @@ const Instruments: React.FC = () => {
{instrument.description}
</Typography>
<Box sx={{ flex: 1, marginLeft: 4 }}>
{/* List of associated scientists */}
<Typography variant="body2" sx={{ fontWeight: 'bold', color: theme.palette.text.primary }}>
Scientists:
</Typography>
Expand All @@ -131,10 +142,12 @@ const Instruments: React.FC = () => {
</List>
</Box>
</Box>
{/* Link to more information */}
<Link href={instrument.infoPage} target="_blank" rel="noopener" underline="always">
{instrument.infoPage}
</Link>
<Box marginTop={2}>
{/* Button to view reduction history */}
<Button
variant="contained"
component={RouterLink}
Expand Down
49 changes: 32 additions & 17 deletions src/Jobs/JobsAll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,35 @@ import { useHistory, useParams, Link } from 'react-router-dom';
import { useTheme } from '@mui/material/styles';
import { TableCell } from '@mui/material';

// Local data
// Local data and utilities
import JobsBase, { useFetchJobs, useFetchTotalCount, Job, headerStyles } from './JobsBase';

const JobsAll: React.FC = () => {
// Extract instrument name from URL parameters
const { instrumentName } = useParams<{ instrumentName: string }>();

// Retrieve the current theme for styling
const theme = useTheme();

// Hook for handling navigation within the app
const history = useHistory();
const [jobs, setJobs] = useState<Job[]>([]);
const [totalRows, setTotalRows] = useState(0);
const [currentPage, setCurrentPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(25);
const [orderDirection, setOrderDirection] = useState<'asc' | 'desc'>('desc');
const [selectedInstrument, setSelectedInstrument] = useState(instrumentName || 'ALL');
const [orderBy, setOrderBy] = useState<string>('run_start');

// State variables for job data and table pagination
const [jobs, setJobs] = useState<Job[]>([]); // Stores fetched job data
const [totalRows, setTotalRows] = useState(0); // Stores total number of job entries
const [currentPage, setCurrentPage] = useState(0); // Current page in pagination
const [rowsPerPage, setRowsPerPage] = useState(25); // Number of rows displayed per page
const [orderDirection, setOrderDirection] = useState<'asc' | 'desc'>('desc'); // Sorting order (ascending/descending)
const [selectedInstrument, setSelectedInstrument] = useState(instrumentName || 'ALL'); // Selected instrument filter
const [orderBy, setOrderBy] = useState<string>('run_start'); // Column to sort by

// Calculate the offset for API query based on current page
const offset = currentPage * rowsPerPage;

// Construct API query string with pagination and sorting parameters
const query = `limit=${rowsPerPage}&offset=${offset}&order_by=${orderBy}&order_direction=${orderDirection}&include_run=true`;

// Fetch job data and total count using custom hooks
const fetchJobs = useFetchJobs(`/jobs`, query, setJobs);
const fetchTotalCount = useFetchTotalCount(`/jobs/count`, setTotalRows);

Expand All @@ -32,25 +45,27 @@ const JobsAll: React.FC = () => {
const newInstrument = event.target.value;
setSelectedInstrument(newInstrument);
setCurrentPage(0);
history.push(`/reduction-history/${newInstrument}`);
history.push(`/reduction-history/${newInstrument}`); // Navigate to selected instrument's history
}}
jobs={jobs}
totalRows={totalRows}
currentPage={currentPage}
rowsPerPage={rowsPerPage}
handleChangePage={(_, newPage) => setCurrentPage(newPage)}
handleChangeRowsPerPage={(e) => setRowsPerPage(parseInt(e.target.value, 10))}
handleChangePage={(_, newPage) => setCurrentPage(newPage)} // Update page number
handleChangeRowsPerPage={(e) => setRowsPerPage(parseInt(e.target.value, 10))} // Update rows per page based on what the user has selected (10 is the radix, do not confuse for number of rows)
handleSort={(property) => {
const isAsc = orderBy === property && orderDirection === 'asc';
setOrderDirection(isAsc ? 'desc' : 'asc');
setOrderDirection(isAsc ? 'desc' : 'asc'); // Toggle sorting order
setOrderBy(property);
setCurrentPage(0);
setCurrentPage(0); // Reset to first page after sorting
}}
orderBy={orderBy}
orderDirection={orderDirection}
fetchJobs={fetchJobs}
fetchTotalCount={fetchTotalCount}
// Custom table header for instrument column
customHeaders={<TableCell sx={{ width: '10%', ...headerStyles(theme) }}>Instrument</TableCell>}
// Custom rendering of job row cells for instrument name with a clickable link
customRowCells={(job: Job) => (
<TableCell sx={{ width: '10%' }}>
{job.run?.instrument_name ? (
Expand All @@ -60,17 +75,17 @@ const JobsAll: React.FC = () => {
color: theme.palette.mode === 'dark' ? '#86b4ff' : theme.palette.primary.main,
textDecoration: 'none',
}}
onMouseEnter={(e) => (e.currentTarget.style.textDecoration = 'underline')}
onMouseLeave={(e) => (e.currentTarget.style.textDecoration = 'none')}
onMouseEnter={(e) => (e.currentTarget.style.textDecoration = 'underline')} // Underline on hover
onMouseLeave={(e) => (e.currentTarget.style.textDecoration = 'none')} // Remove underline on hover out
>
{job.run.instrument_name}
</Link>
) : (
'Unknown'
'Unknown' // Display 'Unknown' if no instrument name exists
)}
</TableCell>
)}
maxHeight={650}
maxHeight={650} // Limit table height
/>
);
};
Expand Down
Loading

0 comments on commit 7cf65c4

Please sign in to comment.